misc: Merge branch 'release-staging-v22-0' into stable
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bdbcc2d..189b63f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -144,13 +144,17 @@
 A canonical commit message consists of three parts:
  * A short summary line describing the change. This line starts with one or
    more keywords (found in the MAINTAINERS file) separated by commas followed
-   by a colon and a description of the change. This line should be no more than
-   65 characters long since version control systems usually add a prefix that
-   causes line-wrapping for longer lines.
+   by a colon and a description of the change. This short description is
+   written in the imperative mood, and should say what happens when the patch
+   is applied. Keep it short and simple. Write it in sentence case preferably
+   not ending in a period. This line should be no more than 65 characters long
+   since version control systems usually add a prefix that causes line-wrapping
+   for longer lines.
  * (Optional, but highly recommended) A detailed description. This describes
    what you have done and why. If the change isn't obvious, you might want to
    motivate why it is needed. Lines need to be wrapped to 72 characters or
-   less.
+   less. Leave a blank line between the first short summary line and this
+   detailed description.
  * Tags describing patch metadata. You are highly recommended to use
    tags to acknowledge reviewers for their work. Gerrit will automatically add
    most tags.
diff --git a/KCONFIG.md b/KCONFIG.md
new file mode 100644
index 0000000..9292539
--- /dev/null
+++ b/KCONFIG.md
@@ -0,0 +1,114 @@
+This file explains how to work with gem5's implementation of the kconfig
+configuration system, very similar to what's used by the linux kernel. It talks
+about how to work with the Kconfig files themselves which define what user
+adjustable configuration parameters there are, and how they work and
+interoperate.
+
+This file does *not*:
+
+ * Describe how kconfig works generally. This is well documented elsewhere, for
+    instance [here](
+    https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html):
+ * The settings in gem5's kconfig files. These should be documented with help
+   text within the kconfig files, and in the code where these options are
+   consumed.
+ * The various tools which can manipulate a given configuration. These are
+   documented in gem5's SCons help text.
+
+# kconfiglib
+
+gem5 uses the kconfiglib python library for consuming and manipulating kconfig
+configurations. It is very similar to the kconfig implementation used by the
+linux kernel, but is slightly different and has some small extensions added to
+it. Almost all kconfig documentation written for the kernel's implementation
+should apply here as well, but it may occasionally be necessary to refer to the
+kconfiglib documentation.
+
+Also, because gem5's build system is more modular than kconfig's design
+supports out of the box, particularly for "choice" blocks, we have extended
+kconfiglib and added a "cont_choice" keyword. This keyword is very similar to
+"choice", except "cont_choice" blocks can be re-opened and extended with more
+options further into the config.
+
+This can be used to set up a central point where the user can choose between
+mutually exclusive options, and still allow new Kconfig files to add new
+options without modifying the original source.
+
+Having to choose between mutually exclusive options should be avoided in
+general, but is unavoidable in a few key places in gem5 at the moment. Once
+those areas have been addressed, this keyword may be removed in the future.
+
+# The 'CONF' dict in the SCons environment
+
+In "env" SCons environment in SConscript files, or the "main" environment in
+SConsopts files, can hold many variables which help SCons operate generally,
+like setting what include paths to use, what the compiler command line is, etc.
+These environments each have a 'CONF' sub-dict which holds all the variables
+which are actually used to configure gem5, and not to configure SCons and the
+build process itself.
+
+All variables in this dict are automatically available to include in c++. To
+access the value of env['CONF']['FOO'], you would #include "config/foo.hh".
+Because these variables are in a shared namespace, their names should be unique
+and distinctive.
+
+These values are available in config scripts through the m5.defines.buildEnv
+dict.
+
+# Automatic/measured configuration values.
+
+Some configuration values are not set by the user, and are measured from the
+host environment. These could reflect the availability of a header file,
+library or tool, whether the compiler supports a particular option or language
+feature, etc.
+
+These values should be measured in SConsopts files, and stored in the 'CONF'
+dict described above. Like any other variable in 'CONF', they are then
+available to C++ through generated header files, to config scripts through
+buildEnv, etc. They are also available in the kconfig files themselves through
+a mechanism discussed below.
+
+# Accessing 'CONF' values in Kconfig files.
+
+When the gem5 Kconfig files are processed to either manipulate a configuration
+through a tool, or to apply a configuration to the gem5 build, all the values
+in 'CONF' are temporarily put into environment variables. In the Kconfig files
+themselves, these environment variables can be accessed using $(FOO) syntax,
+which is described in kconfiglib's documentation.
+
+Note that this is slightly different from the kernel's Kconfig syntax, where
+the environment variables would have to be imported in using other keywords
+first.
+
+This is generally used to make automatic/measured settings which were
+determined in SConsopts files available in Kconfig files. They can then be used
+to compute dependencies, or to set default values, etc.
+
+# Structure of the Kconfig hierarchy
+
+Unlike SConscript files, gem5 does not find Kconfig files automatically, and
+they are only used if they are included explicitly in other Kconfig files.
+
+Kconfig options should be defined as close as possible to where they are used.
+This makes them easier to find, keeps related functionality grouped
+together in the source tree, and minimizes conflicts from modifying the same
+few, central files when changing unrelated parts of gem5.
+
+When including a Kconfig file in another, you should use the "rsource" keyword
+which is a kconfiglib extension. This lets you include the other file using a
+path which is relative to the current file, and also helps make the kconfig
+files more modular and self contained.
+
+# EXTRAS directories.
+
+The EXTRAS variable can be set to a list of directories which hold additional
+source that should be built into gem5. Because there's no way to know what (if
+any) paths will be in EXTRAS ahead of time, it is not possible to explicitly
+include kconfig files in those directories from a static file.
+
+Instead, gem5's real root Kconfig file, which includes the one in src, is
+automatically generated as part of the build. It uses the kconfiglib extension
+"osource" to optionally source a file called Kconfig in the base of each EXTRAS
+directory after it has sourced gem5's main Kconfig. If you want to add Kconfig
+options to your EXTRAS directory, you can create that file, and then rsource
+any additional internal Kconfig files as needed.
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 5d569b4..d199fed 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,3 +1,118 @@
+# Version 22.0.0.0
+
+gem5 version 22.0 has been slightly delayed, but we a have a very strong release!
+This release has 660 changes from 48 unique contributors.
+While there are not too many big ticket features, the community has done a lot to improve the stablity and add bugfixes to gem5 over this release.
+That said, we have a few cool new features like full system GPU support, a huge number of Arm improvements, and an improved HBM model.
+
+See below for more details!
+
+## New features
+
+- [Arm now models DVM messages for TLBIs and DSBs accurately](https://gem5.atlassian.net/browse/GEM5-1097). This is implemented in the CHI protocol.
+- EL2/EL3 support on by default in ArmSystem
+- HBM controller which supports pseudo channels
+- [Improved Ruby's SimpleNetwork routing](https://gem5.atlassian.net/browse/GEM5-920)
+- Added x86 bare metal workload and better real mode support
+- [Added round-robin arbitration when using multiple prefetchers](https://gem5.atlassian.net/browse/GEM5-1169)
+- [KVM Emulation added for ARM GIGv3](https://gem5.atlassian.net/browse/GEM5-1138)
+- Many improvements to the CHI protocol
+
+## Many RISC-V instructions added
+
+The following RISCV instructions have been added to gem5's RISC-V ISA:
+
+* Zba instructions: add.uw, sh1add, sh1add.uw, sh2add, sh2add.uw, sh3add, sh3add.uw, slli.uw
+* Zbb instructions: andn, orn, xnor, clz, clzw, ctz, ctzw, cpop, cpopw, max, maxu, min, minu, sext.b, sext.h, zext.h, rol, rolw, ror, rori, roriw, rorw, orc.b, rev8
+* Zbc instructions: clmul, clmulh, clmulr
+* Zbs instructions: bclr, bclri, bext, bexti, binv, binvi, bset, bseti
+* Zfh instructions: flh, fsh, fmadd.h, fmsub.h, fnmsub.h, fnmadd.h, fadd.h, fsub.h, fmul.h, fdiv.h, fsqrt.h, fsgnj.h, fsgnjn.h, fsgnjx.h, fmin.h, fmax.h, fcvt.s.h, fcvt.h.s, fcvt.d.h, fcvt.h.d, fcvt.w.h, fcvt.h.w, fcvt.wu.h, fcvt.h.wu
+
+### Improvements to the stdlib automatic resource downloader
+
+The gem5 standard library's downloader has been re-engineered to more efficiently obtain the `resources.json` file.
+It is now cached instead of retrieved on each resource retrieval.
+
+The `resources.json` directory has been moved to a more permament URL at <http://resources.gem5.org/resources.json>.
+
+Tests have also been added to ensure the resources module continues to function correctly.
+
+### gem5 in SystemC support revamped
+
+The gem5 in SystemC has been revamped to accomodate new research needs.
+These changes include stability improvements and bugs fixes.
+The gem5 testing suite has also been expanded to include gem5 in SystemC tests.
+
+### Improved GPU support
+
+Users may now simulate an AMD GPU device in full system mode using the ROCm 4.2 compute stack.
+Until v21.2, gem5 only supported GPU simulation in Syscall-Emulation mode with ROCm 4.0.
+See [`src/gpu-fs/README.md`](https://gem5.googlesource.com/public/gem5-resources/+/refs/heads/stable/src/gpu-fs/) in gem5-resources and example scripts in [`configs/example/gpufs/`](https://gem5.googlesource.com/public/gem5/+/refs/tags/v22.0.0.0/configs/example/gpufs/) for example scripts which run GPU full system simulations.
+
+A [GPU Ruby random tester has been added](https://gem5-review.googlesource.com/c/public/gem5/+/59272) to help validate the correctness of the CPU and GPU Ruby coherence protocols as part of every kokoro check-in.
+This helps validate the correctness of the protocols before new changes are checked in.
+Currently the tester focuses on the protocols used with the GPU, but the ideas are extensible to other protocols.
+The work is based on "Autonomous Data-Race-Free GPU Testing", IISWC 2019, Tuan Ta, Xianwei Zhang, Anthony Gutierrez, and Bradford M. Beckmann.
+
+### An Arm board has been added to the gem5 Standard Library
+
+Via [this change](https://gem5-review.googlesource.com/c/public/gem5/+/58910), an ARM Board, `ArmBoard`, has been added to the gem5 standard library.
+This allows for an ARM system to be run using the gem5 stdlib components.
+
+An example gem5 configuration script using this board can be found in `configs/example/gem5_library/arm-ubuntu-boot-exit.py`.
+
+### `createAddrRanges` now supports NUMA configurations
+
+When the system is configured for NUMA, it has multiple memory ranges, and each memory range is mapped to a corresponding NUMA node. For this, the change enables `createAddrRanges` to map address ranges to only a given HNFs.
+
+Jira ticker here: https://gem5.atlassian.net/browse/GEM5-1187.
+
+## API (user-facing) changes
+
+### CPU model types are no longer simply the model name, but they are specialized for each ISA
+
+For instance, the `O3CPU` is now the `X86O3CPU` and `ArmO3CPU`, etc.
+This requires a number of changes if you have your own CPU models.
+See https://gem5-review.googlesource.com/c/public/gem5/+/52490 for details.
+
+Additionally, this requires changes in any configuration script which inherits from the old CPU types.
+
+In many cases, if there is only a single ISA compiled the old name will still work.
+However, this is not 100% true.
+
+Finally, `CPU_MODELS` is no longer a parameter in `build_opts/`.
+Now, if you want to compile a CPU model for a particular ISA you will have to add a new file for the CPU model in the `arch/` directory.
+
+### Many changes in the CPU and ISA APIs
+
+If you have any specialized CPU models or any ISAs which are not in the mainline, expect many changes when rebasing on this release.
+
+- No longer use read/setIntReg (e.g., see https://gem5-review.googlesource.com/c/public/gem5/+/49766)
+- InvalidRegClass has changed (e.g., see https://gem5-review.googlesource.com/c/public/gem5/+/49745)
+- All of the register classes have changed (e.g., see https://gem5-review.googlesource.com/c/public/gem5/+/49764/)
+- `initiateSpecialMemCmd` renamed to `initiateMemMgmtCmd` to generalize to other command beyond HTM (e.g., DVM/TLBI)
+- `OperandDesc` class added (e.g., see https://gem5-review.googlesource.com/c/public/gem5/+/49731)
+- Many cases of `TheISA` have been removed
+
+## Bug Fixes
+
+- [Fixed RISC-V call/ret instruction decoding](https://gem5-review.googlesource.com/c/public/gem5/+/58209). The fix adds IsReturn` and `IsCall` flags for RISC-V jump instructions by defining a new `JumpConstructor` in "standard.isa". Jira Ticket here: https://gem5.atlassian.net/browse/GEM5-1139.
+- [Fixed x86 Read-Modify-Write behavior in multiple timing cores with classic caches](https://gem5-review.googlesource.com/c/public/gem5/+/55744). Jira Ticket here: https://gem5.atlassian.net/browse/GEM5-1105.
+- [The circular buffer for the O3 LSQ has been fixed](https://gem5-review.googlesource.com/c/public/gem5/+/58649). This issue affected running the O3 CPU with large workloaders. Jira Ticket here: https://gem5.atlassian.net/browse/GEM5-1203.
+- [Removed "memory-leak"-like error in RISC-V lr/sc implementation](https://gem5-review.googlesource.com/c/public/gem5/+/55663). Jira issue here: https://gem5.atlassian.net/browse/GEM5-1170.
+- [Resolved issues with Ruby's memtest](https://gem5-review.googlesource.com/c/public/gem5/+/56811). In gem5 v21.2, If the size of the address range was smaller than the maximum number of outstandnig requests allowed downstream, the tester would get stuck trying to find a unique address. This has been resolved.
+
+## Build-related changes
+
+- Variable in `env` in the SConscript files now requires you to use `env['CONF']` to access them. Anywhere that `env['<VARIABLE>']` appeared should noe be `env['CONF']['<VARIABLE>']`
+- Internal build files are now in a per-target `gem5.build` directory
+- All build variable are per-target and there are no longer any shared variables.
+
+## Other changes
+
+- New bootloader is required for Arm VExpress_GEM5_Foundation platform. See https://gem5.atlassian.net/browse/GEM5-1222 for details.
+- The MemCtrl interface has been updated to use more inheritance to make extending it to other memory types (e.g., HBM pseudo channels) easier.
+
 # Version 21.2.1.1
 
 **[HOTFIX]** In order to ensure v21 of gem5 remains compatible with future changes, the gem5 stdlib downloader has been updated to obtain the resources.json file from <https://resources.gem5.org/resources.json>.
diff --git a/src/cpu/minor/SConsopts b/SConsopts
similarity index 76%
copy from src/cpu/minor/SConsopts
copy to SConsopts
index 16ff599..bb2de86 100644
--- a/src/cpu/minor/SConsopts
+++ b/SConsopts
@@ -1,6 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2012-2014 ARM Limited
+# Copyright (c) 2013, 2015-2020 ARM Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -12,6 +10,11 @@
 # unmodified and in its entirety in all distributions of the software,
 # modified or unmodified, in source code or in binary form.
 #
+# Copyright (c) 2011 Advanced Micro Devices, Inc.
+# Copyright (c) 2009 The Hewlett-Packard Development Company
+# Copyright (c) 2004-2005 The Regents of The University of Michigan
+# All rights reserved.
+#
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met: redistributions of source code must retain the above copyright
@@ -35,6 +38,16 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+import os
+import os.path
+
+from gem5_scons import warning
+
 Import('*')
 
-main.Append(ALL_CPU_MODELS=['MinorCPU'])
+sticky_vars.AddVariables(
+    ('BATCH', 'Use batch pool for build and tests', False),
+    ('BATCH_CMD', 'Batch pool submission command name', 'qdo'),
+    ('M5_BUILD_CACHE', 'Cache built objects in this directory', False),
+    ('USE_EFENCE', 'Link with Electric Fence malloc debugger', False),
+    )
diff --git a/SConstruct b/SConstruct
index ceeb1ba..f1f1c64 100755
--- a/SConstruct
+++ b/SConstruct
@@ -80,17 +80,28 @@
 import os
 import sys
 
-from os import mkdir, environ
+from os import mkdir, remove, environ
 from os.path import abspath, dirname, expanduser
 from os.path import isdir, isfile
 from os.path import join, split
 
+import logging
+logging.basicConfig()
+
 # SCons imports
 import SCons
 import SCons.Node
 import SCons.Node.FS
 import SCons.Tool
 
+if getattr(SCons, '__version__', None) in ('3.0.0', '3.0.1'):
+    # Monkey patch a fix which appears in version 3.0.2, since we only
+    # require version 3.0.0
+    def __hash__(self):
+        return hash(self.lstr)
+    import SCons.Subst
+    SCons.Subst.Literal.__hash__ = __hash__
+
 
 ########################################################################
 #
@@ -98,6 +109,8 @@
 #
 ########################################################################
 
+linker_options = ('bfd', 'gold', 'lld', 'mold')
+
 AddOption('--no-colors', dest='use_colors', action='store_false',
           help="Don't add color to abbreviated scons output")
 AddOption('--with-cxx-config', action='store_true',
@@ -106,7 +119,10 @@
           help='Override which build_opts file to use for defaults')
 AddOption('--ignore-style', action='store_true',
           help='Disable style checking hooks')
-AddOption('--gold-linker', action='store_true', help='Use the gold linker')
+AddOption('--linker', action='store', default=None, choices=linker_options,
+          help=f'Select which linker to use ({", ".join(linker_options)})')
+AddOption('--gold-linker', action='store_const', const='gold', dest='linker',
+          help='Use the gold linker. Deprecated: Use --linker=gold')
 AddOption('--no-compress-debug', action='store_true',
           help="Don't compress debug info in build files")
 AddOption('--with-lto', action='store_true',
@@ -233,47 +249,6 @@
     mkdir(build_root)
 main['BUILDROOT'] = build_root
 
-main.SConsignFile(os.path.join(build_root, "sconsign"))
-
-
-########################################################################
-#
-# Set up global sticky variables... these are common to an entire build
-# tree (not specific to a particular build like X86)
-#
-########################################################################
-
-global_vars_file = os.path.join(build_root, 'variables.global')
-
-global_vars = Variables(global_vars_file, args=ARGUMENTS)
-
-global_vars.AddVariables(
-    ('CC', 'C compiler', environ.get('CC', main['CC'])),
-    ('CXX', 'C++ compiler', environ.get('CXX', main['CXX'])),
-    ('CCFLAGS_EXTRA', 'Extra C and C++ compiler flags', ''),
-    ('GEM5PY_CCFLAGS_EXTRA', 'Extra C and C++ gem5py compiler flags', ''),
-    ('GEM5PY_LINKFLAGS_EXTRA', 'Extra marshal gem5py flags', ''),
-    ('LINKFLAGS_EXTRA', 'Extra linker flags', ''),
-    ('PYTHON_CONFIG', 'Python config binary to use',
-     [ 'python3-config', 'python-config']
-    ),
-    ('PROTOC', 'protoc tool', environ.get('PROTOC', 'protoc')),
-    ('BATCH', 'Use batch pool for build and tests', False),
-    ('BATCH_CMD', 'Batch pool submission command name', 'qdo'),
-    ('M5_BUILD_CACHE', 'Cache built objects in this directory', False),
-    ('EXTRAS', 'Add extra directories to the compilation', '')
-    )
-
-# Update main environment with values from ARGUMENTS & global_vars_file
-global_vars.Update(main)
-Help('''
-Global build variables:
-{help}
-'''.format(help=global_vars.GenerateHelpText(main)), append=True)
-
-# Save sticky variable settings back to current variables file
-global_vars.Save(global_vars_file, main)
-
 
 ########################################################################
 #
@@ -281,16 +256,8 @@
 #
 ########################################################################
 
-# Parse EXTRAS variable to build list of all directories where we're
-# look for sources etc.  This list is exported as extras_dir_list.
 base_dir = Dir('#src').abspath
-if main['EXTRAS']:
-    extras_dir_list = makePathListAbsolute(main['EXTRAS'].split(':'))
-else:
-    extras_dir_list = []
-
 Export('base_dir')
-Export('extras_dir_list')
 
 # the ext directory should be on the #includes path
 main.Append(CPPPATH=[Dir('ext')])
@@ -323,151 +290,6 @@
 if main['GCC'] + main['CLANG'] > 1:
     error('Two compilers enabled at once?')
 
-# Set up default C++ compiler flags
-if main['GCC'] or main['CLANG']:
-    # As gcc and clang share many flags, do the common parts here
-    main.Append(CCFLAGS=['-pipe'])
-    main.Append(CCFLAGS=['-fno-strict-aliasing'])
-
-    # Enable -Wall and -Wextra and then disable the few warnings that
-    # we consistently violate
-    main.Append(CCFLAGS=['-Wall', '-Wundef', '-Wextra',
-                         '-Wno-sign-compare', '-Wno-unused-parameter'])
-
-    # We always compile using C++17
-    main.Append(CXXFLAGS=['-std=c++17'])
-
-    if sys.platform.startswith('freebsd'):
-        main.Append(CCFLAGS=['-I/usr/local/include'])
-        main.Append(CXXFLAGS=['-I/usr/local/include'])
-        # On FreeBSD we need libthr.
-        main.Append(LIBS=['thr'])
-
-    with gem5_scons.Configure(main) as conf:
-        conf.CheckLinkFlag('-Wl,--as-needed')
-    if GetOption('gold_linker'):
-        main.Append(LINKFLAGS='-fuse-ld=gold')
-
-else:
-    error('\n'.join((
-          "Don't know what compiler options to use for your compiler.",
-          "compiler: " + main['CXX'],
-          "version: " + CXX_version.replace('\n', '<nl>') if
-                CXX_version else 'COMMAND NOT FOUND!',
-          "If you're trying to use a compiler other than GCC",
-          "or clang, there appears to be something wrong with your",
-          "environment.",
-          "",
-          "If you are trying to use a compiler other than those listed",
-          "above you will need to ease fix SConstruct and ",
-          "src/SConscript to support that compiler.")))
-
-if main['GCC']:
-    if compareVersions(main['CXXVERSION'], "7") < 0:
-        error('gcc version 7 or newer required.\n'
-              'Installed version:', main['CXXVERSION'])
-
-    with gem5_scons.Configure(main) as conf:
-        # This warning has a false positive in the systemc code in g++ 11.1.
-        conf.CheckCxxFlag('-Wno-free-nonheap-object')
-
-    # Add the appropriate Link-Time Optimization (LTO) flags if `--with-lto` is
-    # set.
-    if GetOption('with_lto'):
-        # g++ uses "make" to parallelize LTO. The program can be overriden with
-        # the environment variable "MAKE", but we currently make no attempt to
-        # plumb that variable through.
-        parallelism = ''
-        if main.Detect('make'):
-            parallelism = '=%d' % GetOption('num_jobs')
-        else:
-            warning('"make" not found, link time optimization will be '
-                    'single threaded.')
-
-        for var in 'LTO_CCFLAGS', 'LTO_LINKFLAGS':
-            # Use the same amount of jobs for LTO as we are running scons with.
-            main[var] = ['-flto%s' % parallelism]
-
-    main.Append(TCMALLOC_CCFLAGS=['-fno-builtin-malloc', '-fno-builtin-calloc',
-                                  '-fno-builtin-realloc', '-fno-builtin-free'])
-
-elif main['CLANG']:
-    if compareVersions(main['CXXVERSION'], "6") < 0:
-        error('clang version 6 or newer required.\n'
-              'Installed version:', main['CXXVERSION'])
-
-    # Set the Link-Time Optimization (LTO) flags if enabled.
-    if GetOption('with_lto'):
-        for var in 'LTO_CCFLAGS', 'LTO_LINKFLAGS':
-            main[var] = ['-flto']
-
-    # clang has a few additional warnings that we disable.
-    with gem5_scons.Configure(main) as conf:
-        conf.CheckCxxFlag('-Wno-c99-designator')
-        conf.CheckCxxFlag('-Wno-defaulted-function-deleted')
-
-    main.Append(TCMALLOC_CCFLAGS=['-fno-builtin'])
-
-    # On Mac OS X/Darwin we need to also use libc++ (part of XCode) as
-    # opposed to libstdc++, as the later is dated.
-    if sys.platform == "darwin":
-        main.Append(CXXFLAGS=['-stdlib=libc++'])
-        main.Append(LIBS=['c++'])
-
-# Add sanitizers flags
-sanitizers=[]
-if GetOption('with_ubsan'):
-    sanitizers.append('undefined')
-if GetOption('with_asan'):
-    # Available for gcc >= 5 or llvm >= 3.1 both a requirement
-    # by the build system
-    sanitizers.append('address')
-    suppressions_file = Dir('util').File('lsan-suppressions').get_abspath()
-    suppressions_opt = 'suppressions=%s' % suppressions_file
-    suppressions_opts = ':'.join([suppressions_opt, 'print_suppressions=0'])
-    main['ENV']['LSAN_OPTIONS'] = suppressions_opts
-    print()
-    warning('To suppress false positive leaks, set the LSAN_OPTIONS '
-            'environment variable to "%s" when running gem5' %
-            suppressions_opts)
-    warning('LSAN_OPTIONS=%s' % suppressions_opts)
-    print()
-if sanitizers:
-    sanitizers = ','.join(sanitizers)
-    if main['GCC'] or main['CLANG']:
-        main.Append(CCFLAGS=['-fsanitize=%s' % sanitizers,
-                             '-fno-omit-frame-pointer'],
-                    LINKFLAGS='-fsanitize=%s' % sanitizers)
-    else:
-        warning("Don't know how to enable %s sanitizer(s) for your "
-                "compiler." % sanitizers)
-
-# Do this after we save setting back, or else we'll tack on an
-# extra 'qdo' every time we run scons.
-if main['BATCH']:
-    main['CC']     = main['BATCH_CMD'] + ' ' + main['CC']
-    main['CXX']    = main['BATCH_CMD'] + ' ' + main['CXX']
-    main['AS']     = main['BATCH_CMD'] + ' ' + main['AS']
-    main['AR']     = main['BATCH_CMD'] + ' ' + main['AR']
-    main['RANLIB'] = main['BATCH_CMD'] + ' ' + main['RANLIB']
-
-if sys.platform == 'cygwin':
-    # cygwin has some header file issues...
-    main.Append(CCFLAGS=["-Wno-uninitialized"])
-
-
-# Cache build files in the supplied directory.
-if main['M5_BUILD_CACHE']:
-    print('Using build cache located at', main['M5_BUILD_CACHE'])
-    CacheDir(main['M5_BUILD_CACHE'])
-
-if not GetOption('no_compress_debug'):
-    with gem5_scons.Configure(main) as conf:
-        if not conf.CheckCxxFlag('-gz'):
-            warning("Can't enable object file debug section compression")
-        if not conf.CheckLinkFlag('-gz'):
-            warning("Can't enable executable debug section compression")
-
 
 ########################################################################
 #
@@ -532,112 +354,6 @@
         warning('Embedded python library too new. '
                 f'Python 3 expected, found {ver_string}.')
 
-if main['USE_PYTHON']:
-    config_embedded_python(main)
-    gem5py_env = main.Clone()
-else:
-    gem5py_env = main.Clone()
-    config_embedded_python(gem5py_env)
-
-# Bare minimum environment that only includes python
-gem5py_env.Append(CCFLAGS=['${GEM5PY_CCFLAGS_EXTRA}'])
-gem5py_env.Append(LINKFLAGS=['${GEM5PY_LINKFLAGS_EXTRA}'])
-
-if GetOption('gprof') and GetOption('pprof'):
-    error('Only one type of profiling should be enabled at a time')
-if GetOption('gprof'):
-    main.Append(CCFLAGS=['-g', '-pg'], LINKFLAGS=['-pg'])
-if GetOption('pprof'):
-    main.Append(CCFLAGS=['-g'],
-            LINKFLAGS=['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed'])
-
-main['HAVE_PKG_CONFIG'] = main.Detect('pkg-config')
-
-with gem5_scons.Configure(main) as conf:
-    # On Solaris you need to use libsocket for socket ops
-    if not conf.CheckLibWithHeader(
-            [None, 'socket'], 'sys/socket.h', 'C++', 'accept(0,0,0);'):
-       error("Can't find library with socket calls (e.g. accept()).")
-
-    if not conf.CheckLibWithHeader('z', 'zlib.h', 'C++','zlibVersion();'):
-        error('Did not find needed zlib compression library '
-              'and/or zlib.h header file.\n'
-              'Please install zlib and try again.')
-
-if not GetOption('without_tcmalloc'):
-    with gem5_scons.Configure(main) as conf:
-        if conf.CheckLib('tcmalloc'):
-            conf.env.Append(CCFLAGS=conf.env['TCMALLOC_CCFLAGS'])
-        elif conf.CheckLib('tcmalloc_minimal'):
-            conf.env.Append(CCFLAGS=conf.env['TCMALLOC_CCFLAGS'])
-        else:
-            warning("You can get a 12% performance improvement by "
-                    "installing tcmalloc (libgoogle-perftools-dev package "
-                    "on Ubuntu or RedHat).")
-
-
-########################################################################
-#
-# Read and process SConsopts files. These can add new settings which
-# affect each variant directory independently.
-#
-########################################################################
-
-# Register a callback which is called after all SConsopts files have been read.
-after_sconsopts_callbacks = []
-def AfterSConsopts(cb):
-    after_sconsopts_callbacks.append(cb)
-Export('AfterSConsopts')
-
-# Sticky variables get saved in the variables file so they persist from
-# one invocation to the next (unless overridden, in which case the new
-# value becomes sticky).
-sticky_vars = Variables(args=ARGUMENTS)
-Export('sticky_vars')
-
-# Sticky variables that should be exported to #defines in config/*.hh
-# (see src/SConscript).
-export_vars = []
-Export('export_vars')
-
-# Walk the tree and execute all SConsopts scripts that wil add to the
-# above variables
-if GetOption('verbose'):
-    print("Reading SConsopts")
-for bdir in [ base_dir ] + extras_dir_list:
-    if not isdir(bdir):
-        error("Directory '%s' does not exist." % bdir)
-    for root, dirs, files in os.walk(bdir):
-        if 'SConsopts' in files:
-            if GetOption('verbose'):
-                print("Reading", os.path.join(root, 'SConsopts'))
-            SConscript(os.path.join(root, 'SConsopts'))
-
-# Call any callbacks which the SConsopts files registered.
-for cb in after_sconsopts_callbacks:
-    cb()
-
-# Add any generic sticky variables here.
-sticky_vars.Add(BoolVariable('USE_EFENCE',
-    'Link with Electric Fence malloc debugger', False))
-
-
-########################################################################
-#
-# Find and process all the SConscript files in ext. These are shared by
-# all variants in a build root.
-#
-########################################################################
-
-ext_dir = Dir('#ext').abspath
-ext_build_dirs = []
-for root, dirs, files in os.walk(ext_dir):
-    if 'SConscript' in files:
-        build_dir = os.path.relpath(root, ext_dir)
-        ext_build_dirs.append(build_dir)
-        main.SConscript(os.path.join(root, 'SConscript'),
-                        variant_dir=os.path.join(build_root, build_dir))
-
 
 ########################################################################
 #
@@ -646,38 +362,260 @@
 ########################################################################
 
 for variant_path in variant_paths:
-    if not GetOption('silent'):
-        print("Building in", variant_path)
-
     # Make a copy of the build-root environment to use for this config.
     env = main.Clone()
     env['BUILDDIR'] = variant_path
 
+    gem5_build = os.path.join(build_root, variant_path, 'gem5.build')
+    env['GEM5BUILD'] = gem5_build
+    Execute(Mkdir(gem5_build))
+
+    env.SConsignFile(os.path.join(gem5_build, 'sconsign'))
+
+    # Set up default C++ compiler flags
+    if env['GCC'] or env['CLANG']:
+        # As gcc and clang share many flags, do the common parts here
+        env.Append(CCFLAGS=['-pipe'])
+        env.Append(CCFLAGS=['-fno-strict-aliasing'])
+
+        # Enable -Wall and -Wextra and then disable the few warnings that
+        # we consistently violate
+        env.Append(CCFLAGS=['-Wall', '-Wundef', '-Wextra',
+                            '-Wno-sign-compare', '-Wno-unused-parameter'])
+
+        # We always compile using C++17
+        env.Append(CXXFLAGS=['-std=c++17'])
+
+        if sys.platform.startswith('freebsd'):
+            env.Append(CCFLAGS=['-I/usr/local/include'])
+            env.Append(CXXFLAGS=['-I/usr/local/include'])
+            # On FreeBSD we need libthr.
+            env.Append(LIBS=['thr'])
+
+        with gem5_scons.Configure(env) as conf:
+            conf.CheckLinkFlag('-Wl,--as-needed')
+
+        linker = GetOption('linker')
+        if linker:
+            with gem5_scons.Configure(env) as conf:
+                if not conf.CheckLinkFlag(f'-fuse-ld={linker}'):
+                    # check mold support for gcc older than 12.1.0
+                    if linker == 'mold' and \
+                       (env['GCC'] and \
+                           compareVersions(env['CXXVERSION'],
+                                           "12.1.0") < 0) and \
+                       ((isdir('/usr/libexec/mold') and \
+                           conf.CheckLinkFlag('-B/usr/libexec/mold')) or \
+                       (isdir('/usr/local/libexec/mold') and \
+                           conf.CheckLinkFlag('-B/usr/local/libexec/mold'))):
+                        pass # support mold
+                    else:
+                        error(f'Linker "{linker}" is not supported')
+                if linker == 'gold' and not GetOption('with_lto'):
+                    # Tell the gold linker to use threads. The gold linker
+                    # segfaults if both threads and LTO are enabled.
+                    conf.CheckLinkFlag('-Wl,--threads')
+                    conf.CheckLinkFlag(
+                            '-Wl,--thread-count=%d' % GetOption('num_jobs'))
+
+    else:
+        error('\n'.join((
+              "Don't know what compiler options to use for your compiler.",
+              "compiler: " + env['CXX'],
+              "version: " + CXX_version.replace('\n', '<nl>') if
+                    CXX_version else 'COMMAND NOT FOUND!',
+              "If you're trying to use a compiler other than GCC",
+              "or clang, there appears to be something wrong with your",
+              "environment.",
+              "",
+              "If you are trying to use a compiler other than those listed",
+              "above you will need to ease fix SConstruct and ",
+              "src/SConscript to support that compiler.")))
+
+    if env['GCC']:
+        if compareVersions(env['CXXVERSION'], "7") < 0:
+            error('gcc version 7 or newer required.\n'
+                  'Installed version:', env['CXXVERSION'])
+
+        with gem5_scons.Configure(env) as conf:
+            # This warning has a false positive in the systemc in g++ 11.1.
+            conf.CheckCxxFlag('-Wno-free-nonheap-object')
+
+        # Add the appropriate Link-Time Optimization (LTO) flags if
+        # `--with-lto` is set.
+        if GetOption('with_lto'):
+            # g++ uses "make" to parallelize LTO. The program can be overriden
+            # with the environment variable "MAKE", but we currently make no
+            # attempt to plumb that variable through.
+            parallelism = ''
+            if env.Detect('make'):
+                parallelism = '=%d' % GetOption('num_jobs')
+            else:
+                warning('"make" not found, link time optimization will be '
+                        'single threaded.')
+
+            for var in 'LTO_CCFLAGS', 'LTO_LINKFLAGS':
+                # Use the same amount of jobs for LTO as scons.
+                env[var] = ['-flto%s' % parallelism]
+
+        env.Append(TCMALLOC_CCFLAGS=[
+            '-fno-builtin-malloc', '-fno-builtin-calloc',
+            '-fno-builtin-realloc', '-fno-builtin-free'])
+
+    elif env['CLANG']:
+        if compareVersions(env['CXXVERSION'], "6") < 0:
+            error('clang version 6 or newer required.\n'
+                  'Installed version:', env['CXXVERSION'])
+
+        # Set the Link-Time Optimization (LTO) flags if enabled.
+        if GetOption('with_lto'):
+            for var in 'LTO_CCFLAGS', 'LTO_LINKFLAGS':
+                env[var] = ['-flto']
+
+        # clang has a few additional warnings that we disable.
+        with gem5_scons.Configure(env) as conf:
+            conf.CheckCxxFlag('-Wno-c99-designator')
+            conf.CheckCxxFlag('-Wno-defaulted-function-deleted')
+
+        env.Append(TCMALLOC_CCFLAGS=['-fno-builtin'])
+
+        # On Mac OS X/Darwin we need to also use libc++ (part of XCode) as
+        # opposed to libstdc++, as the later is dated.
+        if sys.platform == "darwin":
+            env.Append(CXXFLAGS=['-stdlib=libc++'])
+            env.Append(LIBS=['c++'])
+
+    # Add sanitizers flags
+    sanitizers=[]
+    if GetOption('with_ubsan'):
+        sanitizers.append('undefined')
+    if GetOption('with_asan'):
+        # Available for gcc >= 5 or llvm >= 3.1 both a requirement
+        # by the build system
+        sanitizers.append('address')
+        suppressions_file = Dir('util').File('lsan-suppressions').get_abspath()
+        suppressions_opt = 'suppressions=%s' % suppressions_file
+        suppressions_opts = ':'.join([suppressions_opt,
+                                      'print_suppressions=0'])
+        env['ENV']['LSAN_OPTIONS'] = suppressions_opts
+        print()
+        warning('To suppress false positive leaks, set the LSAN_OPTIONS '
+                'environment variable to "%s" when running gem5' %
+                suppressions_opts)
+        warning('LSAN_OPTIONS=%s' % suppressions_opts)
+        print()
+    if sanitizers:
+        sanitizers = ','.join(sanitizers)
+        if env['GCC'] or env['CLANG']:
+            env.Append(CCFLAGS=['-fsanitize=%s' % sanitizers,
+                                 '-fno-omit-frame-pointer'],
+                        LINKFLAGS='-fsanitize=%s' % sanitizers)
+        else:
+            warning("Don't know how to enable %s sanitizer(s) for your "
+                    "compiler." % sanitizers)
+
+    if sys.platform == 'cygwin':
+        # cygwin has some header file issues...
+        env.Append(CCFLAGS=["-Wno-uninitialized"])
+
+
+    if not GetOption('no_compress_debug'):
+        with gem5_scons.Configure(env) as conf:
+            if not conf.CheckCxxFlag('-gz'):
+                warning("Can't enable object file debug section compression")
+            if not conf.CheckLinkFlag('-gz'):
+                warning("Can't enable executable debug section compression")
+
+    if env['USE_PYTHON']:
+        config_embedded_python(env)
+        gem5py_env = env.Clone()
+    else:
+        gem5py_env = env.Clone()
+        config_embedded_python(gem5py_env)
+
+    # Bare minimum environment that only includes python
+    gem5py_env.Append(CCFLAGS=['${GEM5PY_CCFLAGS_EXTRA}'])
+    gem5py_env.Append(LINKFLAGS=['${GEM5PY_LINKFLAGS_EXTRA}'])
+
+    if GetOption('gprof') and GetOption('pprof'):
+        error('Only one type of profiling should be enabled at a time')
+    if GetOption('gprof'):
+        env.Append(CCFLAGS=['-g', '-pg'], LINKFLAGS=['-pg'])
+    if GetOption('pprof'):
+        env.Append(CCFLAGS=['-g'],
+                LINKFLAGS=['-Wl,--no-as-needed', '-lprofiler',
+                    '-Wl,--as-needed'])
+
+    env['HAVE_PKG_CONFIG'] = env.Detect('pkg-config')
+
+    with gem5_scons.Configure(env) as conf:
+        # On Solaris you need to use libsocket for socket ops
+        if not conf.CheckLibWithHeader(
+                [None, 'socket'], 'sys/socket.h', 'C++', 'accept(0,0,0);'):
+           error("Can't find library with socket calls (e.g. accept()).")
+
+        if not conf.CheckLibWithHeader('z', 'zlib.h', 'C++','zlibVersion();'):
+            error('Did not find needed zlib compression library '
+                  'and/or zlib.h header file.\n'
+                  'Please install zlib and try again.')
+
+    if not GetOption('without_tcmalloc'):
+        with gem5_scons.Configure(env) as conf:
+            if conf.CheckLib('tcmalloc'):
+                conf.env.Append(CCFLAGS=conf.env['TCMALLOC_CCFLAGS'])
+            elif conf.CheckLib('tcmalloc_minimal'):
+                conf.env.Append(CCFLAGS=conf.env['TCMALLOC_CCFLAGS'])
+            else:
+                warning("You can get a 12% performance improvement by "
+                        "installing tcmalloc (libgoogle-perftools-dev package "
+                        "on Ubuntu or RedHat).")
+
+    if not GetOption('silent'):
+        print("Building in", variant_path)
+
     # variant_dir is the tail component of build path, and is used to
     # determine the build parameters (e.g., 'X86')
     (build_root, variant_dir) = os.path.split(variant_path)
 
+    ####################################################################
+    #
+    # Read and process SConsopts files. These can add new settings which
+    # affect each variant directory independently.
+    #
+    ####################################################################
+
+    # Register a callback to call after all SConsopts files have been read.
+    after_sconsopts_callbacks = []
+    def AfterSConsopts(cb):
+        after_sconsopts_callbacks.append(cb)
+    Export('AfterSConsopts')
+
+    # Sticky variables get saved in the variables file so they persist from
+    # one invocation to the next (unless overridden, in which case the new
+    # value becomes sticky).
+    sticky_vars = Variables(args=ARGUMENTS)
+    Export('sticky_vars')
+
+    # EXTRAS is special since it affects what SConsopts need to be read.
+    sticky_vars.Add(('EXTRAS', 'Add extra directories to the compilation', ''))
+
     # Set env variables according to the build directory config.
     sticky_vars.files = []
     # Variables for $BUILD_ROOT/$VARIANT_DIR are stored in
-    # $BUILD_ROOT/variables/$VARIANT_DIR so you can nuke
-    # $BUILD_ROOT/$VARIANT_DIR without losing your variables settings.
-    current_vars_file = os.path.join(build_root, 'variables', variant_dir)
-    if isfile(current_vars_file):
-        sticky_vars.files.append(current_vars_file)
+    # $BUILD_ROOT/$VARIANT_DIR/gem5.build/variables
+
+    gem5_build_vars = os.path.join(gem5_build, 'variables')
+    build_root_vars = os.path.join(build_root, 'variables', variant_dir)
+    current_vars_files = [gem5_build_vars, build_root_vars]
+    existing_vars_files = list(filter(isfile, current_vars_files))
+    if existing_vars_files:
+        sticky_vars.files.extend(existing_vars_files)
         if not GetOption('silent'):
-            print("Using saved variables file %s" % current_vars_file)
-    elif variant_dir in ext_build_dirs:
-        # Things in ext are built without a variant directory.
-        continue
+            print('Using saved variables file(s) %s' %
+                    ', '.join(existing_vars_files))
     else:
         # Variant specific variables file doesn't exist.
 
-        # Make sure the directory is there so we can create the file later.
-        opt_dir = dirname(current_vars_file)
-        if not isdir(opt_dir):
-            mkdir(opt_dir)
-
         # Get default build variables from source tree.  Variables are
         # normally determined by name of $VARIANT_DIR, but can be
         # overridden by '--default=' arg on command line.
@@ -685,23 +623,64 @@
         opts_dir = Dir('#build_opts').abspath
         if default:
             default_vars_files = [
-                    os.path.join(build_root, 'variables', default),
+                    gem5_build_vars,
+                    build_root_vars,
                     os.path.join(opts_dir, default)
                 ]
         else:
             default_vars_files = [os.path.join(opts_dir, variant_dir)]
-        existing_files = list(filter(isfile, default_vars_files))
-        if existing_files:
-            default_vars_file = existing_files[0]
+        existing_default_files = list(filter(isfile, default_vars_files))
+        if existing_default_files:
+            default_vars_file = existing_default_files[0]
             sticky_vars.files.append(default_vars_file)
-            print("Variables file %s not found,\n  using defaults in %s"
-                  % (current_vars_file, default_vars_file))
+            print("Variables file(s) %s not found,\n  using defaults in %s" %
+                    (' or '.join(current_vars_files), default_vars_file))
         else:
-            error("Cannot find variables file %s or default file(s) %s"
-                  % (current_vars_file, ' or '.join(default_vars_files)))
+            error("Cannot find variables file(s) %s or default file(s) %s" %
+                    (' or '.join(current_vars_files),
+                     ' or '.join(default_vars_files)))
             Exit(1)
 
-    # Apply current variable settings to env
+    # Apply current settings for EXTRAS to env.
+    sticky_vars.Update(env)
+
+    # Parse EXTRAS variable to build list of all directories where we're
+    # look for sources etc.  This list is exported as extras_dir_list.
+    if env['EXTRAS']:
+        extras_dir_list = makePathListAbsolute(env['EXTRAS'].split(':'))
+    else:
+        extras_dir_list = []
+
+    Export('extras_dir_list')
+
+    # Variables which were determined with Configure.
+    env['CONF'] = {}
+
+    # Walk the tree and execute all SConsopts scripts that wil add to the
+    # above variables
+    if GetOption('verbose'):
+        print("Reading SConsopts")
+
+    def trySConsopts(dir):
+        sconsopts_path = os.path.join(dir, 'SConsopts')
+        if not isfile(sconsopts_path):
+            return
+        if GetOption('verbose'):
+            print("Reading", sconsopts_path)
+        SConscript(sconsopts_path, exports={'main': env})
+
+    trySConsopts(Dir('#').abspath)
+    for bdir in [ base_dir ] + extras_dir_list:
+        if not isdir(bdir):
+            error("Directory '%s' does not exist." % bdir)
+        for root, dirs, files in os.walk(bdir):
+            trySConsopts(root)
+
+    # Call any callbacks which the SConsopts files registered.
+    for cb in after_sconsopts_callbacks:
+        cb()
+
+    # Update env for new variables added by the SConsopts.
     sticky_vars.Update(env)
 
     Help('''
@@ -710,21 +689,45 @@
 '''.format(dir=variant_dir, help=sticky_vars.GenerateHelpText(env)),
          append=True)
 
-    # Process variable settings.
-    if env['USE_EFENCE']:
-        env.Append(LIBS=['efence'])
+    # If the old vars file exists, delete it to avoid confusion/stale values.
+    if isfile(build_root_vars):
+        warning(f'Deleting old variant variables file "{build_root_vars}"')
+        remove(build_root_vars)
+    # Save sticky variables back to the gem5.build variant variables file.
+    sticky_vars.Save(gem5_build_vars, env)
 
-    if env['KVM_ISA'] != env['TARGET_ISA']:
-        env['USE_KVM'] = False
+    # Pull all the sticky variables into the CONF dict.
+    env['CONF'].update({key: env[key] for key in sticky_vars.keys()})
 
-    # Save sticky variable settings back to current variables file
-    sticky_vars.Save(current_vars_file, env)
+    # Do this after we save setting back, or else we'll tack on an
+    # extra 'qdo' every time we run scons.
+    if env['CONF']['BATCH']:
+        env['CC']     = env['CONF']['BATCH_CMD'] + ' ' + env['CC']
+        env['CXX']    = env['CONF']['BATCH_CMD'] + ' ' + env['CXX']
+        env['AS']     = env['CONF']['BATCH_CMD'] + ' ' + env['AS']
+        env['AR']     = env['CONF']['BATCH_CMD'] + ' ' + env['AR']
+        env['RANLIB'] = env['CONF']['BATCH_CMD'] + ' ' + env['RANLIB']
+
+    # Cache build files in the supplied directory.
+    if env['CONF']['M5_BUILD_CACHE']:
+        print('Using build cache located at', env['CONF']['M5_BUILD_CACHE'])
+        CacheDir(env['CONF']['M5_BUILD_CACHE'])
+
 
     env.Append(CCFLAGS='$CCFLAGS_EXTRA')
     env.Append(LINKFLAGS='$LINKFLAGS_EXTRA')
 
     exports=['env', 'gem5py_env']
 
+    ext_dir = Dir('#ext').abspath
+    variant_ext = os.path.join(variant_path, 'ext')
+    for root, dirs, files in os.walk(ext_dir):
+        if 'SConscript' in files:
+            build_dir = os.path.relpath(root, ext_dir)
+            SConscript(os.path.join(root, 'SConscript'),
+                       variant_dir=os.path.join(variant_ext, build_dir),
+                       exports=exports)
+
     # The src/SConscript file sets up the build rules in 'env' according
     # to the configured variables.  It returns a list of environments,
     # one for each variant build (debug, opt, etc.)
diff --git a/build_opts/ARM b/build_opts/ARM
index e4cb9a5..5b7da10 100644
--- a/build_opts/ARM
+++ b/build_opts/ARM
@@ -1,3 +1,2 @@
 TARGET_ISA = 'arm'
-CPU_MODELS = 'AtomicSimpleCPU,TimingSimpleCPU,O3CPU,MinorCPU'
 PROTOCOL = 'CHI'
diff --git a/build_opts/ARM_MESI_Three_Level b/build_opts/ARM_MESI_Three_Level
index 29a429c..2ca31b6 100644
--- a/build_opts/ARM_MESI_Three_Level
+++ b/build_opts/ARM_MESI_Three_Level
@@ -2,5 +2,4 @@
 # All rights reserved.
 
 TARGET_ISA = 'arm'
-CPU_MODELS = 'TimingSimpleCPU,O3CPU'
 PROTOCOL = 'MESI_Three_Level'
diff --git a/build_opts/ARM_MESI_Three_Level_HTM b/build_opts/ARM_MESI_Three_Level_HTM
index fd7c164..703398d 100644
--- a/build_opts/ARM_MESI_Three_Level_HTM
+++ b/build_opts/ARM_MESI_Three_Level_HTM
@@ -2,5 +2,4 @@
 # All rights reserved.
 
 TARGET_ISA = 'arm'
-CPU_MODELS = 'TimingSimpleCPU,O3CPU'
 PROTOCOL = 'MESI_Three_Level_HTM'
diff --git a/build_opts/ARM_MOESI_hammer b/build_opts/ARM_MOESI_hammer
index 2ba8ce8..bd5c63f 100644
--- a/build_opts/ARM_MOESI_hammer
+++ b/build_opts/ARM_MOESI_hammer
@@ -2,5 +2,4 @@
 # All rights reserved.
 
 TARGET_ISA = 'arm'
-CPU_MODELS = 'TimingSimpleCPU,O3CPU'
 PROTOCOL = 'MOESI_hammer'
diff --git a/build_opts/GCN3_X86 b/build_opts/GCN3_X86
index 21e3cf0..b396908 100644
--- a/build_opts/GCN3_X86
+++ b/build_opts/GCN3_X86
@@ -2,4 +2,3 @@
 TARGET_ISA = 'x86'
 TARGET_GPU_ISA = 'gcn3'
 BUILD_GPU = True
-CPU_MODELS = 'AtomicSimpleCPU,O3CPU,TimingSimpleCPU'
diff --git a/build_opts/Garnet_standalone b/build_opts/Garnet_standalone
index f749d54..fd730c3 100644
--- a/build_opts/Garnet_standalone
+++ b/build_opts/Garnet_standalone
@@ -1,3 +1,2 @@
 TARGET_ISA = 'null'
-CPU_MODELS = ''
 PROTOCOL = 'Garnet_standalone'
diff --git a/build_opts/MIPS b/build_opts/MIPS
index ecb2b09..26cb23c 100644
--- a/build_opts/MIPS
+++ b/build_opts/MIPS
@@ -1,3 +1,2 @@
 TARGET_ISA = 'mips'
-CPU_MODELS = 'AtomicSimpleCPU,TimingSimpleCPU,O3CPU'
 PROTOCOL = 'MI_example'
diff --git a/build_opts/NULL b/build_opts/NULL
index 1242fa9..b749729 100644
--- a/build_opts/NULL
+++ b/build_opts/NULL
@@ -1,3 +1,2 @@
 TARGET_ISA = 'null'
-CPU_MODELS = ''
 PROTOCOL='MI_example'
diff --git a/build_opts/NULL_MESI_Two_Level b/build_opts/NULL_MESI_Two_Level
index db05046..09147b2 100644
--- a/build_opts/NULL_MESI_Two_Level
+++ b/build_opts/NULL_MESI_Two_Level
@@ -1,3 +1,2 @@
 TARGET_ISA = 'null'
-CPU_MODELS = ''
 PROTOCOL = 'MESI_Two_Level'
diff --git a/build_opts/NULL_MOESI_CMP_directory b/build_opts/NULL_MOESI_CMP_directory
index 7136d0b..466a268 100644
--- a/build_opts/NULL_MOESI_CMP_directory
+++ b/build_opts/NULL_MOESI_CMP_directory
@@ -1,3 +1,2 @@
 TARGET_ISA = 'null'
-CPU_MODELS = ''
 PROTOCOL='MOESI_CMP_directory'
diff --git a/build_opts/NULL_MOESI_CMP_token b/build_opts/NULL_MOESI_CMP_token
index 42fff75..0cd0305 100644
--- a/build_opts/NULL_MOESI_CMP_token
+++ b/build_opts/NULL_MOESI_CMP_token
@@ -1,3 +1,2 @@
 TARGET_ISA = 'null'
-CPU_MODELS = ''
 PROTOCOL='MOESI_CMP_token'
diff --git a/build_opts/NULL_MOESI_hammer b/build_opts/NULL_MOESI_hammer
index ff4c22c..39ebcae 100644
--- a/build_opts/NULL_MOESI_hammer
+++ b/build_opts/NULL_MOESI_hammer
@@ -1,3 +1,2 @@
 TARGET_ISA = 'null'
-CPU_MODELS = ''
 PROTOCOL='MOESI_hammer'
diff --git a/build_opts/POWER b/build_opts/POWER
index 672046c..35772a4 100644
--- a/build_opts/POWER
+++ b/build_opts/POWER
@@ -1,3 +1,2 @@
 TARGET_ISA = 'power'
-CPU_MODELS = 'AtomicSimpleCPU,TimingSimpleCPU,O3CPU'
 PROTOCOL = 'MI_example'
diff --git a/build_opts/RISCV b/build_opts/RISCV
index 38abd92..0bd069d 100644
--- a/build_opts/RISCV
+++ b/build_opts/RISCV
@@ -1,3 +1,2 @@
 TARGET_ISA = 'riscv'
-CPU_MODELS = 'AtomicSimpleCPU,TimingSimpleCPU,MinorCPU,O3CPU'
 PROTOCOL = 'MI_example'
diff --git a/build_opts/SPARC b/build_opts/SPARC
index 63ec7cb..98acfe2 100644
--- a/build_opts/SPARC
+++ b/build_opts/SPARC
@@ -1,3 +1,2 @@
 TARGET_ISA = 'sparc'
-CPU_MODELS = 'AtomicSimpleCPU,TimingSimpleCPU,O3CPU'
 PROTOCOL = 'MI_example'
diff --git a/build_opts/VEGA_X86 b/build_opts/VEGA_X86
index 796b556..11e8232 100644
--- a/build_opts/VEGA_X86
+++ b/build_opts/VEGA_X86
@@ -2,4 +2,3 @@
 TARGET_ISA = 'x86'
 TARGET_GPU_ISA = 'vega'
 BUILD_GPU = True
-CPU_MODELS = 'AtomicSimpleCPU,O3CPU,TimingSimpleCPU'
diff --git a/build_opts/X86 b/build_opts/X86
index 27c1b58..72b200a 100644
--- a/build_opts/X86
+++ b/build_opts/X86
@@ -1,4 +1,3 @@
 TARGET_ISA = 'x86'
-CPU_MODELS = 'TimingSimpleCPU,O3CPU,AtomicSimpleCPU'
 PROTOCOL = 'MESI_Two_Level'
-NUMBER_BITS_PER_SET = '128'
\ No newline at end of file
+NUMBER_BITS_PER_SET = '128'
diff --git a/build_opts/X86_MESI_Two_Level b/build_opts/X86_MESI_Two_Level
index eba850b..72b200a 100644
--- a/build_opts/X86_MESI_Two_Level
+++ b/build_opts/X86_MESI_Two_Level
@@ -1,4 +1,3 @@
 TARGET_ISA = 'x86'
-CPU_MODELS = 'TimingSimpleCPU,O3CPU,AtomicSimpleCPU'
 PROTOCOL = 'MESI_Two_Level'
 NUMBER_BITS_PER_SET = '128'
diff --git a/build_opts/X86_MI_example b/build_opts/X86_MI_example
index 60d1645..483cf04 100644
--- a/build_opts/X86_MI_example
+++ b/build_opts/X86_MI_example
@@ -1,3 +1,2 @@
 TARGET_ISA = 'x86'
-CPU_MODELS = 'TimingSimpleCPU,O3CPU,AtomicSimpleCPU'
 PROTOCOL = 'MI_example'
diff --git a/build_opts/X86_MOESI_AMD_Base b/build_opts/X86_MOESI_AMD_Base
index e85f36d..261bedb 100644
--- a/build_opts/X86_MOESI_AMD_Base
+++ b/build_opts/X86_MOESI_AMD_Base
@@ -1,3 +1,2 @@
 PROTOCOL = 'MOESI_AMD_Base'
 TARGET_ISA = 'x86'
-CPU_MODELS = 'AtomicSimpleCPU,O3CPU,TimingSimpleCPU'
\ No newline at end of file
diff --git a/build_tools/cxx_config_cc.py b/build_tools/cxx_config_cc.py
new file mode 100644
index 0000000..c4a2d89
--- /dev/null
+++ b/build_tools/cxx_config_cc.py
@@ -0,0 +1,299 @@
+# Copyright 2004-2006 The Regents of The University of Michigan
+# Copyright 2010-20013 Advanced Micro Devices, Inc.
+# Copyright 2013 Mark D. Hill and David A. Wood
+# Copyright 2017-2020 ARM Limited
+# Copyright 2021 Google, Inc.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+import importlib
+import os.path
+import sys
+
+import importer
+
+from code_formatter import code_formatter
+
+parser = argparse.ArgumentParser()
+parser.add_argument('modpath', help='module the simobject belongs to')
+parser.add_argument('cxx_config_cc', help='cxx config cc file to generate')
+
+args = parser.parse_args()
+
+basename = os.path.basename(args.cxx_config_cc)
+sim_object_name = os.path.splitext(basename)[0]
+
+importer.install()
+module = importlib.import_module(args.modpath)
+sim_object = getattr(module, sim_object_name)
+
+from m5.params import isSimObjectClass
+import m5.params
+
+code = code_formatter()
+
+entry_class = 'CxxConfigDirectoryEntry_%s' % sim_object_name
+param_class = '%sCxxConfigParams' % sim_object_name
+
+def cxx_bool(b):
+    return 'true' if b else 'false'
+
+code('#include "params/%s.hh"' % sim_object_name)
+
+for param in sim_object._params.values():
+    if isSimObjectClass(param.ptype):
+        code('#include "%s"' % param.ptype._value_dict['cxx_header'])
+        code('#include "params/%s.hh"' % param.ptype.__name__)
+    else:
+        param.ptype.cxx_ini_predecls(code)
+
+code('''#include "${{sim_object._value_dict['cxx_header']}}"
+#include "base/str.hh"
+#include "cxx_config/${sim_object_name}.hh"
+
+namespace gem5
+{
+
+${param_class}::DirectoryEntry::DirectoryEntry()
+{
+''')
+code.indent()
+for param in sim_object._params.values():
+    is_vector = isinstance(param, m5.params.VectorParamDesc)
+    is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+    code('parameters["%s"] = new ParamDesc("%s", %s, %s);' %
+        (param.name, param.name, cxx_bool(is_vector),
+        cxx_bool(is_simobj)));
+
+for port in sim_object._ports.values():
+    is_vector = isinstance(port, m5.params.VectorPort)
+    is_requestor = port.role == 'GEM5 REQUESTOR'
+
+    code('ports["%s"] = new PortDesc("%s", %s, %s);' %
+        (port.name, port.name, cxx_bool(is_vector),
+        cxx_bool(is_requestor)))
+
+code.dedent()
+
+code('''}
+
+bool
+${param_class}::setSimObject(const std::string &name, SimObject *simObject)
+{
+    bool ret = true;
+    if (false) {
+''')
+
+code.indent()
+for param in sim_object._params.values():
+    is_vector = isinstance(param, m5.params.VectorParamDesc)
+    is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+    if is_simobj and not is_vector:
+        code('} else if (name == "${{param.name}}") {')
+        code.indent()
+        code('this->${{param.name}} = '
+            'dynamic_cast<${{param.ptype.cxx_type}}>(simObject);')
+        code('if (simObject && !this->${{param.name}})')
+        code('   ret = false;')
+        code.dedent()
+code.dedent()
+
+code('''
+    } else {
+        ret = false;
+    }
+
+    return ret;
+}
+
+bool
+${param_class}::setSimObjectVector(const std::string &name,
+    const std::vector<SimObject *> &simObjects)
+{
+    bool ret = true;
+
+    if (false) {
+''')
+
+code.indent()
+for param in sim_object._params.values():
+    is_vector = isinstance(param, m5.params.VectorParamDesc)
+    is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+    if is_simobj and is_vector:
+        code('} else if (name == "${{param.name}}") {')
+        code.indent()
+        code('this->${{param.name}}.clear();')
+        code('for (auto i = simObjects.begin(); '
+            'ret && i != simObjects.end(); i ++)')
+        code('{')
+        code.indent()
+        code('${{param.ptype.cxx_type}} object = '
+            'dynamic_cast<${{param.ptype.cxx_type}}>(*i);')
+        code('if (*i && !object)')
+        code('    ret = false;')
+        code('else')
+        code('    this->${{param.name}}.push_back(object);')
+        code.dedent()
+        code('}')
+        code.dedent()
+code.dedent()
+
+code('''
+    } else {
+        ret = false;
+    }
+
+    return ret;
+}
+
+void
+${param_class}::setName(const std::string &name_)
+{
+    this->name = name_;
+}
+
+bool
+${param_class}::setParam(const std::string &name,
+    const std::string &value, const Flags flags)
+{
+    bool ret = true;
+
+    if (false) {
+''')
+
+code.indent()
+for param in sim_object._params.values():
+    is_vector = isinstance(param, m5.params.VectorParamDesc)
+    is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+    if not is_simobj and not is_vector:
+        code('} else if (name == "${{param.name}}") {')
+        code.indent()
+        param.ptype.cxx_ini_parse(code,
+            'value', 'this->%s' % param.name, 'ret =')
+        code.dedent()
+code.dedent()
+
+code('''
+    } else {
+        ret = false;
+    }
+
+    return ret;
+}
+
+bool
+${param_class}::setParamVector(const std::string &name,
+    const std::vector<std::string> &values, const Flags flags)
+{
+    bool ret = true;
+
+    if (false) {
+''')
+
+code.indent()
+for param in sim_object._params.values():
+    is_vector = isinstance(param, m5.params.VectorParamDesc)
+    is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
+
+    if not is_simobj and is_vector:
+        code('} else if (name == "${{param.name}}") {')
+        code.indent()
+        code('${{param.name}}.clear();')
+        code('for (auto i = values.begin(); '
+            'ret && i != values.end(); i ++)')
+        code('{')
+        code.indent()
+        code('${{param.ptype.cxx_type}} elem;')
+        param.ptype.cxx_ini_parse(code,
+            '*i', 'elem', 'ret =')
+        code('if (ret)')
+        code('    this->${{param.name}}.push_back(elem);')
+        code.dedent()
+        code('}')
+        code.dedent()
+code.dedent()
+
+code('''
+    } else {
+        ret = false;
+    }
+
+    return ret;
+}
+
+bool
+${param_class}::setPortConnectionCount(const std::string &name,
+    unsigned int count)
+{
+    bool ret = true;
+
+    if (false) {
+''')
+
+code.indent()
+for port in sim_object._ports.values():
+    code('} else if (name == "${{port.name}}") {')
+    code('    this->port_${{port.name}}_connection_count = count;')
+code.dedent()
+
+code('''
+    } else {
+        ret = false;
+    }
+
+    return ret;
+}
+
+SimObject *
+${param_class}::simObjectCreate()
+{
+''')
+
+code.indent()
+if hasattr(sim_object, 'abstract') and sim_object.abstract:
+    code('return nullptr;')
+else:
+    code('return this->create();')
+code.dedent()
+
+code('''}
+
+} // namespace gem5
+''')
+
+code.write(args.cxx_config_cc)
diff --git a/build_tools/cxx_config_hh.py b/build_tools/cxx_config_hh.py
new file mode 100644
index 0000000..652c488
--- /dev/null
+++ b/build_tools/cxx_config_hh.py
@@ -0,0 +1,115 @@
+# Copyright 2004-2006 The Regents of The University of Michigan
+# Copyright 2010-20013 Advanced Micro Devices, Inc.
+# Copyright 2013 Mark D. Hill and David A. Wood
+# Copyright 2017-2020 ARM Limited
+# Copyright 2021 Google, Inc.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+import importlib
+import os.path
+import sys
+
+import importer
+
+from code_formatter import code_formatter
+
+parser = argparse.ArgumentParser()
+parser.add_argument('modpath', help='module the simobject belongs to')
+parser.add_argument('cxx_config_hh', help='cxx config header file to generate')
+
+args = parser.parse_args()
+
+basename = os.path.basename(args.cxx_config_hh)
+sim_object_name = os.path.splitext(basename)[0]
+
+importer.install()
+module = importlib.import_module(args.modpath)
+sim_object = getattr(module, sim_object_name)
+
+code = code_formatter()
+
+entry_class = 'CxxConfigDirectoryEntry_%s' % sim_object_name
+param_class = '%sCxxConfigParams' % sim_object_name
+
+code('''#include "params/${sim_object_name}.hh"
+
+#include "sim/cxx_config.hh"
+
+namespace gem5
+{
+
+class ${param_class} : public CxxConfigParams, public ${sim_object_name}Params
+{
+  private:
+    class DirectoryEntry : public CxxConfigDirectoryEntry
+    {
+      public:
+        DirectoryEntry();
+
+        CxxConfigParams *
+        makeParamsObject() const
+        {
+            return new ${param_class};
+        }
+    };
+
+    static inline AddToConfigDir dirEntry
+        {"${sim_object_name}", new DirectoryEntry};
+
+  public:
+    bool setSimObject(const std::string &name, SimObject *simObject);
+
+    bool setSimObjectVector(const std::string &name,
+        const std::vector<SimObject *> &simObjects);
+
+    void setName(const std::string &name_);
+
+    const std::string &getName() { return this->name; }
+
+    bool setParam(const std::string &name, const std::string &value,
+        const Flags flags);
+
+    bool setParamVector(const std::string &name,
+        const std::vector<std::string> &values, const Flags flags);
+
+    bool setPortConnectionCount(const std::string &name, unsigned int count);
+
+    SimObject *simObjectCreate();
+};
+
+} // namespace gem5
+''')
+
+code.write(args.cxx_config_hh)
diff --git a/build_tools/enum_cc.py b/build_tools/enum_cc.py
new file mode 100644
index 0000000..c706ffe
--- /dev/null
+++ b/build_tools/enum_cc.py
@@ -0,0 +1,160 @@
+# Copyright 2004-2006 The Regents of The University of Michigan
+# Copyright 2010-20013 Advanced Micro Devices, Inc.
+# Copyright 2013 Mark D. Hill and David A. Wood
+# Copyright 2017-2020 ARM Limited
+# Copyright 2021 Google, Inc.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+import importlib
+import os.path
+import sys
+
+import importer
+
+from code_formatter import code_formatter
+
+parser = argparse.ArgumentParser()
+parser.add_argument('modpath', help='module the enum belongs to')
+parser.add_argument('enum_cc', help='enum cc file to generate')
+parser.add_argument('use_python',
+        help='whether python is enabled in gem5 (True or False)')
+
+args = parser.parse_args()
+
+use_python = args.use_python.lower()
+if use_python == 'true':
+    use_python = True
+elif use_python == 'false':
+    use_python = False
+else:
+    print(f'Unrecognized "use_python" value {use_python}', file=sys.stderr)
+    sys.exit(1)
+
+basename = os.path.basename(args.enum_cc)
+enum_name = os.path.splitext(basename)[0]
+
+importer.install()
+module = importlib.import_module(args.modpath)
+enum = getattr(module, enum_name)
+
+code = code_formatter()
+
+wrapper_name = enum.wrapper_name
+file_name = enum.__name__
+name = enum.__name__ if enum.enum_name is None else enum.enum_name
+
+code('''#include "base/compiler.hh"
+#include "enums/$file_name.hh"
+
+namespace gem5
+{
+
+''')
+
+if enum.wrapper_is_struct:
+    code('const char *${wrapper_name}::${name}Strings'
+        '[Num_${name}] =')
+else:
+    if enum.is_class:
+        code('''\
+const char *${name}Strings[static_cast<int>(${name}::Num_${name})] =
+''')
+    else:
+        code('''GEM5_DEPRECATED_NAMESPACE(Enums, enums);
+namespace enums
+{''')
+        code.indent(1)
+        code('const char *${name}Strings[Num_${name}] =')
+
+code('{')
+code.indent(1)
+for val in enum.vals:
+    code('"$val",')
+code.dedent(1)
+code('};')
+
+if not enum.wrapper_is_struct and not enum.is_class:
+    code.dedent(1)
+    code('} // namespace enums')
+
+code('} // namespace gem5')
+
+
+if use_python:
+
+    name = enum.__name__
+    enum_name = enum.__name__ if enum.enum_name is None else enum.enum_name
+    wrapper_name = enum_name if enum.is_class else enum.wrapper_name
+
+    code('''#include "pybind11/pybind11.h"
+#include "pybind11/stl.h"
+
+#include <sim/init.hh>
+
+namespace py = pybind11;
+
+namespace gem5
+{
+
+static void
+module_init(py::module_ &m_internal)
+{
+    py::module_ m = m_internal.def_submodule("enum_${name}");
+
+''')
+    if enum.is_class:
+        code('py::enum_<${enum_name}>(m, "enum_${name}")')
+    else:
+        code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")')
+
+    code.indent()
+    code.indent()
+    for val in enum.vals:
+        code('.value("${val}", ${wrapper_name}::${val})')
+    code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
+    if not enum.is_class:
+        code('.export_values()')
+    code(';')
+    code.dedent()
+
+    code('}')
+    code.dedent()
+    code('''
+static EmbeddedPyBind embed_enum("enum_${name}", module_init);
+
+} // namespace gem5
+    ''')
+
+code.write(args.enum_cc)
diff --git a/build_tools/enum_hh.py b/build_tools/enum_hh.py
new file mode 100644
index 0000000..2c4a7bb
--- /dev/null
+++ b/build_tools/enum_hh.py
@@ -0,0 +1,116 @@
+# Copyright 2004-2006 The Regents of The University of Michigan
+# Copyright 2010-20013 Advanced Micro Devices, Inc.
+# Copyright 2013 Mark D. Hill and David A. Wood
+# Copyright 2017-2020 ARM Limited
+# Copyright 2021 Google, Inc.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+import importlib
+import os.path
+import sys
+
+import importer
+
+from code_formatter import code_formatter
+
+parser = argparse.ArgumentParser()
+parser.add_argument('modpath', help='module the enum belongs to')
+parser.add_argument('enum_hh', help='enum header file to generate')
+
+args = parser.parse_args()
+
+basename = os.path.basename(args.enum_hh)
+enum_name = os.path.splitext(basename)[0]
+
+importer.install()
+module = importlib.import_module(args.modpath)
+enum = getattr(module, enum_name)
+
+code = code_formatter()
+
+# Generate C++ class declaration for this enum type.
+# Note that we wrap the enum in a class/struct to act as a namespace,
+# so that the enum strings can be brief w/o worrying about collisions.
+wrapper_name = enum.wrapper_name
+wrapper = 'struct' if enum.wrapper_is_struct else 'namespace'
+name = enum.__name__ if enum.enum_name is None else enum.enum_name
+idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
+
+code('''\
+#ifndef $idem_macro
+#define $idem_macro
+
+namespace gem5
+{
+''')
+if enum.is_class:
+    code('''\
+enum class $name
+{
+''')
+else:
+    code('''\
+$wrapper $wrapper_name {
+enum $name
+{
+''')
+    code.indent(1)
+code.indent(1)
+for val in enum.vals:
+    code('$val = ${{enum.map[val]}},')
+code('Num_$name = ${{len(enum.vals)}}')
+code.dedent(1)
+code('};')
+
+if enum.is_class:
+    code('''\
+extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})];
+''')
+elif enum.wrapper_is_struct:
+    code('static const char *${name}Strings[Num_${name}];')
+else:
+    code('extern const char *${name}Strings[Num_${name}];')
+
+if not enum.is_class:
+    code.dedent(1)
+    code('}; // $wrapper_name')
+
+code()
+code('} // namespace gem5')
+
+code()
+code('#endif // $idem_macro')
+
+code.write(args.enum_hh)
diff --git a/build_tools/sim_object_param_struct_cc.py b/build_tools/sim_object_param_struct_cc.py
index 7266556..1b72e3c 100644
--- a/build_tools/sim_object_param_struct_cc.py
+++ b/build_tools/sim_object_param_struct_cc.py
@@ -1,5 +1,18 @@
+# Copyright 2004-2006 The Regents of The University of Michigan
+# Copyright 2010-20013 Advanced Micro Devices, Inc.
+# Copyright 2013 Mark D. Hill and David A. Wood
+# Copyright 2017-2020 ARM Limited
 # Copyright 2021 Google, Inc.
 #
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met: redistributions of source code must retain the above copyright
@@ -57,6 +70,204 @@
 module = importlib.import_module(args.modpath)
 sim_object = getattr(module, sim_object_name)
 
+from m5.objects.SimObject import PyBindProperty
+
 code = code_formatter()
-sim_object.params_create_decl(code, use_python)
+
+py_class_name = sim_object.pybind_class
+
+# The 'local' attribute restricts us to the params declared in
+# the object itself, not including inherited params (which
+# will also be inherited from the base class's param struct
+# here). Sort the params based on their key
+params = list(map(lambda k_v: k_v[1],
+                  sorted(sim_object._params.local.items())))
+ports = sim_object._ports.local
+
+# only include pybind if python is enabled in the build
+if use_python:
+
+    code('''#include "pybind11/pybind11.h"
+#include "pybind11/stl.h"
+
+#include <type_traits>
+
+#include "base/compiler.hh"
+#include "params/$sim_object.hh"
+#include "sim/init.hh"
+#include "sim/sim_object.hh"
+
+#include "${{sim_object.cxx_header}}"
+
+''')
+else:
+    code('''
+#include <type_traits>
+
+#include "base/compiler.hh"
+#include "params/$sim_object.hh"
+
+#include "${{sim_object.cxx_header}}"
+
+''')
+# only include the python params code if python is enabled.
+if use_python:
+    for param in params:
+        param.pybind_predecls(code)
+
+    code('''namespace py = pybind11;
+
+namespace gem5
+{
+
+static void
+module_init(py::module_ &m_internal)
+{
+py::module_ m = m_internal.def_submodule("param_${sim_object}");
+''')
+    code.indent()
+    if sim_object._base:
+        code('py::class_<${sim_object}Params, ' \
+             '${{sim_object._base.type}}Params, ' \
+             'std::unique_ptr<${{sim_object}}Params, py::nodelete>>(' \
+             'm, "${sim_object}Params")')
+    else:
+        code('py::class_<${sim_object}Params, ' \
+            'std::unique_ptr<${sim_object}Params, py::nodelete>>(' \
+            'm, "${sim_object}Params")')
+
+    code.indent()
+    if not hasattr(sim_object, 'abstract') or not sim_object.abstract:
+        code('.def(py::init<>())')
+        code('.def("create", &${sim_object}Params::create)')
+
+    param_exports = sim_object.cxx_param_exports + [
+        PyBindProperty(k)
+        for k, v in sorted(sim_object._params.local.items())
+    ] + [
+        PyBindProperty(f"port_{port.name}_connection_count")
+        for port in ports.values()
+    ]
+    for exp in param_exports:
+        exp.export(code, f"{sim_object}Params")
+
+    code(';')
+    code()
+    code.dedent()
+
+    bases = []
+    if 'cxx_base' in sim_object._value_dict:
+        # If the c++ base class implied by python inheritance was
+        # overridden, use that value.
+        if sim_object.cxx_base:
+            bases.append(sim_object.cxx_base)
+    elif sim_object._base:
+        # If not and if there was a SimObject base, use its c++ class
+        # as this class' base.
+        bases.append(sim_object._base.cxx_class)
+    # Add in any extra bases that were requested.
+    bases.extend(sim_object.cxx_extra_bases)
+
+    if bases:
+        base_str = ", ".join(bases)
+        code('py::class_<${{sim_object.cxx_class}}, ${base_str}, ' \
+            'std::unique_ptr<${{sim_object.cxx_class}}, py::nodelete>>(' \
+            'm, "${py_class_name}")')
+    else:
+        code('py::class_<${{sim_object.cxx_class}}, ' \
+            'std::unique_ptr<${{sim_object.cxx_class}}, py::nodelete>>(' \
+            'm, "${py_class_name}")')
+    code.indent()
+    for exp in sim_object.cxx_exports:
+        exp.export(code, sim_object.cxx_class)
+    code(';')
+    code.dedent()
+    code()
+    code.dedent()
+    code('}')
+    code()
+    code('static EmbeddedPyBind '
+         'embed_obj("${0}", module_init, "${1}");',
+        sim_object, sim_object._base.type if sim_object._base else "")
+    code()
+    code('} // namespace gem5')
+
+# include the create() methods whether or not python is enabled.
+if not hasattr(sim_object, 'abstract') or not sim_object.abstract:
+    if 'type' in sim_object.__dict__:
+        code('''
+namespace gem5
+{
+
+namespace
+{
+
+/*
+ * If we can't define a default create() method for this params
+ * struct because the SimObject doesn't have the right
+ * constructor, use template magic to make it so we're actually
+ * defining a create method for this class instead.
+ */
+class Dummy${sim_object}ParamsClass
+{
+  public:
+    ${{sim_object.cxx_class}} *create() const;
+};
+
+template <class CxxClass, class Enable=void>
+class Dummy${sim_object}Shunt;
+
+/*
+ * This version directs to the real Params struct and the
+ * default behavior of create if there's an appropriate
+ * constructor.
+ */
+template <class CxxClass>
+class Dummy${sim_object}Shunt<CxxClass, std::enable_if_t<
+    std::is_constructible_v<CxxClass, const ${sim_object}Params &>>>
+{
+  public:
+    using Params = ${sim_object}Params;
+    static ${{sim_object.cxx_class}} *
+    create(const Params &p)
+    {
+        return new CxxClass(p);
+    }
+};
+
+/*
+ * This version diverts to the DummyParamsClass and a dummy
+ * implementation of create if the appropriate constructor does
+ * not exist.
+ */
+template <class CxxClass>
+class Dummy${sim_object}Shunt<CxxClass, std::enable_if_t<
+    !std::is_constructible_v<CxxClass, const ${sim_object}Params &>>>
+{
+  public:
+    using Params = Dummy${sim_object}ParamsClass;
+    static ${{sim_object.cxx_class}} *
+    create(const Params &p)
+    {
+        return nullptr;
+    }
+};
+
+} // anonymous namespace
+
+/*
+ * An implementation of either the real Params struct's create
+ * method, or the Dummy one. Either an implementation is
+ * mandantory since this was shunted off to the dummy class, or
+ * one is optional which will override this weak version.
+ */
+[[maybe_unused]] ${{sim_object.cxx_class}} *
+Dummy${sim_object}Shunt<${{sim_object.cxx_class}}>::Params::create() const
+{
+    return Dummy${sim_object}Shunt<${{sim_object.cxx_class}}>::create(*this);
+}
+
+} // namespace gem5
+''')
+
 code.write(args.param_cc)
diff --git a/build_tools/sim_object_param_struct_hh.py b/build_tools/sim_object_param_struct_hh.py
index 0652ae5..261ac9b 100644
--- a/build_tools/sim_object_param_struct_hh.py
+++ b/build_tools/sim_object_param_struct_hh.py
@@ -1,5 +1,18 @@
+# Copyright 2004-2006 The Regents of The University of Michigan
+# Copyright 2010-20013 Advanced Micro Devices, Inc.
+# Copyright 2013 Mark D. Hill and David A. Wood
+# Copyright 2017-2020 ARM Limited
 # Copyright 2021 Google, Inc.
 #
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met: redistributions of source code must retain the above copyright
@@ -45,6 +58,167 @@
 module = importlib.import_module(args.modpath)
 sim_object = getattr(module, sim_object_name)
 
+from m5.objects.SimObject import SimObject
+from m5.params import Enum
+
 code = code_formatter()
-sim_object.cxx_param_decl(code)
+
+# The 'local' attribute restricts us to the params declared in
+# the object itself, not including inherited params (which
+# will also be inherited from the base class's param struct
+# here). Sort the params based on their key
+params = list(map(lambda k_v: k_v[1],
+    sorted(sim_object._params.local.items())))
+ports = sim_object._ports.local
+try:
+    ptypes = [p.ptype for p in params]
+except:
+    print(sim_object)
+    print(params)
+    raise
+
+warned_about_nested_templates = False
+
+class CxxClass(object):
+    def __init__(self, sig, template_params=[]):
+        # Split the signature into its constituent parts. This could
+        # potentially be done with regular expressions, but
+        # it's simple enough to pick appart a class signature
+        # manually.
+        parts = sig.split('<', 1)
+        base = parts[0]
+        t_args = []
+        if len(parts) > 1:
+            # The signature had template arguments.
+            text = parts[1].rstrip(' \t\n>')
+            arg = ''
+            # Keep track of nesting to avoid splitting on ","s embedded
+            # in the arguments themselves.
+            depth = 0
+            for c in text:
+                if c == '<':
+                    depth = depth + 1
+                    if depth > 0 and not warned_about_nested_templates:
+                        warned_about_nested_templates = True
+                        print('Nested template argument in cxx_class.'
+                              ' This feature is largely untested and '
+                              ' may not work.')
+                elif c == '>':
+                    depth = depth - 1
+                elif c == ',' and depth == 0:
+                    t_args.append(arg.strip())
+                    arg = ''
+                else:
+                    arg = arg + c
+            if arg:
+                t_args.append(arg.strip())
+        # Split the non-template part on :: boundaries.
+        class_path = base.split('::')
+
+        # The namespaces are everything except the last part of the class path.
+        self.namespaces = class_path[:-1]
+        # And the class name is the last part.
+        self.name = class_path[-1]
+
+        self.template_params = template_params
+        self.template_arguments = []
+        # Iterate through the template arguments and their values. This
+        # will likely break if parameter packs are used.
+        for arg, param in zip(t_args, template_params):
+            type_keys = ('class', 'typename')
+            # If a parameter is a type, parse it recursively. Otherwise
+            # assume it's a constant, and store it verbatim.
+            if any(param.strip().startswith(kw) for kw in type_keys):
+                self.template_arguments.append(CxxClass(arg))
+            else:
+                self.template_arguments.append(arg)
+
+    def declare(self, code):
+        # First declare any template argument types.
+        for arg in self.template_arguments:
+            if isinstance(arg, CxxClass):
+                arg.declare(code)
+        # Re-open the target namespace.
+        for ns in self.namespaces:
+            code('namespace $ns {')
+        # If this is a class template...
+        if self.template_params:
+            code('template <${{", ".join(self.template_params)}}>')
+        # The actual class declaration.
+        code('class ${{self.name}};')
+        # Close the target namespaces.
+        for ns in reversed(self.namespaces):
+            code('} // namespace $ns')
+
+code('''\
+#ifndef __PARAMS__${sim_object}__
+#define __PARAMS__${sim_object}__
+
+''')
+
+
+# The base SimObject has a couple of params that get
+# automatically set from Python without being declared through
+# the normal Param mechanism; we slip them in here (needed
+# predecls now, actual declarations below)
+if sim_object == SimObject:
+    code('''#include <string>''')
+
+cxx_class = CxxClass(sim_object._value_dict['cxx_class'],
+                     sim_object._value_dict['cxx_template_params'])
+
+# A forward class declaration is sufficient since we are just
+# declaring a pointer.
+cxx_class.declare(code)
+
+for param in params:
+    param.cxx_predecls(code)
+for port in ports.values():
+    port.cxx_predecls(code)
+code()
+
+if sim_object._base:
+    code('#include "params/${{sim_object._base.type}}.hh"')
+    code()
+
+for ptype in ptypes:
+    if issubclass(ptype, Enum):
+        code('#include "enums/${{ptype.__name__}}.hh"')
+        code()
+
+code('namespace gem5')
+code('{')
+code('')
+
+# now generate the actual param struct
+code("struct ${sim_object}Params")
+if sim_object._base:
+    code("    : public ${{sim_object._base.type}}Params")
+code("{")
+if not hasattr(sim_object, 'abstract') or not sim_object.abstract:
+    if 'type' in sim_object.__dict__:
+        code("    ${{sim_object.cxx_type}} create() const;")
+
+code.indent()
+if sim_object == SimObject:
+    code('''
+SimObjectParams() {}
+virtual ~SimObjectParams() {}
+
+std::string name;
+    ''')
+
+for param in params:
+    param.cxx_decl(code)
+for port in ports.values():
+    port.cxx_decl(code)
+
+code.dedent()
+code('};')
+code()
+code('} // namespace gem5')
+
+code()
+code('#endif // __PARAMS__${sim_object}__')
+
 code.write(args.param_hh)
diff --git a/configs/common/FSConfig.py b/configs/common/FSConfig.py
index 8b8fb4e..febe146 100644
--- a/configs/common/FSConfig.py
+++ b/configs/common/FSConfig.py
@@ -74,8 +74,7 @@
     viodir = os.path.realpath(os.path.join(m5.options.outdir, '9p'))
     viopci.vio.root = os.path.join(viodir, 'share')
     viopci.vio.socketPath = os.path.join(viodir, 'socket')
-    if not os.path.exists(viopci.vio.root):
-        os.makedirs(viopci.vio.root)
+    os.makedirs(viopci.vio.root, exist_ok=True)
     if os.path.exists(viopci.vio.socketPath):
         os.remove(viopci.vio.socketPath)
     parent.viopci = viopci
@@ -445,6 +444,8 @@
 def makeX86System(mem_mode, numCPUs=1, mdesc=None, workload=None, Ruby=False):
     self = System()
 
+    self.m5ops_base = 0xffff0000
+
     if workload is None:
         workload = X86FsWorkload()
     self.workload = workload
diff --git a/configs/common/FileSystemConfig.py b/configs/common/FileSystemConfig.py
index 66a6315..f60bf23 100644
--- a/configs/common/FileSystemConfig.py
+++ b/configs/common/FileSystemConfig.py
@@ -140,7 +140,7 @@
 
     # Set up /sys/devices/system/cpu
     cpudir = joinpath(sysdir, 'devices', 'system', 'cpu')
-    makedirs(cpudir)
+    makedirs(cpudir, exist_ok=True)
 
     file_append((cpudir, 'online'), '0-%d' % (len(cpus) - 1))
     file_append((cpudir, 'possible'), '0-%d' % (len(cpus) - 1))
@@ -168,7 +168,7 @@
                            'system', 'node')
 
     nodedir = joinpath(nodebasedir,'node%d' % node_number)
-    makedirs(nodedir)
+    makedirs(nodedir, exist_ok=True)
 
     file_append((nodedir, 'cpumap'), hex_mask(cpu_list))
     file_append((nodedir, 'meminfo'),
@@ -180,10 +180,8 @@
     cpudir = joinpath(m5.options.outdir, 'fs',  'sys', 'devices', 'system',
                       'cpu', 'cpu%d' % core_id)
 
-    if not isdir(joinpath(cpudir, 'topology')):
-        makedirs(joinpath(cpudir, 'topology'))
-    if not isdir(joinpath(cpudir, 'cache')):
-        makedirs(joinpath(cpudir, 'cache'))
+    makedirs(joinpath(cpudir, 'topology'), exist_ok=True)
+    makedirs(joinpath(cpudir, 'cache'))
 
     file_append((cpudir, 'online'), '1')
     file_append((cpudir, 'topology', 'physical_package_id'),
@@ -204,7 +202,7 @@
         while isdir(joinpath(cachedir, 'index%d' % j)):
             j += 1
         indexdir = joinpath(cachedir, 'index%d' % j)
-        makedirs(indexdir)
+        makedirs(indexdir, exist_ok=True)
 
         file_append((indexdir, 'level'), level)
         file_append((indexdir, 'type'), idu_type)
diff --git a/configs/common/GPUTLBConfig.py b/configs/common/GPUTLBConfig.py
index f6aaccf..740c748 100644
--- a/configs/common/GPUTLBConfig.py
+++ b/configs/common/GPUTLBConfig.py
@@ -34,41 +34,69 @@
 import m5
 from m5.objects import *
 
-def TLB_constructor(level):
+def TLB_constructor(options, level, gpu_ctrl=None, full_system=False):
 
-    constructor_call = "X86GPUTLB(size = options.L%(level)dTLBentries, \
-            assoc = options.L%(level)dTLBassoc, \
-            hitLatency = options.L%(level)dAccessLatency,\
-            missLatency2 = options.L%(level)dMissLatency,\
-            maxOutstandingReqs = options.L%(level)dMaxOutstandingReqs,\
-            accessDistance = options.L%(level)dAccessDistanceStat,\
-            clk_domain = SrcClockDomain(\
-                clock = options.gpu_clock,\
-                voltage_domain = VoltageDomain(\
-                    voltage = options.gpu_voltage)))" % locals()
-    return constructor_call
-
-def Coalescer_constructor(level):
-
-    constructor_call = "TLBCoalescer(probesPerCycle = \
-                options.L%(level)dProbesPerCycle, \
-                coalescingWindow = options.L%(level)dCoalescingWindow,\
-                disableCoalescing = options.L%(level)dDisableCoalescing,\
+    if full_system:
+        constructor_call = "VegaGPUTLB(\
+                gpu_device = gpu_ctrl, \
+                size = options.L%(level)dTLBentries, \
+                assoc = options.L%(level)dTLBassoc, \
+                hitLatency = options.L%(level)dAccessLatency,\
+                missLatency1 = options.L%(level)dMissLatency,\
+                missLatency2 = options.L%(level)dMissLatency,\
+                maxOutstandingReqs = options.L%(level)dMaxOutstandingReqs,\
+                clk_domain = SrcClockDomain(\
+                    clock = options.gpu_clock,\
+                    voltage_domain = VoltageDomain(\
+                        voltage = options.gpu_voltage)))" % locals()
+    else:
+        constructor_call = "X86GPUTLB(size = options.L%(level)dTLBentries, \
+                assoc = options.L%(level)dTLBassoc, \
+                hitLatency = options.L%(level)dAccessLatency,\
+                missLatency2 = options.L%(level)dMissLatency,\
+                maxOutstandingReqs = options.L%(level)dMaxOutstandingReqs,\
+                accessDistance = options.L%(level)dAccessDistanceStat,\
                 clk_domain = SrcClockDomain(\
                     clock = options.gpu_clock,\
                     voltage_domain = VoltageDomain(\
                         voltage = options.gpu_voltage)))" % locals()
     return constructor_call
 
+def Coalescer_constructor(options, level, full_system):
+
+    if full_system:
+        constructor_call = "VegaTLBCoalescer(probesPerCycle = \
+            options.L%(level)dProbesPerCycle, \
+            tlb_level  = %(level)d ,\
+            coalescingWindow = options.L%(level)dCoalescingWindow,\
+            disableCoalescing = options.L%(level)dDisableCoalescing,\
+            clk_domain = SrcClockDomain(\
+                clock = options.gpu_clock,\
+                voltage_domain = VoltageDomain(\
+                    voltage = options.gpu_voltage)))" % locals()
+    else:
+        constructor_call = "TLBCoalescer(probesPerCycle = \
+            options.L%(level)dProbesPerCycle, \
+            coalescingWindow = options.L%(level)dCoalescingWindow,\
+            disableCoalescing = options.L%(level)dDisableCoalescing,\
+            clk_domain = SrcClockDomain(\
+                clock = options.gpu_clock,\
+                voltage_domain = VoltageDomain(\
+                    voltage = options.gpu_voltage)))" % locals()
+    return constructor_call
+
 def create_TLB_Coalescer(options, my_level, my_index, tlb_name,
-    coalescer_name):
+                         coalescer_name, gpu_ctrl=None, full_system=False):
     # arguments: options, TLB level, number of private structures for this
     # Level, TLB name and  Coalescer name
     for i in range(my_index):
-        tlb_name.append(eval(TLB_constructor(my_level)))
-        coalescer_name.append(eval(Coalescer_constructor(my_level)))
+        tlb_name.append(
+            eval(TLB_constructor(options, my_level, gpu_ctrl, full_system)))
+        coalescer_name.append(
+            eval(Coalescer_constructor(options, my_level, full_system)))
 
-def config_tlb_hierarchy(options, system, shader_idx):
+def config_tlb_hierarchy(options, system, shader_idx, gpu_ctrl=None,
+                         full_system=False):
     n_cu = options.num_compute_units
 
     if options.TLB_config == "perLane":
@@ -121,7 +149,7 @@
                     options.L1TLBassoc = options.L1TLBentries
             # call the constructors for the TLB and the Coalescer
             create_TLB_Coalescer(options, level, TLB_index,\
-                TLB_array, Coalescer_array)
+                TLB_array, Coalescer_array, gpu_ctrl, full_system)
 
             system_TLB_name = TLB_type['name'] + '_tlb'
             system_Coalescer_name = TLB_type['name'] + '_coalescer'
@@ -198,8 +226,17 @@
                     system.l2_coalescer[0].cpu_side_ports[%d]' % \
                     (name, index, l2_coalescer_index))
             l2_coalescer_index += 1
+
     # L2 <-> L3
     system.l2_tlb[0].mem_side_ports[0] = \
         system.l3_coalescer[0].cpu_side_ports[0]
 
+    # L3 TLB Vega page table walker to memory for full system only
+    if full_system:
+        for TLB_type in L3:
+            name = TLB_type['name']
+            for index in range(TLB_type['width']):
+                exec('system._dma_ports.append(system.%s_tlb[%d].walker)' % \
+                        (name, index))
+
     return system
diff --git a/configs/common/MemConfig.py b/configs/common/MemConfig.py
index 15af26f..332fd6b 100644
--- a/configs/common/MemConfig.py
+++ b/configs/common/MemConfig.py
@@ -235,7 +235,7 @@
                 # Create a controller if not sharing a channel with DRAM
                 # in which case the controller has already been created
                 if not opt_hybrid_channel:
-                    mem_ctrl = m5.objects.MemCtrl()
+                    mem_ctrl = m5.objects.HeteroMemCtrl()
                     mem_ctrl.nvm = nvm_intf
 
                     mem_ctrls.append(mem_ctrl)
diff --git a/configs/common/Simulation.py b/configs/common/Simulation.py
index 3b9efc0..2416773 100644
--- a/configs/common/Simulation.py
+++ b/configs/common/Simulation.py
@@ -488,6 +488,7 @@
                     options.indirect_bp_type)
                 switch_cpus[i].branchPred.indirectBranchPred = \
                     IndirectBPClass()
+            switch_cpus[i].createThreads()
 
         # If elastic tracing is enabled attach the elastic trace probe
         # to the switch CPUs
diff --git a/configs/common/cores/arm/HPI.py b/configs/common/cores/arm/HPI.py
index 620c01e..3a11133 100644
--- a/configs/common/cores/arm/HPI.py
+++ b/configs/common/cores/arm/HPI.py
@@ -1379,7 +1379,7 @@
     write_buffers = 16
     # prefetcher FIXME
 
-class HPI(MinorCPU):
+class HPI(ArmMinorCPU):
     # Inherit the doc string from the module to avoid repeating it
     # here.
     __doc__ = __doc__
diff --git a/configs/common/cores/arm/O3_ARM_v7a.py b/configs/common/cores/arm/O3_ARM_v7a.py
index 8cacc65..d032a1a 100644
--- a/configs/common/cores/arm/O3_ARM_v7a.py
+++ b/configs/common/cores/arm/O3_ARM_v7a.py
@@ -99,7 +99,7 @@
     RASSize = 16
     instShiftAmt = 2
 
-class O3_ARM_v7a_3(DerivO3CPU):
+class O3_ARM_v7a_3(ArmO3CPU):
     LQEntries = 16
     SQEntries = 16
     LSQDepCheckShift = 0
diff --git a/configs/common/cores/arm/ex5_LITTLE.py b/configs/common/cores/arm/ex5_LITTLE.py
index bcbaa92..57f6a6b 100644
--- a/configs/common/cores/arm/ex5_LITTLE.py
+++ b/configs/common/cores/arm/ex5_LITTLE.py
@@ -88,7 +88,7 @@
         ex5_LITTLE_FP(), ex5_LITTLE_MemFU(),
         ex5_LITTLE_MiscFU()]
 
-class ex5_LITTLE(MinorCPU):
+class ex5_LITTLE(ArmMinorCPU):
     executeFuncUnits = ex5_LITTLE_FUP()
 
 class L1Cache(Cache):
diff --git a/configs/common/cores/arm/ex5_big.py b/configs/common/cores/arm/ex5_big.py
index eb5f53f..de7a450 100644
--- a/configs/common/cores/arm/ex5_big.py
+++ b/configs/common/cores/arm/ex5_big.py
@@ -99,7 +99,7 @@
     RASSize = 48
     instShiftAmt = 2
 
-class ex5_big(DerivO3CPU):
+class ex5_big(ArmO3CPU):
     LQEntries = 16
     SQEntries = 16
     LSQDepCheckShift = 0
diff --git a/configs/example/apu_se.py b/configs/example/apu_se.py
index 882d181..b5fb9ff 100644
--- a/configs/example/apu_se.py
+++ b/configs/example/apu_se.py
@@ -161,7 +161,7 @@
                     ' m5_switchcpu pseudo-ops will toggle back and forth')
 parser.add_argument("--num-hw-queues", type=int, default=10,
                     help="number of hw queues in packet processor")
-parser.add_argument("--reg-alloc-policy", type=str, default="simple",
+parser.add_argument("--reg-alloc-policy", type=str, default="dynamic",
                     help="register allocation policy (simple/dynamic)")
 
 parser.add_argument("--dgpu", action="store_true", default=False,
@@ -544,6 +544,7 @@
     have_kvm_support = 'BaseKvmCPU' in globals()
     if have_kvm_support and buildEnv['TARGET_ISA'] == "x86":
         system.vm = KvmVM()
+        system.m5ops_base = 0xffff0000
         for i in range(len(host_cpu.workload)):
             host_cpu.workload[i].useArchPT = True
             host_cpu.workload[i].kvmInSE = True
diff --git a/configs/example/arm/baremetal.py b/configs/example/arm/baremetal.py
index 0944344..44e3fd1 100644
--- a/configs/example/arm/baremetal.py
+++ b/configs/example/arm/baremetal.py
@@ -141,8 +141,6 @@
     system.realview.gic.gicv4 = False
 
     system.highest_el_is_64 = True
-    system.release.add(ArmExtension('SECURITY'))
-    system.release.add(ArmExtension('VIRTUALIZATION'))
 
     workload_class = workloads.workload_list.get(args.workload)
     system.workload = workload_class(
diff --git a/configs/example/arm/devices.py b/configs/example/arm/devices.py
index 9122e7c..a488ab3 100644
--- a/configs/example/arm/devices.py
+++ b/configs/example/arm/devices.py
@@ -176,7 +176,7 @@
 class AtomicCluster(CpuCluster):
     def __init__(self, system, num_cpus, cpu_clock, cpu_voltage="1.0V"):
         cpu_config = [ ObjectList.cpu_list.get("AtomicSimpleCPU"), None,
-                       None, None, None ]
+                       None, None ]
         super(AtomicCluster, self).__init__(system, num_cpus, cpu_clock,
                                             cpu_voltage, *cpu_config)
     def addL1(self):
@@ -185,7 +185,7 @@
 class KvmCluster(CpuCluster):
     def __init__(self, system, num_cpus, cpu_clock, cpu_voltage="1.0V"):
         cpu_config = [ ObjectList.cpu_list.get("ArmV8KvmCPU"), None, None,
-            None, None ]
+            None ]
         super(KvmCluster, self).__init__(system, num_cpus, cpu_clock,
                                          cpu_voltage, *cpu_config)
     def addL1(self):
diff --git a/configs/example/arm/ruby_fs.py b/configs/example/arm/ruby_fs.py
index d820f86..ecca22b 100644
--- a/configs/example/arm/ruby_fs.py
+++ b/configs/example/arm/ruby_fs.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016-2017, 2020-2021 Arm Limited
+# Copyright (c) 2016-2017, 2020-2022 Arm Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -46,7 +46,7 @@
 from common import ObjectList
 from common import Options
 from common import SysPaths
-from common.cores.arm import HPI
+from common.cores.arm import O3_ARM_v7a, HPI
 from ruby import Ruby
 
 import devices
@@ -57,18 +57,12 @@
 default_root_device = '/dev/vda1'
 
 
-# Pre-defined CPU configurations. Each tuple must be ordered as : (cpu_class,
-# l1_icache_class, l1_dcache_class, walk_cache_class, l2_Cache_class). Any of
-# the cache class may be 'None' if the particular cache is not present.
+# Pre-defined CPU configurations.
 cpu_types = {
-
-    "noncaching" : ( NonCachingSimpleCPU, None, None, None),
-    "minor" : (MinorCPU,
-               devices.L1I, devices.L1D,
-               devices.L2),
-    "hpi" : ( HPI.HPI,
-              HPI.HPI_ICache, HPI.HPI_DCache,
-              HPI.HPI_L2)
+    "noncaching" : NonCachingSimpleCPU,
+    "minor" : MinorCPU,
+    "hpi" : HPI.HPI,
+    "o3" : O3_ARM_v7a.O3_ARM_v7a_3,
 }
 
 def create_cow_image(name):
@@ -100,7 +94,7 @@
         print("Error: Bootscript %s does not exist" % args.script)
         sys.exit(1)
 
-    cpu_class = cpu_types[args.cpu][0]
+    cpu_class = cpu_types[args.cpu]
     mem_mode = cpu_class.memory_mode()
 
     system = devices.ArmRubySystem(args.mem_size,
@@ -115,7 +109,7 @@
         devices.CpuCluster(system,
                            args.num_cpus,
                            args.cpu_freq, "1.0V",
-                           *cpu_types[args.cpu]),
+                           cpu_class, None, None, None),
     ]
 
     # Add the PCI devices we need for this system. The base system
diff --git a/configs/example/arm/starter_fs.py b/configs/example/arm/starter_fs.py
index 40e645b..140f102 100644
--- a/configs/example/arm/starter_fs.py
+++ b/configs/example/arm/starter_fs.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016-2017, 2020 ARM Limited
+# Copyright (c) 2016-2017, 2020, 2022 Arm Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -50,7 +50,7 @@
 from common import SysPaths
 from common import ObjectList
 from common import MemConfig
-from common.cores.arm import HPI
+from common.cores.arm import O3_ARM_v7a, HPI
 
 import devices
 
@@ -61,17 +61,19 @@
 
 
 # Pre-defined CPU configurations. Each tuple must be ordered as : (cpu_class,
-# l1_icache_class, l1_dcache_class, walk_cache_class, l2_Cache_class). Any of
+# l1_icache_class, l1_dcache_class, l2_Cache_class). Any of
 # the cache class may be 'None' if the particular cache is not present.
 cpu_types = {
-
-    "atomic" : ( AtomicSimpleCPU, None, None, None),
+    "atomic" : (AtomicSimpleCPU, None, None, None),
     "minor" : (MinorCPU,
                devices.L1I, devices.L1D,
                devices.L2),
-    "hpi" : ( HPI.HPI,
-              HPI.HPI_ICache, HPI.HPI_DCache,
-              HPI.HPI_L2)
+    "hpi" : (HPI.HPI,
+             HPI.HPI_ICache, HPI.HPI_DCache,
+             HPI.HPI_L2),
+    "o3" : (O3_ARM_v7a.O3_ARM_v7a_3,
+            O3_ARM_v7a.O3_ARM_v7a_ICache, O3_ARM_v7a.O3_ARM_v7a_DCache,
+            O3_ARM_v7a.O3_ARM_v7aL2),
 }
 
 def create_cow_image(name):
@@ -148,6 +150,9 @@
             os.path.join(m5.options.outdir, 'system.dtb')
         system.generateDtb(system.workload.dtb_filename)
 
+    if args.initrd:
+        system.workload.initrd_filename = args.initrd
+
     # Linux boot command flags
     kernel_cmd = [
         # Tell Linux to use the simulated serial port as a console
@@ -196,6 +201,8 @@
                         help="DTB file to load")
     parser.add_argument("--kernel", type=str, default=default_kernel,
                         help="Linux kernel")
+    parser.add_argument("--initrd", type=str, default=None,
+                        help="initrd/initramfs file to load")
     parser.add_argument("--disk-image", type=str,
                         default=default_disk,
                         help="Disk to instantiate")
diff --git a/configs/example/gem5_library/arm-hello.py b/configs/example/gem5_library/arm-hello.py
index b1f6f38..d94fb33 100644
--- a/configs/example/gem5_library/arm-hello.py
+++ b/configs/example/gem5_library/arm-hello.py
@@ -37,7 +37,7 @@
 
 ```
 scons build/ARM/gem5.opt
-./build/ARM/gem5.opt configs/gem5_library/arm-hello.py
+./build/ARM/gem5.opt configs/example/gem5_library/arm-hello.py
 ```
 """
 
@@ -62,7 +62,7 @@
 memory = SingleChannelDDR3_1600(size="32MB")
 
 # We use a simple Timing processor with one core.
-processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, num_cores=1)
+processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, isa=ISA.ARM, num_cores=1)
 
 # The gem5 library simble board which can be used to run simple SE-mode
 # simulations.
@@ -88,7 +88,7 @@
 )
 
 # Lastly we run the simulation.
-simulator = Simulator(board=board, full_system=False)
+simulator = Simulator(board=board)
 simulator.run()
 
 print(
diff --git a/configs/example/gem5_library/arm-ubuntu-boot-exit.py b/configs/example/gem5_library/arm-ubuntu-boot-exit.py
new file mode 100644
index 0000000..163f45a
--- /dev/null
+++ b/configs/example/gem5_library/arm-ubuntu-boot-exit.py
@@ -0,0 +1,152 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+This script shows an example of booting an ARM based full system Ubuntu
+disk image using the gem5's standard library. This simulation boots the disk
+image using 2 TIMING CPU cores. The simulation ends when the startup is
+completed successfully (i.e. when an `m5_exit instruction is reached on
+successful boot).
+
+Usage
+-----
+
+```
+scons build/ARM/gem5.opt -j<NUM_CPUS>
+./build/ARM/gem5.opt configs/example/gem5_library/arm-ubuntu-boot-exit.py
+```
+
+"""
+
+from gem5.isas import ISA
+from m5.objects import ArmDefaultRelease
+from gem5.utils.requires import requires
+from gem5.resources.resource import Resource
+from gem5.simulate.simulator import Simulator
+from m5.objects import VExpress_GEM5_Foundation
+from gem5.components.boards.arm_board import ArmBoard
+from gem5.components.memory import DualChannelDDR4_2400
+from gem5.components.processors.cpu_types import CPUTypes
+from gem5.components.processors.simple_processor import SimpleProcessor
+
+# This runs a check to ensure the gem5 binary is compiled for ARM.
+
+requires(
+    isa_required=ISA.ARM,
+)
+
+# With ARM, we use simple caches.
+
+from gem5.components.cachehierarchies.classic\
+    .private_l1_private_l2_cache_hierarchy import (
+    PrivateL1PrivateL2CacheHierarchy,
+)
+
+
+# Here we setup the parameters of the l1 and l2 caches.
+
+cache_hierarchy = PrivateL1PrivateL2CacheHierarchy(
+    l1d_size="16kB",
+    l1i_size="16kB",
+    l2_size="256kB",
+)
+
+# Memory: Dual Channel DDR4 2400 DRAM device.
+
+memory = DualChannelDDR4_2400(size = "2GB")
+
+# Here we setup the processor. We use a simple TIMING processor. The config
+# script was also tested with ATOMIC processor.
+
+processor = SimpleProcessor(
+    cpu_type=CPUTypes.TIMING,
+    num_cores=2,
+)
+
+# The ArmBoard requires a `release` to be specified. This adds all the
+# extensions or features to the system. We are setting this to Armv8
+# (ArmDefaultRelease) in this example config script. However, the ArmBoard
+# currently does not support SECURITY extension.
+
+release = ArmDefaultRelease()
+
+# Removing the SECURITY extension.
+
+release.extensions.remove(release.extensions[2])
+
+# The platform sets up the memory ranges of all the on-chip and off-chip
+# devices present on the ARM system.
+
+platform = VExpress_GEM5_Foundation()
+
+# Here we setup the board. The ArmBoard allows for Full-System ARM simulations.
+
+board = ArmBoard(
+    clk_freq = "3GHz",
+    processor = processor,
+    memory = memory,
+    cache_hierarchy = cache_hierarchy,
+    release = release,
+    platform = platform
+)
+
+# Here we set the Full System workload.
+
+# The `set_kernel_disk_workload` function on the ArmBoard accepts an ARM
+# kernel, a disk image, and, path to the bootloader.
+
+board.set_kernel_disk_workload(
+
+    # The ARM kernel will be automatically downloaded to the `~/.cache/gem5`
+    # directory if not already present. The arm-ubuntu-boot-exit was tested
+    # with `vmlinux.arm64`
+
+    kernel = Resource("arm64-linux-kernel-5.4.49"),
+
+    # The ARM ubuntu image will be automatically downloaded to the
+    # `~/.cache/gem5` directory if not already present.
+
+    disk_image = Resource("arm64-ubuntu-18.04-img"),
+
+    # We need to specify the path for the bootloader file.
+
+    bootloader = Resource("arm64-bootloader-foundation"),
+
+    # For the arm64-ubuntu-18.04.img, we need to specify the readfile content
+
+    readfile_contents = "m5 exit"
+)
+
+# We define the system with the aforementioned system defined.
+
+simulator = Simulator(board = board)
+
+# Once the system successfully boots, it encounters an
+# `m5_exit instruction encountered`. We stop the simulation then. When the
+# simulation has ended you may inspect `m5out/board.terminal` to see
+# the stdout.
+
+simulator.run()
diff --git a/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py b/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py
new file mode 100644
index 0000000..4b06267
--- /dev/null
+++ b/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py
@@ -0,0 +1,112 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+This gem5 configuation script creates a simple board sharing the same
+structure as the one in
+configs/example/gem5_library/checkpoint/riscv-hello-save-checkpoint.py.
+This script restores the checkpoint generated by the above script, and
+runs the rest of "riscv-hello" binary simulation.
+This configuration serves as an example of restoring a checkpoint.
+
+This is setup is the close to the simplest setup possible using the gem5
+library. It does not contain any kind of caching, IO, or any non-essential
+components.
+
+Usage
+-----
+
+```
+scons build/RISCV/gem5.opt
+./build/RISCV/gem5.opt \
+    configs/example/gem5_library/checkpoint/riscv-hello-restore-checkpoint.py
+```
+"""
+
+from gem5.isas import ISA
+from gem5.utils.requires import requires
+from gem5.resources.resource import Resource
+from gem5.components.memory import SingleChannelDDR3_1600
+from gem5.components.processors.cpu_types import CPUTypes
+from gem5.components.boards.simple_board import SimpleBoard
+from gem5.components.cachehierarchies.classic.no_cache import NoCache
+from gem5.components.processors.simple_processor import SimpleProcessor
+from gem5.simulate.simulator import Simulator
+
+# This check ensures the gem5 binary is compiled to the RISCV ISA target.
+# If not, an exception will be thrown.
+requires(isa_required=ISA.RISCV)
+
+# In this setup we don't have a cache. `NoCache` can be used for such setups.
+cache_hierarchy = NoCache()
+
+# We use a single channel DDR3_1600 memory system
+memory = SingleChannelDDR3_1600(size="32MB")
+
+# We use a simple Timing processor with one core.
+processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, isa=ISA.RISCV,
+                            num_cores=1)
+
+# The gem5 library simble board which can be used to run simple SE-mode
+# simulations.
+board = SimpleBoard(
+    clk_freq="3GHz",
+    processor=processor,
+    memory=memory,
+    cache_hierarchy=cache_hierarchy,
+)
+
+# Here we set the workload. In this case we want to run a simple "Hello World!"
+# program compiled to the RISCV ISA. The `Resource` class will automatically
+# download the binary from the gem5 Resources cloud bucket if it's not already
+# present.
+board.set_se_binary_workload(
+    # the workload should be the same as the save-checkpoint script
+    Resource("riscv-hello")
+)
+
+# Getting the pre-taken checkpoint from gem5-resources. This checkpoint
+# was taken from running this gem5 configuration script,
+# configs/example/gem5_library/checkpoints/riscv-hello-save-checkpoint.py
+checkpoint_resource = Resource("riscv-hello-example-checkpoint")
+
+# Now we restore the checkpoint by passing the path to the checkpoint to
+# the Simulator object. The checkpoint_path could be a string containing
+# the path to the checkpoint folder. However, here, we use gem5 resources
+# to automatically download the checkpoint folder, and use .get_local_path()
+# to obtain the path to that folder.
+checkpoint_path = checkpoint_resource.get_local_path()
+print("Restore a checkpoint at", checkpoint_path)
+simulator = Simulator(board=board, full_system=False,
+                      checkpoint_path=checkpoint_path)
+simulator.run()
+
+print(
+    "Exiting @ tick {} because {}.".format(
+        simulator.get_current_tick(),
+        simulator.get_last_exit_event_cause(),
+    )
+)
diff --git a/configs/example/gem5_library/checkpoints/riscv-hello-save-checkpoint.py b/configs/example/gem5_library/checkpoints/riscv-hello-save-checkpoint.py
new file mode 100644
index 0000000..fd81d45
--- /dev/null
+++ b/configs/example/gem5_library/checkpoints/riscv-hello-save-checkpoint.py
@@ -0,0 +1,108 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+This gem5 configuation script creates a simple board to run the first
+10^6 ticks of "riscv-hello" binary simulation and saves a checkpoint.
+This configuration serves as an example of taking a checkpoint.
+
+This is setup is the close to the simplest setup possible using the gem5
+library. It does not contain any kind of caching, IO, or any non-essential
+components.
+
+Usage
+-----
+
+```
+scons build/RISCV/gem5.opt
+./build/RISCV/gem5.opt \
+    configs/example/gem5_library/checkpoint/riscv-hello-save-checkpoint.py
+```
+"""
+
+from gem5.isas import ISA
+from gem5.utils.requires import requires
+from gem5.resources.resource import Resource
+from gem5.components.memory import SingleChannelDDR3_1600
+from gem5.components.processors.cpu_types import CPUTypes
+from gem5.components.boards.simple_board import SimpleBoard
+from gem5.components.cachehierarchies.classic.no_cache import NoCache
+from gem5.components.processors.simple_processor import SimpleProcessor
+from gem5.simulate.simulator import Simulator
+
+# This check ensures the gem5 binary is compiled to the RISCV ISA target.
+# If not, an exception will be thrown.
+requires(isa_required=ISA.RISCV)
+
+# In this setup we don't have a cache. `NoCache` can be used for such setups.
+cache_hierarchy = NoCache()
+
+# We use a single channel DDR3_1600 memory system
+memory = SingleChannelDDR3_1600(size="32MB")
+
+# We use a simple Timing processor with one core.
+processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, isa=ISA.RISCV,
+                            num_cores=1)
+
+# The gem5 library simble board which can be used to run simple SE-mode
+# simulations.
+board = SimpleBoard(
+    clk_freq="3GHz",
+    processor=processor,
+    memory=memory,
+    cache_hierarchy=cache_hierarchy,
+)
+
+# Here we set the workload. In this case we want to run a simple "Hello World!"
+# program compiled to the RISCV ISA. The `Resource` class will automatically
+# download the binary from the gem5 Resources cloud bucket if it's not already
+# present.
+board.set_se_binary_workload(
+    # The `Resource` class reads the `resources.json` file from the gem5
+    # resources repository:
+    # https://gem5.googlesource.com/public/gem5-resource.
+    # Any resource specified in this file will be automatically retrieved.
+    # At the time of writing, this file is a WIP and does not contain all
+    # resources. Jira ticket: https://gem5.atlassian.net/browse/GEM5-1096
+    Resource("riscv-hello")
+)
+
+# Lastly we run the simulation.
+max_ticks = 10**6
+simulator = Simulator(board=board, full_system=False)
+simulator.run(max_ticks = max_ticks)
+
+print(
+    "Exiting @ tick {} because {}.".format(
+        simulator.get_current_tick(),
+        simulator.get_last_exit_event_cause(),
+    )
+)
+
+checkpoint_path = "riscv-hello-checkpoint/"
+print("Taking a checkpoint at", checkpoint_path)
+simulator.save_checkpoint(checkpoint_path)
+print("Done taking a checkpoint")
diff --git a/configs/example/gem5_library/riscv-fs.py b/configs/example/gem5_library/riscv-fs.py
index 4c1f117..dffb3d4 100644
--- a/configs/example/gem5_library/riscv-fs.py
+++ b/configs/example/gem5_library/riscv-fs.py
@@ -66,7 +66,9 @@
 memory = SingleChannelDDR3_1600()
 
 # Setup a single core Processor.
-processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, num_cores=1)
+processor = SimpleProcessor(
+    cpu_type=CPUTypes.TIMING, isa=ISA.RISCV, num_cores=1
+)
 
 # Setup the board.
 board = RiscvBoard(
diff --git a/configs/example/gem5_library/riscv-ubuntu-run.py b/configs/example/gem5_library/riscv-ubuntu-run.py
index f25d20f..f3e6d13 100644
--- a/configs/example/gem5_library/riscv-ubuntu-run.py
+++ b/configs/example/gem5_library/riscv-ubuntu-run.py
@@ -53,6 +53,7 @@
 from gem5.isas import ISA
 from gem5.coherence_protocol import CoherenceProtocol
 from gem5.resources.resource import Resource
+from gem5.simulate.simulator import Simulator
 
 # This runs a check to ensure the gem5 binary is compiled for RISCV.
 
@@ -79,6 +80,7 @@
 # Here we setup the processor. We use a simple processor.
 processor = SimpleProcessor(
     cpu_type=CPUTypes.TIMING,
+    isa=ISA.RISCV,
     num_cores=2,
 )
 
@@ -113,34 +115,5 @@
     ),
 )
 
-root = Root(full_system=True, system=board)
-
-m5.instantiate()
-
-# We simulate the system till we encounter `m5_exit instruction encountered`.
-
-exit_event = m5.simulate()
-
-# We check whether the simulation ended with `m5_exit instruction encountered`
-
-if exit_event.getCause() == "m5_exit instruction encountered":
-    # We acknowledge the user that the boot was successful.
-
-    print("Successfully completed booting!")
-else:
-    # `m5_exit instruction encountered` was never encountered. We exit the
-    # program unsuccessfully.
-
-    print("The startup was not completed successfully!",)
-    print(
-    "Exiting @ tick {} because {}."\
-        .format(m5.curTick(), exit_event.getCause())
-    )
-    exit(-1)
-
-# We are done with the simulation. We exit the program now.
-
-print(
-"Exiting @ tick {} because {}."\
-    .format(m5.curTick(), exit_event.getCause())
-)
+simulator = Simulator(board=board)
+simulator.run()
diff --git a/configs/example/gem5_library/x86-gapbs-benchmarks.py b/configs/example/gem5_library/x86-gapbs-benchmarks.py
index 92746aa..50f56d5 100644
--- a/configs/example/gem5_library/x86-gapbs-benchmarks.py
+++ b/configs/example/gem5_library/x86-gapbs-benchmarks.py
@@ -147,6 +147,7 @@
 processor = SimpleSwitchableProcessor(
     starting_core_type=CPUTypes.KVM,
     switch_core_type=CPUTypes.TIMING,
+    isa=ISA.X86,
     num_cores=2,
 )
 
diff --git a/configs/example/gem5_library/x86-npb-benchmarks.py b/configs/example/gem5_library/x86-npb-benchmarks.py
index 1609521..83cc700 100644
--- a/configs/example/gem5_library/x86-npb-benchmarks.py
+++ b/configs/example/gem5_library/x86-npb-benchmarks.py
@@ -164,6 +164,7 @@
 processor = SimpleSwitchableProcessor(
     starting_core_type=CPUTypes.KVM,
     switch_core_type=CPUTypes.TIMING,
+    isa=ISA.X86,
     num_cores=2,
 )
 
diff --git a/configs/example/gem5_library/x86-parsec-benchmarks.py b/configs/example/gem5_library/x86-parsec-benchmarks.py
index c3dd9ea..0d2e665 100644
--- a/configs/example/gem5_library/x86-parsec-benchmarks.py
+++ b/configs/example/gem5_library/x86-parsec-benchmarks.py
@@ -137,6 +137,7 @@
 processor = SimpleSwitchableProcessor(
     starting_core_type=CPUTypes.KVM,
     switch_core_type=CPUTypes.TIMING,
+    isa=ISA.X86,
     num_cores=2,
 )
 
diff --git a/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py b/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py
index c1b7a3b..2e52eef 100644
--- a/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py
+++ b/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py
@@ -187,6 +187,7 @@
 processor = SimpleSwitchableProcessor(
     starting_core_type=CPUTypes.KVM,
     switch_core_type=CPUTypes.TIMING,
+    isa=ISA.X86,
     num_cores=2,
 )
 
diff --git a/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py b/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py
index 2a03389..4e77dd0 100644
--- a/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py
+++ b/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py
@@ -193,6 +193,7 @@
 processor = SimpleSwitchableProcessor(
     starting_core_type=CPUTypes.KVM,
     switch_core_type=CPUTypes.TIMING,
+    isa=ISA.X86,
     num_cores=2,
 )
 
diff --git a/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py b/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py
index 1aea258..a47eb2b 100644
--- a/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py
+++ b/configs/example/gem5_library/x86-ubuntu-run-with-kvm.py
@@ -89,6 +89,7 @@
 processor = SimpleSwitchableProcessor(
     starting_core_type=CPUTypes.KVM,
     switch_core_type=CPUTypes.TIMING,
+    isa=ISA.X86,
     num_cores=2,
 )
 
diff --git a/configs/example/gpufs/DisjointNetwork.py b/configs/example/gpufs/DisjointNetwork.py
new file mode 100644
index 0000000..e1838bb
--- /dev/null
+++ b/configs/example/gpufs/DisjointNetwork.py
@@ -0,0 +1,107 @@
+# Copyright (c) 2021 Advanced Micro Devices, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from m5.objects import *
+from m5.util import fatal
+
+from importlib import *
+
+from network import Network
+
+class DisjointSimple(SimpleNetwork):
+
+    def __init__(self, ruby_system):
+        super(DisjointSimple, self).__init__()
+
+        self.netifs = []
+        self.routers = []
+        self.int_links = []
+        self.ext_links = []
+        self.ruby_system = ruby_system
+
+    def connectCPU(self, opts, controllers):
+
+        # Setup parameters for makeTopology call for CPU network
+        topo_module = import_module("topologies.%s" % opts.cpu_topology)
+        topo_class = getattr(topo_module, opts.cpu_topology)
+        _topo = topo_class(controllers)
+        _topo.makeTopology(opts, self, SimpleIntLink,
+                           SimpleExtLink, Switch)
+
+        self.initSimple(opts, self.int_links, self.ext_links)
+
+    def connectGPU(self, opts, controllers):
+
+        # Setup parameters for makeTopology call for GPU network
+        topo_module = import_module("topologies.%s" % opts.gpu_topology)
+        topo_class = getattr(topo_module, opts.gpu_topology)
+        _topo = topo_class(controllers)
+        _topo.makeTopology(opts, self, SimpleIntLink,
+                           SimpleExtLink, Switch)
+
+        self.initSimple(opts, self.int_links, self.ext_links)
+
+
+    def initSimple(self, opts, int_links, ext_links):
+
+        # Attach links to network
+        self.int_links = int_links
+        self.ext_links = ext_links
+
+        self.setup_buffers()
+
+class DisjointGarnet(GarnetNetwork):
+
+    def __init__(self, ruby_system):
+        super(DisjointGarnet, self).__init__()
+
+        self.netifs = []
+        self.ruby_system = ruby_system
+
+    def connectCPU(self, opts, controllers):
+
+        # Setup parameters for makeTopology call for CPU network
+        topo_module = import_module("topologies.%s" % opts.cpu_topology)
+        topo_class = getattr(topo_module, opts.cpu_topology)
+        _topo = topo_class(controllers)
+        _topo.makeTopology(opts, self, GarnetIntLink,
+                           GarnetExtLink, GarnetRouter)
+
+        Network.init_network(opts, self, GarnetNetworkInterface)
+
+    def connectGPU(self, opts, controllers):
+
+        # Setup parameters for makeTopology call
+        topo_module = import_module("topologies.%s" % opts.gpu_topology)
+        topo_class = getattr(topo_module, opts.gpu_topology)
+        _topo = topo_class(controllers)
+        _topo.makeTopology(opts, self, GarnetIntLink,
+                           GarnetExtLink, GarnetRouter)
+
+        Network.init_network(opts, self, GarnetNetworkInterface)
diff --git a/configs/example/gpufs/Disjoint_VIPER.py b/configs/example/gpufs/Disjoint_VIPER.py
new file mode 100644
index 0000000..8ddaeac
--- /dev/null
+++ b/configs/example/gpufs/Disjoint_VIPER.py
@@ -0,0 +1,196 @@
+# Copyright (c) 2021 Advanced Micro Devices, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from m5.defines import buildEnv
+from m5.objects import *
+from m5.util import fatal
+
+from example.gpufs.DisjointNetwork import *
+from ruby.GPU_VIPER import *
+from ruby import Ruby
+
+
+class DummySystem():
+    def __init__(self, mem_ranges):
+
+        self.mem_ctrls = []
+        self.mem_ranges = mem_ranges
+
+
+class Disjoint_VIPER(RubySystem):
+    def __init__(self):
+        if buildEnv['PROTOCOL'] != "GPU_VIPER":
+            fatal("This ruby config only supports the GPU_VIPER protocol")
+
+        super(Disjoint_VIPER, self).__init__()
+
+    def create(self, options, system, piobus, dma_devices):
+
+        # Disjoint network topology
+        if "garnet" in options.network:
+            self.network_cpu = DisjointGarnet(self)
+            self.network_gpu = DisjointGarnet(self)
+        else:
+            self.network_cpu = DisjointSimple(self)
+            self.network_gpu = DisjointSimple(self)
+
+
+        # Construct CPU controllers
+        cpu_dir_nodes = \
+            construct_dirs(options, system, self, self.network_cpu)
+        (cp_sequencers, cp_cntrl_nodes) = \
+            construct_corepairs(options, system, self, self.network_cpu)
+
+        # Construct GPU controllers
+        (tcp_sequencers, tcp_cntrl_nodes) = \
+            construct_tcps(options, system, self, self.network_gpu)
+        (sqc_sequencers, sqc_cntrl_nodes) = \
+            construct_sqcs(options, system, self, self.network_gpu)
+        (scalar_sequencers, scalar_cntrl_nodes) = \
+            construct_scalars(options, system, self, self.network_gpu)
+        tcc_cntrl_nodes = \
+            construct_tccs(options, system, self, self.network_gpu)
+
+        # Construct CPU memories
+        Ruby.setup_memory_controllers(system, self, cpu_dir_nodes, options)
+
+        # Construct GPU memories
+        (gpu_dir_nodes, gpu_mem_ctrls) = \
+            construct_gpudirs(options, system, self, self.network_gpu)
+
+        # Configure the directories based on which network they are in
+        for cpu_dir_node in cpu_dir_nodes:
+            cpu_dir_node.CPUonly = True
+            cpu_dir_node.GPUonly = False
+        for gpu_dir_node in gpu_dir_nodes:
+            gpu_dir_node.CPUonly = False
+            gpu_dir_node.GPUonly = True
+
+        # Set access backing store if specified
+        if options.access_backing_store:
+            self.access_backing_store = True
+
+        # Assign the memory controllers to the system
+        cpu_abstract_mems = []
+        for mem_ctrl in system.mem_ctrls:
+            cpu_abstract_mems.append(mem_ctrl.dram)
+        system.memories = cpu_abstract_mems
+
+        gpu_abstract_mems = []
+        for mem_ctrl in gpu_mem_ctrls:
+            gpu_abstract_mems.append(mem_ctrl.dram)
+        system.pc.south_bridge.gpu.memories = gpu_abstract_mems
+
+        # Setup DMA controllers
+        gpu_dma_types = ["VegaPagetableWalker", "AMDGPUMemoryManager"]
+
+        cpu_dma_ctrls = []
+        gpu_dma_ctrls = []
+        dma_cntrls = []
+        for i, dma_device in enumerate(dma_devices):
+            dma_seq = DMASequencer(version=i, ruby_system=self)
+            dma_cntrl = DMA_Controller(version=i, dma_sequencer=dma_seq,
+                                       ruby_system=self)
+
+            # Handle inconsistently named ports on various DMA devices:
+            if not hasattr(dma_device, 'type'):
+                # IDE doesn't have a .type but seems like everything else does.
+                dma_seq.in_ports = dma_device
+            elif dma_device.type in gpu_dma_types:
+                dma_seq.in_ports = dma_device.port
+            else:
+                dma_seq.in_ports = dma_device.dma
+
+            if hasattr(dma_device, 'type') and \
+                    dma_device.type in gpu_dma_types:
+                dma_cntrl.requestToDir = MessageBuffer(buffer_size=0)
+                dma_cntrl.requestToDir.out_port = self.network_gpu.in_port
+                dma_cntrl.responseFromDir = MessageBuffer(buffer_size=0)
+                dma_cntrl.responseFromDir.in_port = self.network_gpu.out_port
+                dma_cntrl.mandatoryQueue = MessageBuffer(buffer_size = 0)
+
+                gpu_dma_ctrls.append(dma_cntrl)
+            else:
+                dma_cntrl.requestToDir = MessageBuffer(buffer_size=0)
+                dma_cntrl.requestToDir.out_port = self.network_cpu.in_port
+                dma_cntrl.responseFromDir = MessageBuffer(buffer_size=0)
+                dma_cntrl.responseFromDir.in_port = self.network_cpu.out_port
+                dma_cntrl.mandatoryQueue = MessageBuffer(buffer_size = 0)
+
+                cpu_dma_ctrls.append(dma_cntrl)
+
+            dma_cntrls.append(dma_cntrl)
+
+        system.dma_cntrls = dma_cntrls
+
+
+        # Collect CPU and GPU controllers into seperate lists
+        cpu_cntrls = cpu_dir_nodes + cp_cntrl_nodes + cpu_dma_ctrls
+        gpu_cntrls = tcp_cntrl_nodes + sqc_cntrl_nodes + \
+                scalar_cntrl_nodes + tcc_cntrl_nodes + gpu_dma_ctrls + \
+                gpu_dir_nodes
+
+
+        # Setup number of vnets
+        self.number_of_virtual_networks = 11
+        self.network_cpu.number_of_virtual_networks = 11
+        self.network_gpu.number_of_virtual_networks = 11
+
+
+        # Set up the disjoint topology
+        self.network_cpu.connectCPU(options, cpu_cntrls)
+        self.network_gpu.connectGPU(options, gpu_cntrls)
+
+
+        # Create port proxy for connecting system port. System port is used
+        # for loading from outside guest, e.g., binaries like vmlinux.
+        system.sys_port_proxy = RubyPortProxy(ruby_system = self)
+        system.sys_port_proxy.pio_request_port = piobus.cpu_side_ports
+        system.system_port = system.sys_port_proxy.in_ports
+
+
+        # Only CPU sequencers connect to PIO bus. This acts as the "default"
+        # destination for unknown address ranges. PCIe requests fall under
+        # this category.
+        for i in range(len(cp_sequencers)):
+            cp_sequencers[i].pio_request_port = piobus.cpu_side_ports
+            cp_sequencers[i].mem_request_port = piobus.cpu_side_ports
+
+            # The CorePairs in MOESI_AMD_Base round up when constructing
+            # sequencers, but if the CPU does not exit there would be no
+            # sequencer to send a range change, leading to assert.
+            if i < options.num_cpus:
+                cp_sequencers[i].pio_response_port = piobus.mem_side_ports
+
+
+        # Setup ruby port. Both CPU and GPU are actually connected here.
+        all_sequencers = cp_sequencers + tcp_sequencers + \
+                         sqc_sequencers + scalar_sequencers
+        self._cpu_ports = all_sequencers
+        self.num_of_sequencers = len(all_sequencers)
diff --git a/configs/example/gpufs/hip_cookbook.py b/configs/example/gpufs/hip_cookbook.py
new file mode 100644
index 0000000..cd0e284
--- /dev/null
+++ b/configs/example/gpufs/hip_cookbook.py
@@ -0,0 +1,131 @@
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import m5
+import runfs
+import tempfile
+import argparse
+import sys
+import os
+
+from amd import AmdGPUOptions
+from common import Options
+from common import GPUTLBOptions
+from ruby import Ruby
+
+cookbook_runscript = '''\
+export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH
+export HSA_ENABLE_SDMA=0
+dmesg -n3
+dd if=/root/roms/vega10.rom of=/dev/mem bs=1k seek=768 count=128
+if [ ! -f /lib/modules/`uname -r`/updates/dkms/amdgpu.ko ]; then
+    echo "ERROR: Missing DKMS package for kernel `uname -r`. Exiting gem5."
+    /sbin/m5 exit
+fi
+modprobe -v amdgpu ip_block_mask=0xff ppfeaturemask=0 dpm=0 audio=0
+echo "Running {}"
+cd /opt/rocm/hip/samples/2_Cookbook/{}/
+make clean
+make
+/sbin/m5 exit
+'''
+
+def addCookbookOptions(parser):
+    parser.add_argument("-a", "--app", default=None,
+                        choices=['0_MatrixTranspose',
+                                 '1_hipEvent',
+                                 '3_shared_memory',
+                                 '4_shfl',
+                                 '5_2dshfl',
+                                 '6_dynamic_shared',
+                                 '7_streams',
+                                 '8_peer2peer',
+                                 '9_unroll',
+                                 '10_inline_asm',
+                                 '11_texture_driver',
+                                 '13_occupancy',
+                                 '14_gpu_arch',
+                                 '15_static_library'],
+                        help="GPU application to run")
+    parser.add_argument("-o", "--opts", default="",
+                        help="GPU application arguments")
+
+if __name__ == "__m5_main__":
+    parser = argparse.ArgumentParser()
+    runfs.addRunFSOptions(parser)
+    Options.addCommonOptions(parser)
+    AmdGPUOptions.addAmdGPUOptions(parser)
+    Ruby.define_options(parser)
+    GPUTLBOptions.tlb_options(parser)
+    addCookbookOptions(parser)
+
+    # Parse now so we can override options
+    args = parser.parse_args()
+
+    # Create temp script to run application
+    if args.app is None:
+        print("No application given. Use %s -a <app>" % sys.argv[0])
+        sys.exit(1)
+    elif args.kernel is None:
+        print("No kernel path given. Use %s --kernel <vmlinux>" % sys.argv[0])
+        sys.exit(1)
+    elif args.disk_image is None:
+        print("No disk path given. Use %s --disk-image <linux>" % sys.argv[0])
+        sys.exit(1)
+    elif args.gpu_mmio_trace is None:
+        print("No MMIO trace path. Use %s --gpu-mmio-trace <path>"
+                % sys.argv[0])
+        sys.exit(1)
+
+    _, tempRunscript = tempfile.mkstemp()
+    with open(tempRunscript, 'w') as b64file:
+        runscriptStr = cookbook_runscript.format(args.app, args.app)
+        b64file.write(runscriptStr)
+
+    if args.second_disk == None:
+        args.second_disk = args.disk_image
+
+    # Defaults for Vega10
+    args.ruby = True
+    args.cpu_type = 'X86KvmCPU'
+    args.num_cpus = 1
+    args.mem_size = '3GB'
+    args.dgpu = True
+    args.dgpu_mem_size = '16GB'
+    args.dgpu_start = '0GB'
+    args.checkpoint_restore = 0
+    args.disjoint = True
+    args.timing_gpu = True
+    args.script = tempRunscript
+    args.dgpu_xor_low_bit = 0
+
+    print(args.disk_image)
+
+    # Run gem5
+    runfs.runGpuFSSystem(args)
diff --git a/configs/example/gpufs/hip_rodinia.py b/configs/example/gpufs/hip_rodinia.py
new file mode 100644
index 0000000..3d7cef4
--- /dev/null
+++ b/configs/example/gpufs/hip_rodinia.py
@@ -0,0 +1,139 @@
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import m5
+import runfs
+import base64
+import tempfile
+import argparse
+import sys
+import os
+
+from amd import AmdGPUOptions
+from common import Options
+from common import GPUTLBOptions
+from ruby import Ruby
+
+rodinia_runscript = '''\
+export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH
+export HSA_ENABLE_SDMA=0
+dmesg -n3
+dd if=/root/roms/vega10.rom of=/dev/mem bs=1k seek=768 count=128
+if [ ! -f /lib/modules/`uname -r`/updates/dkms/amdgpu.ko ]; then
+    echo "ERROR: Missing DKMS package for kernel `uname -r`. Exiting gem5."
+    /sbin/m5 exit
+fi
+modprobe -v amdgpu ip_block_mask=0xff ppfeaturemask=0 dpm=0 audio=0
+echo "Running {}"
+cd /home/gem5/HIP-Examples/rodinia_3.0/hip/{}/
+make clean
+make
+make test
+/sbin/m5 exit
+'''
+
+def addRodiniaOptions(parser):
+    parser.add_argument("-a", "--app", default=None,
+                        choices=['b+tree',
+                                 'backprop',
+                                 'bfs',
+                                 'cfd',
+                                 'dwt2d',
+                                 'gaussian',
+                                 'heartwall',
+                                 'hotspot',
+                                 'hybridsort',
+                                 'kmeans',
+                                 'lavaMD',
+                                 'leukocyte',
+                                 'lud',
+                                 'myocyte',
+                                 'nn',
+                                 'nw',
+                                 'particlefilter',
+                                 'pathfinder',
+                                 'srad',
+                                 'streamcluster'],
+                        help="GPU application to run")
+    parser.add_argument("-o", "--opts", default="",
+                        help="GPU application arguments")
+
+if __name__ == "__m5_main__":
+    parser = argparse.ArgumentParser()
+    runfs.addRunFSOptions(parser)
+    Options.addCommonOptions(parser)
+    AmdGPUOptions.addAmdGPUOptions(parser)
+    Ruby.define_options(parser)
+    GPUTLBOptions.tlb_options(parser)
+    addRodiniaOptions(parser)
+
+    # Parse now so we can override options
+    args = parser.parse_args()
+
+    # Create temp script to run application
+    if args.app is None:
+        print("No application given. Use %s -a <app>" % sys.argv[0])
+        sys.exit(1)
+    elif args.kernel is None:
+        print("No kernel path given. Use %s --kernel <vmlinux>" % sys.argv[0])
+        sys.exit(1)
+    elif args.disk_image is None:
+        print("No disk path given. Use %s --disk-image <linux>" % sys.argv[0])
+        sys.exit(1)
+    elif args.gpu_mmio_trace is None:
+        print("No MMIO trace path. Use %s --gpu-mmio-trace <path>"
+                % sys.argv[0])
+        sys.exit(1)
+
+    _, tempRunscript = tempfile.mkstemp()
+    with open(tempRunscript, 'w') as b64file:
+        runscriptStr = rodinia_runscript.format(args.app, args.app)
+        b64file.write(runscriptStr)
+
+    if args.second_disk == None:
+        args.second_disk = args.disk_image
+
+    # Defaults for Vega10
+    args.ruby = True
+    args.cpu_type = 'X86KvmCPU'
+    args.num_cpus = 1
+    args.mem_size = '3GB'
+    args.dgpu = True
+    args.dgpu_mem_size = '16GB'
+    args.dgpu_start = '0GB'
+    args.checkpoint_restore = 0
+    args.disjoint = True
+    args.timing_gpu = True
+    args.script = tempRunscript
+    args.dgpu_xor_low_bit = 0
+
+    print(args.disk_image)
+
+    # Run gem5
+    runfs.runGpuFSSystem(args)
diff --git a/configs/example/gpufs/hip_samples.py b/configs/example/gpufs/hip_samples.py
new file mode 100644
index 0000000..b3a66a7
--- /dev/null
+++ b/configs/example/gpufs/hip_samples.py
@@ -0,0 +1,129 @@
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import m5
+import runfs
+import tempfile
+import argparse
+import sys
+import os
+
+from amd import AmdGPUOptions
+from common import Options
+from common import GPUTLBOptions
+from ruby import Ruby
+
+samples_runscript = '''\
+export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH
+export HSA_ENABLE_SDMA=0
+dmesg -n3
+dd if=/root/roms/vega10.rom of=/dev/mem bs=1k seek=768 count=128
+if [ ! -f /lib/modules/`uname -r`/updates/dkms/amdgpu.ko ]; then
+    echo "ERROR: Missing DKMS package for kernel `uname -r`. Exiting gem5."
+    /sbin/m5 exit
+fi
+modprobe -v amdgpu ip_block_mask=0xff ppfeaturemask=0 dpm=0 audio=0
+echo "Running {}"
+cd /home/gem5/HIP-Examples/HIP-Examples-Applications/{}/
+make clean
+make
+/sbin/m5 exit
+'''
+
+def addSamplesOptions(parser):
+    parser.add_argument("-a", "--app", default=None,
+                        choices=['BinomialOption',
+                                 'BitonicSort',
+                                 'FastWalshTransform',
+                                 'FloydWarshall',
+                                 'HelloWorld',
+                                 'Histogram',
+                                 'MatrixMultiplication',
+                                 'PrefixSum',
+                                 'RecursiveGaussian',
+                                 'SimpleConvolution',
+                                 'dct',
+                                 'dwtHaar1D'],
+                        help="GPU application to run")
+    parser.add_argument("-o", "--opts", default="",
+                        help="GPU application arguments")
+
+if __name__ == "__m5_main__":
+    parser = argparse.ArgumentParser()
+    runfs.addRunFSOptions(parser)
+    Options.addCommonOptions(parser)
+    AmdGPUOptions.addAmdGPUOptions(parser)
+    Ruby.define_options(parser)
+    GPUTLBOptions.tlb_options(parser)
+    addSamplesOptions(parser)
+
+    # Parse now so we can override options
+    args = parser.parse_args()
+
+    # Create temp script to run application
+    if args.app is None:
+        print("No application given. Use %s -a <app>" % sys.argv[0])
+        sys.exit(1)
+    elif args.kernel is None:
+        print("No kernel path given. Use %s --kernel <vmlinux>" % sys.argv[0])
+        sys.exit(1)
+    elif args.disk_image is None:
+        print("No disk path given. Use %s --disk-image <linux>" % sys.argv[0])
+        sys.exit(1)
+    elif args.gpu_mmio_trace is None:
+        print("No MMIO trace path. Use %s --gpu-mmio-trace <path>"
+                % sys.argv[0])
+        sys.exit(1)
+
+    _, tempRunscript = tempfile.mkstemp()
+    with open(tempRunscript, 'w') as b64file:
+        runscriptStr = samples_runscript.format(args.app, args.app)
+        b64file.write(runscriptStr)
+
+    if args.second_disk == None:
+        args.second_disk = args.disk_image
+
+    # Defaults for Vega10
+    args.ruby = True
+    args.cpu_type = 'X86KvmCPU'
+    args.num_cpus = 1
+    args.mem_size = '3GB'
+    args.dgpu = True
+    args.dgpu_mem_size = '16GB'
+    args.dgpu_start = '0GB'
+    args.checkpoint_restore = 0
+    args.disjoint = True
+    args.timing_gpu = True
+    args.script = tempRunscript
+    args.dgpu_xor_low_bit = 0
+
+    print(args.disk_image)
+
+    # Run gem5
+    runfs.runGpuFSSystem(args)
diff --git a/configs/example/gpufs/runfs.py b/configs/example/gpufs/runfs.py
index e5acf05..b198552 100644
--- a/configs/example/gpufs/runfs.py
+++ b/configs/example/gpufs/runfs.py
@@ -71,7 +71,20 @@
                         help="Take a checkpoint before driver sends MMIOs. "
                         "This is used to switch out of KVM mode and into "
                         "timing mode required to read the VGA ROM on boot.")
-
+    parser.add_argument("--cpu-topology", type=str, default="Crossbar",
+                        help="Network topology to use for CPU side. "
+                        "Check configs/topologies for complete set")
+    parser.add_argument("--gpu-topology", type=str, default="Crossbar",
+                        help="Network topology to use for GPU side. "
+                        "Check configs/topologies for complete set")
+    parser.add_argument("--dgpu-mem-size", action="store", type=str,
+                        default="16GB", help="Specify the dGPU physical memory"
+                        "  size")
+    parser.add_argument("--dgpu-num-dirs", type=int, default=1, help="Set "
+                        "the number of dGPU directories (memory controllers")
+    parser.add_argument("--dgpu-mem-type", default="HBM_1000_4H_1x128",
+                        choices=ObjectList.mem_list.get_names(),
+                        help="type of memory to use")
 
 def runGpuFSSystem(args):
     '''
diff --git a/configs/example/gpufs/system/system.py b/configs/example/gpufs/system/system.py
index d06cd2c..972a4f9 100644
--- a/configs/example/gpufs/system/system.py
+++ b/configs/example/gpufs/system/system.py
@@ -33,9 +33,12 @@
 
 from common.Benchmarks import *
 from common.FSConfig import *
+from common import GPUTLBConfig
 from common import Simulation
 from ruby import Ruby
 
+from example.gpufs.Disjoint_VIPER import *
+
 def makeGpuFSSystem(args):
     # Boot options are standard gem5 options plus:
     # - Framebuffer device emulation 0 to reduce driver code paths.
@@ -54,7 +57,10 @@
 
     # Use the common FSConfig to setup a Linux X86 System
     (TestCPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(args)
-    bm = SysConfig(disks=[args.disk_image], mem=args.mem_size)
+    disks = [args.disk_image]
+    if args.second_disk is not None:
+        disks.extend([args.second_disk])
+    bm = SysConfig(disks=disks, mem=args.mem_size)
     system = makeLinuxX86System(test_mem_mode, args.num_cpus, bm, True,
                                   cmdline=cmdline)
     system.workload.object_file = binary(args.kernel)
@@ -77,45 +83,102 @@
     system.shadow_rom_ranges = [AddrRange(0xc0000, size = Addr('128kB'))]
 
     # Create specified number of CPUs. GPUFS really only needs one.
-    system.cpu = [TestCPUClass(clk_domain=system.cpu_clk_domain, cpu_id=i)
+    system.cpu = [X86KvmCPU(clk_domain=system.cpu_clk_domain, cpu_id=i)
                     for i in range(args.num_cpus)]
-
-    if ObjectList.is_kvm_cpu(TestCPUClass) or \
-        ObjectList.is_kvm_cpu(FutureClass):
-        system.kvm_vm = KvmVM()
+    system.kvm_vm = KvmVM()
 
     # Create AMDGPU and attach to southbridge
     shader = createGPU(system, args)
     connectGPU(system, args)
 
+    # The shader core will be whatever is after the CPU cores are accounted for
+    shader_idx = args.num_cpus
+    system.cpu.append(shader)
+
     # This arbitrary address is something in the X86 I/O hole
     hsapp_gpu_map_paddr = 0xe00000000
+    hsapp_pt_walker = VegaPagetableWalker()
     gpu_hsapp = HSAPacketProcessor(pioAddr=hsapp_gpu_map_paddr,
-                                   numHWQueues=args.num_hw_queues)
+                                   numHWQueues=args.num_hw_queues,
+                                   walker=hsapp_pt_walker)
     dispatcher = GPUDispatcher()
+    cp_pt_walker = VegaPagetableWalker()
     gpu_cmd_proc = GPUCommandProcessor(hsapp=gpu_hsapp,
-                                       dispatcher=dispatcher)
+                                       dispatcher=dispatcher,
+                                       walker=cp_pt_walker)
     shader.dispatcher = dispatcher
     shader.gpu_cmd_proc = gpu_cmd_proc
 
+    system.pc.south_bridge.gpu.cp = gpu_cmd_proc
+
+    # GPU Interrupt Handler
+    device_ih = AMDGPUInterruptHandler()
+    system.pc.south_bridge.gpu.device_ih = device_ih
+
+    # Setup the SDMA engines
+    sdma0_pt_walker = VegaPagetableWalker()
+    sdma1_pt_walker = VegaPagetableWalker()
+
+    sdma0 = SDMAEngine(walker=sdma0_pt_walker)
+    sdma1 = SDMAEngine(walker=sdma1_pt_walker)
+
+    system.pc.south_bridge.gpu.sdma0 = sdma0
+    system.pc.south_bridge.gpu.sdma1 = sdma1
+
+    # Setup PM4 packet processor
+    pm4_pkt_proc = PM4PacketProcessor()
+    system.pc.south_bridge.gpu.pm4_pkt_proc = pm4_pkt_proc
+
+    # GPU data path
+    gpu_mem_mgr = AMDGPUMemoryManager()
+    system.pc.south_bridge.gpu.memory_manager = gpu_mem_mgr
+
+    # CPU data path (SystemHub)
+    system_hub = AMDGPUSystemHub()
+    shader.system_hub = system_hub
+
     # GPU, HSAPP, and GPUCommandProc are DMA devices
     system._dma_ports.append(gpu_hsapp)
     system._dma_ports.append(gpu_cmd_proc)
     system._dma_ports.append(system.pc.south_bridge.gpu)
+    system._dma_ports.append(sdma0)
+    system._dma_ports.append(sdma1)
+    system._dma_ports.append(device_ih)
+    system._dma_ports.append(pm4_pkt_proc)
+    system._dma_ports.append(system_hub)
+    system._dma_ports.append(gpu_mem_mgr)
+    system._dma_ports.append(hsapp_pt_walker)
+    system._dma_ports.append(cp_pt_walker)
+    system._dma_ports.append(sdma0_pt_walker)
+    system._dma_ports.append(sdma1_pt_walker)
 
     gpu_hsapp.pio = system.iobus.mem_side_ports
     gpu_cmd_proc.pio = system.iobus.mem_side_ports
     system.pc.south_bridge.gpu.pio = system.iobus.mem_side_ports
+    sdma0.pio = system.iobus.mem_side_ports
+    sdma1.pio = system.iobus.mem_side_ports
+    device_ih.pio = system.iobus.mem_side_ports
+    pm4_pkt_proc.pio = system.iobus.mem_side_ports
+    system_hub.pio = system.iobus.mem_side_ports
 
-    # Create Ruby system using Ruby.py for now
-    Ruby.create_system(args, True, system, system.iobus,
-                      system._dma_ports)
+    # Full system needs special TLBs for SQC, Scalar, and vector data ports
+    args.full_system = True
+    GPUTLBConfig.config_tlb_hierarchy(args, system, shader_idx,
+                                      system.pc.south_bridge.gpu, True)
+
+    # Create Ruby system using disjoint VIPER topology
+    system.ruby = Disjoint_VIPER()
+    system.ruby.create(args, system, system.iobus, system._dma_ports)
 
     # Create a seperate clock domain for Ruby
     system.ruby.clk_domain = SrcClockDomain(clock = args.ruby_clock,
                                    voltage_domain = system.voltage_domain)
 
     for (i, cpu) in enumerate(system.cpu):
+        # Break once we reach the shader "CPU"
+        if i == args.num_cpus:
+            break
+
         #
         # Tie the cpu ports to the correct ruby system ports
         #
@@ -125,9 +188,8 @@
 
         system.ruby._cpu_ports[i].connectCpuPorts(cpu)
 
-    # The shader core will be whatever is after the CPU cores are accounted for
-    shader_idx = args.num_cpus
-    system.cpu.append(shader)
+        for j in range(len(system.cpu[i].isa)):
+            system.cpu[i].isa[j].vendor_string = "AuthenticAMD"
 
     gpu_port_idx = len(system.ruby._cpu_ports) \
                    - args.num_compute_units - args.num_sqc \
diff --git a/configs/example/gpufs/vega10_kvm.py b/configs/example/gpufs/vega10_kvm.py
new file mode 100644
index 0000000..baee077
--- /dev/null
+++ b/configs/example/gpufs/vega10_kvm.py
@@ -0,0 +1,124 @@
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import m5
+import runfs
+import base64
+import tempfile
+import argparse
+import sys
+import os
+
+from amd import AmdGPUOptions
+from common import Options
+from common import GPUTLBOptions
+from ruby import Ruby
+
+
+demo_runscript = '''\
+export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH
+export HSA_ENABLE_SDMA=0
+dmesg -n3
+dd if=/root/roms/vega10.rom of=/dev/mem bs=1k seek=768 count=128
+if [ ! -f /lib/modules/`uname -r`/updates/dkms/amdgpu.ko ]; then
+    echo "ERROR: Missing DKMS package for kernel `uname -r`. Exiting gem5."
+    /sbin/m5 exit
+fi
+modprobe -v amdgpu ip_block_mask=0xff ppfeaturemask=0 dpm=0 audio=0
+echo "Running {} {}"
+echo "{}" | base64 -d > myapp
+chmod +x myapp
+./myapp {}
+/sbin/m5 exit
+'''
+
+def addDemoOptions(parser):
+    parser.add_argument("-a", "--app", default=None,
+                        help="GPU application to run")
+    parser.add_argument("-o", "--opts", default="",
+                        help="GPU application arguments")
+
+if __name__ == "__m5_main__":
+    parser = argparse.ArgumentParser()
+    runfs.addRunFSOptions(parser)
+    Options.addCommonOptions(parser)
+    AmdGPUOptions.addAmdGPUOptions(parser)
+    Ruby.define_options(parser)
+    GPUTLBOptions.tlb_options(parser)
+    addDemoOptions(parser)
+
+    # Parse now so we can override options
+    args = parser.parse_args()
+
+    # Create temp script to run application
+    if args.app is None:
+        print("No application given. Use %s -a <app>" % sys.argv[0])
+        sys.exit(1)
+    elif args.kernel is None:
+        print("No kernel path given. Use %s --kernel <vmlinux>" % sys.argv[0])
+        sys.exit(1)
+    elif args.disk_image is None:
+        print("No disk path given. Use %s --disk-image <linux>" % sys.argv[0])
+        sys.exit(1)
+    elif args.gpu_mmio_trace is None:
+        print("No MMIO trace path. Use %s --gpu-mmio-trace <path>"
+                % sys.argv[0])
+        sys.exit(1)
+    elif not os.path.isfile(args.app):
+        print("Could not find applcation", args.app)
+        sys.exit(1)
+
+    with open(os.path.abspath(args.app), 'rb') as binfile:
+        encodedBin = base64.b64encode(binfile.read()).decode()
+
+    _, tempRunscript = tempfile.mkstemp()
+    with open(tempRunscript, 'w') as b64file:
+        runscriptStr = demo_runscript.format(args.app, args.opts, encodedBin,
+                                             args.opts)
+        b64file.write(runscriptStr)
+
+    if args.second_disk == None:
+        args.second_disk = args.disk_image
+
+    # Defaults for Vega10
+    args.ruby = True
+    args.cpu_type = 'X86KvmCPU'
+    args.num_cpus = 1
+    args.mem_size = '3GB'
+    args.dgpu = True
+    args.dgpu_mem_size = '16GB'
+    args.dgpu_start = '0GB'
+    args.checkpoint_restore = 0
+    args.disjoint = True
+    args.timing_gpu = True
+    args.script = tempRunscript
+    args.dgpu_xor_low_bit = 0
+
+    # Run gem5
+    runfs.runGpuFSSystem(args)
diff --git a/configs/example/hmc_hello.py b/configs/example/hmc_hello.py
index 4507768..11d52c0 100644
--- a/configs/example/hmc_hello.py
+++ b/configs/example/hmc_hello.py
@@ -49,7 +49,7 @@
 system = System()
 # use timing mode for the interaction between requestor-responder ports
 system.mem_mode = 'timing'
-# set the clock fequency of the system
+# set the clock frequency of the system
 clk = '1GHz'
 vd = VoltageDomain(voltage='1V')
 system.clk_domain = SrcClockDomain(clock=clk, voltage_domain=vd)
diff --git a/configs/example/hmctest.py b/configs/example/hmctest.py
index f4c0a2c..429ea4a 100644
--- a/configs/example/hmctest.py
+++ b/configs/example/hmctest.py
@@ -50,7 +50,7 @@
     system = System()
     # use timing mode for the interaction between requestor-responder ports
     system.mem_mode = 'timing'
-    # set the clock fequency of the system
+    # set the clock frequency of the system
     clk = '100GHz'
     vd = VoltageDomain(voltage='1V')
     system.clk_domain = SrcClockDomain(clock=clk, voltage_domain=vd)
diff --git a/configs/example/lupv/run_lupv.py b/configs/example/lupv/run_lupv.py
index 93eaa86..e87d392 100644
--- a/configs/example/lupv/run_lupv.py
+++ b/configs/example/lupv/run_lupv.py
@@ -36,7 +36,6 @@
 import m5
 from m5.objects import Root
 
-from gem5.runtime import get_runtime_isa
 from gem5.components.boards.experimental.lupv_board import LupvBoard
 from gem5.components.memory.single_channel import SingleChannelDDR3_1600
 from gem5.components.processors.simple_processor import SimpleProcessor
@@ -83,11 +82,11 @@
 # Setup a single core Processor.
 if args.cpu_type == "atomic":
     processor = SimpleProcessor(
-        cpu_type=CPUTypes.ATOMIC, num_cores=args.num_cpus
+        cpu_type=CPUTypes.ATOMIC, num_cores=args.num_cpus, isa=ISA.RISCV
     )
 elif args.cpu_type == "timing":
     processor = SimpleProcessor(
-        cpu_type=CPUTypes.TIMING, num_cores=args.num_cpus
+        cpu_type=CPUTypes.TIMING, num_cores=args.num_cpus, isa=ISA.RISCV
     )
 
 # Setup the board.
@@ -106,7 +105,7 @@
 
 
 # Begin running of the simulation.
-print("Running with ISA: " + get_runtime_isa().name)
+print("Running with ISA: " + processor.get_isa().name)
 print()
 root = Root(full_system=True, system=board)
 m5.instantiate()
diff --git a/configs/example/noc_config/2x4.py b/configs/example/noc_config/2x4.py
index fe2522b..2d10da6 100644
--- a/configs/example/noc_config/2x4.py
+++ b/configs/example/noc_config/2x4.py
@@ -60,6 +60,10 @@
     class NoC_Params(CHI_config.CHI_HNF.NoC_Params):
         router_list = [1, 2, 5, 6]
 
+class CHI_MN(CHI_config.CHI_MN):
+    class NoC_Params(CHI_config.CHI_MN.NoC_Params):
+        router_list = [4]
+
 class CHI_SNF_MainMem(CHI_config.CHI_SNF_MainMem):
     class NoC_Params(CHI_config.CHI_SNF_MainMem.NoC_Params):
         router_list = [0, 4]
diff --git a/configs/example/ruby_gpu_random_test.py b/configs/example/ruby_gpu_random_test.py
index a5d9a49..029a97d 100644
--- a/configs/example/ruby_gpu_random_test.py
+++ b/configs/example/ruby_gpu_random_test.py
@@ -79,7 +79,7 @@
                     help="Random seed number. Default value (i.e., 0) means \
                         using runtime-specific value")
 parser.add_argument("--log-file", type=str, default="gpu-ruby-test.log")
-parser.add_argument("--num-dmas", type=int, default=0,
+parser.add_argument("--num-dmas", type=int, default=None,
                     help="The number of DMA engines to use in tester config.")
 
 args = parser.parse_args()
@@ -108,7 +108,7 @@
     args.wf_size = 1
     args.wavefronts_per_cu = 1
     args.num_cpus = 1
-    args.num_dmas = 1
+    n_DMAs = 1
     args.cu_per_sqc = 1
     args.cu_per_scalar_cache = 1
     args.num_compute_units = 1
@@ -117,7 +117,7 @@
     args.wf_size = 16
     args.wavefronts_per_cu = 4
     args.num_cpus = 4
-    args.num_dmas = 2
+    n_DMAs = 2
     args.cu_per_sqc = 4
     args.cu_per_scalar_cache = 4
     args.num_compute_units = 4
@@ -126,11 +126,19 @@
     args.wf_size = 32
     args.wavefronts_per_cu = 4
     args.num_cpus = 4
-    args.num_dmas = 4
+    n_DMAs = 4
     args.cu_per_sqc = 4
     args.cu_per_scalar_cache = 4
     args.num_compute_units = 8
 
+# Number of DMA engines
+if not(args.num_dmas is None):
+    n_DMAs = args.num_dmas
+    # currently the tester does not support requests returned as
+    # aliased, thus we need num_dmas to be 0 for it
+    if not(args.num_dmas == 0):
+        print("WARNING: num_dmas != 0 not supported with VIPER")
+
 #
 # Set address range - 2 options
 #   level 0: small
@@ -173,9 +181,6 @@
 # For now we're testing only GPU protocol, so we force num_cpus to be 0
 args.num_cpus = 0
 
-# Number of DMA engines
-n_DMAs = args.num_dmas
-
 # Number of CUs
 n_CUs = args.num_compute_units
 
@@ -234,11 +239,12 @@
 #
 # Make generic DMA sequencer for Ruby to use
 #
-dma_devices = [TesterDma()] * n_DMAs
-system.piobus = IOXBar()
-for _, dma_device in enumerate(dma_devices):
-    dma_device.pio = system.piobus.mem_side_ports
-system.dma_devices = dma_devices
+if n_DMAs > 0:
+    dma_devices = [TesterDma()] * n_DMAs
+    system.piobus = IOXBar()
+    for _, dma_device in enumerate(dma_devices):
+        dma_device.pio = system.piobus.mem_side_ports
+    system.dma_devices = dma_devices
 
 #
 # Create the Ruby system
@@ -250,7 +256,8 @@
 # size of system.cpu
 cpu_list = [ system.cpu ] * args.num_cpus
 Ruby.create_system(args, full_system = False,
-                   system = system, dma_ports = system.dma_devices,
+                   system = system,
+                   dma_ports = system.dma_devices if n_DMAs > 0 else [],
                    cpus = cpu_list)
 
 #
@@ -275,7 +282,10 @@
 for i, ruby_port in enumerate(system.ruby._cpu_ports):
     ruby_port.no_retry_on_stall = True
     ruby_port.using_ruby_tester = True
-    ruby_port.mem_request_port = system.piobus.cpu_side_ports
+
+    # piobus is only created if there are DMAs
+    if n_DMAs > 0:
+        ruby_port.mem_request_port = system.piobus.cpu_side_ports
 
     if i < n_CUs:
         tester.cu_vector_ports = ruby_port.in_ports
diff --git a/configs/example/se.py b/configs/example/se.py
index a3b5cb9..3a8203d 100644
--- a/configs/example/se.py
+++ b/configs/example/se.py
@@ -200,6 +200,7 @@
 if ObjectList.is_kvm_cpu(CPUClass) or ObjectList.is_kvm_cpu(FutureClass):
     if buildEnv['TARGET_ISA'] == 'x86':
         system.kvm_vm = KvmVM()
+        system.m5ops_base = 0xffff0000
         for process in multiprocesses:
             process.useArchPT = True
             process.kvmInSE = True
diff --git a/configs/learning_gem5/part1/simple.py b/configs/learning_gem5/part1/simple.py
index 235165b..7810c3d 100644
--- a/configs/learning_gem5/part1/simple.py
+++ b/configs/learning_gem5/part1/simple.py
@@ -43,7 +43,7 @@
 # create the system we are going to simulate
 system = System()
 
-# Set the clock fequency of the system (and all of its children)
+# Set the clock frequency of the system (and all of its children)
 system.clk_domain = SrcClockDomain()
 system.clk_domain.clock = '1GHz'
 system.clk_domain.voltage_domain = VoltageDomain()
diff --git a/configs/learning_gem5/part1/two_level.py b/configs/learning_gem5/part1/two_level.py
index 591be0c..7a7956c 100644
--- a/configs/learning_gem5/part1/two_level.py
+++ b/configs/learning_gem5/part1/two_level.py
@@ -70,7 +70,7 @@
 # create the system we are going to simulate
 system = System()
 
-# Set the clock fequency of the system (and all of its children)
+# Set the clock frequency of the system (and all of its children)
 system.clk_domain = SrcClockDomain()
 system.clk_domain.clock = '1GHz'
 system.clk_domain.voltage_domain = VoltageDomain()
diff --git a/configs/learning_gem5/part2/simple_cache.py b/configs/learning_gem5/part2/simple_cache.py
index adda386..dc3a162 100644
--- a/configs/learning_gem5/part2/simple_cache.py
+++ b/configs/learning_gem5/part2/simple_cache.py
@@ -39,7 +39,7 @@
 # create the system we are going to simulate
 system = System()
 
-# Set the clock fequency of the system (and all of its children)
+# Set the clock frequency of the system (and all of its children)
 system.clk_domain = SrcClockDomain()
 system.clk_domain.clock = '1GHz'
 system.clk_domain.voltage_domain = VoltageDomain()
diff --git a/configs/learning_gem5/part2/simple_memobj.py b/configs/learning_gem5/part2/simple_memobj.py
index 49b0846..eaceae9 100644
--- a/configs/learning_gem5/part2/simple_memobj.py
+++ b/configs/learning_gem5/part2/simple_memobj.py
@@ -39,7 +39,7 @@
 # create the system we are going to simulate
 system = System()
 
-# Set the clock fequency of the system (and all of its children)
+# Set the clock frequency of the system (and all of its children)
 system.clk_domain = SrcClockDomain()
 system.clk_domain.clock = '1GHz'
 system.clk_domain.voltage_domain = VoltageDomain()
diff --git a/configs/learning_gem5/part3/ruby_test.py b/configs/learning_gem5/part3/ruby_test.py
index d0c3910..05096ec 100644
--- a/configs/learning_gem5/part3/ruby_test.py
+++ b/configs/learning_gem5/part3/ruby_test.py
@@ -44,7 +44,7 @@
 # create the system we are going to simulate
 system = System()
 
-# Set the clock fequency of the system (and all of its children)
+# Set the clock frequency of the system (and all of its children)
 system.clk_domain = SrcClockDomain()
 system.clk_domain.clock = '1GHz'
 system.clk_domain.voltage_domain = VoltageDomain()
diff --git a/configs/learning_gem5/part3/simple_ruby.py b/configs/learning_gem5/part3/simple_ruby.py
index 2e65ebd..3d9fe9a 100644
--- a/configs/learning_gem5/part3/simple_ruby.py
+++ b/configs/learning_gem5/part3/simple_ruby.py
@@ -53,7 +53,7 @@
 # create the system we are going to simulate
 system = System()
 
-# Set the clock fequency of the system (and all of its children)
+# Set the clock frequency of the system (and all of its children)
 system.clk_domain = SrcClockDomain()
 system.clk_domain.clock = '1GHz'
 system.clk_domain.voltage_domain = VoltageDomain()
diff --git a/configs/network/Network.py b/configs/network/Network.py
index 91f0076..e5a86f6 100644
--- a/configs/network/Network.py
+++ b/configs/network/Network.py
@@ -80,6 +80,10 @@
         "--garnet-deadlock-threshold", action="store",
         type=int, default=50000,
         help="network-level deadlock threshold.")
+    parser.add_argument("--simple-physical-channels", action="store_true",
+        default=False,
+        help="""SimpleNetwork links uses a separate physical
+            channel for each virtual network""")
 
 def create_network(options, ruby):
 
@@ -187,6 +191,9 @@
             extLink.int_cred_bridge = int_cred_bridges
 
     if options.network == "simple":
+        if options.simple_physical_channels:
+            network.physical_vnets_channels = \
+                [1] * int(network.number_of_virtual_networks)
         network.setup_buffers()
 
     if InterfaceClass != None:
diff --git a/configs/nvm/sweep.py b/configs/nvm/sweep.py
index 7693fb8..09371f9 100644
--- a/configs/nvm/sweep.py
+++ b/configs/nvm/sweep.py
@@ -106,14 +106,14 @@
 # controller with an NVM interface, check to be sure
 if not isinstance(system.mem_ctrls[0], m5.objects.MemCtrl):
     fatal("This script assumes the controller is a MemCtrl subclass")
-if not isinstance(system.mem_ctrls[0].nvm, m5.objects.NVMInterface):
+if not isinstance(system.mem_ctrls[0].dram, m5.objects.NVMInterface):
     fatal("This script assumes the memory is a NVMInterface class")
 
 # there is no point slowing things down by saving any data
-system.mem_ctrls[0].nvm.null = True
+system.mem_ctrls[0].dram.null = True
 
 # Set the address mapping based on input argument
-system.mem_ctrls[0].nvm.addr_mapping = args.addr_map
+system.mem_ctrls[0].dram.addr_mapping = args.addr_map
 
 # stay in each state for 0.25 ms, long enough to warm things up, and
 # short enough to avoid hitting a refresh
@@ -124,21 +124,21 @@
 # the DRAM maximum bandwidth to ensure that it is saturated
 
 # get the number of regions
-nbr_banks = system.mem_ctrls[0].nvm.banks_per_rank.value
+nbr_banks = system.mem_ctrls[0].dram.banks_per_rank.value
 
 # determine the burst length in bytes
-burst_size = int((system.mem_ctrls[0].nvm.devices_per_rank.value *
-                  system.mem_ctrls[0].nvm.device_bus_width.value *
-                  system.mem_ctrls[0].nvm.burst_length.value) / 8)
+burst_size = int((system.mem_ctrls[0].dram.devices_per_rank.value *
+                  system.mem_ctrls[0].dram.device_bus_width.value *
+                  system.mem_ctrls[0].dram.burst_length.value) / 8)
 
 
 # next, get the page size in bytes
-buffer_size = system.mem_ctrls[0].nvm.devices_per_rank.value * \
-    system.mem_ctrls[0].nvm.device_rowbuffer_size.value
+buffer_size = system.mem_ctrls[0].dram.devices_per_rank.value * \
+    system.mem_ctrls[0].dram.device_rowbuffer_size.value
 
 # match the maximum bandwidth of the memory, the parameter is in seconds
 # and we need it in ticks (ps)
-itt = system.mem_ctrls[0].nvm.tBURST.value * 1000000000000
+itt = system.mem_ctrls[0].dram.tBURST.value * 1000000000000
 
 # assume we start at 0
 max_addr = mem_range.end
@@ -179,7 +179,7 @@
                             0, max_addr, burst_size, int(itt), int(itt),
                             args.rd_perc, 0,
                             num_seq_pkts, buffer_size, nbr_banks, bank,
-                            addr_map, args.nvm_ranks)
+                            addr_map, args.dram_ranks)
     yield system.tgen.createExit(0)
 
 system.tgen.start(trace())
diff --git a/configs/nvm/sweep_hybrid.py b/configs/nvm/sweep_hybrid.py
index 4594f78..6bccdef 100644
--- a/configs/nvm/sweep_hybrid.py
+++ b/configs/nvm/sweep_hybrid.py
@@ -117,8 +117,8 @@
 
 # the following assumes that we are using the native controller
 # with NVM and DRAM interfaces, check to be sure
-if not isinstance(system.mem_ctrls[0], m5.objects.MemCtrl):
-    fatal("This script assumes the controller is a MemCtrl subclass")
+if not isinstance(system.mem_ctrls[0], m5.objects.HeteroMemCtrl):
+    fatal("This script assumes the controller is a HeteroMemCtrl subclass")
 if not isinstance(system.mem_ctrls[0].dram, m5.objects.DRAMInterface):
     fatal("This script assumes the first memory is a DRAMInterface subclass")
 if not isinstance(system.mem_ctrls[0].nvm, m5.objects.NVMInterface):
diff --git a/configs/ruby/CHI.py b/configs/ruby/CHI.py
index e4a2477..c94dc94 100644
--- a/configs/ruby/CHI.py
+++ b/configs/ruby/CHI.py
@@ -43,6 +43,7 @@
                         default=None,
                         help="NoC config. parameters and bindings. "
                            "Required for CustomMesh topology")
+    parser.add_argument("--enable-dvm", default=False, action="store_true")
 
 def read_config_file(file):
     ''' Read file as a module and return it '''
@@ -65,6 +66,13 @@
     if options.num_l3caches < 1:
         m5.fatal('--num-l3caches must be at least 1')
 
+    if full_system and options.enable_dvm:
+        if len(cpus) <= 1:
+            m5.fatal("--enable-dvm can't be used with a single CPU")
+        for cpu in cpus:
+            for decoder in cpu.decoder:
+                decoder.dvm_enabled = True
+
     # read specialized classes from config file if provided
     if options.chi_config:
         chi_defs = read_config_file(options.chi_config)
@@ -79,6 +87,7 @@
     # Node types
     CHI_RNF = chi_defs.CHI_RNF
     CHI_HNF = chi_defs.CHI_HNF
+    CHI_MN = chi_defs.CHI_MN
     CHI_SNF_MainMem = chi_defs.CHI_SNF_MainMem
     CHI_SNF_BootMem = chi_defs.CHI_SNF_BootMem
     CHI_RNI_DMA = chi_defs.CHI_RNI_DMA
@@ -140,6 +149,14 @@
         network_nodes.append(rnf)
         network_cntrls.extend(rnf.getNetworkSideControllers())
 
+    # Creates one Misc Node
+    ruby_system.mn = [ CHI_MN(ruby_system, [cpu.l1d for cpu in cpus]) ]
+    for mn in ruby_system.mn:
+        all_cntrls.extend(mn.getAllControllers())
+        network_nodes.append(mn)
+        network_cntrls.extend(mn.getNetworkSideControllers())
+        assert(mn.getAllControllers() == mn.getNetworkSideControllers())
+
     # Look for other memories
     other_memories = []
     if bootmem:
@@ -156,8 +173,9 @@
     for m in other_memories:
         sysranges.append(m.range)
 
+    hnf_list = [i for i in range(options.num_l3caches)]
     CHI_HNF.createAddrRanges(sysranges, system.cache_line_size.value,
-                             options.num_l3caches)
+                             hnf_list)
     ruby_system.hnf = [ CHI_HNF(i, ruby_system, HNFCache, None)
                         for i in range(options.num_l3caches) ]
 
diff --git a/configs/ruby/CHI_config.py b/configs/ruby/CHI_config.py
index 097f367..a4b01ca 100644
--- a/configs/ruby/CHI_config.py
+++ b/configs/ruby/CHI_config.py
@@ -230,6 +230,9 @@
         self.number_of_TBEs = 16
         self.number_of_repl_TBEs = 16
         self.number_of_snoop_TBEs = 4
+        self.number_of_DVM_TBEs = 16
+        self.number_of_DVM_snoop_TBEs = 4
+
         self.unify_repl_TBEs = False
 
 class CHI_L2Controller(CHI_Cache_Controller):
@@ -262,6 +265,8 @@
         self.number_of_TBEs = 32
         self.number_of_repl_TBEs = 32
         self.number_of_snoop_TBEs = 16
+        self.number_of_DVM_TBEs = 1 # should not receive any dvm
+        self.number_of_DVM_snoop_TBEs = 1 # should not receive any dvm
         self.unify_repl_TBEs = False
 
 class CHI_HNFController(CHI_Cache_Controller):
@@ -295,8 +300,41 @@
         self.number_of_TBEs = 32
         self.number_of_repl_TBEs = 32
         self.number_of_snoop_TBEs = 1 # should not receive any snoop
+        self.number_of_DVM_TBEs = 1 # should not receive any dvm
+        self.number_of_DVM_snoop_TBEs = 1 # should not receive any dvm
         self.unify_repl_TBEs = False
 
+class CHI_MNController(MiscNode_Controller):
+    '''
+    Default parameters for a Misc Node
+    '''
+
+    def __init__(self, ruby_system, addr_range, l1d_caches,
+                 early_nonsync_comp):
+        super(CHI_MNController, self).__init__(
+            version = Versions.getVersion(MiscNode_Controller),
+            ruby_system = ruby_system,
+            mandatoryQueue = MessageBuffer(),
+            triggerQueue = TriggerMessageBuffer(),
+            retryTriggerQueue = TriggerMessageBuffer(),
+            schedRspTriggerQueue = TriggerMessageBuffer(),
+            reqRdy = TriggerMessageBuffer(),
+            snpRdy = TriggerMessageBuffer(),
+        )
+        # Set somewhat large number since we really a lot on internal
+        # triggers. To limit the controller performance, tweak other
+        # params such as: input port buffer size, cache banks, and output
+        # port latency
+        self.transitions_per_cycle = 1024
+        self.addr_ranges = [addr_range]
+        # 16 total transaction buffer entries, but 1 is reserved for DVMNonSync
+        self.number_of_DVM_TBEs = 16
+        self.number_of_non_sync_TBEs = 1
+        self.early_nonsync_comp = early_nonsync_comp
+
+        # "upstream_destinations" = targets for DVM snoops
+        self.upstream_destinations = l1d_caches
+
 class CHI_DMAController(CHI_Cache_Controller):
     '''
     Default parameters for a DMA controller
@@ -333,6 +371,8 @@
         self.number_of_TBEs = 16
         self.number_of_repl_TBEs = 1
         self.number_of_snoop_TBEs = 1 # should not receive any snoop
+        self.number_of_DVM_TBEs = 1 # should not receive any dvm
+        self.number_of_DVM_snoop_TBEs = 1 # should not receive any dvm
         self.unify_repl_TBEs = False
 
 class CPUSequencerWrapper:
@@ -486,15 +526,14 @@
         '''HNFs may also define the 'pairing' parameter to allow pairing'''
         pairing = None
 
-    _addr_ranges = []
+    _addr_ranges = {}
     @classmethod
-    def createAddrRanges(cls, sys_mem_ranges, cache_line_size, num_hnfs):
+    def createAddrRanges(cls, sys_mem_ranges, cache_line_size, hnfs):
         # Create the HNFs interleaved addr ranges
         block_size_bits = int(math.log(cache_line_size, 2))
-        cls._addr_ranges = []
-        llc_bits = int(math.log(num_hnfs, 2))
+        llc_bits = int(math.log(len(hnfs), 2))
         numa_bit = block_size_bits + llc_bits - 1
-        for i in range(num_hnfs):
+        for i, hnf in enumerate(hnfs):
             ranges = []
             for r in sys_mem_ranges:
                 addr_range = AddrRange(r.start, size = r.size(),
@@ -502,7 +541,7 @@
                                         intlvBits = llc_bits,
                                         intlvMatch = i)
                 ranges.append(addr_range)
-            cls._addr_ranges.append((ranges, numa_bit, i))
+            cls._addr_ranges[hnf] = (ranges, numa_bit)
 
     @classmethod
     def getAddrRanges(cls, hnf_idx):
@@ -514,10 +553,9 @@
     def __init__(self, hnf_idx, ruby_system, llcache_type, parent):
         super(CHI_HNF, self).__init__(ruby_system)
 
-        addr_ranges,intlvHighBit,intlvMatch = self.getAddrRanges(hnf_idx)
+        addr_ranges,intlvHighBit = self.getAddrRanges(hnf_idx)
         # All ranges should have the same interleaving
         assert(len(addr_ranges) >= 1)
-        assert(intlvMatch == hnf_idx)
 
         ll_cache = llcache_type(start_index_bit = intlvHighBit + 1)
         self._cntrl = CHI_HNFController(ruby_system, ll_cache, NULL,
@@ -537,6 +575,40 @@
         return [self._cntrl]
 
 
+class CHI_MN(CHI_Node):
+    '''
+    Encapsulates a Misc Node controller.
+    '''
+
+    class NoC_Params(CHI_Node.NoC_Params):
+        '''HNFs may also define the 'pairing' parameter to allow pairing'''
+        pairing = None
+
+
+    # The CHI controller can be a child of this object or another if
+    # 'parent' if specified
+    def __init__(self, ruby_system, l1d_caches, early_nonsync_comp=False):
+        super(CHI_MN, self).__init__(ruby_system)
+
+        # MiscNode has internal address range starting at 0
+        addr_range = AddrRange(0, size = "1kB")
+
+        self._cntrl = CHI_MNController(ruby_system, addr_range, l1d_caches,
+                                       early_nonsync_comp)
+
+        self.cntrl = self._cntrl
+
+        self.connectController(self._cntrl)
+
+    def connectController(self, cntrl):
+        CHI_Node.connectController(self, cntrl)
+
+    def getAllControllers(self):
+        return [self._cntrl]
+
+    def getNetworkSideControllers(self):
+        return [self._cntrl]
+
 class CHI_SNF_Base(CHI_Node):
     '''
     Creates CHI node controllers for the memory controllers
diff --git a/configs/ruby/GPU_VIPER.py b/configs/ruby/GPU_VIPER.py
index f8a7386..dc99429 100644
--- a/configs/ruby/GPU_VIPER.py
+++ b/configs/ruby/GPU_VIPER.py
@@ -34,6 +34,8 @@
 from m5.util import addToPath
 from .Ruby import create_topology
 from .Ruby import send_evicts
+from common import ObjectList
+from common import MemConfig
 from common import FileSystemConfig
 
 addToPath('../')
@@ -443,6 +445,66 @@
 
         dir_cntrl.triggerQueue = MessageBuffer(ordered = True)
         dir_cntrl.L3triggerQueue = MessageBuffer(ordered = True)
+        dir_cntrl.requestToMemory = MessageBuffer(ordered = True)
+        dir_cntrl.responseFromMemory = MessageBuffer(ordered = True)
+
+        dir_cntrl.requestFromDMA = MessageBuffer(ordered=True)
+        dir_cntrl.requestFromDMA.in_port = network.out_port
+
+        dir_cntrl.responseToDMA = MessageBuffer()
+        dir_cntrl.responseToDMA.out_port = network.in_port
+
+        exec("ruby_system.dir_cntrl%d = dir_cntrl" % i)
+        dir_cntrl_nodes.append(dir_cntrl)
+
+    return dir_cntrl_nodes
+
+def construct_gpudirs(options, system, ruby_system, network):
+
+    dir_cntrl_nodes = []
+    mem_ctrls = []
+
+    xor_low_bit = 0
+
+    # For an odd number of CPUs, still create the right number of controllers
+    TCC_bits = int(math.log(options.num_tccs, 2))
+
+    dir_bits = int(math.log(options.dgpu_num_dirs, 2))
+    block_size_bits = int(math.log(options.cacheline_size, 2))
+    numa_bit = block_size_bits + dir_bits - 1
+
+    gpu_mem_range = AddrRange(0, size = options.dgpu_mem_size)
+    for i in range(options.dgpu_num_dirs):
+        addr_range = m5.objects.AddrRange(gpu_mem_range.start,
+                                          size = gpu_mem_range.size(),
+                                          intlvHighBit = numa_bit,
+                                          intlvBits = dir_bits,
+                                          intlvMatch = i,
+                                          xorHighBit = xor_low_bit)
+
+        dir_cntrl = DirCntrl(noTCCdir = True, TCC_select_num_bits = TCC_bits)
+        dir_cntrl.create(options, [addr_range], ruby_system, system)
+        dir_cntrl.number_of_TBEs = options.num_tbes
+        dir_cntrl.useL3OnWT = False
+
+        # Connect the Directory controller to the ruby network
+        dir_cntrl.requestFromCores = MessageBuffer(ordered = True)
+        dir_cntrl.requestFromCores.in_port = network.out_port
+
+        dir_cntrl.responseFromCores = MessageBuffer()
+        dir_cntrl.responseFromCores.in_port = network.out_port
+
+        dir_cntrl.unblockFromCores = MessageBuffer()
+        dir_cntrl.unblockFromCores.in_port = network.out_port
+
+        dir_cntrl.probeToCore = MessageBuffer()
+        dir_cntrl.probeToCore.out_port = network.in_port
+
+        dir_cntrl.responseToCore = MessageBuffer()
+        dir_cntrl.responseToCore.out_port = network.in_port
+
+        dir_cntrl.triggerQueue = MessageBuffer(ordered = True)
+        dir_cntrl.L3triggerQueue = MessageBuffer(ordered = True)
         dir_cntrl.requestToMemory = MessageBuffer()
         dir_cntrl.responseFromMemory = MessageBuffer()
 
@@ -455,10 +517,28 @@
         dir_cntrl.requestToMemory = MessageBuffer()
         dir_cntrl.responseFromMemory = MessageBuffer()
 
-        exec("ruby_system.dir_cntrl%d = dir_cntrl" % i)
-        dir_cntrl_nodes.append(dir_cntrl)
+        # Create memory controllers too
+        mem_type = ObjectList.mem_list.get(options.dgpu_mem_type)
+        dram_intf = MemConfig.create_mem_intf(mem_type, gpu_mem_range, i,
+            int(math.log(options.dgpu_num_dirs, 2)), options.cacheline_size,
+            xor_low_bit)
+        if issubclass(mem_type, DRAMInterface):
+            mem_ctrl = m5.objects.MemCtrl(dram = dram_intf)
+        else:
+            mem_ctrl = dram_intf
 
-    return dir_cntrl_nodes
+        mem_ctrl.port = dir_cntrl.memory_out_port
+        mem_ctrl.dram.enable_dram_powerdown = False
+        dir_cntrl.addr_ranges = dram_intf.range
+
+        # Append
+        exec("system.ruby.gpu_dir_cntrl%d = dir_cntrl" % i)
+        dir_cntrl_nodes.append(dir_cntrl)
+        mem_ctrls.append(mem_ctrl)
+
+    system.gpu_mem_ctrls = mem_ctrls
+
+    return dir_cntrl_nodes, mem_ctrls
 
 def construct_corepairs(options, system, ruby_system, network):
 
diff --git a/configs/ruby/Ruby.py b/configs/ruby/Ruby.py
index 631c65c..ba94c15 100644
--- a/configs/ruby/Ruby.py
+++ b/configs/ruby/Ruby.py
@@ -130,7 +130,7 @@
         if len(system.mem_ranges) > 1:
             crossbar = IOXBar()
             crossbars.append(crossbar)
-            dir_cntrl.memory = crossbar.cpu_side_ports
+            dir_cntrl.memory_out_port = crossbar.cpu_side_ports
 
         dir_ranges = []
         for r in system.mem_ranges:
@@ -152,7 +152,7 @@
             if crossbar != None:
                 mem_ctrl.port = crossbar.mem_side_ports
             else:
-                mem_ctrl.port = dir_cntrl.memory
+                mem_ctrl.port = dir_cntrl.memory_out_port
 
             # Enable low-power DRAM states if option is set
             if issubclass(mem_type, DRAMInterface):
diff --git a/configs/topologies/CustomMesh.py b/configs/topologies/CustomMesh.py
index 70bf55d..2519bdd 100644
--- a/configs/topologies/CustomMesh.py
+++ b/configs/topologies/CustomMesh.py
@@ -239,6 +239,7 @@
         # classify nodes into different types
         rnf_nodes = []
         hnf_nodes = []
+        mn_nodes = []
         mem_nodes = []
         io_mem_nodes = []
         rni_dma_nodes = []
@@ -248,6 +249,7 @@
         # the same base type.
         rnf_params = None
         hnf_params = None
+        mn_params = None
         mem_params = None
         io_mem_params = None
         rni_dma_params = None
@@ -264,6 +266,9 @@
             elif isinstance(n, CHI.CHI_HNF):
                 hnf_nodes.append(n)
                 hnf_params = check_same(type(n).NoC_Params, hnf_params)
+            elif isinstance(n, CHI.CHI_MN):
+                mn_nodes.append(n)
+                mn_params = check_same(type(n).NoC_Params, mn_params)
             elif isinstance(n, CHI.CHI_SNF_MainMem):
                 mem_nodes.append(n)
                 mem_params = check_same(type(n).NoC_Params, mem_params)
@@ -298,6 +303,9 @@
         # Place CHI_HNF on the mesh
         self.distributeNodes(hnf_params, hnf_nodes)
 
+        # Place CHI_MN on the mesh
+        self.distributeNodes(options, mn_params, mn_nodes)
+
         # Place CHI_SNF_MainMem on the mesh
         self.distributeNodes(mem_params, mem_nodes)
 
diff --git a/ext/Kconfiglib/.gitignore b/ext/Kconfiglib/.gitignore
new file mode 100644
index 0000000..4a18d79
--- /dev/null
+++ b/ext/Kconfiglib/.gitignore
@@ -0,0 +1,4 @@
+*.py[co]
+build/
+*.egg-info/
+dist/
diff --git a/ext/Kconfiglib/LICENSE.txt b/ext/Kconfiglib/LICENSE.txt
new file mode 100644
index 0000000..8b31efc
--- /dev/null
+++ b/ext/Kconfiglib/LICENSE.txt
@@ -0,0 +1,5 @@
+Copyright (c) 2011-2019, Ulf Magnusson <ulfalizer@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/ext/Kconfiglib/MANIFEST.in b/ext/Kconfiglib/MANIFEST.in
new file mode 100644
index 0000000..881a794
--- /dev/null
+++ b/ext/Kconfiglib/MANIFEST.in
@@ -0,0 +1,2 @@
+# Include the license file in source distributions
+include LICENSE.txt
diff --git a/ext/Kconfiglib/README.rst b/ext/Kconfiglib/README.rst
new file mode 100644
index 0000000..b59f04e
--- /dev/null
+++ b/ext/Kconfiglib/README.rst
@@ -0,0 +1,841 @@
+.. contents:: Table of contents
+   :backlinks: none
+
+News
+----
+
+Dependency loop with recent linux-next kernels
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To fix issues with dependency loops on recent linux-next kernels, apply `this
+patch <https://www.spinics.net/lists/linux-kbuild/msg23455.html>`_. Hopefully,
+it will be in ``linux-next`` soon.
+
+``windows-curses`` is no longer automatically installed on Windows
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Starting with Kconfiglib 13.0.0, the `windows-curses
+<https://github.com/zephyrproject-rtos/windows-curses>`__ package is no longer
+automatically installed on Windows, and needs to be installed manually for the
+terminal ``menuconfig`` to work.
+
+This fixes installation of Kconfiglib on MSYS2, which is not compatible with
+``windows-curses``. See `this issue
+<https://github.com/ulfalizer/Kconfiglib/issues/77>`__.
+
+The ``menuconfig`` now shows a hint re. installing ``windows-curses`` when the
+``curses`` module can't be imported on Windows.
+
+Sorry if this change caused problems!
+
+Overview
+--------
+
+Kconfiglib is a `Kconfig
+<https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-language.rst>`__
+implementation in Python 2/3. It started out as a helper library, but now has a
+enough functionality to also work well as a standalone Kconfig implementation
+(including `terminal and GUI menuconfig interfaces <Menuconfig interfaces_>`_
+and `Kconfig extensions`_).
+
+The entire library is contained in `kconfiglib.py
+<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_. The
+bundled scripts are implemented on top of it. Implementing your own scripts
+should be relatively easy, if needed.
+
+Kconfiglib is used exclusively by e.g. the `Zephyr
+<https://www.zephyrproject.org/>`__, `esp-idf
+<https://github.com/espressif/esp-idf>`__, and `ACRN
+<https://projectacrn.org/>`__ projects. It is also used for many small helper
+scripts in various projects.
+
+Since Kconfiglib is based around a library, it can be used e.g. to generate a
+`Kconfig cross-reference
+<https://docs.zephyrproject.org/latest/reference/kconfig/index.html>`_, using
+the same robust Kconfig parser used for other Kconfig tools, instead of brittle
+ad-hoc parsing. The documentation generation script can be found `here
+<https://github.com/zephyrproject-rtos/zephyr/blob/master/doc/scripts/genrest.py>`__.
+
+Kconfiglib implements the recently added `Kconfig preprocessor
+<https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-macro-language.rst>`__.
+For backwards compatibility, environment variables can be referenced both as
+``$(FOO)`` (the new syntax) and as ``$FOO`` (the old syntax). The old syntax is
+deprecated, but will probably be supported for a long time, as it's needed to
+stay compatible with older Linux kernels. The major version will be increased
+if support is ever dropped. Using the old syntax with an undefined environment
+variable keeps the string as is.
+
+Note: See `this issue <https://github.com/ulfalizer/Kconfiglib/issues/47>`__ if
+you run into a "macro expanded to blank string" error with kernel 4.18+.
+
+See `this page
+<https://docs.zephyrproject.org/latest/guides/kconfig/tips.html>`__ for some
+Kconfig tips and best practices.
+
+Installation
+------------
+
+Installation with pip
+~~~~~~~~~~~~~~~~~~~~~
+
+Kconfiglib is available on `PyPI <https://pypi.python.org/pypi/kconfiglib/>`_ and can be
+installed with e.g.
+
+.. code::
+
+    $ pip(3) install kconfiglib
+
+Microsoft Windows is supported.
+
+The ``pip`` installation will give you both the base library and the following
+executables. All but two (``genconfig`` and ``setconfig``) mirror functionality
+available in the C tools.
+
+- `menuconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_
+
+- `guiconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_
+
+- `oldconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/oldconfig.py>`_
+
+- `olddefconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/olddefconfig.py>`_
+
+- `savedefconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/savedefconfig.py>`_
+
+- `defconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/defconfig.py>`_
+
+- `alldefconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/alldefconfig.py>`_
+
+- `allnoconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/allnoconfig.py>`_
+
+- `allmodconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/allmodconfig.py>`_
+
+- `allyesconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/allyesconfig.py>`_
+
+- `listnewconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/listnewconfig.py>`_
+
+- `genconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/genconfig.py>`_
+
+- `setconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/setconfig.py>`_
+
+``genconfig`` is intended to be run at build time. It generates a C header from
+the configuration and (optionally) information that can be used to rebuild only
+files that reference Kconfig symbols that have changed value.
+
+Starting with Kconfiglib version 12.2.0, all utilities are compatible with both
+Python 2 and Python 3. Previously, ``menuconfig.py`` only ran under Python 3
+(i.e., it's now more backwards compatible than before).
+
+**Note:** If you install Kconfiglib with ``pip``'s ``--user`` flag, make sure
+that your ``PATH`` includes the directory where the executables end up. You can
+list the installed files with ``pip(3) show -f kconfiglib``.
+
+All releases have a corresponding tag in the git repository, e.g. ``v14.1.0``
+(the latest version).
+
+`Semantic versioning <http://semver.org/>`_ is used. There's been ten small
+changes to the behavior of the API, a Windows packaging change, and a hashbang
+change to use ``python3``
+(`1 <https://github.com/ulfalizer/Kconfiglib/commit/e8b4ecb6ff6ccc1c7be0818314fbccda2ef2b2ee>`_,
+`2 <https://github.com/ulfalizer/Kconfiglib/commit/db633015a4d7b0ba1e882f665e191f350932b2af>`_,
+`3 <https://github.com/ulfalizer/Kconfiglib/commit/8983f7eb297dd614faf0beee3129559bc8ba338e>`_,
+`4 <https://github.com/ulfalizer/Kconfiglib/commit/cbf32e29a130d22bc734b7778e6304ac9df2a3e8>`_,
+`5 <https://github.com/ulfalizer/Kconfiglib/commit/eb6c21a9b33a2d6e2bed9882d4f930d0cab2f03b>`_,
+`6 <https://github.com/ulfalizer/Kconfiglib/commit/c19fc11355b13d75d97286402c7a933fb23d3b70>`_,
+`7 <https://github.com/ulfalizer/Kconfiglib/commit/7a428aa415606820a44291f475248b08e3952c4b>`_,
+`8 <https://github.com/ulfalizer/Kconfiglib/commit/f247ddf618ad29718e5efd3e69f8baf75d4d347b>`_,
+`9 <https://github.com/ulfalizer/Kconfiglib/commit/4fed39d9271ceb68be4157ab3f96a45b94f77dc0>`_,
+`10 <https://github.com/ulfalizer/Kconfiglib/commit/55bc8c380869ea663092212e8fe388ad7abae596>`_,
+`Windows packaging change <https://github.com/ulfalizer/Kconfiglib/commit/21b4c1e3b6e2867b9a0788d21a358f6b1f581d86>`_,
+`Python 3 hashbang change <https://github.com/ulfalizer/Kconfiglib/commit/9e0a8d29fa76adcb3f27bb2e20f16fefc2a8591e>`_),
+which is why the major version is at 14 rather than 2. I do major version bumps
+for all behavior changes, even tiny ones, and most of these were fixes for baby
+issues in the early days of the Kconfiglib 2 API.
+
+Manual installation
+~~~~~~~~~~~~~~~~~~~
+
+Just drop ``kconfiglib.py`` and the scripts you want somewhere. There are no
+third-party dependencies, but the terminal ``menuconfig`` won't work on Windows
+unless a package like `windows-curses
+<https://github.com/zephyrproject-rtos/windows-curses>`__ is installed.
+
+Installation for the Linux kernel
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+See the module docstring at the top of `kconfiglib.py <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_.
+
+Python version compatibility (2.7/3.2+)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Kconfiglib and all utilities run under both Python 2.7 and Python 3.2 and
+later. The code mostly uses basic Python features and has no third-party
+dependencies, so keeping it backwards-compatible is pretty low effort.
+
+The 3.2 requirement comes from ``argparse``. ``format()`` with unnumbered
+``{}`` is used as well.
+
+A recent Python 3 version is recommended if you have a choice, as it'll give
+you better Unicode handling.
+
+Getting started
+---------------
+
+1. `Install <Installation_>`_ the library and the utilities.
+
+2. Write `Kconfig
+   <https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-language.rst>`__
+   files that describe the available configuration options. See `this page
+   <https://docs.zephyrproject.org/latest/guides/kconfig/tips.html>`__ for some
+   general Kconfig advice.
+
+3. Generate an initial configuration with e.g. ``menuconfig``/``guiconfig`` or
+   ``alldefconfig``. The configuration is saved as ``.config`` by default.
+
+   For more advanced projects, the ``defconfig`` utility can be used to
+   generate the initial configuration from an existing configuration file.
+   Usually, this existing configuration file would be a minimal configuration
+   file, as generated by e.g. ``savedefconfig``.
+
+4. Run ``genconfig`` to generate a header file. By default, it is saved as
+   ``config.h``.
+
+   Normally, ``genconfig`` would be run automatically as part of the build.
+
+   Before writing a header file or other configuration output, Kconfiglib
+   compares the old contents of the file against the new contents. If there's
+   no change, the write is skipped. This avoids updating file metadata like the
+   modification time, and might save work depending on your build setup.
+   
+   Adding new configuration output formats should be relatively straightforward.
+   See the implementation of ``write_config()`` in `kconfiglib.py
+   <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_.
+   The documentation for the ``Symbol.config_string`` property has some tips as
+   well.
+   
+5. To update an old ``.config`` file after the Kconfig files have changed (e.g.
+   to add new options), run ``oldconfig`` (prompts for values for new options)
+   or ``olddefconfig`` (gives new options their default value). Entering the
+   ``menuconfig`` or ``guiconfig`` interface and saving the configuration will
+   also update it (the configuration interfaces always prompt for saving
+   on exit if it would modify the contents of the ``.config`` file).
+
+   Due to Kconfig semantics, simply loading an old ``.config`` file performs an
+   implicit ``olddefconfig``, so building will normally not be affected by
+   having an outdated configuration.
+
+Whenever ``.config`` is overwritten, the previous version of the file is saved
+to ``.config.old`` (or, more generally, to ``$KCONFIG_CONFIG.old``).
+
+Using ``.config`` files as Make input
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``.config`` files use Make syntax and can be included directly in Makefiles to
+read configuration values from there. This is why ``n``-valued
+``bool``/``tristate`` values are written out as ``# CONFIG_FOO is not set`` (a
+Make comment) in ``.config``, allowing them to be tested with ``ifdef`` in
+Make.
+
+If you make use of this, you might want to pass ``--config-out <filename>`` to
+``genconfig`` and include the configuration file it generates instead of
+including ``.config`` directly. This has the advantage that the generated
+configuration file will always be a "full" configuration file, even if
+``.config`` is outdated. Otherwise, it might be necessary to run
+``old(def)config`` or ``menuconfig``/``guiconfig`` before rebuilding with an
+outdated ``.config``.
+
+If you use ``--sync-deps`` to generate incremental build information, you can
+include ``deps/auto.conf`` instead, which is also a full configuration file.
+
+Useful helper macros
+~~~~~~~~~~~~~~~~~~~~
+
+The `include/linux/kconfig.h
+<https://github.com/torvalds/linux/blob/master/include/linux/kconfig.h>`_
+header in the Linux kernel defines some useful helper macros for testing
+Kconfig configuration values.
+
+``IS_ENABLED()`` is generally useful, allowing configuration values to be
+tested in ``if`` statements with no runtime overhead.
+
+Incremental building
+~~~~~~~~~~~~~~~~~~~~
+
+See the docstring for ``Kconfig.sync_deps()`` in `kconfiglib.py
+<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_ for hints
+on implementing incremental builds (rebuilding just source files that reference
+changed configuration values).
+
+Running the ``scripts/basic/fixdep.c`` tool from the kernel on the output of
+``gcc -MD <source file>`` might give you an idea of how it all fits together.
+
+Library documentation
+---------------------
+
+Kconfiglib comes with extensive documentation in the form of docstrings. To view it, run e.g.
+the following command:
+
+.. code:: sh
+
+    $ pydoc(3) kconfiglib
+
+For HTML output, add ``-w``:
+
+.. code:: sh
+
+    $ pydoc(3) -w kconfiglib
+
+This will also work after installing Kconfiglib with ``pip(3)``.
+
+Documentation for other modules can be viewed in the same way (though a plain
+``--help`` will work when they're run as executables):
+
+.. code:: sh
+
+    $ pydoc(3) menuconfig/guiconfig/...
+
+A good starting point for learning the library is to read the module docstring
+(which you could also just read directly at the beginning of `kconfiglib.py
+<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_). It
+gives an introduction to symbol values, the menu tree, and expressions.
+
+After reading the module docstring, a good next step is to read the ``Kconfig``
+class documentation, and then the documentation for the ``Symbol``, ``Choice``,
+and ``MenuNode`` classes.
+
+Please tell me if something is unclear or can be explained better.
+
+Library features
+----------------
+
+Kconfiglib can do the following, among other things:
+
+- **Programmatically get and set symbol values**
+
+  See `allnoconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/allnoconfig.py>`_ and
+  `allyesconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/allyesconfig.py>`_,
+  which are automatically verified to produce identical output to the standard
+  ``make allnoconfig`` and ``make allyesconfig``.
+
+- **Read and write .config and defconfig files**
+
+  The generated ``.config`` and ``defconfig`` (minimal configuration) files are
+  character-for-character identical to what the C implementation would generate
+  (except for the header comment). The test suite relies on this, as it
+  compares the generated files.
+  
+- **Write C headers**
+
+  The generated headers use the same format as ``include/generated/autoconf.h``
+  from the Linux kernel. Output for symbols appears in the order that they're
+  defined, unlike in the C tools (where the order depends on the hash table
+  implementation).
+
+- **Implement incremental builds**
+
+  This uses the same scheme as the ``include/config`` directory in the kernel:
+  Symbols are translated into files that are touched when the symbol's value
+  changes between builds, which can be used to avoid having to do a full
+  rebuild whenever the configuration is changed.
+
+  See the ``sync_deps()`` function for more information.
+
+- **Inspect symbols**
+
+  Printing a symbol or other item (which calls ``__str__()``) returns its
+  definition in Kconfig format. This also works for symbols defined in multiple
+  locations.
+
+  A helpful ``__repr__()`` is  on all objects too.
+
+  All ``__str__()`` and ``__repr__()`` methods are deliberately implemented
+  with just public APIs, so all symbol information can be fetched separately as
+  well.
+
+- **Inspect expressions**
+
+  Expressions use a simple tuple-based format that can be processed manually
+  if needed. Expression printing and evaluation functions are provided,
+  implemented with public APIs.
+
+- **Inspect the menu tree**
+
+  The underlying menu tree is exposed, including submenus created implicitly
+  from symbols depending on preceding symbols. This can be used e.g. to
+  implement menuconfig-like functionality.
+  
+  See `menuconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_/`guiconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_ and the
+  minimalistic `menuconfig_example.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/menuconfig_example.py>`_
+  example.
+
+Kconfig extensions
+~~~~~~~~~~~~~~~~~~
+
+The following Kconfig extensions are available:
+
+- ``source`` supports glob patterns and includes each matching file. A pattern
+  is required to match at least one file.
+
+  A separate ``osource`` statement is available for cases where it's okay for
+  the pattern to match no files (in which case ``osource`` turns into a no-op).
+  
+- A relative ``source`` statement (``rsource``) is available, where file paths
+  are specified relative to the directory of the current Kconfig file. An
+  ``orsource`` statement is available as well, analogous to ``osource``.
+
+- Preprocessor user functions can be defined in Python, which makes it simple
+  to integrate information from existing Python tools into Kconfig (e.g. to
+  have Kconfig symbols depend on hardware information stored in some other
+  format).
+
+  See the *Kconfig extensions* section in the
+  `kconfiglib.py <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_
+  module docstring for more information.
+
+- ``def_int``, ``def_hex``, and ``def_string`` are available in addition to
+  ``def_bool`` and ``def_tristate``, allowing ``int``, ``hex``, and ``string``
+  symbols to be given a type and a default at the same time.
+
+  These can be useful in projects that make use of symbols defined in multiple
+  locations, and remove some Kconfig inconsistency.
+  
+- Environment variables are expanded directly in e.g. ``source`` and
+  ``mainmenu`` statements, meaning ``option env`` symbols are redundant.
+
+  This is the standard behavior with the new `Kconfig preprocessor
+  <https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-macro-language.rst>`__,
+  which Kconfiglib implements.
+
+  ``option env`` symbols are accepted but ignored, which leads the caveat that
+  they must have the same name as the environment variables they reference
+  (Kconfiglib warns if the names differ). This keeps Kconfiglib compatible with
+  older Linux kernels, where the name of the ``option env`` symbol always
+  matched the environment variable. Compatibility with older Linux kernels is
+  the main reason ``option env`` is still supported.
+
+  The C tools have dropped support for ``option env``.
+
+- Two extra optional warnings can be enabled by setting environment variables,
+  covering cases that are easily missed when making changes to Kconfig files:
+
+  * ``KCONFIG_WARN_UNDEF``: If set to ``y``, warnings will be generated for all
+    references to undefined symbols within Kconfig files. The only gotcha is
+    that all hex literals must be prefixed with ``0x`` or ``0X``, to make it
+    possible to distinguish them from symbol references.
+
+    Some projects (e.g. the Linux kernel) use multiple Kconfig trees with many
+    shared Kconfig files, leading to some safe undefined symbol references.
+    ``KCONFIG_WARN_UNDEF`` is useful in projects that only have a single
+    Kconfig tree though.
+
+    ``KCONFIG_STRICT`` is an older alias for this environment variable,
+    supported for backwards compatibility.
+
+  * ``KCONFIG_WARN_UNDEF_ASSIGN``: If set to ``y``, warnings will be generated
+    for all assignments to undefined symbols within ``.config`` files. By
+    default, no such warnings are generated.
+
+    This warning can also be enabled/disabled by setting
+    ``Kconfig.warn_assign_undef`` to ``True``/``False``.
+
+Other features
+--------------
+
+- **Single-file implementation**
+  
+  The entire library is contained in `kconfiglib.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_.
+
+  The tools implemented on top of it are one file each.
+
+- **Robust and highly compatible with the C Kconfig tools**
+  
+  The `test suite <https://github.com/ulfalizer/Kconfiglib/blob/master/testsuite.py>`_
+  automatically compares output from Kconfiglib and the C tools
+  by diffing the generated ``.config`` files for the real kernel Kconfig and
+  defconfig files, for all ARCHes.
+  
+  This currently involves comparing the output for 36 ARCHes and 498 defconfig
+  files (or over 18000 ARCH/defconfig combinations in "obsessive" test suite
+  mode). All tests are expected to pass.
+
+  A comprehensive suite of selftests is included as well.
+
+- **Not horribly slow despite being a pure Python implementation**
+  
+  The `allyesconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/allyesconfig.py>`_
+  script currently runs in about 1.3 seconds on the Linux kernel on a Core i7
+  2600K (with a warm file cache), including the ``make`` overhead from ``make
+  scriptconfig``. Note that the Linux kernel Kconfigs are absolutely massive
+  (over 14k symbols for x86) compared to most projects, and also have overhead
+  from running shell commands via the Kconfig preprocessor.
+  
+  Kconfiglib is especially speedy in cases where multiple ``.config`` files
+  need to be processed, because the ``Kconfig`` files will only need to be parsed
+  once.
+
+  For long-running jobs, `PyPy <https://pypy.org/>`_ gives a big performance
+  boost. CPython is faster for short-running jobs as PyPy needs some time to
+  warm up.
+  
+  Kconfiglib also works well with the
+  `multiprocessing <https://docs.python.org/3/library/multiprocessing.html>`_
+  module. No global state is kept.
+
+- **Generates more warnings than the C implementation**
+
+  Generates the same warnings as the C implementation, plus additional ones.
+  Also detects dependency and ``source`` loops.
+
+  All warnings point out the location(s) in the ``Kconfig`` files where a
+  symbol is defined, where applicable.
+
+- **Unicode support**
+
+  Unicode characters in string literals in ``Kconfig`` and ``.config`` files are
+  correctly handled. This support mostly comes for free from Python.
+
+- **Windows support**
+
+  Nothing Linux-specific is used. Universal newlines mode is used for both
+  Python 2 and Python 3.
+  
+  The `Zephyr <https://www.zephyrproject.org/>`_ project uses Kconfiglib to
+  generate ``.config`` files and C headers on Linux as well as Windows.
+
+- **Internals that (mostly) mirror the C implementation**
+  
+  While being simpler to understand and tweak.
+
+Menuconfig interfaces
+---------------------
+
+Three configuration interfaces are currently available:
+
+- `menuconfig.py <https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_
+  is a terminal-based configuration interface implemented using the standard
+  Python ``curses`` module. ``xconfig`` features like showing invisible symbols and
+  showing symbol names are included, and it's possible to jump directly to a symbol
+  in the menu tree (even if it's currently invisible).
+  
+  .. image:: https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/menuconfig.gif
+
+  *There is now also a show-help mode that shows the help text of the currently
+  selected symbol in the help window at the bottom.*
+
+  Starting with Kconfiglib 12.2.0, ``menuconfig.py`` runs under both Python 2
+  and Python 3 (previously, it only ran under Python 3, so this was a
+  backport). Running it under Python 3 provides better support for Unicode text
+  entry (``get_wch()`` is not available in the ``curses`` module on Python 2).
+
+  There are no third-party dependencies on \*nix. On Windows,
+  the ``curses`` modules is not available by default, but support
+  can be added by installing the ``windows-curses`` package:
+  
+  .. code-block:: shell
+
+      $ pip install windows-curses
+
+  This uses wheels built from `this repository
+  <https://github.com/zephyrproject-rtos/windows-curses>`_, which is in turn
+  based on Christoph Gohlke's `Python Extension Packages for Windows
+  <https://www.lfd.uci.edu/~gohlke/pythonlibs/#curses>`_.
+
+  See the docstring at the top of `menuconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_ for
+  more information about the terminal menuconfig implementation.
+
+- `guiconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_ is a
+  graphical configuration interface written in `Tkinter
+  <https://docs.python.org/3/library/tkinter.html>`_. Like ``menuconfig.py``,
+  it supports showing all symbols (with invisible symbols in red) and jumping
+  directly to symbols. Symbol values can also be changed directly from the
+  jump-to dialog.
+
+  When single-menu mode is enabled, a single menu is shown at a time, like in
+  the terminal menuconfig. Only this mode distinguishes between symbols defined
+  with ``config`` and symbols defined with ``menuconfig``.
+
+  ``guiconfig.py`` has been tested on X11, Windows, and macOS, and is
+  compatible with both Python 2 and Python 3.
+
+  Despite being part of the Python standard library, ``tkinter`` often isn't
+  included by default in Python installations on Linux. These commands will
+  install it on a few different distributions:
+
+  - Ubuntu: ``sudo apt install python-tk``/``sudo apt install python3-tk``
+
+  - Fedora: ``dnf install python2-tkinter``/``dnf install python3-tkinter``
+
+  - Arch: ``sudo pacman -S tk``
+
+  - Clear Linux: ``sudo swupd bundle-add python3-tcl``
+
+  Screenshot below, with show-all mode enabled and the jump-to dialog open:
+
+  .. image:: https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/guiconfig.png
+
+  To avoid having to carry around a bunch of GIFs, the image data is embedded
+  in ``guiconfig.py``. To use separate GIF files instead, change
+  ``_USE_EMBEDDED_IMAGES`` to ``False`` in ``guiconfig.py``. The image files
+  can be found in the `screenshots
+  <https://github.com/ulfalizer/Kconfiglib/tree/screenshots/guiconfig>`_
+  branch.
+
+  I did my best with the images, but some are definitely only art adjacent.
+  Touch-ups are welcome. :)
+
+- `pymenuconfig <https://github.com/RomaVis/pymenuconfig>`_, built by `RomaVis
+  <https://github.com/RomaVis>`_, is an older portable Python 2/3 TkInter
+  menuconfig implementation.
+
+  Screenshot below:
+
+  .. image:: https://raw.githubusercontent.com/RomaVis/pymenuconfig/master/screenshot.PNG
+
+  While working on the terminal menuconfig implementation, I added a few APIs
+  to Kconfiglib that turned out to be handy. ``pymenuconfig`` predates
+  ``menuconfig.py`` and ``guiconfig.py``, and so didn't have them available.
+  Blame me for any workarounds.
+
+Examples
+--------
+
+Example scripts
+~~~~~~~~~~~~~~~
+
+The `examples/ <https://github.com/ulfalizer/Kconfiglib/blob/master/examples>`_ directory contains some simple example scripts. Among these are the following ones. Make sure you run them with the latest version of Kconfiglib, as they might make use of newly added features.
+
+- `eval_expr.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/eval_expr.py>`_ evaluates an expression in the context of a configuration.
+
+- `find_symbol.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/find_symbol.py>`_ searches through expressions to find references to a symbol, also printing a "backtrace" with parents for each reference found.
+
+- `help_grep.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/help_grep.py>`_ searches for a string in all help texts.
+
+- `print_tree.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/print_tree.py>`_ prints a tree of all configuration items.
+
+- `print_config_tree.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/print_config_tree.py>`_ is similar to ``print_tree.py``, but dumps the tree as it would appear in ``menuconfig``, including values. This can be handy for visually diffing between ``.config`` files and different versions of ``Kconfig`` files.
+
+- `list_undefined.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/list_undefined.py>`_ finds references to symbols that are not defined by any architecture in the Linux kernel.
+
+- `merge_config.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/merge_config.py>`_ merges configuration fragments to produce a complete .config, similarly to ``scripts/kconfig/merge_config.sh`` from the kernel.
+
+- `menuconfig_example.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/menuconfig_example.py>`_ implements a configuration interface that uses notation similar to ``make menuconfig``. It's deliberately kept as simple as possible to demonstrate just the core concepts.
+
+Real-world examples
+~~~~~~~~~~~~~~~~~~~
+
+- `kconfig.py
+  <https://github.com/zephyrproject-rtos/zephyr/blob/master/scripts/kconfig/kconfig.py>`_
+  from the `Zephyr <https://www.zephyrproject.org/>`_ project handles
+  ``.config`` and header file generation, also doing configuration fragment
+  merging
+
+- `genrest.py
+  <https://github.com/zephyrproject-rtos/zephyr/blob/master/doc/scripts/genrest.py>`_
+  generates a Kconfig symbol cross-reference, which can be viewed `here
+  <http://docs.zephyrproject.org/reference/kconfig/index.html>`__
+
+- `CMake and IDE integration
+  <https://github.com/espressif/esp-idf/tree/master/tools/kconfig_new>`_ from
+  the ESP-IDF project, via a configuration server program.
+
+- `A script for turning on USB-related options
+  <https://github.com/google/syzkaller/blob/master/dashboard/config/kconfiglib-merge-usb-configs.py>`_,
+  from the `syzkaller <https://github.com/google/syzkaller>`_ project.
+
+- `Various automated checks
+  <https://github.com/zephyrproject-rtos/ci-tools/blob/master/scripts/check_compliance.py>`_,
+  including a check for references to undefined Kconfig symbols in source code.
+  See the ``KconfigCheck`` class.
+
+- `Various utilities
+  <https://github.com/projectacrn/acrn-hypervisor/tree/master/scripts/kconfig>`_
+  from the `ACRN <https://projectacrn.org/>`_ project
+
+These use the older Kconfiglib 1 API, which was clunkier and not as general
+(functions instead of properties, no direct access to the menu structure or
+properties, uglier ``__str__()`` output):
+
+- `genboardscfg.py <http://git.denx.de/?p=u-boot.git;a=blob;f=tools/genboardscfg.py;hb=HEAD>`_ from `Das U-Boot <http://www.denx.de/wiki/U-Boot>`_ generates some sort of legacy board database by pulling information from a newly added Kconfig-based configuration system (as far as I understand it :).
+
+- `gen-manual-lists.py <https://git.busybox.net/buildroot/tree/support/scripts/gen-manual-lists.py?id=5676a2deea896f38123b99781da0a612865adeb0>`_ generated listings for an appendix in the `Buildroot <https://buildroot.org>`_ manual. (The listing has since been removed.)
+
+- `gen_kconfig_doc.py <https://github.com/espressif/esp-idf/blob/master/docs/gen-kconfig-doc.py>`_ from the `esp-idf <https://github.com/espressif/esp-idf>`_ project generates documentation from Kconfig files.
+
+- `SConf <https://github.com/CoryXie/SConf>`_ builds an interactive configuration interface (like ``menuconfig``) on top of Kconfiglib, for use e.g. with `SCons <scons.org>`_.
+
+- `kconfig-diff.py <https://gist.github.com/dubiousjim/5638961>`_ -- a script by `dubiousjim <https://github.com/dubiousjim>`_ that compares kernel configurations.
+
+- Originally, Kconfiglib was used in chapter 4 of my `master's thesis <http://liu.diva-portal.org/smash/get/diva2:473038/FULLTEXT01.pdf>`_ to automatically generate a "minimal" kernel for a given system. Parts of it bother me a bit now, but that's how it goes with old work.
+
+Sample ``make iscriptconfig`` session
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following log should give some idea of the functionality available in the API:
+
+.. code-block::
+
+    $ make iscriptconfig
+    A Kconfig instance 'kconf' for the architecture x86 has been created.
+    >>> kconf  # Calls Kconfig.__repr__()
+    <configuration with 13711 symbols, main menu prompt "Linux/x86 4.14.0-rc7 Kernel Configuration", srctree ".", config symbol prefix "CONFIG_", warnings enabled, undef. symbol assignment warnings disabled>
+    >>> kconf.mainmenu_text  # Expanded main menu text
+    'Linux/x86 4.14.0-rc7 Kernel Configuration'
+    >>> kconf.top_node  # The implicit top-level menu
+    <menu node for menu, prompt "Linux/x86 4.14.0-rc7 Kernel Configuration" (visibility y), deps y, 'visible if' deps y, has child, Kconfig:5>
+    >>> kconf.top_node.list  # First child menu node
+    <menu node for symbol SRCARCH, deps y, has next, Kconfig:7>
+    >>> print(kconf.top_node.list)  # Calls MenuNode.__str__()
+    config SRCARCH
+    	string
+    	option env="SRCARCH"
+    	default "x86"
+    >>> sym = kconf.top_node.list.next.item  # Item contained in next menu node
+    >>> print(sym)  # Calls Symbol.__str__()
+    config 64BIT
+    	bool "64-bit kernel" if ARCH = "x86"
+    	default ARCH != "i386"
+    	help
+    	  Say yes to build a 64-bit kernel - formerly known as x86_64
+    	  Say no to build a 32-bit kernel - formerly known as i386
+    >>> sym  # Calls Symbol.__repr__()
+    <symbol 64BIT, bool, "64-bit kernel", value y, visibility y, direct deps y, arch/x86/Kconfig:2>
+    >>> sym.assignable  # Currently assignable values (0, 1, 2 = n, m, y)
+    (0, 2)
+    >>> sym.set_value(0)  # Set it to n
+    True
+    >>> sym.tri_value  # Check the new value
+    0
+    >>> sym = kconf.syms["X86_MPPARSE"]  # Look up symbol by name
+    >>> print(sym)
+    config X86_MPPARSE
+    	bool "Enable MPS table" if (ACPI || SFI) && X86_LOCAL_APIC
+    	default y if X86_LOCAL_APIC
+    	help
+    	  For old smp systems that do not have proper acpi support. Newer systems
+    	  (esp with 64bit cpus) with acpi support, MADT and DSDT will override it
+    >>> default = sym.defaults[0]  # Fetch its first default
+    >>> sym = default[1]  # Fetch the default's condition (just a Symbol here)
+    >>> print(sym)
+    config X86_LOCAL_APIC
+    	bool
+    	default y
+    	select IRQ_DOMAIN_HIERARCHY
+    	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+    	depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI
+    >>> sym.nodes  # Show the MenuNode(s) associated with it
+    [<menu node for symbol X86_LOCAL_APIC, deps n, has next, arch/x86/Kconfig:1015>]
+    >>> kconfiglib.expr_str(sym.defaults[0][1])  # Print the default's condition
+    'X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI'
+    >>> kconfiglib.expr_value(sym.defaults[0][1])  # Evaluate it (0 = n)
+    0
+    >>> kconf.syms["64BIT"].set_value(2)
+    True
+    >>> kconfiglib.expr_value(sym.defaults[0][1])  # Evaluate it again (2 = y)
+    2
+    >>> kconf.write_config("myconfig")  # Save a .config
+    >>> ^D
+    $ cat myconfig
+    # Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
+    CONFIG_64BIT=y
+    CONFIG_X86_64=y
+    CONFIG_X86=y
+    CONFIG_INSTRUCTION_DECODER=y
+    CONFIG_OUTPUT_FORMAT="elf64-x86-64"
+    CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig"
+    CONFIG_LOCKDEP_SUPPORT=y
+    CONFIG_STACKTRACE_SUPPORT=y
+    CONFIG_MMU=y
+    ...
+ 
+Test suite
+----------
+
+The test suite is run with
+
+.. code::
+
+    $ python(3) Kconfiglib/testsuite.py
+    
+`pypy <https://pypy.org/>`_ works too, and is much speedier for everything except ``allnoconfig.py``/``allnoconfig_simpler.py``/``allyesconfig.py``, where it doesn't have time to warm up since
+the scripts are run via ``make scriptconfig``.
+
+The test suite must be run from the top-level kernel directory. It requires that the
+Kconfiglib git repository has been cloned into it and that the makefile patch has been applied.
+
+To get rid of warnings generated for the kernel ``Kconfig`` files, add ``2>/dev/null`` to the command to
+discard ``stderr``.
+
+**NOTE: Forgetting to apply the Makefile patch will cause some tests that compare generated configurations to fail**
+
+**NOTE: The test suite overwrites .config in the kernel root, so make sure to back it up.**
+
+The test suite consists of a set of selftests and a set of compatibility tests that
+compare configurations generated by Kconfiglib with
+configurations generated by the C tools, for a number of cases. See
+`testsuite.py <https://github.com/ulfalizer/Kconfiglib/blob/master/testsuite.py>`_
+for the available options.
+
+The `tests/reltest <https://github.com/ulfalizer/Kconfiglib/blob/master/tests/reltest>`_ script runs the test suite
+and all the example scripts for both Python 2 and Python 3, verifying that everything works.
+
+Rarely, the output from the C tools is changed slightly (most recently due to a
+`change <https://www.spinics.net/lists/linux-kbuild/msg17074.html>`_ I added).
+If you get test suite failures, try running the test suite again against the
+`linux-next tree <https://www.kernel.org/doc/man-pages/linux-next.html>`_,
+which has all the latest changes. I will make it clear if any
+non-backwards-compatible changes appear.
+
+A lot of time is spent waiting around for ``make`` and the C utilities (which need to reparse all the
+Kconfig files for each defconfig test). Adding some multiprocessing to the test suite would make sense
+too.
+
+Notes
+-----
+
+* This is version 2 of Kconfiglib, which is not backwards-compatible with
+  Kconfiglib 1. A summary of changes between Kconfiglib 1 and Kconfiglib
+  2 can be found `here
+  <https://github.com/ulfalizer/Kconfiglib/blob/screenshots/kconfiglib-2-changes.txt>`__.
+
+* I sometimes see people add custom output formats, which is pretty
+  straightforward to do (see the implementations of ``write_autoconf()`` and
+  ``write_config()`` for a template, and also the documentation of the
+  ``Symbol.config_string`` property). If you come up with something you think
+  might be useful to other people, I'm happy to take it in upstream. Batteries
+  included and all that.
+
+* Kconfiglib assumes the modules symbol is ``MODULES``, which is backwards-compatible.
+  A warning is printed by default if ``option modules`` is set on some other symbol.
+  
+  Let me know if you need proper ``option modules`` support. It wouldn't be that
+  hard to add.
+
+Thanks
+------
+
+- To `RomaVis <https://github.com/RomaVis>`_, for making
+  `pymenuconfig <https://github.com/RomaVis/pymenuconfig>`_ and suggesting
+  the ``rsource`` keyword.
+
+- To `Mitja Horvat <https://github.com/pinkfluid>`_, for adding support
+  for user-defined styles to the terminal menuconfig.
+
+- To `Philip Craig <https://github.com/philipc>`_ for adding
+  support for the ``allnoconfig_y`` option and fixing an obscure issue
+  with ``comment``\s inside ``choice``\s (that didn't affect correctness but
+  made outputs differ). ``allnoconfig_y`` is used to force certain symbols
+  to ``y`` during ``make allnoconfig`` to improve coverage.
+
+License
+-------
+
+See `LICENSE.txt <https://github.com/ulfalizer/Kconfiglib/blob/master/LICENSE.txt>`_. SPDX license identifiers are used in the
+source code.
diff --git a/ext/Kconfiglib/alldefconfig.py b/ext/Kconfiglib/alldefconfig.py
new file mode 100755
index 0000000..56c4caa
--- /dev/null
+++ b/ext/Kconfiglib/alldefconfig.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where all symbols are set to their their default
+values.
+
+The default output filename is '.config'. A different filename can be passed in
+the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/alldefconfig.py
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+    kconf.load_allconfig("alldef.config")
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/allmodconfig.py b/ext/Kconfiglib/allmodconfig.py
new file mode 100755
index 0000000..bfb72b4
--- /dev/null
+++ b/ext/Kconfiglib/allmodconfig.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where as many symbols as possible are set to 'm'.
+
+The default output filename is '.config'. A different filename can be passed
+in the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/allmodconfig.py
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+
+    # See allnoconfig.py
+    kconf.warn = False
+
+    for sym in kconf.unique_defined_syms:
+        if sym.orig_type == kconfiglib.BOOL:
+            # 'bool' choice symbols get their default value, as determined by
+            # e.g. 'default's on the choice
+            if not sym.choice:
+                # All other bool symbols get set to 'y', like for allyesconfig
+                sym.set_value(2)
+        elif sym.orig_type == kconfiglib.TRISTATE:
+            sym.set_value(1)
+
+    for choice in kconf.unique_choices:
+        choice.set_value(2 if choice.orig_type == kconfiglib.BOOL else 1)
+
+    kconf.warn = True
+
+    kconf.load_allconfig("allmod.config")
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/allnoconfig.py b/ext/Kconfiglib/allnoconfig.py
new file mode 100755
index 0000000..de90d8b
--- /dev/null
+++ b/ext/Kconfiglib/allnoconfig.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where as many symbols as possible are set to 'n'.
+
+The default output filename is '.config'. A different filename can be passed
+in the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/allnoconfig.py
+"""
+
+# See examples/allnoconfig_walk.py for another way to implement this script
+
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+
+    # Avoid warnings that would otherwise get printed by Kconfiglib for the
+    # following:
+    #
+    # 1. Assigning a value to a symbol without a prompt, which never has any
+    #    effect
+    #
+    # 2. Assigning values invalid for the type (only bool/tristate symbols
+    #    accept 0/1/2, for n/m/y). The assignments will be ignored for other
+    #    symbol types, which is what we want.
+    kconf.warn = False
+    for sym in kconf.unique_defined_syms:
+        sym.set_value(2 if sym.is_allnoconfig_y else 0)
+    kconf.warn = True
+
+    kconf.load_allconfig("allno.config")
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/allyesconfig.py b/ext/Kconfiglib/allyesconfig.py
new file mode 100755
index 0000000..90eb9b8
--- /dev/null
+++ b/ext/Kconfiglib/allyesconfig.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where as many symbols as possible are set to 'y'.
+
+The default output filename is '.config'. A different filename can be passed
+in the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/allyesconfig.py
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+
+    # See allnoconfig.py
+    kconf.warn = False
+
+    # Try to set all symbols to 'y'. Dependencies might truncate the value down
+    # later, but this will at least give the highest possible value.
+    #
+    # Assigning 0/1/2 to non-bool/tristate symbols has no effect (int/hex
+    # symbols still take a string, because they preserve formatting).
+    for sym in kconf.unique_defined_syms:
+        # Set choice symbols to 'm'. This value will be ignored for choices in
+        # 'y' mode (the "normal" mode), which will instead just get their
+        # default selection, but will set all symbols in m-mode choices to 'm',
+        # which is as high as they can go.
+        #
+        # Here's a convoluted example of how you might get an m-mode choice
+        # even during allyesconfig:
+        #
+        #   choice
+        #           tristate "weird choice"
+        #           depends on m
+        sym.set_value(1 if sym.choice else 2)
+
+    # Set all choices to the highest possible mode
+    for choice in kconf.unique_choices:
+        choice.set_value(2)
+
+    kconf.warn = True
+
+    kconf.load_allconfig("allyes.config")
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/defconfig.py b/ext/Kconfiglib/defconfig.py
new file mode 100755
index 0000000..b179273
--- /dev/null
+++ b/ext/Kconfiglib/defconfig.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Reads a specified configuration file, then writes a new configuration file.
+This can be used to initialize the configuration from e.g. an arch-specific
+configuration file. This input configuration file would usually be a minimal
+configuration file, as generated by e.g. savedefconfig.
+
+The default output filename is '.config'. A different filename can be passed in
+the KCONFIG_CONFIG environment variable.
+"""
+import argparse
+
+import kconfiglib
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--kconfig",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    parser.add_argument(
+        "config",
+        metavar="CONFIGURATION",
+        help="Input configuration file")
+
+    args = parser.parse_args()
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    print(kconf.load_config(args.config))
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/examples/Kmenuconfig b/ext/Kconfiglib/examples/Kmenuconfig
new file mode 100644
index 0000000..f1cb67b
--- /dev/null
+++ b/ext/Kconfiglib/examples/Kmenuconfig
@@ -0,0 +1,102 @@
+mainmenu "Example Kconfig configuration"
+
+config MODULES
+	bool "Enable loadable module support"
+	option modules
+	default y
+
+menu "Bool and tristate symbols"
+
+config BOOL
+	bool "Bool symbol"
+	default y
+
+config BOOL_DEP
+	bool "Dependent bool symbol"
+	depends on BOOL
+
+# Mix it up a bit with an 'if' instead of a 'depends on'
+if BOOL
+
+config TRI_DEP
+	tristate "Dependent tristate symbol"
+	select SELECTED_BY_TRI_DEP
+	imply IMPLIED_BY_TRI_DEP
+
+endif
+
+config TWO_MENU_NODES
+	bool "First prompt"
+	depends on BOOL
+
+config TRI
+	tristate "Tristate symbol"
+
+config TWO_MENU_NODES
+	bool "Second prompt"
+
+comment "These are selected by TRI_DEP"
+
+config SELECTED_BY_TRI_DEP
+	tristate "Tristate selected by TRI_DEP"
+
+config IMPLIED_BY_TRI_DEP
+	tristate "Tristate implied by TRI_DEP"
+
+endmenu
+
+
+menu "String, int, and hex symbols"
+
+config STRING
+	string "String symbol"
+	default "foo"
+
+config INT
+	int "Int symbol"
+	default 747
+
+config HEX
+	hex "Hex symbol"
+	default 0xABC
+
+endmenu
+
+
+menu "Various choices"
+
+choice BOOL_CHOICE
+	bool "Bool choice"
+
+config BOOL_CHOICE_SYM_1
+	bool "Bool choice sym 1"
+
+config BOOL_CHOICE_SYM_2
+	bool "Bool choice sym 2"
+
+endchoice
+
+choice TRI_CHOICE
+	tristate "Tristate choice"
+
+config TRI_CHOICE_SYM_1
+	tristate "Tristate choice sym 1"
+
+config TRI_CHOICE_SYM_2
+	tristate "Tristate choice sym 2"
+
+endchoice
+
+choice OPT_BOOL_CHOICE
+	bool "Optional bool choice"
+	optional
+
+config OPT_BOOL_CHOICE_SYM_1
+	bool "Optional bool choice sym 1"
+
+config OPT_BOOL_CHOICE_SYM_2
+	bool "Optional bool choice sym 2"
+
+endchoice
+
+endmenu
diff --git a/ext/Kconfiglib/examples/allnoconfig_walk.py b/ext/Kconfiglib/examples/allnoconfig_walk.py
new file mode 100644
index 0000000..5a8cc23
--- /dev/null
+++ b/ext/Kconfiglib/examples/allnoconfig_walk.py
@@ -0,0 +1,66 @@
+# This is tree-walking version of allnoconfig.py, for demonstration purposes.
+# Verified by the test suite to generate identical output to 'make allnoconfig'
+# for all ARCHes.
+#
+# Note: A more practical version would use Kconfig.node_iter(). The manual tree
+# walking is for demonstration purposes.
+#
+# Usage for the Linux kernel:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/allnoconfig_walk.py
+
+import sys
+
+from kconfiglib import Kconfig, Symbol
+
+
+def do_allnoconfig(node):
+    global changed
+
+    # Walk the tree of menu nodes. You can imagine this as going down/into menu
+    # entries in the menuconfig interface, setting each to n (or the lowest
+    # assignable value).
+
+    while node:
+        if isinstance(node.item, Symbol):
+            sym = node.item
+
+            # Is the symbol a non-allnoconfig_y symbol that can be set to a
+            # lower value than its current value?
+            if (not sym.is_allnoconfig_y and
+                sym.assignable and
+                sym.assignable[0] < sym.tri_value):
+
+                # Yup, lower it
+                sym.set_value(sym.assignable[0])
+                changed = True
+
+        # Recursively lower children
+        if node.list:
+            do_allnoconfig(node.list)
+
+        node = node.next
+
+
+# Parse the Kconfig files
+kconf = Kconfig(sys.argv[1])
+
+# Do an initial pass to set 'option allnoconfig_y' symbols to y
+for sym in kconf.unique_defined_syms:
+    if sym.is_allnoconfig_y:
+        sym.set_value(2)
+
+while True:
+    # Changing later symbols in the configuration can sometimes allow earlier
+    # symbols to be lowered, e.g. if a later symbol 'select's an earlier
+    # symbol. To handle such situations, we do additional passes over the tree
+    # until we're no longer able to change the value of any symbol in a pass.
+    changed = False
+
+    do_allnoconfig(kconf.top_node)
+
+    # Did the pass change any symbols?
+    if not changed:
+        break
+
+print(kconf.write_config())
diff --git a/ext/Kconfiglib/examples/defconfig_oldconfig.py b/ext/Kconfiglib/examples/defconfig_oldconfig.py
new file mode 100644
index 0000000..68336c6
--- /dev/null
+++ b/ext/Kconfiglib/examples/defconfig_oldconfig.py
@@ -0,0 +1,39 @@
+# Produces exactly the same output as the following script:
+#
+# make defconfig
+# echo CONFIG_ETHERNET=n >> .config
+# make oldconfig
+# echo CONFIG_ETHERNET=y >> .config
+# yes n | make oldconfig
+#
+# This came up in https://github.com/ulfalizer/Kconfiglib/issues/15.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/defconfig_oldconfig.py
+
+import sys
+
+import kconfiglib
+
+
+kconf = kconfiglib.Kconfig(sys.argv[1])
+
+# Mirrors defconfig
+kconf.load_config("arch/x86/configs/x86_64_defconfig")
+kconf.write_config()
+
+# Mirrors the first oldconfig
+kconf.load_config()
+kconf.syms["ETHERNET"].set_value(0)
+kconf.write_config()
+
+# Mirrors the second oldconfig
+kconf.load_config()
+kconf.syms["ETHERNET"].set_value(2)
+for s in kconf.unique_defined_syms:
+    if s.user_value is None and 0 in s.assignable:
+        s.set_value(0)
+
+# Write the final configuration
+print(kconf.write_config())
diff --git a/ext/Kconfiglib/examples/dumpvars.py b/ext/Kconfiglib/examples/dumpvars.py
new file mode 100644
index 0000000..0f8ab43
--- /dev/null
+++ b/ext/Kconfiglib/examples/dumpvars.py
@@ -0,0 +1,15 @@
+# Prints all (set) environment variables referenced in the Kconfig files
+# together with their values, as a list of assignments.
+#
+# Note: This only works for environment variables referenced via the $(FOO)
+# preprocessor syntax. The older $FOO syntax is maintained for backwards
+# compatibility.
+
+import os
+import sys
+
+import kconfiglib
+
+
+print(" ".join("{}='{}'".format(var, os.environ[var])
+               for var in kconfiglib.Kconfig(sys.argv[1]).env_vars))
diff --git a/ext/Kconfiglib/examples/eval_expr.py b/ext/Kconfiglib/examples/eval_expr.py
new file mode 100644
index 0000000..23eedb4
--- /dev/null
+++ b/ext/Kconfiglib/examples/eval_expr.py
@@ -0,0 +1,24 @@
+# Evaluates an expression (e.g. "X86_64 || (X86_32 && X86_LOCAL_APIC)") in the
+# context of a configuration. Note that this always yields a tristate value (n,
+# m, or y).
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/eval_expr.py SCRIPT_ARG=<expr>
+
+import sys
+
+import kconfiglib
+
+
+if len(sys.argv) < 3:
+    sys.exit("Pass the expression to evaluate with SCRIPT_ARG=<expression>")
+
+kconf = kconfiglib.Kconfig(sys.argv[1])
+expr = sys.argv[2]
+
+# Enable modules so that m doesn't get demoted to n
+kconf.modules.set_value(2)
+
+print("the expression '{}' evaluates to {}"
+      .format(expr, kconf.eval_string(expr)))
diff --git a/ext/Kconfiglib/examples/find_symbol.py b/ext/Kconfiglib/examples/find_symbol.py
new file mode 100644
index 0000000..f747103
--- /dev/null
+++ b/ext/Kconfiglib/examples/find_symbol.py
@@ -0,0 +1,112 @@
+# Prints all menu nodes that reference a given symbol any of their properties
+# or property conditions, along with their parent menu nodes.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/find_symbol.py SCRIPT_ARG=<name>
+#
+# Example output for SCRIPT_ARG=X86:
+#
+#   Found 470 locations that reference X86:
+#
+#   ========== Location 1 (init/Kconfig:1108) ==========
+#
+#   config SGETMASK_SYSCALL
+#   	bool
+#   	prompt "sgetmask/ssetmask syscalls support" if EXPERT
+#   	default PARISC || M68K || PPC || MIPS || X86 || SPARC || MICROBLAZE || SUPERH
+#   	help
+#   	  sys_sgetmask and sys_ssetmask are obsolete system calls
+#   	  no longer supported in libc but still enabled by default in some
+#   	  architectures.
+#
+#   	  If unsure, leave the default option here.
+#
+#   ---------- Parent 1 (init/Kconfig:1077)  ----------
+#
+#   menuconfig EXPERT
+#   	bool
+#   	prompt "Configure standard kernel features (expert users)"
+#   	select DEBUG_KERNEL
+#   	help
+#   	  This option allows certain base kernel options and settings
+#   	  to be disabled or tweaked. This is for specialized
+#   	  environments which can tolerate a "non-standard" kernel.
+#   	  Only use this if you really know what you are doing.
+#
+#   ---------- Parent 2 (init/Kconfig:39)  ----------
+#
+#   menu "General setup"
+#
+#   ========== Location 2 (arch/Kconfig:29) ==========
+#
+#   config OPROFILE_EVENT_MULTIPLEX
+#   	bool
+#   	prompt "OProfile multiplexing support (EXPERIMENTAL)"
+#   	default "n"
+#   	depends on OPROFILE && X86
+#   	help
+#   	  The number of hardware counters is limited. The multiplexing
+#   	  feature enables OProfile to gather more events than counters
+#   	  are provided by the hardware. This is realized by switching
+#   	  between events at a user specified time interval.
+#
+#   	  If unsure, say N.
+#
+#   ---------- Parent 1 (arch/Kconfig:16)  ----------
+#
+#   config OPROFILE
+#   	tristate
+#   	prompt "OProfile system profiling"
+#   	select RING_BUFFER
+#   	select RING_BUFFER_ALLOW_SWAP
+#   	depends on PROFILING && HAVE_OPROFILE
+#   	help
+#   	  OProfile is a profiling system capable of profiling the
+#   	  whole system, include the kernel, kernel modules, libraries,
+#   	  and applications.
+#
+#   	  If unsure, say N.
+#
+#   ---------- Parent 2 (init/Kconfig:39)  ----------
+#
+#   menu "General setup"
+#
+#   ... (tons more)
+
+import sys
+
+import kconfiglib
+
+
+if len(sys.argv) < 3:
+    sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=<name>')
+
+kconf = kconfiglib.Kconfig(sys.argv[1])
+sym_name = sys.argv[2]
+if sym_name not in kconf.syms:
+    print("No symbol {} exists in the configuration".format(sym_name))
+    sys.exit(0)
+
+referencing = [node for node in kconf.node_iter()
+               if kconf.syms[sym_name] in node.referenced]
+if not referencing:
+    print("No references to {} found".format(sym_name))
+    sys.exit(0)
+
+print("Found {} locations that reference {}:\n"
+      .format(len(referencing), sym_name))
+
+for i, node in enumerate(referencing, 1):
+    print("========== Location {} ({}:{}) ==========\n\n{}"
+          .format(i, node.filename, node.linenr, node))
+
+    # Print the parents of the menu node too
+
+    node = node.parent
+    parent_i = 1
+    while node is not kconf.top_node:
+        print("---------- Parent {} ({}:{})  ----------\n\n{}"
+              .format(parent_i, node.filename, node.linenr, node))
+        node = node.parent
+        parent_i += 1
diff --git a/ext/Kconfiglib/examples/help_grep.py b/ext/Kconfiglib/examples/help_grep.py
new file mode 100644
index 0000000..157d8f2
--- /dev/null
+++ b/ext/Kconfiglib/examples/help_grep.py
@@ -0,0 +1,64 @@
+# Does a case-insensitive search for a regular expression in the help texts of
+# symbols and choices and the prompts of menus and comments. Prints the
+# matching items together with their locations and the matching text.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=<regex>
+#
+# Shortened example output for SCRIPT_ARG=general:
+#
+#   menu "General setup"
+#   location: init/Kconfig:39
+#
+#   config SYSVIPC
+#   	bool
+#   	prompt "System V IPC"
+#   	help
+#   	  ...
+#   	  exchange information. It is generally considered to be a good thing,
+#   	  ...
+#
+#   location: init/Kconfig:233
+#
+#   config BSD_PROCESS_ACCT
+#   	bool
+#   	prompt "BSD Process Accounting" if MULTIUSER
+#   	help
+#   	  ...
+#   	  information.  This is generally a good idea, so say Y.
+#
+#   location: init/Kconfig:403
+#
+#   ...
+
+
+import re
+import sys
+
+from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT
+
+
+if len(sys.argv) < 3:
+    sys.exit("Pass the regex with SCRIPT_ARG=<regex>")
+
+search = re.compile(sys.argv[2], re.IGNORECASE).search
+
+for node in Kconfig(sys.argv[1]).node_iter():
+    match = False
+
+    if isinstance(node.item, (Symbol, Choice)) and \
+       node.help is not None and search(node.help):
+        print(node.item)
+        match = True
+
+    elif node.item == MENU and search(node.prompt[0]):
+        print('menu "{}"'.format(node.prompt[0]))
+        match = True
+
+    elif node.item == COMMENT and search(node.prompt[0]):
+        print('comment "{}"'.format(node.prompt[0]))
+        match = True
+
+    if match:
+        print("location: {}:{}\n".format(node.filename, node.linenr))
diff --git a/ext/Kconfiglib/examples/list_undefined.py b/ext/Kconfiglib/examples/list_undefined.py
new file mode 100644
index 0000000..4a3bc9b
--- /dev/null
+++ b/ext/Kconfiglib/examples/list_undefined.py
@@ -0,0 +1,156 @@
+# Prints a list of symbols that are referenced in the Kconfig files of some
+# architecture but not defined by the Kconfig files of any architecture.
+#
+# A Kconfig file might be shared between many architectures and legitimately
+# reference undefined symbols for some of them, but if no architecture defines
+# the symbol, it usually indicates a problem or potential cleanup.
+#
+# This script could be sped up a lot if needed. See the comment near the
+# referencing_nodes() call.
+#
+# Run with the following command in the kernel root:
+#
+#   $ python(3) Kconfiglib/examples/list_undefined.py
+#
+# Example output:
+#
+#   Registering defined and undefined symbols for all arches
+#     Processing mips
+#     Processing ia64
+#     Processing metag
+#     ...
+#
+#   Finding references to each undefined symbol
+#     Processing mips
+#     Processing ia64
+#     Processing metag
+#     ...
+#
+#   The following globally undefined symbols were found, listed here
+#   together with the locations of the items that reference them.
+#   References might come from enclosing menus and ifs.
+#
+#     ARM_ERRATA_753970: arch/arm/mach-mvebu/Kconfig:56, arch/arm/mach-mvebu/Kconfig:39
+#     SUNXI_CCU_MP: drivers/clk/sunxi-ng/Kconfig:14
+#     SUNXI_CCU_DIV: drivers/clk/sunxi-ng/Kconfig:14
+#     AC97: sound/ac97/Kconfig:6
+#     ...
+
+import os
+import subprocess
+
+from kconfiglib import Kconfig
+
+
+# Referenced inside the Kconfig files
+os.environ["KERNELVERSION"] = str(
+    subprocess.check_output(("make", "kernelversion")).decode("utf-8").rstrip()
+)
+
+
+def all_arch_srcarch_pairs():
+    """
+    Generates all valid (ARCH, SRCARCH) tuples for the kernel, corresponding to
+    different architectures. SRCARCH holds the arch/ subdirectory.
+    """
+    for srcarch in os.listdir("arch"):
+        # Each subdirectory of arch/ containing a Kconfig file corresponds to
+        # an architecture
+        if os.path.exists(os.path.join("arch", srcarch, "Kconfig")):
+            yield (srcarch, srcarch)
+
+    # Some architectures define additional ARCH settings with ARCH != SRCARCH
+    # (search for "Additional ARCH settings for" in the top-level Makefile)
+
+    yield ("i386", "x86")
+    yield ("x86_64", "x86")
+
+    yield ("sparc32", "sparc")
+    yield ("sparc64", "sparc")
+
+    yield ("sh64", "sh")
+
+    yield ("um", "um")
+
+
+def all_arch_srcarch_kconfigs():
+    """
+    Generates Kconfig instances for all the architectures in the kernel
+    """
+
+    os.environ["srctree"] = "."
+    os.environ["HOSTCC"] = "gcc"
+    os.environ["HOSTCXX"] = "g++"
+    os.environ["CC"] = "gcc"
+    os.environ["LD"] = "ld"
+
+    for arch, srcarch in all_arch_srcarch_pairs():
+        print("  Processing " + arch)
+
+        os.environ["ARCH"] = arch
+        os.environ["SRCARCH"] = srcarch
+
+        # um (User Mode Linux) uses a different base Kconfig file
+        yield Kconfig("Kconfig" if arch != "um" else "arch/x86/um/Kconfig",
+                      warn=False)
+
+
+print("Registering defined and undefined symbols for all arches")
+
+# Sets holding the names of all defined and undefined symbols, for all
+# architectures
+defined = set()
+undefined = set()
+
+for kconf in all_arch_srcarch_kconfigs():
+    for name, sym in kconf.syms.items():
+        if sym.nodes:
+            # If the symbol has a menu node, it is defined
+            defined.add(name)
+        else:
+            # Undefined symbol. We skip some of the uninteresting ones.
+
+            # Due to how Kconfig works, integer literals show up as symbols
+            # (from e.g. 'default 1'). Skip those.
+            try:
+                int(name, 0)
+                continue
+            except ValueError:
+                # Interesting undefined symbol
+                undefined.add(name)
+
+
+print("\nFinding references to each undefined symbol")
+
+def referencing_nodes(kconf, name):
+    # Returns a list of all menu nodes that reference a symbol named 'name' in
+    # any of their properties or property conditions
+    res = []
+
+    for node in kconf.node_iter():
+        for ref in node.referenced:
+            if ref.name == name:
+                res.append(node)
+
+    return res
+
+
+# Maps each globally undefined symbol to the menu nodes that reference it
+undef_sym_refs = [(name, set()) for name in undefined - defined]
+
+for kconf in all_arch_srcarch_kconfigs():
+    for name, refs in undef_sym_refs:
+        # This means that we search the entire configuration tree for each
+        # undefined symbol, which is terribly inefficient. We could speed
+        # things up by tweaking referencing_nodes() to compare each symbol to
+        # multiple symbols while walking the configuration tree.
+        for node in referencing_nodes(kconf, name):
+            refs.add("{}:{}".format(node.filename, node.linenr))
+
+
+print("\nThe following globally undefined symbols were found, listed here\n"
+      "together with the locations of the items that reference them.\n"
+      "References might come from enclosing menus and ifs.\n")
+
+for name, refs in undef_sym_refs:
+    print("  {}: {}".format(name, ", ".join(refs)))
diff --git a/ext/Kconfiglib/examples/menuconfig_example.py b/ext/Kconfiglib/examples/menuconfig_example.py
new file mode 100755
index 0000000..606f756
--- /dev/null
+++ b/ext/Kconfiglib/examples/menuconfig_example.py
@@ -0,0 +1,341 @@
+#!/usr/bin/env python3
+
+# Implements a simple configuration interface on top of Kconfiglib to
+# demonstrate concepts for building a menuconfig-like. Emulates how the
+# standard menuconfig prints menu entries.
+#
+# Always displays the entire Kconfig tree to keep things as simple as possible
+# (all symbols, choices, menus, and comments).
+#
+# Usage:
+#
+#   $ python(3) Kconfiglib/examples/menuconfig.py <Kconfig file>
+#
+# A sample Kconfig is available in Kconfiglib/examples/Kmenuconfig.
+#
+# Here's a notation guide. The notation matches the one used by menuconfig
+# (scripts/kconfig/mconf):
+#
+#   [ ] prompt      - Bool
+#   < > prompt      - Tristate
+#   {M} prompt      - Tristate selected to m. Can only be set to m or y.
+#   -*- prompt      - Bool/tristate selected to y, pinning it
+#   -M- prompt      - Tristate selected to m that also has m visibility,
+#                     pinning it to m
+#   (foo) prompt    - String/int/hex symbol with value "foo"
+#   --> prompt      - The selected symbol in a choice in y mode. This
+#                     syntax is unique to this example.
+#
+# When modules are disabled, the .type attribute of TRISTATE symbols and
+# choices automatically changes to BOOL. This trick is used by the C
+# implementation as well, and gives the expected behavior without having to do
+# anything extra here. The original type is available in .orig_type if needed.
+#
+# The Kconfiglib/examples/Kmenuconfig example uses named choices to be able to
+# refer to choices by name. Named choices are supported in the C tools too, but
+# I don't think I've ever seen them used in the wild.
+#
+# Sample session:
+#
+#   $ python Kconfiglib/examples/menuconfig.py Kconfiglib/examples/Kmenuconfig
+#
+#   ======== Example Kconfig configuration ========
+#
+#   [*] Enable loadable module support (MODULES)
+#       Bool and tristate symbols
+#           [*] Bool symbol (BOOL)
+#                   [ ] Dependent bool symbol (BOOL_DEP)
+#                   < > Dependent tristate symbol (TRI_DEP)
+#                   [ ] First prompt (TWO_MENU_NODES)
+#           < > Tristate symbol (TRI)
+#           [ ] Second prompt (TWO_MENU_NODES)
+#               *** These are selected by TRI_DEP ***
+#           < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
+#           < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
+#       String, int, and hex symbols
+#           (foo) String symbol (STRING)
+#           (747) Int symbol (INT)
+#           (0xABC) Hex symbol (HEX)
+#       Various choices
+#           -*- Bool choice (BOOL_CHOICE)
+#                   --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
+#                       Bool choice sym 2 (BOOL_CHOICE_SYM_2)
+#           {M} Tristate choice (TRI_CHOICE)
+#                   < > Tristate choice sym 1 (TRI_CHOICE_SYM_1)
+#                   < > Tristate choice sym 2 (TRI_CHOICE_SYM_2)
+#           [ ] Optional bool choice (OPT_BOOL_CHOICE)
+#
+#   Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): BOOL
+#   Value for BOOL (available: n, y): n
+#
+#   ======== Example Kconfig configuration ========
+#
+#   [*] Enable loadable module support (MODULES)
+#       Bool and tristate symbols
+#           [ ] Bool symbol (BOOL)
+#           < > Tristate symbol (TRI)
+#           [ ] Second prompt (TWO_MENU_NODES)
+#               *** These are selected by TRI_DEP ***
+#           < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
+#           < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
+#       String, int, and hex symbols
+#           (foo) String symbol (STRING)
+#           (747) Int symbol (INT)
+#           (0xABC) Hex symbol (HEX)
+#       Various choices
+#           -*- Bool choice (BOOL_CHOICE)
+#                   --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
+#                       Bool choice sym 2 (BOOL_CHOICE_SYM_2)
+#           {M} Tristate choice (TRI_CHOICE)
+#                   < > Tristate choice sym 1 (TRI_CHOICE_SYM_1)
+#                   < > Tristate choice sym 2 (TRI_CHOICE_SYM_2)
+#          [ ] Optional bool choice (OPT_BOOL_CHOICE)
+#
+#   Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): MODULES
+#   Value for MODULES (available: n, y): n
+#
+#   ======== Example Kconfig configuration ========
+#
+#   [ ] Enable loadable module support (MODULES)
+#       Bool and tristate symbols
+#           [ ] Bool symbol (BOOL)
+#           [ ] Tristate symbol (TRI)
+#           [ ] Second prompt (TWO_MENU_NODES)
+#               *** These are selected by TRI_DEP ***
+#           [ ] Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
+#           [ ] Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
+#       String, int, and hex symbols
+#           (foo) String symbol (STRING)
+#           (747) Int symbol (INT)
+#           (0xABC) Hex symbol (HEX)
+#       Various choices
+#           -*- Bool choice (BOOL_CHOICE)
+#                   --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
+#                       Bool choice sym 2 (BOOL_CHOICE_SYM_2)
+#           -*- Tristate choice (TRI_CHOICE)
+#                   --> Tristate choice sym 1 (TRI_CHOICE_SYM_1)
+#                       Tristate choice sym 2 (TRI_CHOICE_SYM_2)
+#           [ ] Optional bool choice (OPT_BOOL_CHOICE)
+#
+#   Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): ^D
+
+from __future__ import print_function
+import readline
+import sys
+
+from kconfiglib import Kconfig, \
+                       Symbol, MENU, COMMENT, \
+                       BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \
+                       expr_value, \
+                       TRI_TO_STR
+
+
+# Python 2/3 compatibility hack
+if sys.version_info[0] < 3:
+    input = raw_input
+
+
+def indent_print(s, indent):
+    print(indent*" " + s)
+
+
+def value_str(sc):
+    """
+    Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu entry.
+
+    sc: Symbol or Choice.
+    """
+    if sc.type in (STRING, INT, HEX):
+        return "({})".format(sc.str_value)
+
+    # BOOL or TRISTATE
+
+    # The choice mode is an upper bound on the visibility of choice symbols, so
+    # we can check the choice symbols' own visibility to see if the choice is
+    # in y mode
+    if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2:
+        # For choices in y mode, print '-->' next to the selected symbol
+        return "-->" if sc.choice.selection is sc else "   "
+
+    tri_val_str = (" ", "M", "*")[sc.tri_value]
+
+    if len(sc.assignable) == 1:
+        # Pinned to a single value
+        return "-{}-".format(tri_val_str)
+
+    if sc.type == BOOL:
+        return "[{}]".format(tri_val_str)
+
+    if sc.type == TRISTATE:
+        if sc.assignable == (1, 2):
+            # m and y available
+            return "{" + tri_val_str + "}"  # Gets a bit confusing with .format()
+        return "<{}>".format(tri_val_str)
+
+
+def node_str(node):
+    """
+    Returns the complete menu entry text for a menu node, or "" for invisible
+    menu nodes. Invisible menu nodes are those that lack a prompt or that do
+    not have a satisfied prompt condition.
+
+    Example return value: "[*] Bool symbol (BOOL)"
+
+    The symbol name is printed in parentheses to the right of the prompt. This
+    is so that symbols can easily be referred to in the configuration
+    interface.
+    """
+    if not node.prompt:
+        return ""
+
+    # Even for menu nodes for symbols and choices, it's wrong to check
+    # Symbol.visibility / Choice.visibility here. The reason is that a symbol
+    # (and a choice, in theory) can be defined in multiple locations, giving it
+    # multiple menu nodes, which do not necessarily all have the same prompt
+    # visibility. Symbol.visibility / Choice.visibility is calculated as the OR
+    # of the visibility of all the prompts.
+    prompt, prompt_cond = node.prompt
+    if not expr_value(prompt_cond):
+        return ""
+
+    if node.item == MENU:
+        return "    " + prompt
+
+    if node.item == COMMENT:
+        return "    *** {} ***".format(prompt)
+
+    # Symbol or Choice
+
+    sc = node.item
+
+    if sc.type == UNKNOWN:
+        # Skip symbols defined without a type (these are obscure and generate
+        # a warning)
+        return ""
+
+    # {:3} sets the field width to three. Gives nice alignment for empty string
+    # values.
+    res = "{:3} {}".format(value_str(sc), prompt)
+
+    # Don't print the name for unnamed choices (the normal kind)
+    if sc.name is not None:
+        res += " ({})".format(sc.name)
+
+    return res
+
+
+def print_menuconfig_nodes(node, indent):
+    """
+    Prints a tree with all the menu entries rooted at 'node'. Child menu
+    entries are indented.
+    """
+    while node:
+        string = node_str(node)
+        if string:
+            indent_print(string, indent)
+
+        if node.list:
+            print_menuconfig_nodes(node.list, indent + 8)
+
+        node = node.next
+
+
+def print_menuconfig(kconf):
+    """
+    Prints all menu entries for the configuration.
+    """
+    # Print the expanded mainmenu text at the top. This is the same as
+    # kconf.top_node.prompt[0], but with variable references expanded.
+    print("\n======== {} ========\n".format(kconf.mainmenu_text))
+
+    print_menuconfig_nodes(kconf.top_node.list, 0)
+    print("")
+
+
+def get_value_from_user(sc):
+    """
+    Prompts the user for a value for the symbol or choice 'sc'. For
+    bool/tristate symbols and choices, provides a list of all the assignable
+    values.
+    """
+    if not sc.visibility:
+        print(sc.name + " is not currently visible")
+        return False
+
+    prompt = "Value for {}".format(sc.name)
+    if sc.type in (BOOL, TRISTATE):
+        prompt += " (available: {})" \
+                  .format(", ".join(TRI_TO_STR[val] for val in sc.assignable))
+    prompt += ": "
+
+    val = input(prompt)
+
+    # Automatically add a "0x" prefix for hex symbols, like the menuconfig
+    # interface does. This isn't done when loading .config files, hence why
+    # set_value() doesn't do it automatically.
+    if sc.type == HEX and not val.startswith(("0x", "0X")):
+        val = "0x" + val
+
+    # Let Kconfiglib itself print a warning here if the value is invalid. We
+    # could also disable warnings temporarily with 'kconf.warn = False' and
+    # print our own warning.
+    return sc.set_value(val)
+
+
+if __name__ == "__main__":
+    if len(sys.argv) != 2:
+        sys.exit("usage: menuconfig.py <Kconfig file>")
+
+    # Load Kconfig configuration files
+    kconf = Kconfig(sys.argv[1])
+
+    # Print the initial configuration tree
+    print_menuconfig(kconf)
+
+    while True:
+        try:
+            cmd = input('Enter a symbol/choice name, "load_config", or '
+                        '"write_config" (or press CTRL+D to exit): ').strip()
+        except EOFError:
+            print("")
+            break
+
+        if cmd == "load_config":
+            config_filename = input(".config file to load: ")
+            try:
+                # Returns a message telling which file got loaded
+                print(kconf.load_config(config_filename))
+            except EnvironmentError as e:
+                print(e, file=sys.stderr)
+
+            print_menuconfig(kconf)
+            continue
+
+        if cmd == "write_config":
+            config_filename = input("To this file: ")
+            try:
+                # Returns a message telling which file got saved
+                print(kconf.write_config(config_filename))
+            except EnvironmentError as e:
+                print(e, file=sys.stderr)
+
+            continue
+
+        # Assume 'cmd' is the name of a symbol or choice if it isn't one of the
+        # commands above, prompt the user for a value for it, and print the new
+        # configuration tree
+
+        if cmd in kconf.syms:
+            if get_value_from_user(kconf.syms[cmd]):
+                print_menuconfig(kconf)
+
+            continue
+
+        if cmd in kconf.named_choices:
+            if get_value_from_user(kconf.named_choices[cmd]):
+                print_menuconfig(kconf)
+
+            continue
+
+        print("No symbol/choice named '{}' in the configuration".format(cmd),
+              file=sys.stderr)
diff --git a/ext/Kconfiglib/examples/merge_config.py b/ext/Kconfiglib/examples/merge_config.py
new file mode 100755
index 0000000..777fe2c
--- /dev/null
+++ b/ext/Kconfiglib/examples/merge_config.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+
+# This script functions similarly to scripts/kconfig/merge_config.sh from the
+# kernel tree, merging multiple configurations fragments to produce a complete
+# .config, with unspecified values filled in as for alldefconfig.
+#
+# The generated .config respects symbol dependencies, and a warning is printed
+# if any symbol gets a different value from the assigned value.
+#
+# For a real-world merging example based on this script, see
+# https://github.com/zephyrproject-rtos/zephyr/blob/master/scripts/kconfig/kconfig.py.
+#
+# Here's a demo:
+#
+# Kconfig contents:
+#
+#     config FOO
+#         bool "FOO"
+#
+#     config BAR
+#         bool "BAR"
+#
+#     config BAZ
+#         string "BAZ"
+#
+#     config QAZ
+#         bool "QAZ" if n
+#
+#
+# conf1 contents:
+#
+#     CONFIG_FOO=y
+#
+#
+# conf2 contents:
+#
+#     CONFIG_BAR=y
+#
+#
+# conf3 contents:
+#
+#     # Assigned twice (would generate warning if 'warn_assign_override' was
+#     # True)
+#     # CONFIG_FOO is not set
+#
+#     # Ops... this symbol doesn't exist
+#     CONFIG_OPS=y
+#
+#     CONFIG_BAZ="baz string"
+#
+#
+# conf4 contents:
+#
+#     CONFIG_QAZ=y
+#
+#
+# Running:
+#
+#     $ python(3) merge_config.py Kconfig merged conf1 conf2 conf3 conf4
+#     Merged configuration 'conf1'
+#     Merged configuration 'conf2'
+#     conf3:5: warning: attempt to assign the value 'y' to the undefined symbol OPS
+#     Merged configuration 'conf3'
+#     Merged configuration 'conf4'
+#     Configuration saved to 'merged'
+#     warning: QAZ (defined at Kconfig:10) was assigned the value 'y' but got the value 'n' -- check dependencies
+#     $ cat merged
+#     Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
+#     # CONFIG_FOO is not set
+#     CONFIG_BAR=y
+#     CONFIG_BAZ="baz string"
+
+from __future__ import print_function
+import sys
+
+from kconfiglib import Kconfig, BOOL, TRISTATE, TRI_TO_STR
+
+
+if len(sys.argv) < 4:
+    sys.exit("usage: merge_config.py Kconfig merged_config config1 [config2 ...]")
+
+kconf = Kconfig(sys.argv[1], suppress_traceback=True)
+
+# Enable warnings for assignments to undefined symbols
+kconf.warn_assign_undef = True
+
+# (This script uses alldefconfig as the base. Other starting states could be
+# set up here as well. The approach in examples/allnoconfig_simpler.py could
+# provide an allnoconfig starting state for example.)
+
+# Disable warnings generated for multiple assignments to the same symbol within
+# a (set of) configuration files. Assigning a symbol multiple times might be
+# done intentionally when merging configuration files.
+kconf.warn_assign_override = False
+kconf.warn_assign_redun = False
+
+# Create a merged configuration by loading the fragments with replace=False.
+# load_config() and write_config() returns a message to print.
+for config in sys.argv[3:]:
+    print(kconf.load_config(config, replace=False))
+
+# Write the merged configuration
+print(kconf.write_config(sys.argv[2]))
+
+# Print warnings for symbols whose actual value doesn't match the assigned
+# value
+for sym in kconf.defined_syms:
+    # Was the symbol assigned to?
+    if sym.user_value is not None:
+        # Tristate values are represented as 0, 1, 2. Having them as
+        # "n", "m", "y" is more convenient here, so convert.
+        if sym.type in (BOOL, TRISTATE):
+            user_value = TRI_TO_STR[sym.user_value]
+        else:
+            user_value = sym.user_value
+
+        if user_value != sym.str_value:
+            print("warning: {} was assigned the value '{}' but got the "
+                  "value '{}' -- check dependencies".format(
+                      sym.name_and_loc, user_value, sym.str_value),
+                  file=sys.stderr)
diff --git a/ext/Kconfiglib/examples/print_config_tree.py b/ext/Kconfiglib/examples/print_config_tree.py
new file mode 100644
index 0000000..dc81d9d
--- /dev/null
+++ b/ext/Kconfiglib/examples/print_config_tree.py
@@ -0,0 +1,199 @@
+# Prints menu entries as a tree with its value in the .config file. This can be
+# handy e.g. for diffing between different .config files or versions of Kconfig files.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=print_config_tree.py [SCRIPT_ARG=<.config>]
+#
+#   If the variable WITH_HELP_DESC is modified to 'True', the help is added
+#   to the symbols.
+#
+# Here's a notation guide. The notation matches the one used by menuconfig
+# (scripts/kconfig/mconf):
+#
+#   [ ] prompt      - Bool
+#   < > prompt      - Tristate
+#   {M} prompt      - Tristate selected to m. Can only be set to m or y.
+#   -*- prompt      - Bool/tristate selected to y, pinning it
+#   -M- prompt      - Tristate selected to m that also has m visibility,
+#                     pinning it to m
+#   (foo) prompt    - String/int/hex symbol with value "foo"
+#   --> prompt      - The selected symbol in a choice in y mode. This
+#                     syntax is unique to this example.
+#
+# When modules are disabled, the .type attribute of TRISTATE symbols and
+# choices automatically changes to BOOL. This trick is used by the C
+# implementation as well, and gives the expected behavior without having to do
+# anything extra here. The original type is available in .orig_type if needed.
+#
+# Example output:
+#
+#   $ make scriptconfig SCRIPT=Kconfiglib/examples/print_config_tree.py [SCRIPT_ARG=<.config file>]
+#
+#   ======== Linux/x86 4.9.82 Kernel Configuration ========
+#
+#   [*] 64-bit kernel (64BIT)
+#       General setup
+#          ()  Cross-compiler tool prefix (CROSS_COMPILE)
+#          [ ] Compile also drivers which will not load (COMPILE_TEST)
+#          ()  Local version - append to kernel release (LOCALVERSION)
+#          [*] Automatically append version information to the version string (LOCALVERSION_AUTO)
+#          -*- Kernel compression mode
+#          ...
+#
+# With the variable WITH_HELP_DESC modified to 'True':
+#
+#   ======== Linux/x86 4.9.82 Kernel Configuration ========
+#
+#   [*] 64-bit kernel - Say yes to build a 64-bit kernel - formerly known as x86_64 Say no to build a 32-bit kernel - formerly known as i386  (64BIT)
+#       General setup
+#           ()  Cross-compiler tool prefix - Same as running 'make CROSS_COMPILE=prefix-' but stored for default make runs in this kernel build directory.  You don't need to set this unless you want the configured kernel build directory to select the cross-compiler automatically.  (CROSS_COMPILE)
+#           [ ] Compile also drivers which will not load - Some drivers can be compiled on a different platform than they are intended to be run on. Despite they cannot be loaded there (or even when they load they cannot be used due to missing HW support), developers still, opposing to distributors, might want to build such drivers to compile-test them.  If you are a developer and want to build everything available, say Y here. If you are a user/distributor, say N here to exclude useless drivers to be distributed.  (COMPILE_TEST)
+#           ...
+
+import sys
+
+from kconfiglib import Kconfig, \
+                       Symbol, MENU, COMMENT, \
+                       BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \
+                       expr_value
+
+
+# Add help description to output
+WITH_HELP_DESC = False
+
+
+def indent_print(s, indent):
+    print(indent*" " + s)
+
+
+def value_str(sc):
+    """
+    Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu entry.
+
+    sc: Symbol or Choice.
+    """
+    if sc.type in (STRING, INT, HEX):
+        return "({})".format(sc.str_value)
+
+    # BOOL or TRISTATE
+
+    # The choice mode is an upper bound on the visibility of choice symbols, so
+    # we can check the choice symbols' own visibility to see if the choice is
+    # in y mode
+    if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2:
+        # For choices in y mode, print '-->' next to the selected symbol
+        return "-->" if sc.choice.selection is sc else "   "
+
+    tri_val_str = (" ", "M", "*")[sc.tri_value]
+
+    if len(sc.assignable) == 1:
+        # Pinned to a single value
+        return "-{}-".format(tri_val_str)
+
+    if sc.type == BOOL:
+        return "[{}]".format(tri_val_str)
+
+    if sc.type == TRISTATE:
+        if sc.assignable == (1, 2):
+            # m and y available
+            return "{" + tri_val_str + "}"  # Gets a bit confusing with .format()
+        return "<{}>".format(tri_val_str)
+
+
+def node_str(node):
+    """
+    Returns the complete menu entry text for a menu node, or "" for invisible
+    menu nodes. Invisible menu nodes are those that lack a prompt or that do
+    not have a satisfied prompt condition.
+
+    Example return value: "[*] Bool symbol (BOOL)"
+
+    The symbol name is printed in parentheses to the right of the prompt.
+    """
+    if not node.prompt:
+        return ""
+
+    # Even for menu nodes for symbols and choices, it's wrong to check
+    # Symbol.visibility / Choice.visibility here. The reason is that a symbol
+    # (and a choice, in theory) can be defined in multiple locations, giving it
+    # multiple menu nodes, which do not necessarily all have the same prompt
+    # visibility. Symbol.visibility / Choice.visibility is calculated as the OR
+    # of the visibility of all the prompts.
+    prompt, prompt_cond = node.prompt
+    if not expr_value(prompt_cond):
+        return ""
+
+    if node.item == MENU:
+        return "    " + prompt
+
+    if node.item == COMMENT:
+        return "    *** {} ***".format(prompt)
+
+    # Symbol or Choice
+
+    sc = node.item
+
+    if sc.type == UNKNOWN:
+        # Skip symbols defined without a type (these are obscure and generate
+        # a warning)
+        return ""
+
+    # Add help text
+    if WITH_HELP_DESC:
+        prompt += ' - ' + str(node.help).replace('\n', ' ').replace('\r', '')
+
+    # {:3} sets the field width to three. Gives nice alignment for empty string
+    # values.
+    res = "{:3} {}".format(value_str(sc), prompt)
+
+    # Don't print the name for unnamed choices (the normal kind)
+    if sc.name is not None:
+        res += " ({})".format(sc.name)
+
+    return res
+
+
+def print_menuconfig_nodes(node, indent):
+    """
+    Prints a tree with all the menu entries rooted at 'node'. Child menu
+    entries are indented.
+    """
+    while node:
+        string = node_str(node)
+        if string:
+            indent_print(string, indent)
+
+        if node.list:
+            print_menuconfig_nodes(node.list, indent + 8)
+
+        node = node.next
+
+
+def print_menuconfig(kconf):
+    """
+    Prints all menu entries for the configuration.
+    """
+    # Print the expanded mainmenu text at the top. This is the same as
+    # kconf.top_node.prompt[0], but with variable references expanded.
+    print("\n======== {} ========\n".format(kconf.mainmenu_text))
+
+    print_menuconfig_nodes(kconf.top_node.list, 0)
+    print("")
+
+
+if __name__ == "__main__":
+
+    # Load Kconfig configuration files
+    kconf = Kconfig(sys.argv[1])
+
+    # Set default .config file or load it from argv
+    if len(sys.argv) == 2:
+        config_filename = '.config'
+    else:
+        config_filename = sys.argv[2]
+
+    kconf.load_config(config_filename)
+
+    # Print the configuration tree
+    print_menuconfig(kconf)
diff --git a/ext/Kconfiglib/examples/print_sym_info.py b/ext/Kconfiglib/examples/print_sym_info.py
new file mode 100644
index 0000000..ea6fc72
--- /dev/null
+++ b/ext/Kconfiglib/examples/print_sym_info.py
@@ -0,0 +1,54 @@
+# Loads a Kconfig and a .config and prints a symbol.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/print_sym_info.py SCRIPT_ARG=<name>
+#
+# Example output for SCRIPT_ARG=MODULES:
+#
+# menuconfig MODULES
+# 	bool
+# 	prompt "Enable loadable module support"
+# 	option modules
+# 	help
+# 	  Kernel modules are small pieces of compiled code which can
+# 	  be inserted in the running kernel, rather than being
+# 	  permanently built into the kernel.  You use the "modprobe"
+# 	  tool to add (and sometimes remove) them.  If you say Y here,
+# 	  many parts of the kernel can be built as modules (by
+# 	  answering M instead of Y where indicated): this is most
+# 	  useful for infrequently used options which are not required
+# 	  for booting.  For more information, see the man pages for
+# 	  modprobe, lsmod, modinfo, insmod and rmmod.
+#
+# 	  If you say Y here, you will need to run "make
+# 	  modules_install" to put the modules under /lib/modules/
+# 	  where modprobe can find them (you may need to be root to do
+# 	  this).
+#
+# 	  If unsure, say Y.
+#
+# value = n
+# visibility = y
+# currently assignable values: n, y
+# defined at init/Kconfig:1674
+
+import sys
+
+from kconfiglib import Kconfig, TRI_TO_STR
+
+
+if len(sys.argv) < 3:
+    sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=<name>')
+
+kconf = Kconfig(sys.argv[1])
+sym = kconf.syms[sys.argv[2]]
+
+print(sym)
+print("value = " + sym.str_value)
+print("visibility = " + TRI_TO_STR[sym.visibility])
+print("currently assignable values: " +
+      ", ".join([TRI_TO_STR[v] for v in sym.assignable]))
+
+for node in sym.nodes:
+    print("defined at {}:{}".format(node.filename, node.linenr))
diff --git a/ext/Kconfiglib/examples/print_tree.py b/ext/Kconfiglib/examples/print_tree.py
new file mode 100644
index 0000000..49cb954
--- /dev/null
+++ b/ext/Kconfiglib/examples/print_tree.py
@@ -0,0 +1,75 @@
+# Prints the menu tree of the configuration. Dependencies between symbols can
+# sometimes implicitly alter the menu structure (see kconfig-language.txt), and
+# that's implemented too.
+#
+# Note: See the Kconfig.node_iter() function as well, which provides a simpler
+# interface for walking the menu tree.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
+#
+# Example output:
+#
+#   ...
+#   config HAVE_KERNEL_LZO
+#   config HAVE_KERNEL_LZ4
+#   choice
+#     config KERNEL_GZIP
+#     config KERNEL_BZIP2
+#     config KERNEL_LZMA
+#     config KERNEL_XZ
+#     config KERNEL_LZO
+#     config KERNEL_LZ4
+#   config DEFAULT_HOSTNAME
+#   config SWAP
+#   config SYSVIPC
+#     config SYSVIPC_SYSCTL
+#   config POSIX_MQUEUE
+#     config POSIX_MQUEUE_SYSCTL
+#   config CROSS_MEMORY_ATTACH
+#   config FHANDLE
+#   config USELIB
+#   config AUDIT
+#   config HAVE_ARCH_AUDITSYSCALL
+#   config AUDITSYSCALL
+#   config AUDIT_WATCH
+#   config AUDIT_TREE
+#   menu "IRQ subsystem"
+#     config MAY_HAVE_SPARSE_IRQ
+#     config GENERIC_IRQ_LEGACY
+#     config GENERIC_IRQ_PROBE
+#   ...
+
+import sys
+
+from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT
+
+
+def indent_print(s, indent):
+    print(indent*" " + s)
+
+
+def print_items(node, indent):
+    while node:
+        if isinstance(node.item, Symbol):
+            indent_print("config " + node.item.name, indent)
+
+        elif isinstance(node.item, Choice):
+            indent_print("choice", indent)
+
+        elif node.item == MENU:
+            indent_print('menu "{}"'.format(node.prompt[0]), indent)
+
+        elif node.item == COMMENT:
+            indent_print('comment "{}"'.format(node.prompt[0]), indent)
+
+
+        if node.list:
+            print_items(node.list, indent + 2)
+
+        node = node.next
+
+
+kconf = Kconfig(sys.argv[1])
+print_items(kconf.top_node, 0)
diff --git a/ext/Kconfiglib/gem5notes.txt b/ext/Kconfiglib/gem5notes.txt
new file mode 100644
index 0000000..b3ad870
--- /dev/null
+++ b/ext/Kconfiglib/gem5notes.txt
@@ -0,0 +1,15 @@
+Two symlinks have been deleted from this kconfig distribution because they
+caused problems with git:
+
+tests/symlink => sub/sub
+examples/kconfiglib.py -> ../kconfiglib.py
+
+To run kconfig's tests, you will likely need to recreate tests/symlink. To
+run the examples, you will need to make sure kconfiglib.py is in the import
+path, either by recreating examples/kconfiglib.py, or by adjusting the module
+search path at the python level.
+
+Also, to avoid adding unnecessary python files to the root of the module search
+path, this change also moves kconfiglib.py into a directory called "import"
+where it lives by itself. The examples/kconfiglib.py symlink would need to
+point to the library in this new location, if recreated.
diff --git a/ext/Kconfiglib/genconfig.py b/ext/Kconfiglib/genconfig.py
new file mode 100755
index 0000000..62f065b
--- /dev/null
+++ b/ext/Kconfiglib/genconfig.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Generates a header file with #defines from the configuration, matching the
+format of include/generated/autoconf.h in the Linux kernel.
+
+Optionally, also writes the configuration output as a .config file. See
+--config-out.
+
+The --sync-deps, --file-list, and --env-list options generate information that
+can be used to avoid needless rebuilds/reconfigurations.
+
+Before writing a header or configuration file, Kconfiglib compares the old
+contents of the file against the new contents. If there's no change, the write
+is skipped. This avoids updating file metadata like the modification time, and
+might save work depending on your build setup.
+
+By default, the configuration is generated from '.config'. A different
+configuration file can be passed in the KCONFIG_CONFIG environment variable.
+
+A custom header string can be inserted at the beginning of generated
+configuration and header files by setting the KCONFIG_CONFIG_HEADER and
+KCONFIG_AUTOHEADER_HEADER environment variables, respectively (this also works
+for other scripts). The string is not automatically made a comment (this is by
+design, to allow anything to be added), and no trailing newline is added, so
+add '/* */', '#', and newlines as appropriate.
+
+See https://www.gnu.org/software/make/manual/make.html#Multi_002dLine for a
+handy way to define multi-line variables in makefiles, for use with custom
+headers. Remember to export the variable to the environment.
+"""
+import argparse
+import os
+import sys
+
+import kconfiglib
+
+
+DEFAULT_SYNC_DEPS_PATH = "deps/"
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--header-path",
+        metavar="HEADER_FILE",
+        help="""
+Path to write the generated header file to. If not specified, the path in the
+environment variable KCONFIG_AUTOHEADER is used if it is set, and 'config.h'
+otherwise.
+""")
+
+    parser.add_argument(
+        "--config-out",
+        metavar="CONFIG_FILE",
+        help="""
+Write the configuration to CONFIG_FILE. This is useful if you include .config
+files in Makefiles, as the generated configuration file will be a full .config
+file even if .config is outdated. The generated configuration matches what
+olddefconfig would produce. If you use sync-deps, you can include
+deps/auto.conf instead. --config-out is meant for cases where incremental build
+information isn't needed.
+""")
+
+    parser.add_argument(
+        "--sync-deps",
+        metavar="OUTPUT_DIR",
+        nargs="?",
+        const=DEFAULT_SYNC_DEPS_PATH,
+        help="""
+Enable generation of symbol dependency information for incremental builds,
+optionally specifying the output directory (default: {}). See the docstring of
+Kconfig.sync_deps() in Kconfiglib for more information.
+""".format(DEFAULT_SYNC_DEPS_PATH))
+
+    parser.add_argument(
+        "--file-list",
+        metavar="OUTPUT_FILE",
+        help="""
+Write a list of all Kconfig files to OUTPUT_FILE, with one file per line. The
+paths are relative to $srctree (or to the current directory if $srctree is
+unset). Files appear in the order they're 'source'd.
+""")
+
+    parser.add_argument(
+        "--env-list",
+        metavar="OUTPUT_FILE",
+        help="""
+Write a list of all environment variables referenced in Kconfig files to
+OUTPUT_FILE, with one variable per line. Each line has the format NAME=VALUE.
+Only environment variables referenced with the preprocessor $(VAR) syntax are
+included, and not variables referenced with the older $VAR syntax (which is
+only supported for backwards compatibility).
+""")
+
+    parser.add_argument(
+        "kconfig",
+        metavar="KCONFIG",
+        nargs="?",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    args = parser.parse_args()
+
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    kconf.load_config()
+
+    if args.header_path is None:
+        if "KCONFIG_AUTOHEADER" in os.environ:
+            kconf.write_autoconf()
+        else:
+            # Kconfiglib defaults to include/generated/autoconf.h to be
+            # compatible with the C tools. 'config.h' is used here instead for
+            # backwards compatibility. It's probably a saner default for tools
+            # as well.
+            kconf.write_autoconf("config.h")
+    else:
+        kconf.write_autoconf(args.header_path)
+
+    if args.config_out is not None:
+        kconf.write_config(args.config_out, save_old=False)
+
+    if args.sync_deps is not None:
+        kconf.sync_deps(args.sync_deps)
+
+    if args.file_list is not None:
+        with _open_write(args.file_list) as f:
+            for path in kconf.kconfig_filenames:
+                f.write(path + "\n")
+
+    if args.env_list is not None:
+        with _open_write(args.env_list) as f:
+            for env_var in kconf.env_vars:
+                f.write("{}={}\n".format(env_var, os.environ[env_var]))
+
+
+def _open_write(path):
+    # Python 2/3 compatibility. io.open() is available on both, but makes
+    # write() expect 'unicode' strings on Python 2.
+
+    if sys.version_info[0] < 3:
+        return open(path, "w")
+    return open(path, "w", encoding="utf-8")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/guiconfig.py b/ext/Kconfiglib/guiconfig.py
new file mode 100755
index 0000000..7804fdc
--- /dev/null
+++ b/ext/Kconfiglib/guiconfig.py
@@ -0,0 +1,2324 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Overview
+========
+
+A Tkinter-based menuconfig implementation, based around a treeview control and
+a help display. The interface should feel familiar to people used to qconf
+('make xconfig'). Compatible with both Python 2 and Python 3.
+
+The display can be toggled between showing the full tree and showing just a
+single menu (like menuconfig.py). Only single-menu mode distinguishes between
+symbols defined with 'config' and symbols defined with 'menuconfig'.
+
+A show-all mode is available that shows invisible items in red.
+
+Supports both mouse and keyboard controls. The following keyboard shortcuts are
+available:
+
+  Ctrl-S   : Save configuration
+  Ctrl-O   : Open configuration
+  Ctrl-A   : Toggle show-all mode
+  Ctrl-N   : Toggle show-name mode
+  Ctrl-M   : Toggle single-menu mode
+  Ctrl-F, /: Open jump-to dialog
+  ESC      : Close
+
+Running
+=======
+
+guiconfig.py can be run either as a standalone executable or by calling the
+menuconfig() function with an existing Kconfig instance. The second option is a
+bit inflexible in that it will still load and save .config, etc.
+
+When run in standalone mode, the top-level Kconfig file to load can be passed
+as a command-line argument. With no argument, it defaults to "Kconfig".
+
+The KCONFIG_CONFIG environment variable specifies the .config file to load (if
+it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+
+$srctree is supported through Kconfiglib.
+"""
+
+# Note: There's some code duplication with menuconfig.py below, especially for
+# the help text. Maybe some of it could be moved into kconfiglib.py or a shared
+# helper script, but OTOH it's pretty nice to have things standalone and
+# customizable.
+
+import errno
+import os
+import sys
+
+_PY2 = sys.version_info[0] < 3
+
+if _PY2:
+    # Python 2
+    from Tkinter import *
+    import ttk
+    import tkFont as font
+    import tkFileDialog as filedialog
+    import tkMessageBox as messagebox
+else:
+    # Python 3
+    from tkinter import *
+    import tkinter.ttk as ttk
+    import tkinter.font as font
+    from tkinter import filedialog, messagebox
+
+from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \
+                       BOOL, TRISTATE, STRING, INT, HEX, \
+                       AND, OR, \
+                       expr_str, expr_value, split_expr, \
+                       standard_sc_expr_str, \
+                       TRI_TO_STR, TYPE_TO_STR, \
+                       standard_kconfig, standard_config_filename
+
+
+# If True, use GIF image data embedded in this file instead of separate GIF
+# files. See _load_images().
+_USE_EMBEDDED_IMAGES = True
+
+
+# Help text for the jump-to dialog
+_JUMP_TO_HELP = """\
+Type one or more strings/regexes and press Enter to list items that match all
+of them. Python's regex flavor is used (see the 're' module). Double-clicking
+an item will jump to it. Item values can be toggled directly within the dialog.\
+"""
+
+
+def _main():
+    menuconfig(standard_kconfig(__doc__))
+
+
+# Global variables used below:
+#
+#   _root:
+#     The Toplevel instance for the main window
+#
+#   _tree:
+#     The Treeview in the main window
+#
+#   _jump_to_tree:
+#     The Treeview in the jump-to dialog. None if the jump-to dialog isn't
+#     open. Doubles as a flag.
+#
+#   _jump_to_matches:
+#     List of Nodes shown in the jump-to dialog
+#
+#   _menupath:
+#     The Label that shows the menu path of the selected item
+#
+#   _backbutton:
+#     The button shown in single-menu mode for jumping to the parent menu
+#
+#   _status_label:
+#     Label with status text shown at the bottom of the main window
+#     ("Modified", "Saved to ...", etc.)
+#
+#   _id_to_node:
+#     We can't use Node objects directly as Treeview item IDs, so we use their
+#     id()s instead. This dictionary maps Node id()s back to Nodes. (The keys
+#     are actually str(id(node)), just to simplify lookups.)
+#
+#   _cur_menu:
+#     The current menu. Ignored outside single-menu mode.
+#
+#   _show_all_var/_show_name_var/_single_menu_var:
+#     Tkinter Variable instances bound to the corresponding checkboxes
+#
+#   _show_all/_single_menu:
+#     Plain Python bools that track _show_all_var and _single_menu_var, to
+#     speed up and simplify things a bit
+#
+#   _conf_filename:
+#     File to save the configuration to
+#
+#   _minconf_filename:
+#     File to save minimal configurations to
+#
+#   _conf_changed:
+#     True if the configuration has been changed. If False, we don't bother
+#     showing the save-and-quit dialog.
+#
+#     We reset this to False whenever the configuration is saved.
+#
+#   _*_img:
+#     PhotoImage instances for images
+
+
+def menuconfig(kconf):
+    """
+    Launches the configuration interface, returning after the user exits.
+
+    kconf:
+      Kconfig instance to be configured
+    """
+    global _kconf
+    global _conf_filename
+    global _minconf_filename
+    global _jump_to_tree
+    global _cur_menu
+
+    _kconf = kconf
+
+    _jump_to_tree = None
+
+    _create_id_to_node()
+
+    _create_ui()
+
+    # Filename to save configuration to
+    _conf_filename = standard_config_filename()
+
+    # Load existing configuration and check if it's outdated
+    _set_conf_changed(_load_config())
+
+    # Filename to save minimal configuration to
+    _minconf_filename = "defconfig"
+
+    # Current menu in single-menu mode
+    _cur_menu = _kconf.top_node
+
+    # Any visible items in the top menu?
+    if not _shown_menu_nodes(kconf.top_node):
+        # Nothing visible. Start in show-all mode and try again.
+        _show_all_var.set(True)
+        if not _shown_menu_nodes(kconf.top_node):
+            # Give up and show an error. It's nice to be able to assume that
+            # the tree is non-empty in the rest of the code.
+            _root.wait_visibility()
+            messagebox.showerror(
+                "Error",
+                "Empty configuration -- nothing to configure.\n\n"
+                "Check that environment variables are set properly.")
+            _root.destroy()
+            return
+
+    # Build the initial tree
+    _update_tree()
+
+    # Select the first item and focus the Treeview, so that keyboard controls
+    # work immediately
+    _select(_tree, _tree.get_children()[0])
+    _tree.focus_set()
+
+    # Make geometry information available for centering the window. This
+    # indirectly creates the window, so hide it so that it's never shown at the
+    # old location.
+    _root.withdraw()
+    _root.update_idletasks()
+
+    # Center the window
+    _root.geometry("+{}+{}".format(
+        (_root.winfo_screenwidth() - _root.winfo_reqwidth())//2,
+        (_root.winfo_screenheight() - _root.winfo_reqheight())//2))
+
+    # Show it
+    _root.deiconify()
+
+    # Prevent the window from being automatically resized. Otherwise, it
+    # changes size when scrollbars appear/disappear before the user has
+    # manually resized it.
+    _root.geometry(_root.geometry())
+
+    _root.mainloop()
+
+
+def _load_config():
+    # Loads any existing .config file. See the Kconfig.load_config() docstring.
+    #
+    # Returns True if .config is missing or outdated. We always prompt for
+    # saving the configuration in that case.
+
+    print(_kconf.load_config())
+    if not os.path.exists(_conf_filename):
+        # No .config
+        return True
+
+    return _needs_save()
+
+
+def _needs_save():
+    # Returns True if a just-loaded .config file is outdated (would get
+    # modified when saving)
+
+    if _kconf.missing_syms:
+        # Assignments to undefined symbols in the .config
+        return True
+
+    for sym in _kconf.unique_defined_syms:
+        if sym.user_value is None:
+            if sym.config_string:
+                # Unwritten symbol
+                return True
+        elif sym.orig_type in (BOOL, TRISTATE):
+            if sym.tri_value != sym.user_value:
+                # Written bool/tristate symbol, new value
+                return True
+        elif sym.str_value != sym.user_value:
+            # Written string/int/hex symbol, new value
+            return True
+
+    # No need to prompt for save
+    return False
+
+
+def _create_id_to_node():
+    global _id_to_node
+
+    _id_to_node = {str(id(node)): node for node in _kconf.node_iter()}
+
+
+def _create_ui():
+    # Creates the main window UI
+
+    global _root
+    global _tree
+
+    # Create the root window. This initializes Tkinter and makes e.g.
+    # PhotoImage available, so do it early.
+    _root = Tk()
+
+    _load_images()
+    _init_misc_ui()
+    _fix_treeview_issues()
+
+    _create_top_widgets()
+    # Create the pane with the Kconfig tree and description text
+    panedwindow, _tree = _create_kconfig_tree_and_desc(_root)
+    panedwindow.grid(column=0, row=1, sticky="nsew")
+    _create_status_bar()
+
+    _root.columnconfigure(0, weight=1)
+    # Only the pane with the Kconfig tree and description grows vertically
+    _root.rowconfigure(1, weight=1)
+
+    # Start with show-name disabled
+    _do_showname()
+
+    _tree.bind("<Left>", _tree_left_key)
+    _tree.bind("<Right>", _tree_right_key)
+    # Note: Binding this for the jump-to tree as well would cause issues due to
+    # the Tk bug mentioned in _tree_open()
+    _tree.bind("<<TreeviewOpen>>", _tree_open)
+    # add=True to avoid overriding the description text update
+    _tree.bind("<<TreeviewSelect>>", _update_menu_path, add=True)
+
+    _root.bind("<Control-s>", _save)
+    _root.bind("<Control-o>", _open)
+    _root.bind("<Control-a>", _toggle_showall)
+    _root.bind("<Control-n>", _toggle_showname)
+    _root.bind("<Control-m>", _toggle_tree_mode)
+    _root.bind("<Control-f>", _jump_to_dialog)
+    _root.bind("/", _jump_to_dialog)
+    _root.bind("<Escape>", _on_quit)
+
+
+def _load_images():
+    # Loads GIF images, creating the global _*_img PhotoImage variables.
+    # Base64-encoded images embedded in this script are used if
+    # _USE_EMBEDDED_IMAGES is True, and separate image files in the same
+    # directory as the script otherwise.
+    #
+    # Using a global variable indirectly prevents the image from being
+    # garbage-collected. Passing an image to a Tkinter function isn't enough to
+    # keep it alive.
+
+    def load_image(name, data):
+        var_name = "_{}_img".format(name)
+
+        if _USE_EMBEDDED_IMAGES:
+            globals()[var_name] = PhotoImage(data=data, format="gif")
+        else:
+            globals()[var_name] = PhotoImage(
+                file=os.path.join(os.path.dirname(__file__), name + ".gif"),
+                format="gif")
+
+    # Note: Base64 data can be put on the clipboard with
+    #   $ base64 -w0 foo.gif | xclip
+
+    load_image("icon", "R0lGODlhMAAwAPEDAAAAAADQAO7u7v///yH5BAUKAAMALAAAAAAwADAAAAL/nI+gy+2Pokyv2jazuZxryQjiSJZmyXxHeLbumH6sEATvW8OLNtf5bfLZRLFITzgEipDJ4mYxYv6A0ubuqYhWk66tVTE4enHer7jcKvt0LLUw6P45lvEprT6c0+v7OBuqhYdHohcoqIbSAHc4ljhDwrh1UlgSydRCWWlp5wiYZvmSuSh4IzrqV6p4cwhkCsmY+nhK6uJ6t1mrOhuJqfu6+WYiCiwl7HtLjNSZZZis/MeM7NY3TaRKS40ooDeoiVqIultsrav92bi9c3a5KkkOsOJZpSS99m4k/0zPng4Gks9JSbB+8DIcoQfnjwpZCHv5W+ip4aQrKrB0uOikYhiMCBw1/uPoQUMBADs=")
+    load_image("n_bool", "R0lGODdhEAAQAPAAAAgICP///ywAAAAAEAAQAAACIISPacHtvp5kcb5qG85hZ2+BkyiRF8BBaEqtrKkqslEAADs=")
+    load_image("y_bool", "R0lGODdhEAAQAPEAAAgICADQAP///wAAACwAAAAAEAAQAAACMoSPacLtvlh4YrIYsst2cV19AvaVF9CUXBNJJoum7ymrsKuCnhiupIWjSSjAFuWhSCIKADs=")
+    load_image("n_tri", "R0lGODlhEAAQAPD/AAEBAf///yH5BAUKAAIALAAAAAAQABAAAAInlI+pBrAKQnCPSUlXvFhznlkfeGwjKZhnJ65h6nrfi6h0st2QXikFADs=")
+    load_image("m_tri", "R0lGODlhEAAQAPEDAAEBAeQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nI+pBrAWAhPCjYhiAJQCnWmdoElHGVBoiK5M21ofXFpXRIrgiecqxkuNciZIhNOZFRNI24PhfEoLADs=")
+    load_image("y_tri", "R0lGODlhEAAQAPEDAAICAgDQAP///wAAACH5BAUKAAMALAAAAAAQABAAAAI0nI+pBrAYBhDCRRUypfmergmgZ4xjMpmaw2zmxk7cCB+pWiVqp4MzDwn9FhGZ5WFjIZeGAgA7")
+    load_image("m_my", "R0lGODlhEAAQAPEDAAAAAOQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nIGpxiAPI2ghxFinq/ZygQhc94zgZopmOLYf67anGr+oZdp02emfV5n9MEHN5QhqICETxkABbQ4KADs=")
+    load_image("y_my", "R0lGODlhEAAQAPH/AAAAAADQAAPRA////yH5BAUKAAQALAAAAAAQABAAAAM+SArcrhCMSSuIM9Q8rxxBWIXawIBkmWonupLd565Um9G1PIs59fKmzw8WnAlusBYR2SEIN6DmAmqBLBxYSAIAOw==")
+    load_image("n_locked", "R0lGODlhEAAQAPABAAAAAP///yH5BAUKAAEALAAAAAAQABAAAAIgjB8AyKwN04pu0vMutpqqz4Hih4ydlnUpyl2r23pxUAAAOw==")
+    load_image("m_locked", "R0lGODlhEAAQAPD/AAAAAOQMuiH5BAUKAAIALAAAAAAQABAAAAIylC8AyKwN04ohnGcqqlZmfXDWI26iInZoyiore05walolV39ftxsYHgL9QBBMBGFEFAAAOw==")
+    load_image("y_locked", "R0lGODlhEAAQAPD/AAAAAADQACH5BAUKAAIALAAAAAAQABAAAAIylC8AyKzNgnlCtoDTwvZwrHydIYpQmR3KWq4uK74IOnp0HQPmnD3cOVlUIAgKsShkFAAAOw==")
+    load_image("not_selected", "R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIrlA2px6IBw2IpWglOvTYhzmUbGD3kNZ5QqrKn2YrqigCxZoMelU6No9gdCgA7")
+    load_image("selected", "R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIzlA2px6IBw2IpWglOvTah/kTZhimASJomiqonlLov1qptHTsgKSEzh9H8QI0QzNPwmRoFADs=")
+    load_image("edit", "R0lGODlhEAAQAPIFAAAAAKOLAMuuEPvXCvrxvgAAAAAAAAAAACH5BAUKAAUALAAAAAAQABAAAANCWLqw/gqMBp8cszJxcwVC2FEOEIAi5kVBi3IqWZhuCGMyfdpj2e4pnK+WAshmvxeAcETWlsxPkkBtsqBMa8TIBSQAADs=")
+
+
+def _fix_treeview_issues():
+    # Fixes some Treeview issues
+
+    global _treeview_rowheight
+
+    style = ttk.Style()
+
+    # The treeview rowheight isn't adjusted automatically on high-DPI displays,
+    # so do it ourselves. The font will probably always be TkDefaultFont, but
+    # play it safe and look it up.
+
+    _treeview_rowheight = font.Font(font=style.lookup("Treeview", "font")) \
+        .metrics("linespace") + 2
+
+    style.configure("Treeview", rowheight=_treeview_rowheight)
+
+    # Work around regression in https://core.tcl.tk/tk/tktview?name=509cafafae,
+    # which breaks tag background colors
+
+    for option in "foreground", "background":
+        # Filter out any styles starting with ("!disabled", "!selected", ...).
+        # style.map() returns an empty list for missing options, so this should
+        # be future-safe.
+        style.map(
+            "Treeview",
+            **{option: [elm for elm in style.map("Treeview", query_opt=option)
+                        if elm[:2] != ("!disabled", "!selected")]})
+
+
+def _init_misc_ui():
+    # Does misc. UI initialization, like setting the title, icon, and theme
+
+    _root.title(_kconf.mainmenu_text)
+    # iconphoto() isn't available in Python 2's Tkinter
+    _root.tk.call("wm", "iconphoto", _root._w, "-default", _icon_img)
+    # Reducing the width of the window to 1 pixel makes it move around, at
+    # least on GNOME. Prevent weird stuff like that.
+    _root.minsize(128, 128)
+    _root.protocol("WM_DELETE_WINDOW", _on_quit)
+
+    # Use the 'clam' theme on *nix if it's available. It looks nicer than the
+    # 'default' theme.
+    if _root.tk.call("tk", "windowingsystem") == "x11":
+        style = ttk.Style()
+        if "clam" in style.theme_names():
+            style.theme_use("clam")
+
+
+def _create_top_widgets():
+    # Creates the controls above the Kconfig tree in the main window
+
+    global _show_all_var
+    global _show_name_var
+    global _single_menu_var
+    global _menupath
+    global _backbutton
+
+    topframe = ttk.Frame(_root)
+    topframe.grid(column=0, row=0, sticky="ew")
+
+    ttk.Button(topframe, text="Save", command=_save) \
+        .grid(column=0, row=0, sticky="ew", padx=".05c", pady=".05c")
+
+    ttk.Button(topframe, text="Save as...", command=_save_as) \
+        .grid(column=1, row=0, sticky="ew")
+
+    ttk.Button(topframe, text="Save minimal (advanced)...",
+               command=_save_minimal) \
+        .grid(column=2, row=0, sticky="ew", padx=".05c")
+
+    ttk.Button(topframe, text="Open...", command=_open) \
+        .grid(column=3, row=0)
+
+    ttk.Button(topframe, text="Jump to...", command=_jump_to_dialog) \
+        .grid(column=4, row=0, padx=".05c")
+
+    _show_name_var = BooleanVar()
+    ttk.Checkbutton(topframe, text="Show name", command=_do_showname,
+                    variable=_show_name_var) \
+        .grid(column=0, row=1, sticky="nsew", padx=".05c", pady="0 .05c",
+              ipady=".2c")
+
+    _show_all_var = BooleanVar()
+    ttk.Checkbutton(topframe, text="Show all", command=_do_showall,
+                    variable=_show_all_var) \
+        .grid(column=1, row=1, sticky="nsew", pady="0 .05c")
+
+    # Allow the show-all and single-menu status to be queried via plain global
+    # Python variables, which is faster and simpler
+
+    def show_all_updated(*_):
+        global _show_all
+        _show_all = _show_all_var.get()
+
+    _trace_write(_show_all_var, show_all_updated)
+    _show_all_var.set(False)
+
+    _single_menu_var = BooleanVar()
+    ttk.Checkbutton(topframe, text="Single-menu mode", command=_do_tree_mode,
+                    variable=_single_menu_var) \
+        .grid(column=2, row=1, sticky="nsew", padx=".05c", pady="0 .05c")
+
+    _backbutton = ttk.Button(topframe, text="<--", command=_leave_menu,
+                             state="disabled")
+    _backbutton.grid(column=0, row=4, sticky="nsew", padx=".05c", pady="0 .05c")
+
+    def tree_mode_updated(*_):
+        global _single_menu
+        _single_menu = _single_menu_var.get()
+
+        if _single_menu:
+            _backbutton.grid()
+        else:
+            _backbutton.grid_remove()
+
+    _trace_write(_single_menu_var, tree_mode_updated)
+    _single_menu_var.set(False)
+
+    # Column to the right of the buttons that the menu path extends into, so
+    # that it can grow wider than the buttons
+    topframe.columnconfigure(5, weight=1)
+
+    _menupath = ttk.Label(topframe)
+    _menupath.grid(column=0, row=3, columnspan=6, sticky="w", padx="0.05c",
+                   pady="0 .05c")
+
+
+def _create_kconfig_tree_and_desc(parent):
+    # Creates a Panedwindow with a Treeview that shows Kconfig nodes and a Text
+    # that shows a description of the selected node. Returns a tuple with the
+    # Panedwindow and the Treeview. This code is shared between the main window
+    # and the jump-to dialog.
+
+    panedwindow = ttk.Panedwindow(parent, orient=VERTICAL)
+
+    tree_frame, tree = _create_kconfig_tree(panedwindow)
+    desc_frame, desc = _create_kconfig_desc(panedwindow)
+
+    panedwindow.add(tree_frame, weight=1)
+    panedwindow.add(desc_frame)
+
+    def tree_select(_):
+        # The Text widget does not allow editing the text in its disabled
+        # state. We need to temporarily enable it.
+        desc["state"] = "normal"
+
+        sel = tree.selection()
+        if not sel:
+            desc.delete("1.0", "end")
+            desc["state"] = "disabled"
+            return
+
+        # Text.replace() is not available in Python 2's Tkinter
+        desc.delete("1.0", "end")
+        desc.insert("end", _info_str(_id_to_node[sel[0]]))
+
+        desc["state"] = "disabled"
+
+    tree.bind("<<TreeviewSelect>>", tree_select)
+    tree.bind("<1>", _tree_click)
+    tree.bind("<Double-1>", _tree_double_click)
+    tree.bind("<Return>", _tree_enter)
+    tree.bind("<KP_Enter>", _tree_enter)
+    tree.bind("<space>", _tree_toggle)
+    tree.bind("n", _tree_set_val(0))
+    tree.bind("m", _tree_set_val(1))
+    tree.bind("y", _tree_set_val(2))
+
+    return panedwindow, tree
+
+
+def _create_kconfig_tree(parent):
+    # Creates a Treeview for showing Kconfig nodes
+
+    frame = ttk.Frame(parent)
+
+    tree = ttk.Treeview(frame, selectmode="browse", height=20,
+                        columns=("name",))
+    tree.heading("#0", text="Option", anchor="w")
+    tree.heading("name", text="Name", anchor="w")
+
+    tree.tag_configure("n-bool", image=_n_bool_img)
+    tree.tag_configure("y-bool", image=_y_bool_img)
+    tree.tag_configure("m-tri", image=_m_tri_img)
+    tree.tag_configure("n-tri", image=_n_tri_img)
+    tree.tag_configure("m-tri", image=_m_tri_img)
+    tree.tag_configure("y-tri", image=_y_tri_img)
+    tree.tag_configure("m-my", image=_m_my_img)
+    tree.tag_configure("y-my", image=_y_my_img)
+    tree.tag_configure("n-locked", image=_n_locked_img)
+    tree.tag_configure("m-locked", image=_m_locked_img)
+    tree.tag_configure("y-locked", image=_y_locked_img)
+    tree.tag_configure("not-selected", image=_not_selected_img)
+    tree.tag_configure("selected", image=_selected_img)
+    tree.tag_configure("edit", image=_edit_img)
+    tree.tag_configure("invisible", foreground="red")
+
+    tree.grid(column=0, row=0, sticky="nsew")
+
+    _add_vscrollbar(frame, tree)
+
+    frame.columnconfigure(0, weight=1)
+    frame.rowconfigure(0, weight=1)
+
+    # Create items for all menu nodes. These can be detached/moved later.
+    # Micro-optimize this a bit.
+    insert = tree.insert
+    id_ = id
+    Symbol_ = Symbol
+    for node in _kconf.node_iter():
+        item = node.item
+        insert("", "end", iid=id_(node),
+               values=item.name if item.__class__ is Symbol_ else "")
+
+    return frame, tree
+
+
+def _create_kconfig_desc(parent):
+    # Creates a Text for showing the description of the selected Kconfig node
+
+    frame = ttk.Frame(parent)
+
+    desc = Text(frame, height=12, wrap="none", borderwidth=0,
+                state="disabled")
+    desc.grid(column=0, row=0, sticky="nsew")
+
+    # Work around not being to Ctrl-C/V text from a disabled Text widget, with a
+    # tip found in https://stackoverflow.com/questions/3842155/is-there-a-way-to-make-the-tkinter-text-widget-read-only
+    desc.bind("<1>", lambda _: desc.focus_set())
+
+    _add_vscrollbar(frame, desc)
+
+    frame.columnconfigure(0, weight=1)
+    frame.rowconfigure(0, weight=1)
+
+    return frame, desc
+
+
+def _add_vscrollbar(parent, widget):
+    # Adds a vertical scrollbar to 'widget' that's only shown as needed
+
+    vscrollbar = ttk.Scrollbar(parent, orient="vertical",
+                               command=widget.yview)
+    vscrollbar.grid(column=1, row=0, sticky="ns")
+
+    def yscrollcommand(first, last):
+        # Only show the scrollbar when needed. 'first' and 'last' are
+        # strings.
+        if float(first) <= 0.0 and float(last) >= 1.0:
+            vscrollbar.grid_remove()
+        else:
+            vscrollbar.grid()
+
+        vscrollbar.set(first, last)
+
+    widget["yscrollcommand"] = yscrollcommand
+
+
+def _create_status_bar():
+    # Creates the status bar at the bottom of the main window
+
+    global _status_label
+
+    _status_label = ttk.Label(_root, anchor="e", padding="0 0 0.4c 0")
+    _status_label.grid(column=0, row=3, sticky="ew")
+
+
+def _set_status(s):
+    # Sets the text in the status bar to 's'
+
+    _status_label["text"] = s
+
+
+def _set_conf_changed(changed):
+    # Updates the status re. whether there are unsaved changes
+
+    global _conf_changed
+
+    _conf_changed = changed
+    if changed:
+        _set_status("Modified")
+
+
+def _update_tree():
+    # Updates the Kconfig tree in the main window by first detaching all nodes
+    # and then updating and reattaching them. The tree structure might have
+    # changed.
+
+    # If a selected/focused item is detached and later reattached, it stays
+    # selected/focused. That can give multiple selections even though
+    # selectmode=browse. Save and later restore the selection and focus as a
+    # workaround.
+    old_selection = _tree.selection()
+    old_focus = _tree.focus()
+
+    # Detach all tree items before re-stringing them. This is relatively fast,
+    # luckily.
+    _tree.detach(*_id_to_node.keys())
+
+    if _single_menu:
+        _build_menu_tree()
+    else:
+        _build_full_tree(_kconf.top_node)
+
+    _tree.selection_set(old_selection)
+    _tree.focus(old_focus)
+
+
+def _build_full_tree(menu):
+    # Updates the tree starting from menu.list, in full-tree mode. To speed
+    # things up, only open menus are updated. The menu-at-a-time logic here is
+    # to deal with invisible items that can show up outside show-all mode (see
+    # _shown_full_nodes()).
+
+    for node in _shown_full_nodes(menu):
+        _add_to_tree(node, _kconf.top_node)
+
+        # _shown_full_nodes() includes nodes from menus rooted at symbols, so
+        # we only need to check "real" menus/choices here
+        if node.list and not isinstance(node.item, Symbol):
+            if _tree.item(id(node), "open"):
+                _build_full_tree(node)
+            else:
+                # We're just probing here, so _shown_menu_nodes() will work
+                # fine, and might be a bit faster
+                shown = _shown_menu_nodes(node)
+                if shown:
+                    # Dummy element to make the open/closed toggle appear
+                    _tree.move(id(shown[0]), id(shown[0].parent), "end")
+
+
+def _shown_full_nodes(menu):
+    # Returns the list of menu nodes shown in 'menu' (a menu node for a menu)
+    # for full-tree mode. A tricky detail is that invisible items need to be
+    # shown if they have visible children.
+
+    def rec(node):
+        res = []
+
+        while node:
+            if _visible(node) or _show_all:
+                res.append(node)
+                if node.list and isinstance(node.item, Symbol):
+                    # Nodes from menu created from dependencies
+                    res += rec(node.list)
+
+            elif node.list and isinstance(node.item, Symbol):
+                # Show invisible symbols (defined with either 'config' and
+                # 'menuconfig') if they have visible children. This can happen
+                # for an m/y-valued symbol with an optional prompt
+                # ('prompt "foo" is COND') that is currently disabled.
+                shown_children = rec(node.list)
+                if shown_children:
+                    res.append(node)
+                    res += shown_children
+
+            node = node.next
+
+        return res
+
+    return rec(menu.list)
+
+
+def _build_menu_tree():
+    # Updates the tree in single-menu mode. See _build_full_tree() as well.
+
+    for node in _shown_menu_nodes(_cur_menu):
+        _add_to_tree(node, _cur_menu)
+
+
+def _shown_menu_nodes(menu):
+    # Used for single-menu mode. Similar to _shown_full_nodes(), but doesn't
+    # include children of symbols defined with 'menuconfig'.
+
+    def rec(node):
+        res = []
+
+        while node:
+            if _visible(node) or _show_all:
+                res.append(node)
+                if node.list and not node.is_menuconfig:
+                    res += rec(node.list)
+
+            elif node.list and isinstance(node.item, Symbol):
+                shown_children = rec(node.list)
+                if shown_children:
+                    # Invisible item with visible children
+                    res.append(node)
+                    if not node.is_menuconfig:
+                        res += shown_children
+
+            node = node.next
+
+        return res
+
+    return rec(menu.list)
+
+
+def _visible(node):
+    # Returns True if the node should appear in the menu (outside show-all
+    # mode)
+
+    return node.prompt and expr_value(node.prompt[1]) and not \
+        (node.item == MENU and not expr_value(node.visibility))
+
+
+def _add_to_tree(node, top):
+    # Adds 'node' to the tree, at the end of its menu. We rely on going through
+    # the nodes linearly to get the correct order. 'top' holds the menu that
+    # corresponds to the top-level menu, and can vary in single-menu mode.
+
+    parent = node.parent
+    _tree.move(id(node), "" if parent is top else id(parent), "end")
+    _tree.item(
+        id(node),
+        text=_node_str(node),
+        # The _show_all test avoids showing invisible items in red outside
+        # show-all mode, which could look confusing/broken. Invisible symbols
+        # are shown outside show-all mode if an invisible symbol has visible
+        # children in an implicit menu.
+        tags=_img_tag(node) if _visible(node) or not _show_all else
+            _img_tag(node) + " invisible")
+
+
+def _node_str(node):
+    # Returns the string shown to the right of the image (if any) for the node
+
+    if node.prompt:
+        if node.item == COMMENT:
+            s = "*** {} ***".format(node.prompt[0])
+        else:
+            s = node.prompt[0]
+
+        if isinstance(node.item, Symbol):
+            sym = node.item
+
+            # Print "(NEW)" next to symbols without a user value (from e.g. a
+            # .config), but skip it for choice symbols in choices in y mode,
+            # and for symbols of UNKNOWN type (which generate a warning though)
+            if sym.user_value is None and sym.type and not \
+                (sym.choice and sym.choice.tri_value == 2):
+
+                s += " (NEW)"
+
+    elif isinstance(node.item, Symbol):
+        # Symbol without prompt (can show up in show-all)
+        s = "<{}>".format(node.item.name)
+
+    else:
+        # Choice without prompt. Use standard_sc_expr_str() so that it shows up
+        # as '<choice (name if any)>'.
+        s = standard_sc_expr_str(node.item)
+
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+        if sym.orig_type == STRING:
+            s += ": " + sym.str_value
+        elif sym.orig_type in (INT, HEX):
+            s = "({}) {}".format(sym.str_value, s)
+
+    elif isinstance(node.item, Choice) and node.item.tri_value == 2:
+        # Print the prompt of the selected symbol after the choice for
+        # choices in y mode
+        sym = node.item.selection
+        if sym:
+            for sym_node in sym.nodes:
+                # Use the prompt used at this choice location, in case the
+                # choice symbol is defined in multiple locations
+                if sym_node.parent is node and sym_node.prompt:
+                    s += " ({})".format(sym_node.prompt[0])
+                    break
+            else:
+                # If the symbol isn't defined at this choice location, then
+                # just use whatever prompt we can find for it
+                for sym_node in sym.nodes:
+                    if sym_node.prompt:
+                        s += " ({})".format(sym_node.prompt[0])
+                        break
+
+    # In single-menu mode, print "--->" next to nodes that have menus that can
+    # potentially be entered. Print "----" if the menu is empty. We don't allow
+    # those to be entered.
+    if _single_menu and node.is_menuconfig:
+        s += "  --->" if _shown_menu_nodes(node) else "  ----"
+
+    return s
+
+
+def _img_tag(node):
+    # Returns the tag for the image that should be shown next to 'node', or the
+    # empty string if it shouldn't have an image
+
+    item = node.item
+
+    if item in (MENU, COMMENT) or not item.orig_type:
+        return ""
+
+    if item.orig_type in (STRING, INT, HEX):
+        return "edit"
+
+    # BOOL or TRISTATE
+
+    if _is_y_mode_choice_sym(item):
+        # Choice symbol in y-mode choice
+        return "selected" if item.choice.selection is item else "not-selected"
+
+    if len(item.assignable) <= 1:
+        # Pinned to a single value
+        return "" if isinstance(item, Choice) else item.str_value + "-locked"
+
+    if item.type == BOOL:
+        return item.str_value + "-bool"
+
+    # item.type == TRISTATE
+    if item.assignable == (1, 2):
+        return item.str_value + "-my"
+    return item.str_value + "-tri"
+
+
+def _is_y_mode_choice_sym(item):
+    # The choice mode is an upper bound on the visibility of choice symbols, so
+    # we can check the choice symbols' own visibility to see if the choice is
+    # in y mode
+    return isinstance(item, Symbol) and item.choice and item.visibility == 2
+
+
+def _tree_click(event):
+    # Click on the Kconfig Treeview
+
+    tree = event.widget
+    if tree.identify_element(event.x, event.y) == "image":
+        item = tree.identify_row(event.y)
+        # Select the item before possibly popping up a dialog for
+        # string/int/hex items, so that its help is visible
+        _select(tree, item)
+        _change_node(_id_to_node[item], tree.winfo_toplevel())
+        return "break"
+
+
+def _tree_double_click(event):
+    # Double-click on the Kconfig treeview
+
+    # Do an extra check to avoid weirdness when double-clicking in the tree
+    # heading area
+    if not _in_heading(event):
+        return _tree_enter(event)
+
+
+def _in_heading(event):
+    # Returns True if 'event' took place in the tree heading
+
+    tree = event.widget
+    return hasattr(tree, "identify_region") and \
+        tree.identify_region(event.x, event.y) in ("heading", "separator")
+
+
+def _tree_enter(event):
+    # Enter press or double-click within the Kconfig treeview. Prefer to
+    # open/close/enter menus, but toggle the value if that's not possible.
+
+    tree = event.widget
+    sel = tree.focus()
+    if sel:
+        node = _id_to_node[sel]
+
+        if tree.get_children(sel):
+            _tree_toggle_open(sel)
+        elif _single_menu_mode_menu(node, tree):
+            _enter_menu_and_select_first(node)
+        else:
+            _change_node(node, tree.winfo_toplevel())
+
+        return "break"
+
+
+def _tree_toggle(event):
+    # Space press within the Kconfig treeview. Prefer to toggle the value, but
+    # open/close/enter the menu if that's not possible.
+
+    tree = event.widget
+    sel = tree.focus()
+    if sel:
+        node = _id_to_node[sel]
+
+        if _changeable(node):
+            _change_node(node, tree.winfo_toplevel())
+        elif _single_menu_mode_menu(node, tree):
+            _enter_menu_and_select_first(node)
+        elif tree.get_children(sel):
+            _tree_toggle_open(sel)
+
+        return "break"
+
+
+def _tree_left_key(_):
+    # Left arrow key press within the Kconfig treeview
+
+    if _single_menu:
+        # Leave the current menu in single-menu mode
+        _leave_menu()
+        return "break"
+
+    # Otherwise, default action
+
+
+def _tree_right_key(_):
+    # Right arrow key press within the Kconfig treeview
+
+    sel = _tree.focus()
+    if sel:
+        node = _id_to_node[sel]
+        # If the node can be entered in single-menu mode, do it
+        if _single_menu_mode_menu(node, _tree):
+            _enter_menu_and_select_first(node)
+            return "break"
+
+    # Otherwise, default action
+
+
+def _single_menu_mode_menu(node, tree):
+    # Returns True if single-menu mode is on and 'node' is an (interface)
+    # menu that can be entered
+
+    return _single_menu and tree is _tree and node.is_menuconfig and \
+           _shown_menu_nodes(node)
+
+
+def _changeable(node):
+    # Returns True if 'node' is a Symbol/Choice whose value can be changed
+
+    sc = node.item
+
+    if not isinstance(sc, (Symbol, Choice)):
+        return False
+
+    # This will hit for invisible symbols, which appear in show-all mode and
+    # when an invisible symbol has visible children (which can happen e.g. for
+    # symbols with optional prompts)
+    if not (node.prompt and expr_value(node.prompt[1])):
+        return False
+
+    return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \
+           or _is_y_mode_choice_sym(sc)
+
+
+def _tree_toggle_open(item):
+    # Opens/closes the Treeview item 'item'
+
+    if _tree.item(item, "open"):
+        _tree.item(item, open=False)
+    else:
+        node = _id_to_node[item]
+        if not isinstance(node.item, Symbol):
+            # Can only get here in full-tree mode
+            _build_full_tree(node)
+        _tree.item(item, open=True)
+
+
+def _tree_set_val(tri_val):
+    def tree_set_val(event):
+        # n/m/y press within the Kconfig treeview
+
+        # Sets the value of the currently selected item to 'tri_val', if that
+        # value can be assigned
+
+        sel = event.widget.focus()
+        if sel:
+            sc = _id_to_node[sel].item
+            if isinstance(sc, (Symbol, Choice)) and tri_val in sc.assignable:
+                _set_val(sc, tri_val)
+
+    return tree_set_val
+
+
+def _tree_open(_):
+    # Lazily populates the Kconfig tree when menus are opened in full-tree mode
+
+    if _single_menu:
+        # Work around https://core.tcl.tk/tk/tktview?name=368fa4561e
+        # ("ttk::treeview open/closed indicators can be toggled while hidden").
+        # Clicking on the hidden indicator will call _build_full_tree() in
+        # single-menu mode otherwise.
+        return
+
+    node = _id_to_node[_tree.focus()]
+    # _shown_full_nodes() includes nodes from menus rooted at symbols, so we
+    # only need to check "real" menus and choices here
+    if not isinstance(node.item, Symbol):
+        _build_full_tree(node)
+
+
+def _update_menu_path(_):
+    # Updates the displayed menu path when nodes are selected in the Kconfig
+    # treeview
+
+    sel = _tree.selection()
+    _menupath["text"] = _menu_path_info(_id_to_node[sel[0]]) if sel else ""
+
+
+def _item_row(item):
+    # Returns the row number 'item' appears on within the Kconfig treeview,
+    # starting from the top of the tree. Used to preserve scrolling.
+    #
+    # ttkTreeview.c in the Tk sources defines a RowNumber() function that does
+    # the same thing, but it's not exposed.
+
+    row = 0
+
+    while True:
+        prev = _tree.prev(item)
+        if prev:
+            item = prev
+            row += _n_rows(item)
+        else:
+            item = _tree.parent(item)
+            if not item:
+                return row
+            row += 1
+
+
+def _n_rows(item):
+    # _item_row() helper. Returns the number of rows occupied by 'item' and #
+    # its children.
+
+    rows = 1
+
+    if _tree.item(item, "open"):
+        for child in _tree.get_children(item):
+            rows += _n_rows(child)
+
+    return rows
+
+
+def _attached(item):
+    # Heuristic for checking if a Treeview item is attached. Doesn't seem to be
+    # good APIs for this. Might fail for super-obscure cases with tiny trees,
+    # but you'd just get a small scroll mess-up.
+
+    return bool(_tree.next(item) or _tree.prev(item) or _tree.parent(item))
+
+
+def _change_node(node, parent):
+    # Toggles/changes the value of 'node'. 'parent' is the parent window
+    # (either the main window or the jump-to dialog), in case we need to pop up
+    # a dialog.
+
+    if not _changeable(node):
+        return
+
+    # sc = symbol/choice
+    sc = node.item
+
+    if sc.type in (INT, HEX, STRING):
+        s = _set_val_dialog(node, parent)
+
+        # Tkinter can return 'unicode' strings on Python 2, which Kconfiglib
+        # can't deal with. UTF-8-encode the string to work around it.
+        if _PY2 and isinstance(s, unicode):
+            s = s.encode("utf-8", "ignore")
+
+        if s is not None:
+            _set_val(sc, s)
+
+    elif len(sc.assignable) == 1:
+        # Handles choice symbols for choices in y mode, which are a special
+        # case: .assignable can be (2,) while .tri_value is 0.
+        _set_val(sc, sc.assignable[0])
+
+    else:
+        # Set the symbol to the value after the current value in
+        # sc.assignable, with wrapping
+        val_index = sc.assignable.index(sc.tri_value)
+        _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)])
+
+
+def _set_val(sc, val):
+    # Wrapper around Symbol/Choice.set_value() for updating the menu state and
+    # _conf_changed
+
+    # Use the string representation of tristate values. This makes the format
+    # consistent for all symbol types.
+    if val in TRI_TO_STR:
+        val = TRI_TO_STR[val]
+
+    if val != sc.str_value:
+        sc.set_value(val)
+        _set_conf_changed(True)
+
+        # Update the tree and try to preserve the scroll. Do a cheaper variant
+        # than in the show-all case, that might mess up the scroll slightly in
+        # rare cases, but is fast and flicker-free.
+
+        stayput = _loc_ref_item()  # Item to preserve scroll for
+        old_row = _item_row(stayput)
+
+        _update_tree()
+
+        # If the reference item disappeared (can happen if the change was done
+        # from the jump-to dialog), then avoid messing with the scroll and hope
+        # for the best
+        if _attached(stayput):
+            _tree.yview_scroll(_item_row(stayput) - old_row, "units")
+
+        if _jump_to_tree:
+            _update_jump_to_display()
+
+
+def _set_val_dialog(node, parent):
+    # Pops up a dialog for setting the value of the string/int/hex
+    # symbol at node 'node'. 'parent' is the parent window.
+
+    def ok(_=None):
+        # No 'nonlocal' in Python 2
+        global _entry_res
+
+        s = entry.get()
+        if sym.type == HEX and not s.startswith(("0x", "0X")):
+            s = "0x" + s
+
+        if _check_valid(dialog, entry, sym, s):
+            _entry_res = s
+            dialog.destroy()
+
+    def cancel(_=None):
+        global _entry_res
+        _entry_res = None
+        dialog.destroy()
+
+    sym = node.item
+
+    dialog = Toplevel(parent)
+    dialog.title("Enter {} value".format(TYPE_TO_STR[sym.type]))
+    dialog.resizable(False, False)
+    dialog.transient(parent)
+    dialog.protocol("WM_DELETE_WINDOW", cancel)
+
+    ttk.Label(dialog, text=node.prompt[0] + ":") \
+        .grid(column=0, row=0, columnspan=2, sticky="w", padx=".3c",
+              pady=".2c .05c")
+
+    entry = ttk.Entry(dialog, width=30)
+    # Start with the previous value in the editbox, selected
+    entry.insert(0, sym.str_value)
+    entry.selection_range(0, "end")
+    entry.grid(column=0, row=1, columnspan=2, sticky="ew", padx=".3c")
+    entry.focus_set()
+
+    range_info = _range_info(sym)
+    if range_info:
+        ttk.Label(dialog, text=range_info) \
+            .grid(column=0, row=2, columnspan=2, sticky="w", padx=".3c",
+                  pady=".2c 0")
+
+    ttk.Button(dialog, text="OK", command=ok) \
+        .grid(column=0, row=4 if range_info else 3, sticky="e", padx=".3c",
+              pady=".4c")
+
+    ttk.Button(dialog, text="Cancel", command=cancel) \
+        .grid(column=1, row=4 if range_info else 3, padx="0 .3c")
+
+    # Give all horizontal space to the grid cell with the OK button, so that
+    # Cancel moves to the right
+    dialog.columnconfigure(0, weight=1)
+
+    _center_on_root(dialog)
+
+    # Hack to scroll the entry so that the end of the text is shown, from
+    # https://stackoverflow.com/questions/29334544/why-does-tkinters-entry-xview-moveto-fail.
+    # Related Tk ticket: https://core.tcl.tk/tk/info/2513186fff
+    def scroll_entry(_):
+        _root.update_idletasks()
+        entry.unbind("<Expose>")
+        entry.xview_moveto(1)
+    entry.bind("<Expose>", scroll_entry)
+
+    # The dialog must be visible before we can grab the input
+    dialog.wait_visibility()
+    dialog.grab_set()
+
+    dialog.bind("<Return>", ok)
+    dialog.bind("<KP_Enter>", ok)
+    dialog.bind("<Escape>", cancel)
+
+    # Wait for the user to be done with the dialog
+    parent.wait_window(dialog)
+
+    # Regrab the input in the parent
+    parent.grab_set()
+
+    return _entry_res
+
+
+def _center_on_root(dialog):
+    # Centers 'dialog' on the root window. It often ends up at some bad place
+    # like the top-left corner of the screen otherwise. See the menuconfig()
+    # function, which has similar logic.
+
+    dialog.withdraw()
+    _root.update_idletasks()
+
+    dialog_width = dialog.winfo_reqwidth()
+    dialog_height = dialog.winfo_reqheight()
+
+    screen_width = _root.winfo_screenwidth()
+    screen_height = _root.winfo_screenheight()
+
+    x = _root.winfo_rootx() + (_root.winfo_width() - dialog_width)//2
+    y = _root.winfo_rooty() + (_root.winfo_height() - dialog_height)//2
+
+    # Clamp so that no part of the dialog is outside the screen
+    if x + dialog_width > screen_width:
+        x = screen_width - dialog_width
+    elif x < 0:
+        x = 0
+    if y + dialog_height > screen_height:
+        y = screen_height - dialog_height
+    elif y < 0:
+        y = 0
+
+    dialog.geometry("+{}+{}".format(x, y))
+
+    dialog.deiconify()
+
+
+def _check_valid(dialog, entry, sym, s):
+    # Returns True if the string 's' is a well-formed value for 'sym'.
+    # Otherwise, pops up an error and returns False.
+
+    if sym.type not in (INT, HEX):
+        # Anything goes for non-int/hex symbols
+        return True
+
+    base = 10 if sym.type == INT else 16
+    try:
+        int(s, base)
+    except ValueError:
+        messagebox.showerror(
+            "Bad value",
+            "'{}' is a malformed {} value".format(
+                s, TYPE_TO_STR[sym.type]),
+            parent=dialog)
+        entry.focus_set()
+        return False
+
+    for low_sym, high_sym, cond in sym.ranges:
+        if expr_value(cond):
+            low_s = low_sym.str_value
+            high_s = high_sym.str_value
+
+            if not int(low_s, base) <= int(s, base) <= int(high_s, base):
+                messagebox.showerror(
+                    "Value out of range",
+                    "{} is outside the range {}-{}".format(s, low_s, high_s),
+                    parent=dialog)
+                entry.focus_set()
+                return False
+
+            break
+
+    return True
+
+
+def _range_info(sym):
+    # Returns a string with information about the valid range for the symbol
+    # 'sym', or None if 'sym' doesn't have a range
+
+    if sym.type in (INT, HEX):
+        for low, high, cond in sym.ranges:
+            if expr_value(cond):
+                return "Range: {}-{}".format(low.str_value, high.str_value)
+
+    return None
+
+
+def _save(_=None):
+    # Tries to save the configuration
+
+    if _try_save(_kconf.write_config, _conf_filename, "configuration"):
+        _set_conf_changed(False)
+
+    _tree.focus_set()
+
+
+def _save_as():
+    # Pops up a dialog for saving the configuration to a specific location
+
+    global _conf_filename
+
+    filename = _conf_filename
+    while True:
+        filename = filedialog.asksaveasfilename(
+            title="Save configuration as",
+            initialdir=os.path.dirname(filename),
+            initialfile=os.path.basename(filename),
+            parent=_root)
+
+        if not filename:
+            break
+
+        if _try_save(_kconf.write_config, filename, "configuration"):
+            _conf_filename = filename
+            break
+
+    _tree.focus_set()
+
+
+def _save_minimal():
+    # Pops up a dialog for saving a minimal configuration (defconfig) to a
+    # specific location
+
+    global _minconf_filename
+
+    filename = _minconf_filename
+    while True:
+        filename = filedialog.asksaveasfilename(
+            title="Save minimal configuration as",
+            initialdir=os.path.dirname(filename),
+            initialfile=os.path.basename(filename),
+            parent=_root)
+
+        if not filename:
+            break
+
+        if _try_save(_kconf.write_min_config, filename,
+                     "minimal configuration"):
+
+            _minconf_filename = filename
+            break
+
+    _tree.focus_set()
+
+
+def _open(_=None):
+    # Pops up a dialog for loading a configuration
+
+    global _conf_filename
+
+    if _conf_changed and \
+        not messagebox.askokcancel(
+            "Unsaved changes",
+            "You have unsaved changes. Load new configuration anyway?"):
+
+        return
+
+    filename = _conf_filename
+    while True:
+        filename = filedialog.askopenfilename(
+            title="Open configuration",
+            initialdir=os.path.dirname(filename),
+            initialfile=os.path.basename(filename),
+            parent=_root)
+
+        if not filename:
+            break
+
+        if _try_load(filename):
+            # Maybe something fancier could be done here later to try to
+            # preserve the scroll
+
+            _conf_filename = filename
+            _set_conf_changed(_needs_save())
+
+            if _single_menu and not _shown_menu_nodes(_cur_menu):
+                # Turn on show-all if we're in single-menu mode and would end
+                # up with an empty menu
+                _show_all_var.set(True)
+
+            _update_tree()
+
+            break
+
+    _tree.focus_set()
+
+
+def _toggle_showname(_):
+    # Toggles show-name mode on/off
+
+    _show_name_var.set(not _show_name_var.get())
+    _do_showname()
+
+
+def _do_showname():
+    # Updates the UI for the current show-name setting
+
+    # Columns do not automatically shrink/expand, so we have to update
+    # column widths ourselves
+
+    tree_width = _tree.winfo_width()
+
+    if _show_name_var.get():
+        _tree["displaycolumns"] = ("name",)
+        _tree["show"] = "tree headings"
+        name_width = tree_width//3
+        _tree.column("#0", width=max(tree_width - name_width, 1))
+        _tree.column("name", width=name_width)
+    else:
+        _tree["displaycolumns"] = ()
+        _tree["show"] = "tree"
+        _tree.column("#0", width=tree_width)
+
+    _tree.focus_set()
+
+
+def _toggle_showall(_):
+    # Toggles show-all mode on/off
+
+    _show_all_var.set(not _show_all)
+    _do_showall()
+
+
+def _do_showall():
+    # Updates the UI for the current show-all setting
+
+    # Don't allow turning off show-all if we'd end up with no visible nodes
+    if _nothing_shown():
+        _show_all_var.set(True)
+        return
+
+    # Save scroll information. old_scroll can end up negative here, if the
+    # reference item isn't shown (only invisible items on the screen, and
+    # show-all being turned off).
+
+    stayput = _vis_loc_ref_item()
+    # Probe the middle of the first row, to play it safe. identify_row(0) seems
+    # to return the row before the top row.
+    old_scroll = _item_row(stayput) - \
+        _item_row(_tree.identify_row(_treeview_rowheight//2))
+
+    _update_tree()
+
+    if _show_all:
+        # Deep magic: Unless we call update_idletasks(), the scroll adjustment
+        # below is restricted to the height of the old tree, instead of the
+        # height of the new tree. Since the tree with show-all on is guaranteed
+        # to be taller, and we want the maximum range, we only call it when
+        # turning show-all on.
+        #
+        # Strictly speaking, something similar ought to be done when changing
+        # symbol values, but it causes annoying flicker, and in 99% of cases
+        # things work anyway there (with usually minor scroll mess-ups in the
+        # 1% case).
+        _root.update_idletasks()
+
+    # Restore scroll
+    _tree.yview(_item_row(stayput) - old_scroll)
+
+    _tree.focus_set()
+
+
+def _nothing_shown():
+    # _do_showall() helper. Returns True if no nodes would get
+    # shown with the current show-all setting. Also handles the
+    # (obscure) case when there are no visible nodes in the entire
+    # tree, meaning guiconfig was automatically started in
+    # show-all mode, which mustn't be turned off.
+
+    return not _shown_menu_nodes(
+        _cur_menu if _single_menu else _kconf.top_node)
+
+
+def _toggle_tree_mode(_):
+    # Toggles single-menu mode on/off
+
+    _single_menu_var.set(not _single_menu)
+    _do_tree_mode()
+
+
+def _do_tree_mode():
+    # Updates the UI for the current tree mode (full-tree or single-menu)
+
+    loc_ref_node = _id_to_node[_loc_ref_item()]
+
+    if not _single_menu:
+        # _jump_to() -> _enter_menu() already updates the tree, but
+        # _jump_to() -> load_parents() doesn't, because it isn't always needed.
+        # We always need to update the tree here, e.g. to add/remove "--->".
+        _update_tree()
+
+    _jump_to(loc_ref_node)
+    _tree.focus_set()
+
+
+def _enter_menu_and_select_first(menu):
+    # Enters the menu 'menu' and selects the first item. Used in single-menu
+    # mode.
+
+    _enter_menu(menu)
+    _select(_tree, _tree.get_children()[0])
+
+
+def _enter_menu(menu):
+    # Enters the menu 'menu'. Used in single-menu mode.
+
+    global _cur_menu
+
+    _cur_menu = menu
+    _update_tree()
+
+    _backbutton["state"] = "disabled" if menu is _kconf.top_node else "normal"
+
+
+def _leave_menu():
+    # Leaves the current menu. Used in single-menu mode.
+
+    global _cur_menu
+
+    if _cur_menu is not _kconf.top_node:
+        old_menu = _cur_menu
+
+        _cur_menu = _parent_menu(_cur_menu)
+        _update_tree()
+
+        _select(_tree, id(old_menu))
+
+        if _cur_menu is _kconf.top_node:
+            _backbutton["state"] = "disabled"
+
+    _tree.focus_set()
+
+
+def _select(tree, item):
+    # Selects, focuses, and see()s 'item' in 'tree'
+
+    tree.selection_set(item)
+    tree.focus(item)
+    tree.see(item)
+
+
+def _loc_ref_item():
+    # Returns a Treeview item that can serve as a reference for the current
+    # scroll location. We try to make this item stay on the same row on the
+    # screen when updating the tree.
+
+    # If the selected item is visible, use that
+    sel = _tree.selection()
+    if sel and _tree.bbox(sel[0]):
+        return sel[0]
+
+    # Otherwise, use the middle item on the screen. If it doesn't exist, the
+    # tree is probably really small, so use the first item in the entire tree.
+    return _tree.identify_row(_tree.winfo_height()//2) or \
+        _tree.get_children()[0]
+
+
+def _vis_loc_ref_item():
+    # Like _loc_ref_item(), but finds a visible item around the reference item.
+    # Used when changing show-all mode, where non-visible (red) items will
+    # disappear.
+
+    item = _loc_ref_item()
+
+    vis_before = _vis_before(item)
+    if vis_before and _tree.bbox(vis_before):
+        return vis_before
+
+    vis_after = _vis_after(item)
+    if vis_after and _tree.bbox(vis_after):
+        return vis_after
+
+    return vis_before or vis_after
+
+
+def _vis_before(item):
+    # _vis_loc_ref_item() helper. Returns the first visible (not red) item,
+    # searching backwards from 'item'.
+
+    while item:
+        if not _tree.tag_has("invisible", item):
+            return item
+
+        prev = _tree.prev(item)
+        item = prev if prev else _tree.parent(item)
+
+    return None
+
+
+def _vis_after(item):
+    # _vis_loc_ref_item() helper. Returns the first visible (not red) item,
+    # searching forwards from 'item'.
+
+    while item:
+        if not _tree.tag_has("invisible", item):
+            return item
+
+        next = _tree.next(item)
+        if next:
+            item = next
+        else:
+            item = _tree.parent(item)
+            if not item:
+                break
+            item = _tree.next(item)
+
+    return None
+
+
+def _on_quit(_=None):
+    # Called when the user wants to exit
+
+    if not _conf_changed:
+        _quit("No changes to save (for '{}')".format(_conf_filename))
+        return
+
+    while True:
+        ync = messagebox.askyesnocancel("Quit", "Save changes?")
+        if ync is None:
+            return
+
+        if not ync:
+            _quit("Configuration ({}) was not saved".format(_conf_filename))
+            return
+
+        if _try_save(_kconf.write_config, _conf_filename, "configuration"):
+            # _try_save() already prints the "Configuration saved to ..."
+            # message
+            _quit()
+            return
+
+
+def _quit(msg=None):
+    # Quits the application
+
+    # Do not call sys.exit() here, in case we're being run from a script
+    _root.destroy()
+    if msg:
+        print(msg)
+
+
+def _try_save(save_fn, filename, description):
+    # Tries to save a configuration file. Pops up an error and returns False on
+    # failure.
+    #
+    # save_fn:
+    #   Function to call with 'filename' to save the file
+    #
+    # description:
+    #   String describing the thing being saved
+
+    try:
+        # save_fn() returns a message to print
+        msg = save_fn(filename)
+        _set_status(msg)
+        print(msg)
+        return True
+    except EnvironmentError as e:
+        messagebox.showerror(
+            "Error saving " + description,
+            "Error saving {} to '{}': {} (errno: {})"
+            .format(description, e.filename, e.strerror,
+                    errno.errorcode[e.errno]))
+        return False
+
+
+def _try_load(filename):
+    # Tries to load a configuration file. Pops up an error and returns False on
+    # failure.
+    #
+    # filename:
+    #   Configuration file to load
+
+    try:
+        msg = _kconf.load_config(filename)
+        _set_status(msg)
+        print(msg)
+        return True
+    except EnvironmentError as e:
+        messagebox.showerror(
+            "Error loading configuration",
+            "Error loading '{}': {} (errno: {})"
+            .format(filename, e.strerror, errno.errorcode[e.errno]))
+        return False
+
+
+def _jump_to_dialog(_=None):
+    # Pops up a dialog for jumping directly to a particular node. Symbol values
+    # can also be changed within the dialog.
+    #
+    # Note: There's nothing preventing this from doing an incremental search
+    # like menuconfig.py does, but currently it's a bit jerky for large Kconfig
+    # trees, at least when inputting the beginning of the search string. We'd
+    # need to somehow only update the tree items that are shown in the Treeview
+    # to fix it.
+
+    global _jump_to_tree
+
+    def search(_=None):
+        _update_jump_to_matches(msglabel, entry.get())
+
+    def jump_to_selected(event=None):
+        # Jumps to the selected node and closes the dialog
+
+        # Ignore double clicks on the image and in the heading area
+        if event and (tree.identify_element(event.x, event.y) == "image" or
+                      _in_heading(event)):
+            return
+
+        sel = tree.selection()
+        if not sel:
+            return
+
+        node = _id_to_node[sel[0]]
+
+        if node not in _shown_menu_nodes(_parent_menu(node)):
+            _show_all_var.set(True)
+            if not _single_menu:
+                # See comment in _do_tree_mode()
+                _update_tree()
+
+        _jump_to(node)
+
+        dialog.destroy()
+
+    def tree_select(_):
+        jumpto_button["state"] = "normal" if tree.selection() else "disabled"
+
+
+    dialog = Toplevel(_root)
+    dialog.geometry("+{}+{}".format(
+        _root.winfo_rootx() + 50, _root.winfo_rooty() + 50))
+    dialog.title("Jump to symbol/choice/menu/comment")
+    dialog.minsize(128, 128)  # See _create_ui()
+    dialog.transient(_root)
+
+    ttk.Label(dialog, text=_JUMP_TO_HELP) \
+        .grid(column=0, row=0, columnspan=2, sticky="w", padx=".1c",
+              pady=".1c")
+
+    entry = ttk.Entry(dialog)
+    entry.grid(column=0, row=1, sticky="ew", padx=".1c", pady=".1c")
+    entry.focus_set()
+
+    entry.bind("<Return>", search)
+    entry.bind("<KP_Enter>", search)
+
+    ttk.Button(dialog, text="Search", command=search) \
+        .grid(column=1, row=1, padx="0 .1c", pady="0 .1c")
+
+    msglabel = ttk.Label(dialog)
+    msglabel.grid(column=0, row=2, sticky="w", pady="0 .1c")
+
+    panedwindow, tree = _create_kconfig_tree_and_desc(dialog)
+    panedwindow.grid(column=0, row=3, columnspan=2, sticky="nsew")
+
+    # Clear tree
+    tree.set_children("")
+
+    _jump_to_tree = tree
+
+    jumpto_button = ttk.Button(dialog, text="Jump to selected item",
+                               state="disabled", command=jump_to_selected)
+    jumpto_button.grid(column=0, row=4, columnspan=2, sticky="ns", pady=".1c")
+
+    dialog.columnconfigure(0, weight=1)
+    # Only the pane with the Kconfig tree and description grows vertically
+    dialog.rowconfigure(3, weight=1)
+
+    # See the menuconfig() function
+    _root.update_idletasks()
+    dialog.geometry(dialog.geometry())
+
+    # The dialog must be visible before we can grab the input
+    dialog.wait_visibility()
+    dialog.grab_set()
+
+    tree.bind("<Double-1>", jump_to_selected)
+    tree.bind("<Return>", jump_to_selected)
+    tree.bind("<KP_Enter>", jump_to_selected)
+    # add=True to avoid overriding the description text update
+    tree.bind("<<TreeviewSelect>>", tree_select, add=True)
+
+    dialog.bind("<Escape>", lambda _: dialog.destroy())
+
+    # Wait for the user to be done with the dialog
+    _root.wait_window(dialog)
+
+    _jump_to_tree = None
+
+    _tree.focus_set()
+
+
+def _update_jump_to_matches(msglabel, search_string):
+    # Searches for nodes matching the search string and updates
+    # _jump_to_matches. Puts a message in 'msglabel' if there are no matches,
+    # or regex errors.
+
+    global _jump_to_matches
+
+    _jump_to_tree.selection_set(())
+
+    try:
+        # We could use re.IGNORECASE here instead of lower(), but this is
+        # faster for regexes like '.*debug$' (though the '.*' is redundant
+        # there). Those probably have bad interactions with re.search(), which
+        # matches anywhere in the string.
+        regex_searches = [re.compile(regex).search
+                          for regex in search_string.lower().split()]
+    except re.error as e:
+        msg = "Bad regular expression"
+        # re.error.msg was added in Python 3.5
+        if hasattr(e, "msg"):
+            msg += ": " + e.msg
+        msglabel["text"] = msg
+        # Clear tree
+        _jump_to_tree.set_children("")
+        return
+
+    _jump_to_matches = []
+    add_match = _jump_to_matches.append
+
+    for node in _sorted_sc_nodes():
+        # Symbol/choice
+        sc = node.item
+
+        for search in regex_searches:
+            # Both the name and the prompt might be missing, since
+            # we're searching both symbols and choices
+
+            # Does the regex match either the symbol name or the
+            # prompt (if any)?
+            if not (sc.name and search(sc.name.lower()) or
+                    node.prompt and search(node.prompt[0].lower())):
+
+                # Give up on the first regex that doesn't match, to
+                # speed things up a bit when multiple regexes are
+                # entered
+                break
+
+        else:
+            add_match(node)
+
+    # Search menus and comments
+
+    for node in _sorted_menu_comment_nodes():
+        for search in regex_searches:
+            if not search(node.prompt[0].lower()):
+                break
+        else:
+            add_match(node)
+
+    msglabel["text"] = "" if _jump_to_matches else "No matches"
+
+    _update_jump_to_display()
+
+    if _jump_to_matches:
+        item = id(_jump_to_matches[0])
+        _jump_to_tree.selection_set(item)
+        _jump_to_tree.focus(item)
+
+
+def _update_jump_to_display():
+    # Updates the images and text for the items in _jump_to_matches, and sets
+    # them as the items of _jump_to_tree
+
+    # Micro-optimize a bit
+    item = _jump_to_tree.item
+    id_ = id
+    node_str = _node_str
+    img_tag = _img_tag
+    visible = _visible
+    for node in _jump_to_matches:
+        item(id_(node),
+             text=node_str(node),
+             tags=img_tag(node) if visible(node) else
+                 img_tag(node) + " invisible")
+
+    _jump_to_tree.set_children("", *map(id, _jump_to_matches))
+
+
+def _jump_to(node):
+    # Jumps directly to 'node' and selects it
+
+    if _single_menu:
+        _enter_menu(_parent_menu(node))
+    else:
+        _load_parents(node)
+
+    _select(_tree, id(node))
+
+
+# Obscure Python: We never pass a value for cached_nodes, and it keeps pointing
+# to the same list. This avoids a global.
+def _sorted_sc_nodes(cached_nodes=[]):
+    # Returns a sorted list of symbol and choice nodes to search. The symbol
+    # nodes appear first, sorted by name, and then the choice nodes, sorted by
+    # prompt and (secondarily) name.
+
+    if not cached_nodes:
+        # Add symbol nodes
+        for sym in sorted(_kconf.unique_defined_syms,
+                          key=lambda sym: sym.name):
+            # += is in-place for lists
+            cached_nodes += sym.nodes
+
+        # Add choice nodes
+
+        choices = sorted(_kconf.unique_choices,
+                         key=lambda choice: choice.name or "")
+
+        cached_nodes += sorted(
+            [node for choice in choices for node in choice.nodes],
+            key=lambda node: node.prompt[0] if node.prompt else "")
+
+    return cached_nodes
+
+
+def _sorted_menu_comment_nodes(cached_nodes=[]):
+    # Returns a list of menu and comment nodes to search, sorted by prompt,
+    # with the menus first
+
+    if not cached_nodes:
+        def prompt_text(mc):
+            return mc.prompt[0]
+
+        cached_nodes += sorted(_kconf.menus, key=prompt_text)
+        cached_nodes += sorted(_kconf.comments, key=prompt_text)
+
+    return cached_nodes
+
+
+def _load_parents(node):
+    # Menus are lazily populated as they're opened in full-tree mode, but
+    # jumping to an item needs its parent menus to be populated. This function
+    # populates 'node's parents.
+
+    # Get all parents leading up to 'node', sorted with the root first
+    parents = []
+    cur = node.parent
+    while cur is not _kconf.top_node:
+        parents.append(cur)
+        cur = cur.parent
+    parents.reverse()
+
+    for i, parent in enumerate(parents):
+        if not _tree.item(id(parent), "open"):
+            # Found a closed menu. Populate it and all the remaining menus
+            # leading up to 'node'.
+            for parent in parents[i:]:
+                # We only need to populate "real" menus/choices. Implicit menus
+                # are populated when their parents menus are entered.
+                if not isinstance(parent.item, Symbol):
+                    _build_full_tree(parent)
+            return
+
+
+def _parent_menu(node):
+    # Returns the menu node of the menu that contains 'node'. In addition to
+    # proper 'menu's, this might also be a 'menuconfig' symbol or a 'choice'.
+    # "Menu" here means a menu in the interface.
+
+    menu = node.parent
+    while not menu.is_menuconfig:
+        menu = menu.parent
+    return menu
+
+
+def _trace_write(var, fn):
+    # Makes fn() be called whenever the Tkinter Variable 'var' changes value
+
+    # trace_variable() is deprecated according to the docstring,
+    # which recommends trace_add()
+    if hasattr(var, "trace_add"):
+        var.trace_add("write", fn)
+    else:
+        var.trace_variable("w", fn)
+
+
+def _info_str(node):
+    # Returns information about the menu node 'node' as a string.
+    #
+    # The helper functions are responsible for adding newlines. This allows
+    # them to return "" if they don't want to add any output.
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+
+        return (
+            _name_info(sym) +
+            _help_info(sym) +
+            _direct_dep_info(sym) +
+            _defaults_info(sym) +
+            _select_imply_info(sym) +
+            _kconfig_def_info(sym)
+        )
+
+    if isinstance(node.item, Choice):
+        choice = node.item
+
+        return (
+            _name_info(choice) +
+            _help_info(choice) +
+            'Mode: {}\n\n'.format(choice.str_value) +
+            _choice_syms_info(choice) +
+            _direct_dep_info(choice) +
+            _defaults_info(choice) +
+            _kconfig_def_info(choice)
+        )
+
+    # node.item in (MENU, COMMENT)
+    return _kconfig_def_info(node)
+
+
+def _name_info(sc):
+    # Returns a string with the name of the symbol/choice. Choices are shown as
+    # <choice (name if any)>.
+
+    return (sc.name if sc.name else standard_sc_expr_str(sc)) + "\n\n"
+
+
+def _value_info(sym):
+    # Returns a string showing 'sym's value
+
+    # Only put quotes around the value for string symbols
+    return "Value: {}\n".format(
+        '"{}"'.format(sym.str_value)
+        if sym.orig_type == STRING
+        else sym.str_value)
+
+
+def _choice_syms_info(choice):
+    # Returns a string listing the choice symbols in 'choice'. Adds
+    # "(selected)" next to the selected one.
+
+    s = "Choice symbols:\n"
+
+    for sym in choice.syms:
+        s += "  - " + sym.name
+        if sym is choice.selection:
+            s += " (selected)"
+        s += "\n"
+
+    return s + "\n"
+
+
+def _help_info(sc):
+    # Returns a string with the help text(s) of 'sc' (Symbol or Choice).
+    # Symbols and choices defined in multiple locations can have multiple help
+    # texts.
+
+    s = ""
+
+    for node in sc.nodes:
+        if node.help is not None:
+            s += node.help + "\n\n"
+
+    return s
+
+
+def _direct_dep_info(sc):
+    # Returns a string describing the direct dependencies of 'sc' (Symbol or
+    # Choice). The direct dependencies are the OR of the dependencies from each
+    # definition location. The dependencies at each definition location come
+    # from 'depends on' and dependencies inherited from parent items.
+
+    return "" if sc.direct_dep is _kconf.y else \
+        'Direct dependencies (={}):\n{}\n' \
+        .format(TRI_TO_STR[expr_value(sc.direct_dep)],
+                _split_expr_info(sc.direct_dep, 2))
+
+
+def _defaults_info(sc):
+    # Returns a string describing the defaults of 'sc' (Symbol or Choice)
+
+    if not sc.defaults:
+        return ""
+
+    s = "Default"
+    if len(sc.defaults) > 1:
+        s += "s"
+    s += ":\n"
+
+    for val, cond in sc.orig_defaults:
+        s += "  - "
+        if isinstance(sc, Symbol):
+            s += _expr_str(val)
+
+            # Skip the tristate value hint if the expression is just a single
+            # symbol. _expr_str() already shows its value as a string.
+            #
+            # This also avoids showing the tristate value for string/int/hex
+            # defaults, which wouldn't make any sense.
+            if isinstance(val, tuple):
+                s += '  (={})'.format(TRI_TO_STR[expr_value(val)])
+        else:
+            # Don't print the value next to the symbol name for choice
+            # defaults, as it looks a bit confusing
+            s += val.name
+        s += "\n"
+
+        if cond is not _kconf.y:
+            s += "    Condition (={}):\n{}" \
+                 .format(TRI_TO_STR[expr_value(cond)],
+                         _split_expr_info(cond, 4))
+
+    return s + "\n"
+
+
+def _split_expr_info(expr, indent):
+    # Returns a string with 'expr' split into its top-level && or || operands,
+    # with one operand per line, together with the operand's value. This is
+    # usually enough to get something readable for long expressions. A fancier
+    # recursive thingy would be possible too.
+    #
+    # indent:
+    #   Number of leading spaces to add before the split expression.
+
+    if len(split_expr(expr, AND)) > 1:
+        split_op = AND
+        op_str = "&&"
+    else:
+        split_op = OR
+        op_str = "||"
+
+    s = ""
+    for i, term in enumerate(split_expr(expr, split_op)):
+        s += "{}{} {}".format(indent*" ",
+                              "  " if i == 0 else op_str,
+                              _expr_str(term))
+
+        # Don't bother showing the value hint if the expression is just a
+        # single symbol. _expr_str() already shows its value.
+        if isinstance(term, tuple):
+            s += "  (={})".format(TRI_TO_STR[expr_value(term)])
+
+        s += "\n"
+
+    return s
+
+
+def _select_imply_info(sym):
+    # Returns a string with information about which symbols 'select' or 'imply'
+    # 'sym'. The selecting/implying symbols are grouped according to which
+    # value they select/imply 'sym' to (n/m/y).
+
+    def sis(expr, val, title):
+        # sis = selects/implies
+        sis = [si for si in split_expr(expr, OR) if expr_value(si) == val]
+        if not sis:
+            return ""
+
+        res = title
+        for si in sis:
+            res += "  - {}\n".format(split_expr(si, AND)[0].name)
+        return res + "\n"
+
+    s = ""
+
+    if sym.rev_dep is not _kconf.n:
+        s += sis(sym.rev_dep, 2,
+                 "Symbols currently y-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 1,
+                 "Symbols currently m-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 0,
+                 "Symbols currently n-selecting this symbol (no effect):\n")
+
+    if sym.weak_rev_dep is not _kconf.n:
+        s += sis(sym.weak_rev_dep, 2,
+                 "Symbols currently y-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 1,
+                 "Symbols currently m-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 0,
+                 "Symbols currently n-implying this symbol (no effect):\n")
+
+    return s
+
+
+def _kconfig_def_info(item):
+    # Returns a string with the definition of 'item' in Kconfig syntax,
+    # together with the definition location(s) and their include and menu paths
+
+    nodes = [item] if isinstance(item, MenuNode) else item.nodes
+
+    s = "Kconfig definition{}, with parent deps. propagated to 'depends on'\n" \
+        .format("s" if len(nodes) > 1 else "")
+    s += (len(s) - 1)*"="
+
+    for node in nodes:
+        s += "\n\n" \
+             "At {}:{}\n" \
+             "{}" \
+             "Menu path: {}\n\n" \
+             "{}" \
+             .format(node.filename, node.linenr,
+                     _include_path_info(node),
+                     _menu_path_info(node),
+                     node.custom_str(_name_and_val_str))
+
+    return s
+
+
+def _include_path_info(node):
+    if not node.include_path:
+        # In the top-level Kconfig file
+        return ""
+
+    return "Included via {}\n".format(
+        " -> ".join("{}:{}".format(filename, linenr)
+                    for filename, linenr in node.include_path))
+
+
+def _menu_path_info(node):
+    # Returns a string describing the menu path leading up to 'node'
+
+    path = ""
+
+    while node.parent is not _kconf.top_node:
+        node = node.parent
+
+        # Promptless choices might appear among the parents. Use
+        # standard_sc_expr_str() for them, so that they show up as
+        # '<choice (name if any)>'.
+        path = " -> " + (node.prompt[0] if node.prompt else
+                         standard_sc_expr_str(node.item)) + path
+
+    return "(Top)" + path
+
+
+def _name_and_val_str(sc):
+    # Custom symbol/choice printer that shows symbol values after symbols
+
+    # Show the values of non-constant (non-quoted) symbols that don't look like
+    # numbers. Things like 123 are actually symbol references, and only work as
+    # expected due to undefined symbols getting their name as their value.
+    # Showing the symbol value for those isn't helpful though.
+    if isinstance(sc, Symbol) and not sc.is_constant and not _is_num(sc.name):
+        if not sc.nodes:
+            # Undefined symbol reference
+            return "{}(undefined/n)".format(sc.name)
+
+        return '{}(={})'.format(sc.name, sc.str_value)
+
+    # For other items, use the standard format
+    return standard_sc_expr_str(sc)
+
+
+def _expr_str(expr):
+    # Custom expression printer that shows symbol values
+    return expr_str(expr, _name_and_val_str)
+
+
+def _is_num(name):
+    # Heuristic to see if a symbol name looks like a number, for nicer output
+    # when printing expressions. Things like 16 are actually symbol names, only
+    # they get their name as their value when the symbol is undefined.
+
+    try:
+        int(name)
+    except ValueError:
+        if not name.startswith(("0x", "0X")):
+            return False
+
+        try:
+            int(name, 16)
+        except ValueError:
+            return False
+
+    return True
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/ext/Kconfiglib/import/kconfiglib.py b/ext/Kconfiglib/import/kconfiglib.py
new file mode 100644
index 0000000..e5c2dcc
--- /dev/null
+++ b/ext/Kconfiglib/import/kconfiglib.py
@@ -0,0 +1,7192 @@
+# Copyright (c) 2011-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Overview
+========
+
+Kconfiglib is a Python 2/3 library for scripting and extracting information
+from Kconfig (https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt)
+configuration systems.
+
+See the homepage at https://github.com/ulfalizer/Kconfiglib for a longer
+overview.
+
+Since Kconfiglib 12.0.0, the library version is available in
+kconfiglib.VERSION, which is a (<major>, <minor>, <patch>) tuple, e.g.
+(12, 0, 0).
+
+
+Using Kconfiglib on the Linux kernel with the Makefile targets
+==============================================================
+
+For the Linux kernel, a handy interface is provided by the
+scripts/kconfig/Makefile patch, which can be applied with either 'git am' or
+the 'patch' utility:
+
+  $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | git am
+  $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | patch -p1
+
+Warning: Not passing -p1 to patch will cause the wrong file to be patched.
+
+Please tell me if the patch does not apply. It should be trivial to apply
+manually, as it's just a block of text that needs to be inserted near the other
+*conf: targets in scripts/kconfig/Makefile.
+
+Look further down for a motivation for the Makefile patch and for instructions
+on how you can use Kconfiglib without it.
+
+If you do not wish to install Kconfiglib via pip, the Makefile patch is set up
+so that you can also just clone Kconfiglib into the kernel root:
+
+  $ git clone git://github.com/ulfalizer/Kconfiglib.git
+  $ git am Kconfiglib/makefile.patch  (or 'patch -p1 < Kconfiglib/makefile.patch')
+
+Warning: The directory name Kconfiglib/ is significant in this case, because
+it's added to PYTHONPATH by the new targets in makefile.patch.
+
+The targets added by the Makefile patch are described in the following
+sections.
+
+
+make kmenuconfig
+----------------
+
+This target runs the curses menuconfig interface with Python 3. As of
+Kconfiglib 12.2.0, both Python 2 and Python 3 are supported (previously, only
+Python 3 was supported, so this was a backport).
+
+
+make guiconfig
+--------------
+
+This target runs the Tkinter menuconfig interface. Both Python 2 and Python 3
+are supported. To change the Python interpreter used, pass
+PYTHONCMD=<executable> to 'make'. The default is 'python'.
+
+
+make [ARCH=<arch>] iscriptconfig
+--------------------------------
+
+This target gives an interactive Python prompt where a Kconfig instance has
+been preloaded and is available in 'kconf'. To change the Python interpreter
+used, pass PYTHONCMD=<executable> to 'make'. The default is 'python'.
+
+To get a feel for the API, try evaluating and printing the symbols in
+kconf.defined_syms, and explore the MenuNode menu tree starting at
+kconf.top_node by following 'next' and 'list' pointers.
+
+The item contained in a menu node is found in MenuNode.item (note that this can
+be one of the constants kconfiglib.MENU and kconfiglib.COMMENT), and all
+symbols and choices have a 'nodes' attribute containing their menu nodes
+(usually only one). Printing a menu node will print its item, in Kconfig
+format.
+
+If you want to look up a symbol by name, use the kconf.syms dictionary.
+
+
+make scriptconfig SCRIPT=<script> [SCRIPT_ARG=<arg>]
+----------------------------------------------------
+
+This target runs the Python script given by the SCRIPT parameter on the
+configuration. sys.argv[1] holds the name of the top-level Kconfig file
+(currently always "Kconfig" in practice), and sys.argv[2] holds the SCRIPT_ARG
+argument, if given.
+
+See the examples/ subdirectory for example scripts.
+
+
+make dumpvarsconfig
+-------------------
+
+This target prints a list of all environment variables referenced from the
+Kconfig files, together with their values. See the
+Kconfiglib/examples/dumpvars.py script.
+
+Only environment variables that are referenced via the Kconfig preprocessor
+$(FOO) syntax are included. The preprocessor was added in Linux 4.18.
+
+
+Using Kconfiglib without the Makefile targets
+=============================================
+
+The make targets are only needed to pick up environment variables exported from
+the Kbuild makefiles and referenced inside Kconfig files, via e.g.
+'source "arch/$(SRCARCH)/Kconfig" and commands run via '$(shell,...)'.
+
+These variables are referenced as of writing (Linux 4.18), together with sample
+values:
+
+  srctree          (.)
+  ARCH             (x86)
+  SRCARCH          (x86)
+  KERNELVERSION    (4.18.0)
+  CC               (gcc)
+  HOSTCC           (gcc)
+  HOSTCXX          (g++)
+  CC_VERSION_TEXT  (gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0)
+
+Older kernels only reference ARCH, SRCARCH, and KERNELVERSION.
+
+If your kernel is recent enough (4.18+), you can get a list of referenced
+environment variables via 'make dumpvarsconfig' (see above). Note that this
+command is added by the Makefile patch.
+
+To run Kconfiglib without the Makefile patch, set the environment variables
+manually:
+
+  $ srctree=. ARCH=x86 SRCARCH=x86 KERNELVERSION=`make kernelversion` ... python(3)
+  >>> import kconfiglib
+  >>> kconf = kconfiglib.Kconfig()  # filename defaults to "Kconfig"
+
+Search the top-level Makefile for "Additional ARCH settings" to see other
+possibilities for ARCH and SRCARCH.
+
+
+Intro to symbol values
+======================
+
+Kconfiglib has the same assignment semantics as the C implementation.
+
+Any symbol can be assigned a value by the user (via Kconfig.load_config() or
+Symbol.set_value()), but this user value is only respected if the symbol is
+visible, which corresponds to it (currently) being visible in the menuconfig
+interface.
+
+For symbols with prompts, the visibility of the symbol is determined by the
+condition on the prompt. Symbols without prompts are never visible, so setting
+a user value on them is pointless. A warning will be printed by default if
+Symbol.set_value() is called on a promptless symbol. Assignments to promptless
+symbols are normal within a .config file, so no similar warning will be printed
+by load_config().
+
+Dependencies from parents and 'if'/'depends on' are propagated to properties,
+including prompts, so these two configurations are logically equivalent:
+
+(1)
+
+  menu "menu"
+      depends on A
+
+  if B
+
+  config FOO
+      tristate "foo" if D
+      default y
+      depends on C
+
+  endif
+
+  endmenu
+
+(2)
+
+  menu "menu"
+      depends on A
+
+  config FOO
+      tristate "foo" if A && B && C && D
+      default y if A && B && C
+
+  endmenu
+
+In this example, A && B && C && D (the prompt condition) needs to be non-n for
+FOO to be visible (assignable). If its value is m, the symbol can only be
+assigned the value m: The visibility sets an upper bound on the value that can
+be assigned by the user, and any higher user value will be truncated down.
+
+'default' properties are independent of the visibility, though a 'default' will
+often get the same condition as the prompt due to dependency propagation.
+'default' properties are used if the symbol is not visible or has no user
+value.
+
+Symbols with no user value (or that have a user value but are not visible) and
+no (active) 'default' default to n for bool/tristate symbols, and to the empty
+string for other symbol types.
+
+'select' works similarly to symbol visibility, but sets a lower bound on the
+value of the symbol. The lower bound is determined by the value of the
+select*ing* symbol. 'select' does not respect visibility, so non-visible
+symbols can be forced to a particular (minimum) value by a select as well.
+
+For non-bool/tristate symbols, it only matters whether the visibility is n or
+non-n: m visibility acts the same as y visibility.
+
+Conditions on 'default' and 'select' work in mostly intuitive ways. If the
+condition is n, the 'default' or 'select' is disabled. If it is m, the
+'default' or 'select' value (the value of the selecting symbol) is truncated
+down to m.
+
+When writing a configuration with Kconfig.write_config(), only symbols that are
+visible, have an (active) default, or are selected will get written out (note
+that this includes all symbols that would accept user values). Kconfiglib
+matches the .config format produced by the C implementations down to the
+character. This eases testing.
+
+For a visible bool/tristate symbol FOO with value n, this line is written to
+.config:
+
+    # CONFIG_FOO is not set
+
+The point is to remember the user n selection (which might differ from the
+default value the symbol would get), while at the same sticking to the rule
+that undefined corresponds to n (.config uses Makefile format, making the line
+above a comment). When the .config file is read back in, this line will be
+treated the same as the following assignment:
+
+    CONFIG_FOO=n
+
+In Kconfiglib, the set of (currently) assignable values for a bool/tristate
+symbol appear in Symbol.assignable. For other symbol types, just check if
+sym.visibility is non-0 (non-n) to see whether the user value will have an
+effect.
+
+
+Intro to the menu tree
+======================
+
+The menu structure, as seen in e.g. menuconfig, is represented by a tree of
+MenuNode objects. The top node of the configuration corresponds to an implicit
+top-level menu, the title of which is shown at the top in the standard
+menuconfig interface. (The title is also available in Kconfig.mainmenu_text in
+Kconfiglib.)
+
+The top node is found in Kconfig.top_node. From there, you can visit child menu
+nodes by following the 'list' pointer, and any following menu nodes by
+following the 'next' pointer. Usually, a non-None 'list' pointer indicates a
+menu or Choice, but menu nodes for symbols can sometimes have a non-None 'list'
+pointer too due to submenus created implicitly from dependencies.
+
+MenuNode.item is either a Symbol or a Choice object, or one of the constants
+MENU and COMMENT. The prompt of the menu node can be found in MenuNode.prompt,
+which also holds the title for menus and comments. For Symbol and Choice,
+MenuNode.help holds the help text (if any, otherwise None).
+
+Most symbols will only have a single menu node. A symbol defined in multiple
+locations will have one menu node for each location. The list of menu nodes for
+a Symbol or Choice can be found in the Symbol/Choice.nodes attribute.
+
+Note that prompts and help texts for symbols and choices are stored in their
+menu node(s) rather than in the Symbol or Choice objects themselves. This makes
+it possible to define a symbol in multiple locations with a different prompt or
+help text in each location. To get the help text or prompt for a symbol with a
+single menu node, do sym.nodes[0].help and sym.nodes[0].prompt, respectively.
+The prompt is a (text, condition) tuple, where condition determines the
+visibility (see 'Intro to expressions' below).
+
+This organization mirrors the C implementation. MenuNode is called
+'struct menu' there, but I thought "menu" was a confusing name.
+
+It is possible to give a Choice a name and define it in multiple locations,
+hence why Choice.nodes is also a list.
+
+As a convenience, the properties added at a particular definition location are
+available on the MenuNode itself, in e.g. MenuNode.defaults. This is helpful
+when generating documentation, so that symbols/choices defined in multiple
+locations can be shown with the correct properties at each location.
+
+
+Intro to expressions
+====================
+
+Expressions can be evaluated with the expr_value() function and printed with
+the expr_str() function (these are used internally as well). Evaluating an
+expression always yields a tristate value, where n, m, and y are represented as
+0, 1, and 2, respectively.
+
+The following table should help you figure out how expressions are represented.
+A, B, C, ... are symbols (Symbol instances), NOT is the kconfiglib.NOT
+constant, etc.
+
+Expression            Representation
+----------            --------------
+A                     A
+"A"                   A (constant symbol)
+!A                    (NOT, A)
+A && B                (AND, A, B)
+A && B && C           (AND, A, (AND, B, C))
+A || B                (OR, A, B)
+A || (B && C && D)    (OR, A, (AND, B, (AND, C, D)))
+A = B                 (EQUAL, A, B)
+A != "foo"            (UNEQUAL, A, foo (constant symbol))
+A && B = C && D       (AND, A, (AND, (EQUAL, B, C), D))
+n                     Kconfig.n (constant symbol)
+m                     Kconfig.m (constant symbol)
+y                     Kconfig.y (constant symbol)
+"y"                   Kconfig.y (constant symbol)
+
+Strings like "foo" in 'default "foo"' or 'depends on SYM = "foo"' are
+represented as constant symbols, so the only values that appear in expressions
+are symbols***. This mirrors the C implementation.
+
+***For choice symbols, the parent Choice will appear in expressions as well,
+but it's usually invisible as the value interfaces of Symbol and Choice are
+identical. This mirrors the C implementation and makes different choice modes
+"just work".
+
+Manual evaluation examples:
+
+  - The value of A && B is min(A.tri_value, B.tri_value)
+
+  - The value of A || B is max(A.tri_value, B.tri_value)
+
+  - The value of !A is 2 - A.tri_value
+
+  - The value of A = B is 2 (y) if A.str_value == B.str_value, and 0 (n)
+    otherwise. Note that str_value is used here instead of tri_value.
+
+    For constant (as well as undefined) symbols, str_value matches the name of
+    the symbol. This mirrors the C implementation and explains why
+    'depends on SYM = "foo"' above works as expected.
+
+n/m/y are automatically converted to the corresponding constant symbols
+"n"/"m"/"y" (Kconfig.n/m/y) during parsing.
+
+Kconfig.const_syms is a dictionary like Kconfig.syms but for constant symbols.
+
+If a condition is missing (e.g., <cond> when the 'if <cond>' is removed from
+'default A if <cond>'), it is actually Kconfig.y. The standard __str__()
+functions just avoid printing 'if y' conditions to give cleaner output.
+
+
+Kconfig extensions
+==================
+
+Kconfiglib includes a couple of Kconfig extensions:
+
+'source' with relative path
+---------------------------
+
+The 'rsource' statement sources Kconfig files with a path relative to directory
+of the Kconfig file containing the 'rsource' statement, instead of relative to
+the project root.
+
+Consider following directory tree:
+
+  Project
+  +--Kconfig
+  |
+  +--src
+     +--Kconfig
+     |
+     +--SubSystem1
+        +--Kconfig
+        |
+        +--ModuleA
+           +--Kconfig
+
+In this example, assume that src/SubSystem1/Kconfig wants to source
+src/SubSystem1/ModuleA/Kconfig.
+
+With 'source', this statement would be used:
+
+  source "src/SubSystem1/ModuleA/Kconfig"
+
+With 'rsource', this turns into
+
+  rsource "ModuleA/Kconfig"
+
+If an absolute path is given to 'rsource', it acts the same as 'source'.
+
+'rsource' can be used to create "position-independent" Kconfig trees that can
+be moved around freely.
+
+
+Globbing 'source'
+-----------------
+
+'source' and 'rsource' accept glob patterns, sourcing all matching Kconfig
+files. They require at least one matching file, raising a KconfigError
+otherwise.
+
+For example, the following statement might source sub1/foofoofoo and
+sub2/foobarfoo:
+
+  source "sub[12]/foo*foo"
+
+The glob patterns accepted are the same as for the standard glob.glob()
+function.
+
+Two additional statements are provided for cases where it's acceptable for a
+pattern to match no files: 'osource' and 'orsource' (the o is for "optional").
+
+For example, the following statements will be no-ops if neither "foo" nor any
+files matching "bar*" exist:
+
+  osource "foo"
+  osource "bar*"
+
+'orsource' does a relative optional source.
+
+'source' and 'osource' are analogous to 'include' and '-include' in Make.
+
+
+Generalized def_* keywords
+--------------------------
+
+def_int, def_hex, and def_string are available in addition to def_bool and
+def_tristate, allowing int, hex, and string symbols to be given a type and a
+default at the same time.
+
+
+Extra optional warnings
+-----------------------
+
+Some optional warnings can be controlled via environment variables:
+
+  - KCONFIG_WARN_UNDEF: If set to 'y', warnings will be generated for all
+    references to undefined symbols within Kconfig files. The only gotcha is
+    that all hex literals must be prefixed with "0x" or "0X", to make it
+    possible to distinguish them from symbol references.
+
+    Some projects (e.g. the Linux kernel) use multiple Kconfig trees with many
+    shared Kconfig files, leading to some safe undefined symbol references.
+    KCONFIG_WARN_UNDEF is useful in projects that only have a single Kconfig
+    tree though.
+
+    KCONFIG_STRICT is an older alias for this environment variable, supported
+    for backwards compatibility.
+
+  - KCONFIG_WARN_UNDEF_ASSIGN: If set to 'y', warnings will be generated for
+    all assignments to undefined symbols within .config files. By default, no
+    such warnings are generated.
+
+    This warning can also be enabled/disabled via the Kconfig.warn_assign_undef
+    variable.
+
+
+Preprocessor user functions defined in Python
+---------------------------------------------
+
+Preprocessor functions can be defined in Python, which makes it simple to
+integrate information from existing Python tools into Kconfig (e.g. to have
+Kconfig symbols depend on hardware information stored in some other format).
+
+Putting a Python module named kconfigfunctions(.py) anywhere in sys.path will
+cause it to be imported by Kconfiglib (in Kconfig.__init__()). Note that
+sys.path can be customized via PYTHONPATH, and includes the directory of the
+module being run by default, as well as installation directories.
+
+If the KCONFIG_FUNCTIONS environment variable is set, it gives a different
+module name to use instead of 'kconfigfunctions'.
+
+The imported module is expected to define a global dictionary named 'functions'
+that maps function names to Python functions, as follows:
+
+  def my_fn(kconf, name, arg_1, arg_2, ...):
+      # kconf:
+      #   Kconfig instance
+      #
+      # name:
+      #   Name of the user-defined function ("my-fn"). Think argv[0].
+      #
+      # arg_1, arg_2, ...:
+      #   Arguments passed to the function from Kconfig (strings)
+      #
+      # Returns a string to be substituted as the result of calling the
+      # function
+      ...
+
+  def my_other_fn(kconf, name, arg_1, arg_2, ...):
+      ...
+
+  functions = {
+      "my-fn":       (my_fn,       <min.args>, <max.args>/None),
+      "my-other-fn": (my_other_fn, <min.args>, <max.args>/None),
+      ...
+  }
+
+  ...
+
+<min.args> and <max.args> are the minimum and maximum number of arguments
+expected by the function (excluding the implicit 'name' argument). If
+<max.args> is None, there is no upper limit to the number of arguments. Passing
+an invalid number of arguments will generate a KconfigError exception.
+
+Functions can access the current parsing location as kconf.filename/linenr.
+Accessing other fields of the Kconfig object is not safe. See the warning
+below.
+
+Keep in mind that for a variable defined like 'foo = $(fn)', 'fn' will be
+called only when 'foo' is expanded. If 'fn' uses the parsing location and the
+intent is to use the location of the assignment, you want 'foo := $(fn)'
+instead, which calls the function immediately.
+
+Once defined, user functions can be called from Kconfig in the same way as
+other preprocessor functions:
+
+    config FOO
+        ...
+        depends on $(my-fn,arg1,arg2)
+
+If my_fn() returns "n", this will result in
+
+    config FOO
+        ...
+        depends on n
+
+Warning
+*******
+
+User-defined preprocessor functions are called as they're encountered at parse
+time, before all Kconfig files have been processed, and before the menu tree
+has been finalized. There are no guarantees that accessing Kconfig symbols or
+the menu tree via the 'kconf' parameter will work, and it could potentially
+lead to a crash.
+
+Preferably, user-defined functions should be stateless.
+
+
+Feedback
+========
+
+Send bug reports, suggestions, and questions to ulfalizer a.t Google's email
+service, or open a ticket on the GitHub page.
+"""
+import errno
+import importlib
+import os
+import re
+import sys
+
+# Get rid of some attribute lookups. These are obvious in context.
+from glob import iglob
+from os.path import dirname, exists, expandvars, islink, join, realpath
+
+
+VERSION = (14, 1, 0)
+
+
+# File layout:
+#
+# Public classes
+# Public functions
+# Internal functions
+# Global constants
+
+# Line length: 79 columns
+
+
+#
+# Public classes
+#
+
+
+class Kconfig(object):
+    """
+    Represents a Kconfig configuration, e.g. for x86 or ARM. This is the set of
+    symbols, choices, and menu nodes appearing in the configuration. Creating
+    any number of Kconfig objects (including for different architectures) is
+    safe. Kconfiglib doesn't keep any global state.
+
+    The following attributes are available. They should be treated as
+    read-only, and some are implemented through @property magic.
+
+    syms:
+      A dictionary with all symbols in the configuration, indexed by name. Also
+      includes all symbols that are referenced in expressions but never
+      defined, except for constant (quoted) symbols.
+
+      Undefined symbols can be recognized by Symbol.nodes being empty -- see
+      the 'Intro to the menu tree' section in the module docstring.
+
+    const_syms:
+      A dictionary like 'syms' for constant (quoted) symbols
+
+    named_choices:
+      A dictionary like 'syms' for named choices (choice FOO)
+
+    defined_syms:
+      A list with all defined symbols, in the same order as they appear in the
+      Kconfig files. Symbols defined in multiple locations appear multiple
+      times.
+
+      Note: You probably want to use 'unique_defined_syms' instead. This
+      attribute is mostly maintained for backwards compatibility.
+
+    unique_defined_syms:
+      A list like 'defined_syms', but with duplicates removed. Just the first
+      instance is kept for symbols defined in multiple locations. Kconfig order
+      is preserved otherwise.
+
+      Using this attribute instead of 'defined_syms' can save work, and
+      automatically gives reasonable behavior when writing configuration output
+      (symbols defined in multiple locations only generate output once, while
+      still preserving Kconfig order for readability).
+
+    choices:
+      A list with all choices, in the same order as they appear in the Kconfig
+      files.
+
+      Note: You probably want to use 'unique_choices' instead. This attribute
+      is mostly maintained for backwards compatibility.
+
+    unique_choices:
+      Analogous to 'unique_defined_syms', for choices. Named choices can have
+      multiple definition locations.
+
+    menus:
+      A list with all menus, in the same order as they appear in the Kconfig
+      files
+
+    comments:
+      A list with all comments, in the same order as they appear in the Kconfig
+      files
+
+    kconfig_filenames:
+      A list with the filenames of all Kconfig files included in the
+      configuration, relative to $srctree (or relative to the current directory
+      if $srctree isn't set), except absolute paths (e.g.
+      'source "/foo/Kconfig"') are kept as-is.
+
+      The files are listed in the order they are source'd, starting with the
+      top-level Kconfig file. If a file is source'd multiple times, it will
+      appear multiple times. Use set() to get unique filenames.
+
+      Note that Kconfig.sync_deps() already indirectly catches any file
+      modifications that change configuration output.
+
+    env_vars:
+      A set() with the names of all environment variables referenced in the
+      Kconfig files.
+
+      Only environment variables referenced with the preprocessor $(FOO) syntax
+      will be registered. The older $FOO syntax is only supported for backwards
+      compatibility.
+
+      Also note that $(FOO) won't be registered unless the environment variable
+      $FOO is actually set. If it isn't, $(FOO) is an expansion of an unset
+      preprocessor variable (which gives the empty string).
+
+      Another gotcha is that environment variables referenced in the values of
+      recursively expanded preprocessor variables (those defined with =) will
+      only be registered if the variable is actually used (expanded) somewhere.
+
+      The note from the 'kconfig_filenames' documentation applies here too.
+
+    n/m/y:
+      The predefined constant symbols n/m/y. Also available in const_syms.
+
+    modules:
+      The Symbol instance for the modules symbol. Currently hardcoded to
+      MODULES, which is backwards compatible. Kconfiglib will warn if
+      'option modules' is set on some other symbol. Tell me if you need proper
+      'option modules' support.
+
+      'modules' is never None. If the MODULES symbol is not explicitly defined,
+      its tri_value will be 0 (n), as expected.
+
+      A simple way to enable modules is to do 'kconf.modules.set_value(2)'
+      (provided the MODULES symbol is defined and visible). Modules are
+      disabled by default in the kernel Kconfig files as of writing, though
+      nearly all defconfig files enable them (with 'CONFIG_MODULES=y').
+
+    defconfig_list:
+      The Symbol instance for the 'option defconfig_list' symbol, or None if no
+      defconfig_list symbol exists. The defconfig filename derived from this
+      symbol can be found in Kconfig.defconfig_filename.
+
+    defconfig_filename:
+      The filename given by the defconfig_list symbol. This is taken from the
+      first 'default' with a satisfied condition where the specified file
+      exists (can be opened for reading). If a defconfig file foo/defconfig is
+      not found and $srctree was set when the Kconfig was created,
+      $srctree/foo/defconfig is looked up as well.
+
+      'defconfig_filename' is None if either no defconfig_list symbol exists,
+      or if the defconfig_list symbol has no 'default' with a satisfied
+      condition that specifies a file that exists.
+
+      Gotcha: scripts/kconfig/Makefile might pass --defconfig=<defconfig> to
+      scripts/kconfig/conf when running e.g. 'make defconfig'. This option
+      overrides the defconfig_list symbol, meaning defconfig_filename might not
+      always match what 'make defconfig' would use.
+
+    top_node:
+      The menu node (see the MenuNode class) of the implicit top-level menu.
+      Acts as the root of the menu tree.
+
+    mainmenu_text:
+      The prompt (title) of the top menu (top_node). Defaults to "Main menu".
+      Can be changed with the 'mainmenu' statement (see kconfig-language.txt).
+
+    variables:
+      A dictionary with all preprocessor variables, indexed by name. See the
+      Variable class.
+
+    warn:
+      Set this variable to True/False to enable/disable warnings. See
+      Kconfig.__init__().
+
+      When 'warn' is False, the values of the other warning-related variables
+      are ignored.
+
+      This variable as well as the other warn* variables can be read to check
+      the current warning settings.
+
+    warn_to_stderr:
+      Set this variable to True/False to enable/disable warnings on stderr. See
+      Kconfig.__init__().
+
+    warn_assign_undef:
+      Set this variable to True to generate warnings for assignments to
+      undefined symbols in configuration files.
+
+      This variable is False by default unless the KCONFIG_WARN_UNDEF_ASSIGN
+      environment variable was set to 'y' when the Kconfig instance was
+      created.
+
+    warn_assign_override:
+      Set this variable to True to generate warnings for multiple assignments
+      to the same symbol in configuration files, where the assignments set
+      different values (e.g. CONFIG_FOO=m followed by CONFIG_FOO=y, where the
+      last value would get used).
+
+      This variable is True by default. Disabling it might be useful when
+      merging configurations.
+
+    warn_assign_redun:
+      Like warn_assign_override, but for multiple assignments setting a symbol
+      to the same value.
+
+      This variable is True by default. Disabling it might be useful when
+      merging configurations.
+
+    warnings:
+      A list of strings containing all warnings that have been generated, for
+      cases where more flexibility is needed.
+
+      See the 'warn_to_stderr' parameter to Kconfig.__init__() and the
+      Kconfig.warn_to_stderr variable as well. Note that warnings still get
+      added to Kconfig.warnings when 'warn_to_stderr' is True.
+
+      Just as for warnings printed to stderr, only warnings that are enabled
+      will get added to Kconfig.warnings. See the various Kconfig.warn*
+      variables.
+
+    missing_syms:
+      A list with (name, value) tuples for all assignments to undefined symbols
+      within the most recently loaded .config file(s). 'name' is the symbol
+      name without the 'CONFIG_' prefix. 'value' is a string that gives the
+      right-hand side of the assignment verbatim.
+
+      See Kconfig.load_config() as well.
+
+    srctree:
+      The value the $srctree environment variable had when the Kconfig instance
+      was created, or the empty string if $srctree wasn't set. This gives nice
+      behavior with os.path.join(), which treats "" as the current directory,
+      without adding "./".
+
+      Kconfig files are looked up relative to $srctree (unless absolute paths
+      are used), and .config files are looked up relative to $srctree if they
+      are not found in the current directory. This is used to support
+      out-of-tree builds. The C tools use this environment variable in the same
+      way.
+
+      Changing $srctree after creating the Kconfig instance has no effect. Only
+      the value when the configuration is loaded matters. This avoids surprises
+      if multiple configurations are loaded with different values for $srctree.
+
+    config_prefix:
+      The value the CONFIG_ environment variable had when the Kconfig instance
+      was created, or "CONFIG_" if CONFIG_ wasn't set. This is the prefix used
+      (and expected) on symbol names in .config files and C headers. Used in
+      the same way in the C tools.
+
+    config_header:
+      The value the KCONFIG_CONFIG_HEADER environment variable had when the
+      Kconfig instance was created, or the empty string if
+      KCONFIG_CONFIG_HEADER wasn't set. This string is inserted verbatim at the
+      beginning of configuration files. See write_config().
+
+    header_header:
+      The value the KCONFIG_AUTOHEADER_HEADER environment variable had when the
+      Kconfig instance was created, or the empty string if
+      KCONFIG_AUTOHEADER_HEADER wasn't set. This string is inserted verbatim at
+      the beginning of header files. See write_autoconf().
+
+    filename/linenr:
+      The current parsing location, for use in Python preprocessor functions.
+      See the module docstring.
+    """
+    __slots__ = (
+        "_encoding",
+        "_functions",
+        "_set_match",
+        "_srctree_prefix",
+        "_unset_match",
+        "_warn_assign_no_prompt",
+        "choices",
+        "comments",
+        "config_header",
+        "config_prefix",
+        "const_syms",
+        "defconfig_list",
+        "defined_syms",
+        "env_vars",
+        "header_header",
+        "kconfig_filenames",
+        "m",
+        "menus",
+        "missing_syms",
+        "modules",
+        "n",
+        "named_choices",
+        "srctree",
+        "syms",
+        "top_node",
+        "unique_choices",
+        "unique_defined_syms",
+        "variables",
+        "warn",
+        "warn_assign_override",
+        "warn_assign_redun",
+        "warn_assign_undef",
+        "warn_to_stderr",
+        "warnings",
+        "y",
+
+        # Parsing-related
+        "_parsing_kconfigs",
+        "_readline",
+        "filename",
+        "linenr",
+        "_include_path",
+        "_filestack",
+        "_line",
+        "_tokens",
+        "_tokens_i",
+        "_reuse_tokens",
+    )
+
+    #
+    # Public interface
+    #
+
+    def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
+                 encoding="utf-8", suppress_traceback=False):
+        """
+        Creates a new Kconfig object by parsing Kconfig files.
+        Note that Kconfig files are not the same as .config files (which store
+        configuration symbol values).
+
+        See the module docstring for some environment variables that influence
+        default warning settings (KCONFIG_WARN_UNDEF and
+        KCONFIG_WARN_UNDEF_ASSIGN).
+
+        Raises KconfigError on syntax/semantic errors, and OSError or (possibly
+        a subclass of) IOError on IO errors ('errno', 'strerror', and
+        'filename' are available). Note that IOError is an alias for OSError on
+        Python 3, so it's enough to catch OSError there. If you need Python 2/3
+        compatibility, it's easiest to catch EnvironmentError, which is a
+        common base class of OSError/IOError on Python 2 and an alias for
+        OSError on Python 3.
+
+        filename (default: "Kconfig"):
+          The Kconfig file to load. For the Linux kernel, you'll want "Kconfig"
+          from the top-level directory, as environment variables will make sure
+          the right Kconfig is included from there (arch/$SRCARCH/Kconfig as of
+          writing).
+
+          If $srctree is set, 'filename' will be looked up relative to it.
+          $srctree is also used to look up source'd files within Kconfig files.
+          See the class documentation.
+
+          If you are using Kconfiglib via 'make scriptconfig', the filename of
+          the base base Kconfig file will be in sys.argv[1]. It's currently
+          always "Kconfig" in practice.
+
+        warn (default: True):
+          True if warnings related to this configuration should be generated.
+          This can be changed later by setting Kconfig.warn to True/False. It
+          is provided as a constructor argument since warnings might be
+          generated during parsing.
+
+          See the other Kconfig.warn_* variables as well, which enable or
+          suppress certain warnings when warnings are enabled.
+
+          All generated warnings are added to the Kconfig.warnings list. See
+          the class documentation.
+
+        warn_to_stderr (default: True):
+          True if warnings should be printed to stderr in addition to being
+          added to Kconfig.warnings.
+
+          This can be changed later by setting Kconfig.warn_to_stderr to
+          True/False.
+
+        encoding (default: "utf-8"):
+          The encoding to use when reading and writing files, and when decoding
+          output from commands run via $(shell). If None, the encoding
+          specified in the current locale will be used.
+
+          The "utf-8" default avoids exceptions on systems that are configured
+          to use the C locale, which implies an ASCII encoding.
+
+          This parameter has no effect on Python 2, due to implementation
+          issues (regular strings turning into Unicode strings, which are
+          distinct in Python 2). Python 2 doesn't decode regular strings
+          anyway.
+
+          Related PEP: https://www.python.org/dev/peps/pep-0538/
+
+        suppress_traceback (default: False):
+          Helper for tools. When True, any EnvironmentError or KconfigError
+          generated during parsing is caught, the exception message is printed
+          to stderr together with the command name, and sys.exit(1) is called
+          (which generates SystemExit).
+
+          This hides the Python traceback for "expected" errors like syntax
+          errors in Kconfig files.
+
+          Other exceptions besides EnvironmentError and KconfigError are still
+          propagated when suppress_traceback is True.
+        """
+        try:
+            self._init(filename, warn, warn_to_stderr, encoding)
+        except (EnvironmentError, KconfigError) as e:
+            if suppress_traceback:
+                cmd = sys.argv[0]  # Empty string if missing
+                if cmd:
+                    cmd += ": "
+                # Some long exception messages have extra newlines for better
+                # formatting when reported as an unhandled exception. Strip
+                # them here.
+                sys.exit(cmd + str(e).strip())
+            raise
+
+    def _init(self, filename, warn, warn_to_stderr, encoding):
+        # See __init__()
+
+        self._encoding = encoding
+
+        self.srctree = os.getenv("srctree", "")
+        # A prefix we can reliably strip from glob() results to get a filename
+        # relative to $srctree. relpath() can cause issues for symlinks,
+        # because it assumes symlink/../foo is the same as foo/.
+        self._srctree_prefix = realpath(self.srctree) + os.sep
+
+        self.warn = warn
+        self.warn_to_stderr = warn_to_stderr
+        self.warn_assign_undef = os.getenv("KCONFIG_WARN_UNDEF_ASSIGN") == "y"
+        self.warn_assign_override = True
+        self.warn_assign_redun = True
+        self._warn_assign_no_prompt = True
+
+        self.warnings = []
+
+        self.config_prefix = os.getenv("CONFIG_", "CONFIG_")
+        # Regular expressions for parsing .config files
+        self._set_match = _re_match(self.config_prefix + r"([^=]+)=(.*)")
+        self._unset_match = _re_match(r"# {}([^ ]+) is not set".format(
+            self.config_prefix))
+
+        self.config_header = os.getenv("KCONFIG_CONFIG_HEADER", "")
+        self.header_header = os.getenv("KCONFIG_AUTOHEADER_HEADER", "")
+
+        self.syms = {}
+        self.const_syms = {}
+        self.defined_syms = []
+        self.missing_syms = []
+        self.named_choices = {}
+        self.choices = []
+        self.menus = []
+        self.comments = []
+
+        for nmy in "n", "m", "y":
+            sym = Symbol()
+            sym.kconfig = self
+            sym.name = nmy
+            sym.is_constant = True
+            sym.orig_type = TRISTATE
+            sym._cached_tri_val = STR_TO_TRI[nmy]
+
+            self.const_syms[nmy] = sym
+
+        self.n = self.const_syms["n"]
+        self.m = self.const_syms["m"]
+        self.y = self.const_syms["y"]
+
+        # Make n/m/y well-formed symbols
+        for nmy in "n", "m", "y":
+            sym = self.const_syms[nmy]
+            sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        # Maps preprocessor variables names to Variable instances
+        self.variables = {}
+
+        # Predefined preprocessor functions, with min/max number of arguments
+        self._functions = {
+            "info":       (_info_fn,       1, 1),
+            "error-if":   (_error_if_fn,   2, 2),
+            "filename":   (_filename_fn,   0, 0),
+            "lineno":     (_lineno_fn,     0, 0),
+            "shell":      (_shell_fn,      1, 1),
+            "warning-if": (_warning_if_fn, 2, 2),
+        }
+
+        # Add any user-defined preprocessor functions
+        try:
+            self._functions.update(
+                importlib.import_module(
+                    os.getenv("KCONFIG_FUNCTIONS", "kconfigfunctions")
+                ).functions)
+        except ImportError:
+            pass
+
+        # This determines whether previously unseen symbols are registered.
+        # They shouldn't be if we parse expressions after parsing, as part of
+        # Kconfig.eval_string().
+        self._parsing_kconfigs = True
+
+        self.modules = self._lookup_sym("MODULES")
+        self.defconfig_list = None
+
+        self.top_node = MenuNode()
+        self.top_node.kconfig = self
+        self.top_node.item = MENU
+        self.top_node.is_menuconfig = True
+        self.top_node.visibility = self.y
+        self.top_node.prompt = ("Main menu", self.y)
+        self.top_node.parent = None
+        self.top_node.dep = self.y
+        self.top_node.filename = filename
+        self.top_node.linenr = 1
+        self.top_node.include_path = ()
+
+        # Parse the Kconfig files
+
+        # Not used internally. Provided as a convenience.
+        self.kconfig_filenames = [filename]
+        self.env_vars = set()
+
+        # Keeps track of the location in the parent Kconfig files. Kconfig
+        # files usually source other Kconfig files. See _enter_file().
+        self._filestack = []
+        self._include_path = ()
+
+        # The current parsing location
+        self.filename = filename
+        self.linenr = 0
+
+        # Used to avoid retokenizing lines when we discover that they're not
+        # part of the construct currently being parsed. This is kinda like an
+        # unget operation.
+        self._reuse_tokens = False
+
+        # Open the top-level Kconfig file. Store the readline() method directly
+        # as a small optimization.
+        self._readline = self._open(join(self.srctree, filename), "r").readline
+
+        try:
+            # Parse the Kconfig files. Returns the last node, which we
+            # terminate with '.next = None'.
+            self._parse_block(None, self.top_node, self.top_node).next = None
+            self.top_node.list = self.top_node.next
+            self.top_node.next = None
+        except UnicodeDecodeError as e:
+            _decoding_error(e, self.filename)
+
+        # Close the top-level Kconfig file. __self__ fetches the 'file' object
+        # for the method.
+        self._readline.__self__.close()
+
+        self._parsing_kconfigs = False
+
+        # Do various menu tree post-processing
+        self._finalize_node(self.top_node, self.y)
+
+        self.unique_defined_syms = _ordered_unique(self.defined_syms)
+        self.unique_choices = _ordered_unique(self.choices)
+
+        # Do sanity checks. Some of these depend on everything being finalized.
+        self._check_sym_sanity()
+        self._check_choice_sanity()
+
+        # KCONFIG_STRICT is an older alias for KCONFIG_WARN_UNDEF, supported
+        # for backwards compatibility
+        if os.getenv("KCONFIG_WARN_UNDEF") == "y" or \
+           os.getenv("KCONFIG_STRICT") == "y":
+
+            self._check_undef_syms()
+
+        # Build Symbol._dependents for all symbols and choices
+        self._build_dep()
+
+        # Check for dependency loops
+        check_dep_loop_sym = _check_dep_loop_sym  # Micro-optimization
+        for sym in self.unique_defined_syms:
+            check_dep_loop_sym(sym, False)
+
+        # Add extra dependencies from choices to choice symbols that get
+        # awkward during dependency loop detection
+        self._add_choice_deps()
+
+    @property
+    def mainmenu_text(self):
+        """
+        See the class documentation.
+        """
+        return self.top_node.prompt[0]
+
+    @property
+    def defconfig_filename(self):
+        """
+        See the class documentation.
+        """
+        if self.defconfig_list:
+            for filename, cond in self.defconfig_list.defaults:
+                if expr_value(cond):
+                    try:
+                        with self._open_config(filename.str_value) as f:
+                            return f.name
+                    except EnvironmentError:
+                        continue
+
+        return None
+
+    def load_config(self, filename=None, replace=True, verbose=None):
+        """
+        Loads symbol values from a file in the .config format. Equivalent to
+        calling Symbol.set_value() to set each of the values.
+
+        "# CONFIG_FOO is not set" within a .config file sets the user value of
+        FOO to n. The C tools work the same way.
+
+        For each symbol, the Symbol.user_value attribute holds the value the
+        symbol was assigned in the .config file (if any). The user value might
+        differ from Symbol.str/tri_value if there are unsatisfied dependencies.
+
+        Calling this function also updates the Kconfig.missing_syms attribute
+        with a list of all assignments to undefined symbols within the
+        configuration file. Kconfig.missing_syms is cleared if 'replace' is
+        True, and appended to otherwise. See the documentation for
+        Kconfig.missing_syms as well.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename (default: None):
+          Path to load configuration from (a string). Respects $srctree if set
+          (see the class documentation).
+
+          If 'filename' is None (the default), the configuration file to load
+          (if any) is calculated automatically, giving the behavior you'd
+          usually want:
+
+            1. If the KCONFIG_CONFIG environment variable is set, it gives the
+               path to the configuration file to load. Otherwise, ".config" is
+               used. See standard_config_filename().
+
+            2. If the path from (1.) doesn't exist, the configuration file
+               given by kconf.defconfig_filename is loaded instead, which is
+               derived from the 'option defconfig_list' symbol.
+
+            3. If (1.) and (2.) fail to find a configuration file to load, no
+               configuration file is loaded, and symbols retain their current
+               values (e.g., their default values). This is not an error.
+
+           See the return value as well.
+
+        replace (default: True):
+          If True, all existing user values will be cleared before loading the
+          .config. Pass False to merge configurations.
+
+        verbose (default: None):
+          Limited backwards compatibility to prevent crashes. A warning is
+          printed if anything but None is passed.
+
+          Prior to Kconfiglib 12.0.0, this option enabled printing of messages
+          to stdout when 'filename' was None. A message is (always) returned
+          now instead, which is more flexible.
+
+          Will probably be removed in some future version.
+
+        Returns a string with a message saying which file got loaded (or
+        possibly that no file got loaded, when 'filename' is None). This is
+        meant to reduce boilerplate in tools, which can do e.g.
+        print(kconf.load_config()). The returned message distinguishes between
+        loading (replace == True) and merging (replace == False).
+        """
+        if verbose is not None:
+            _warn_verbose_deprecated("load_config")
+
+        msg = None
+        if filename is None:
+            filename = standard_config_filename()
+            if not exists(filename) and \
+               not exists(join(self.srctree, filename)):
+                defconfig = self.defconfig_filename
+                if defconfig is None:
+                    return "Using default symbol values (no '{}')" \
+                           .format(filename)
+
+                msg = " default configuration '{}' (no '{}')" \
+                      .format(defconfig, filename)
+                filename = defconfig
+
+        if not msg:
+            msg = " configuration '{}'".format(filename)
+
+        # Disable the warning about assigning to symbols without prompts. This
+        # is normal and expected within a .config file.
+        self._warn_assign_no_prompt = False
+
+        # This stub only exists to make sure _warn_assign_no_prompt gets
+        # reenabled
+        try:
+            self._load_config(filename, replace)
+        except UnicodeDecodeError as e:
+            _decoding_error(e, filename)
+        finally:
+            self._warn_assign_no_prompt = True
+
+        return ("Loaded" if replace else "Merged") + msg
+
+    def _load_config(self, filename, replace):
+        with self._open_config(filename) as f:
+            if replace:
+                self.missing_syms = []
+
+                # If we're replacing the configuration, keep track of which
+                # symbols and choices got set so that we can unset the rest
+                # later. This avoids invalidating everything and is faster.
+                # Another benefit is that invalidation must be rock solid for
+                # it to work, making it a good test.
+
+                for sym in self.unique_defined_syms:
+                    sym._was_set = False
+
+                for choice in self.unique_choices:
+                    choice._was_set = False
+
+            # Small optimizations
+            set_match = self._set_match
+            unset_match = self._unset_match
+            get_sym = self.syms.get
+
+            for linenr, line in enumerate(f, 1):
+                # The C tools ignore trailing whitespace
+                line = line.rstrip()
+
+                match = set_match(line)
+                if match:
+                    name, val = match.groups()
+                    sym = get_sym(name)
+                    if not sym or not sym.nodes:
+                        self._undef_assign(name, val, filename, linenr)
+                        continue
+
+                    if sym.orig_type in _BOOL_TRISTATE:
+                        # The C implementation only checks the first character
+                        # to the right of '=', for whatever reason
+                        if not (sym.orig_type is BOOL
+                                and val.startswith(("y", "n")) or
+                                sym.orig_type is TRISTATE
+                                and val.startswith(("y", "m", "n"))):
+                            self._warn("'{}' is not a valid value for the {} "
+                                       "symbol {}. Assignment ignored."
+                                       .format(val, TYPE_TO_STR[sym.orig_type],
+                                               sym.name_and_loc),
+                                       filename, linenr)
+                            continue
+
+                        val = val[0]
+
+                        if sym.choice and val != "n":
+                            # During .config loading, we infer the mode of the
+                            # choice from the kind of values that are assigned
+                            # to the choice symbols
+
+                            prev_mode = sym.choice.user_value
+                            if prev_mode is not None and \
+                               TRI_TO_STR[prev_mode] != val:
+
+                                self._warn("both m and y assigned to symbols "
+                                           "within the same choice",
+                                           filename, linenr)
+
+                            # Set the choice's mode
+                            sym.choice.set_value(val)
+
+                    elif sym.orig_type is STRING:
+                        match = _conf_string_match(val)
+                        if not match:
+                            self._warn("malformed string literal in "
+                                       "assignment to {}. Assignment ignored."
+                                       .format(sym.name_and_loc),
+                                       filename, linenr)
+                            continue
+
+                        val = unescape(match.group(1))
+
+                else:
+                    match = unset_match(line)
+                    if not match:
+                        # Print a warning for lines that match neither
+                        # set_match() nor unset_match() and that are not blank
+                        # lines or comments. 'line' has already been
+                        # rstrip()'d, so blank lines show up as "" here.
+                        if line and not line.lstrip().startswith("#"):
+                            self._warn("ignoring malformed line '{}'"
+                                       .format(line),
+                                       filename, linenr)
+
+                        continue
+
+                    name = match.group(1)
+                    sym = get_sym(name)
+                    if not sym or not sym.nodes:
+                        self._undef_assign(name, "n", filename, linenr)
+                        continue
+
+                    if sym.orig_type not in _BOOL_TRISTATE:
+                        continue
+
+                    val = "n"
+
+                # Done parsing the assignment. Set the value.
+
+                if sym._was_set:
+                    self._assigned_twice(sym, val, filename, linenr)
+
+                sym.set_value(val)
+
+        if replace:
+            # If we're replacing the configuration, unset the symbols that
+            # didn't get set
+
+            for sym in self.unique_defined_syms:
+                if not sym._was_set:
+                    sym.unset_value()
+
+            for choice in self.unique_choices:
+                if not choice._was_set:
+                    choice.unset_value()
+
+    def _undef_assign(self, name, val, filename, linenr):
+        # Called for assignments to undefined symbols during .config loading
+
+        self.missing_syms.append((name, val))
+        if self.warn_assign_undef:
+            self._warn(
+                "attempt to assign the value '{}' to the undefined symbol {}"
+                .format(val, name), filename, linenr)
+
+    def _assigned_twice(self, sym, new_val, filename, linenr):
+        # Called when a symbol is assigned more than once in a .config file
+
+        # Use strings for bool/tristate user values in the warning
+        if sym.orig_type in _BOOL_TRISTATE:
+            user_val = TRI_TO_STR[sym.user_value]
+        else:
+            user_val = sym.user_value
+
+        msg = '{} set more than once. Old value "{}", new value "{}".'.format(
+            sym.name_and_loc, user_val, new_val)
+
+        if user_val == new_val:
+            if self.warn_assign_redun:
+                self._warn(msg, filename, linenr)
+        elif self.warn_assign_override:
+            self._warn(msg, filename, linenr)
+
+    def load_allconfig(self, filename):
+        """
+        Helper for all*config. Loads (merges) the configuration file specified
+        by KCONFIG_ALLCONFIG, if any. See Documentation/kbuild/kconfig.txt in
+        the Linux kernel.
+
+        Disables warnings for duplicated assignments within configuration files
+        for the duration of the call
+        (kconf.warn_assign_override/warn_assign_redun = False), and restores
+        the previous warning settings at the end. The KCONFIG_ALLCONFIG
+        configuration file is expected to override symbols.
+
+        Exits with sys.exit() (which raises a SystemExit exception) and prints
+        an error to stderr if KCONFIG_ALLCONFIG is set but the configuration
+        file can't be opened.
+
+        filename:
+          Command-specific configuration filename - "allyes.config",
+          "allno.config", etc.
+        """
+        load_allconfig(self, filename)
+
+    def write_autoconf(self, filename=None, header=None):
+        r"""
+        Writes out symbol values as a C header file, matching the format used
+        by include/generated/autoconf.h in the kernel.
+
+        The ordering of the #defines matches the one generated by
+        write_config(). The order in the C implementation depends on the hash
+        table implementation as of writing, and so won't match.
+
+        If 'filename' exists and its contents is identical to what would get
+        written out, it is left untouched. This avoids updating file metadata
+        like the modification time and possibly triggering redundant work in
+        build tools.
+
+        filename (default: None):
+          Path to write header to.
+
+          If None (the default), the path in the environment variable
+          KCONFIG_AUTOHEADER is used if set, and "include/generated/autoconf.h"
+          otherwise. This is compatible with the C tools.
+
+        header (default: None):
+          Text inserted verbatim at the beginning of the file. You would
+          usually want it enclosed in '/* */' to make it a C comment, and
+          include a trailing newline.
+
+          If None (the default), the value of the environment variable
+          KCONFIG_AUTOHEADER_HEADER had when the Kconfig instance was created
+          will be used if it was set, and no header otherwise. See the
+          Kconfig.header_header attribute.
+
+        Returns a string with a message saying that the header got saved, or
+        that there were no changes to it. This is meant to reduce boilerplate
+        in tools, which can do e.g. print(kconf.write_autoconf()).
+        """
+        if filename is None:
+            filename = os.getenv("KCONFIG_AUTOHEADER",
+                                 "include/generated/autoconf.h")
+
+        if self._write_if_changed(filename, self._autoconf_contents(header)):
+            return "Kconfig header saved to '{}'".format(filename)
+        return "No change to Kconfig header in '{}'".format(filename)
+
+    def _autoconf_contents(self, header):
+        # write_autoconf() helper. Returns the contents to write as a string,
+        # with 'header' or KCONFIG_AUTOHEADER_HEADER at the beginning.
+
+        if header is None:
+            header = self.header_header
+
+        chunks = [header]  # "".join()ed later
+        add = chunks.append
+
+        for sym in self.unique_defined_syms:
+            # _write_to_conf is determined when the value is calculated. This
+            # is a hidden function call due to property magic.
+            #
+            # Note: In client code, you can check if sym.config_string is empty
+            # instead, to avoid accessing the internal _write_to_conf variable
+            # (though it's likely to keep working).
+            val = sym.str_value
+            if not sym._write_to_conf:
+                continue
+
+            if sym.orig_type in _BOOL_TRISTATE:
+                if val == "y":
+                    add("#define {}{} 1\n"
+                        .format(self.config_prefix, sym.name))
+                elif val == "m":
+                    add("#define {}{}_MODULE 1\n"
+                        .format(self.config_prefix, sym.name))
+
+            elif sym.orig_type is STRING:
+                add('#define {}{} "{}"\n'
+                    .format(self.config_prefix, sym.name, escape(val)))
+
+            else:  # sym.orig_type in _INT_HEX:
+                if sym.orig_type is HEX and \
+                   not val.startswith(("0x", "0X")):
+                    val = "0x" + val
+
+                add("#define {}{} {}\n"
+                    .format(self.config_prefix, sym.name, val))
+
+        return "".join(chunks)
+
+    def write_config(self, filename=None, header=None, save_old=True,
+                     verbose=None):
+        r"""
+        Writes out symbol values in the .config format. The format matches the
+        C implementation, including ordering.
+
+        Symbols appear in the same order in generated .config files as they do
+        in the Kconfig files. For symbols defined in multiple locations, a
+        single assignment is written out corresponding to the first location
+        where the symbol is defined.
+
+        See the 'Intro to symbol values' section in the module docstring to
+        understand which symbols get written out.
+
+        If 'filename' exists and its contents is identical to what would get
+        written out, it is left untouched. This avoids updating file metadata
+        like the modification time and possibly triggering redundant work in
+        build tools.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename (default: None):
+          Path to write configuration to (a string).
+
+          If None (the default), the path in the environment variable
+          KCONFIG_CONFIG is used if set, and ".config" otherwise. See
+          standard_config_filename().
+
+        header (default: None):
+          Text inserted verbatim at the beginning of the file. You would
+          usually want each line to start with '#' to make it a comment, and
+          include a trailing newline.
+
+          if None (the default), the value of the environment variable
+          KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
+          be used if it was set, and no header otherwise. See the
+          Kconfig.config_header attribute.
+
+        save_old (default: True):
+          If True and <filename> already exists, a copy of it will be saved to
+          <filename>.old in the same directory before the new configuration is
+          written.
+
+          Errors are silently ignored if <filename>.old cannot be written (e.g.
+          due to being a directory, or <filename> being something like
+          /dev/null).
+
+        verbose (default: None):
+          Limited backwards compatibility to prevent crashes. A warning is
+          printed if anything but None is passed.
+
+          Prior to Kconfiglib 12.0.0, this option enabled printing of messages
+          to stdout when 'filename' was None. A message is (always) returned
+          now instead, which is more flexible.
+
+          Will probably be removed in some future version.
+
+        Returns a string with a message saying which file got saved. This is
+        meant to reduce boilerplate in tools, which can do e.g.
+        print(kconf.write_config()).
+        """
+        if verbose is not None:
+            _warn_verbose_deprecated("write_config")
+
+        if filename is None:
+            filename = standard_config_filename()
+
+        contents = self._config_contents(header)
+        if self._contents_eq(filename, contents):
+            return "No change to configuration in '{}'".format(filename)
+
+        if save_old:
+            _save_old(filename)
+
+        with self._open(filename, "w") as f:
+            f.write(contents)
+
+        return "Configuration saved to '{}'".format(filename)
+
+    def _config_contents(self, header):
+        # write_config() helper. Returns the contents to write as a string,
+        # with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
+        #
+        # More memory friendly would be to 'yield' the strings and
+        # "".join(_config_contents()), but it was a bit slower on my system.
+
+        # node_iter() was used here before commit 3aea9f7 ("Add '# end of
+        # <menu>' after menus in .config"). Those comments get tricky to
+        # implement with it.
+
+        for sym in self.unique_defined_syms:
+            sym._visited = False
+
+        if header is None:
+            header = self.config_header
+
+        chunks = [header]  # "".join()ed later
+        add = chunks.append
+
+        # Did we just print an '# end of ...' comment?
+        after_end_comment = False
+
+        node = self.top_node
+        while 1:
+            # Jump to the next node with an iterative tree walk
+            if node.list:
+                node = node.list
+            elif node.next:
+                node = node.next
+            else:
+                while node.parent:
+                    node = node.parent
+
+                    # Add a comment when leaving visible menus
+                    if node.item is MENU and expr_value(node.dep) and \
+                       expr_value(node.visibility) and \
+                       node is not self.top_node:
+                        add("# end of {}\n".format(node.prompt[0]))
+                        after_end_comment = True
+
+                    if node.next:
+                        node = node.next
+                        break
+                else:
+                    # No more nodes
+                    return "".join(chunks)
+
+            # Generate configuration output for the node
+
+            item = node.item
+
+            if item.__class__ is Symbol:
+                if item._visited:
+                    continue
+                item._visited = True
+
+                conf_string = item.config_string
+                if not conf_string:
+                    continue
+
+                if after_end_comment:
+                    # Add a blank line before the first symbol printed after an
+                    # '# end of ...' comment
+                    after_end_comment = False
+                    add("\n")
+                add(conf_string)
+
+            elif expr_value(node.dep) and \
+                 ((item is MENU and expr_value(node.visibility)) or
+                  item is COMMENT):
+
+                add("\n#\n# {}\n#\n".format(node.prompt[0]))
+                after_end_comment = False
+
+    def write_min_config(self, filename, header=None):
+        """
+        Writes out a "minimal" configuration file, omitting symbols whose value
+        matches their default value. The format matches the one produced by
+        'make savedefconfig'.
+
+        The resulting configuration file is incomplete, but a complete
+        configuration can be derived from it by loading it. Minimal
+        configuration files can serve as a more manageable configuration format
+        compared to a "full" .config file, especially when configurations files
+        are merged or edited by hand.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename:
+          Path to write minimal configuration to.
+
+        header (default: None):
+          Text inserted verbatim at the beginning of the file. You would
+          usually want each line to start with '#' to make it a comment, and
+          include a final terminating newline.
+
+          if None (the default), the value of the environment variable
+          KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
+          be used if it was set, and no header otherwise. See the
+          Kconfig.config_header attribute.
+
+        Returns a string with a message saying the minimal configuration got
+        saved, or that there were no changes to it. This is meant to reduce
+        boilerplate in tools, which can do e.g.
+        print(kconf.write_min_config()).
+        """
+        if self._write_if_changed(filename, self._min_config_contents(header)):
+            return "Minimal configuration saved to '{}'".format(filename)
+        return "No change to minimal configuration in '{}'".format(filename)
+
+    def _min_config_contents(self, header):
+        # write_min_config() helper. Returns the contents to write as a string,
+        # with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
+
+        if header is None:
+            header = self.config_header
+
+        chunks = [header]  # "".join()ed later
+        add = chunks.append
+
+        for sym in self.unique_defined_syms:
+            # Skip symbols that cannot be changed. Only check
+            # non-choice symbols, as selects don't affect choice
+            # symbols.
+            if not sym.choice and \
+               sym.visibility <= expr_value(sym.rev_dep):
+                continue
+
+            # Skip symbols whose value matches their default
+            if sym.str_value == sym._str_default():
+                continue
+
+            # Skip symbols that would be selected by default in a
+            # choice, unless the choice is optional or the symbol type
+            # isn't bool (it might be possible to set the choice mode
+            # to n or the symbol to m in those cases).
+            if sym.choice and \
+               not sym.choice.is_optional and \
+               sym.choice._selection_from_defaults() is sym and \
+               sym.orig_type is BOOL and \
+               sym.tri_value == 2:
+                continue
+
+            add(sym.config_string)
+
+        return "".join(chunks)
+
+    def sync_deps(self, path):
+        """
+        Creates or updates a directory structure that can be used to avoid
+        doing a full rebuild whenever the configuration is changed, mirroring
+        include/config/ in the kernel.
+
+        This function is intended to be called during each build, before
+        compiling source files that depend on configuration symbols.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        path:
+          Path to directory
+
+        sync_deps(path) does the following:
+
+          1. If the directory <path> does not exist, it is created.
+
+          2. If <path>/auto.conf exists, old symbol values are loaded from it,
+             which are then compared against the current symbol values. If a
+             symbol has changed value (would generate different output in
+             autoconf.h compared to before), the change is signaled by
+             touch'ing a file corresponding to the symbol.
+
+             The first time sync_deps() is run on a directory, <path>/auto.conf
+             won't exist, and no old symbol values will be available. This
+             logically has the same effect as updating the entire
+             configuration.
+
+             The path to a symbol's file is calculated from the symbol's name
+             by replacing all '_' with '/' and appending '.h'. For example, the
+             symbol FOO_BAR_BAZ gets the file <path>/foo/bar/baz.h, and FOO
+             gets the file <path>/foo.h.
+
+             This scheme matches the C tools. The point is to avoid having a
+             single directory with a huge number of files, which the underlying
+             filesystem might not handle well.
+
+          3. A new auto.conf with the current symbol values is written, to keep
+             track of them for the next build.
+
+             If auto.conf exists and its contents is identical to what would
+             get written out, it is left untouched. This avoids updating file
+             metadata like the modification time and possibly triggering
+             redundant work in build tools.
+
+
+        The last piece of the puzzle is knowing what symbols each source file
+        depends on. Knowing that, dependencies can be added from source files
+        to the files corresponding to the symbols they depends on. The source
+        file will then get recompiled (only) when the symbol value changes
+        (provided sync_deps() is run first during each build).
+
+        The tool in the kernel that extracts symbol dependencies from source
+        files is scripts/basic/fixdep.c. Missing symbol files also correspond
+        to "not changed", which fixdep deals with by using the $(wildcard) Make
+        function when adding symbol prerequisites to source files.
+
+        In case you need a different scheme for your project, the sync_deps()
+        implementation can be used as a template.
+        """
+        if not exists(path):
+            os.mkdir(path, 0o755)
+
+        # Load old values from auto.conf, if any
+        self._load_old_vals(path)
+
+        for sym in self.unique_defined_syms:
+            # _write_to_conf is determined when the value is calculated. This
+            # is a hidden function call due to property magic.
+            #
+            # Note: In client code, you can check if sym.config_string is empty
+            # instead, to avoid accessing the internal _write_to_conf variable
+            # (though it's likely to keep working).
+            val = sym.str_value
+
+            # n tristate values do not get written to auto.conf and autoconf.h,
+            # making a missing symbol logically equivalent to n
+
+            if sym._write_to_conf:
+                if sym._old_val is None and \
+                   sym.orig_type in _BOOL_TRISTATE and \
+                   val == "n":
+                    # No old value (the symbol was missing or n), new value n.
+                    # No change.
+                    continue
+
+                if val == sym._old_val:
+                    # New value matches old. No change.
+                    continue
+
+            elif sym._old_val is None:
+                # The symbol wouldn't appear in autoconf.h (because
+                # _write_to_conf is false), and it wouldn't have appeared in
+                # autoconf.h previously either (because it didn't appear in
+                # auto.conf). No change.
+                continue
+
+            # 'sym' has a new value. Flag it.
+            _touch_dep_file(path, sym.name)
+
+        # Remember the current values as the "new old" values.
+        #
+        # This call could go anywhere after the call to _load_old_vals(), but
+        # putting it last means _sync_deps() can be safely rerun if it fails
+        # before this point.
+        self._write_old_vals(path)
+
+    def _load_old_vals(self, path):
+        # Loads old symbol values from auto.conf into a dedicated
+        # Symbol._old_val field. Mirrors load_config().
+        #
+        # The extra field could be avoided with some trickery involving dumping
+        # symbol values and restoring them later, but this is simpler and
+        # faster. The C tools also use a dedicated field for this purpose.
+
+        for sym in self.unique_defined_syms:
+            sym._old_val = None
+
+        try:
+            auto_conf = self._open(join(path, "auto.conf"), "r")
+        except EnvironmentError as e:
+            if e.errno == errno.ENOENT:
+                # No old values
+                return
+            raise
+
+        with auto_conf as f:
+            for line in f:
+                match = self._set_match(line)
+                if not match:
+                    # We only expect CONFIG_FOO=... (and possibly a header
+                    # comment) in auto.conf
+                    continue
+
+                name, val = match.groups()
+                if name in self.syms:
+                    sym = self.syms[name]
+
+                    if sym.orig_type is STRING:
+                        match = _conf_string_match(val)
+                        if not match:
+                            continue
+                        val = unescape(match.group(1))
+
+                    self.syms[name]._old_val = val
+                else:
+                    # Flag that the symbol no longer exists, in
+                    # case something still depends on it
+                    _touch_dep_file(path, name)
+
+    def _write_old_vals(self, path):
+        # Helper for writing auto.conf. Basically just a simplified
+        # write_config() that doesn't write any comments (including
+        # '# CONFIG_FOO is not set' comments). The format matches the C
+        # implementation, though the ordering is arbitrary there (depends on
+        # the hash table implementation).
+        #
+        # A separate helper function is neater than complicating write_config()
+        # by passing a flag to it, plus we only need to look at symbols here.
+
+        self._write_if_changed(
+            os.path.join(path, "auto.conf"),
+            self._old_vals_contents())
+
+    def _old_vals_contents(self):
+        # _write_old_vals() helper. Returns the contents to write as a string.
+
+        # Temporary list instead of generator makes this a bit faster
+        return "".join([
+            sym.config_string for sym in self.unique_defined_syms
+                if not (sym.orig_type in _BOOL_TRISTATE and not sym.tri_value)
+        ])
+
+    def node_iter(self, unique_syms=False):
+        """
+        Returns a generator for iterating through all MenuNode's in the Kconfig
+        tree. The iteration is done in Kconfig definition order (each node is
+        visited before its children, and the children of a node are visited
+        before the next node).
+
+        The Kconfig.top_node menu node is skipped. It contains an implicit menu
+        that holds the top-level items.
+
+        As an example, the following code will produce a list equal to
+        Kconfig.defined_syms:
+
+          defined_syms = [node.item for node in kconf.node_iter()
+                          if isinstance(node.item, Symbol)]
+
+        unique_syms (default: False):
+          If True, only the first MenuNode will be included for symbols defined
+          in multiple locations.
+
+          Using kconf.node_iter(True) in the example above would give a list
+          equal to unique_defined_syms.
+        """
+        if unique_syms:
+            for sym in self.unique_defined_syms:
+                sym._visited = False
+
+        node = self.top_node
+        while 1:
+            # Jump to the next node with an iterative tree walk
+            if node.list:
+                node = node.list
+            elif node.next:
+                node = node.next
+            else:
+                while node.parent:
+                    node = node.parent
+                    if node.next:
+                        node = node.next
+                        break
+                else:
+                    # No more nodes
+                    return
+
+            if unique_syms and node.item.__class__ is Symbol:
+                if node.item._visited:
+                    continue
+                node.item._visited = True
+
+            yield node
+
+    def eval_string(self, s):
+        """
+        Returns the tristate value of the expression 's', represented as 0, 1,
+        and 2 for n, m, and y, respectively. Raises KconfigError on syntax
+        errors. Warns if undefined symbols are referenced.
+
+        As an example, if FOO and BAR are tristate symbols at least one of
+        which has the value y, then eval_string("y && (FOO || BAR)") returns
+        2 (y).
+
+        To get the string value of non-bool/tristate symbols, use
+        Symbol.str_value. eval_string() always returns a tristate value, and
+        all non-bool/tristate symbols have the tristate value 0 (n).
+
+        The expression parsing is consistent with how parsing works for
+        conditional ('if ...') expressions in the configuration, and matches
+        the C implementation. m is rewritten to 'm && MODULES', so
+        eval_string("m") will return 0 (n) unless modules are enabled.
+        """
+        # The parser is optimized to be fast when parsing Kconfig files (where
+        # an expression can never appear at the beginning of a line). We have
+        # to monkey-patch things a bit here to reuse it.
+
+        self.filename = None
+
+        self._tokens = self._tokenize("if " + s)
+        # Strip "if " to avoid giving confusing error messages
+        self._line = s
+        self._tokens_i = 1  # Skip the 'if' token
+
+        return expr_value(self._expect_expr_and_eol())
+
+    def unset_values(self):
+        """
+        Removes any user values from all symbols, as if Kconfig.load_config()
+        or Symbol.set_value() had never been called.
+        """
+        self._warn_assign_no_prompt = False
+        try:
+            # set_value() already rejects undefined symbols, and they don't
+            # need to be invalidated (because their value never changes), so we
+            # can just iterate over defined symbols
+            for sym in self.unique_defined_syms:
+                sym.unset_value()
+
+            for choice in self.unique_choices:
+                choice.unset_value()
+        finally:
+            self._warn_assign_no_prompt = True
+
+    def enable_warnings(self):
+        """
+        Do 'Kconfig.warn = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn = True
+
+    def disable_warnings(self):
+        """
+        Do 'Kconfig.warn = False' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn = False
+
+    def enable_stderr_warnings(self):
+        """
+        Do 'Kconfig.warn_to_stderr = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_to_stderr = True
+
+    def disable_stderr_warnings(self):
+        """
+        Do 'Kconfig.warn_to_stderr = False' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_to_stderr = False
+
+    def enable_undef_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_undef = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_assign_undef = True
+
+    def disable_undef_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_undef = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_undef = False
+
+    def enable_override_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_override = True' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_override = True
+
+    def disable_override_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_override = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_override = False
+
+    def enable_redun_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_redun = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_assign_redun = True
+
+    def disable_redun_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_redun = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_redun = False
+
+    def __repr__(self):
+        """
+        Returns a string with information about the Kconfig object when it is
+        evaluated on e.g. the interactive Python prompt.
+        """
+        def status(flag):
+            return "enabled" if flag else "disabled"
+
+        return "<{}>".format(", ".join((
+            "configuration with {} symbols".format(len(self.syms)),
+            'main menu prompt "{}"'.format(self.mainmenu_text),
+            "srctree is current directory" if not self.srctree else
+                'srctree "{}"'.format(self.srctree),
+            'config symbol prefix "{}"'.format(self.config_prefix),
+            "warnings " + status(self.warn),
+            "printing of warnings to stderr " + status(self.warn_to_stderr),
+            "undef. symbol assignment warnings " +
+                status(self.warn_assign_undef),
+            "overriding symbol assignment warnings " +
+                status(self.warn_assign_override),
+            "redundant symbol assignment warnings " +
+                status(self.warn_assign_redun)
+        )))
+
+    #
+    # Private methods
+    #
+
+
+    #
+    # File reading
+    #
+
+    def _open_config(self, filename):
+        # Opens a .config file. First tries to open 'filename', then
+        # '$srctree/filename' if $srctree was set when the configuration was
+        # loaded.
+
+        try:
+            return self._open(filename, "r")
+        except EnvironmentError as e:
+            # This will try opening the same file twice if $srctree is unset,
+            # but it's not a big deal
+            try:
+                return self._open(join(self.srctree, filename), "r")
+            except EnvironmentError as e2:
+                # This is needed for Python 3, because e2 is deleted after
+                # the try block:
+                #
+                # https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
+                e = e2
+
+            raise _KconfigIOError(
+                e, "Could not open '{}' ({}: {}). Check that the $srctree "
+                   "environment variable ({}) is set correctly."
+                   .format(filename, errno.errorcode[e.errno], e.strerror,
+                           "set to '{}'".format(self.srctree) if self.srctree
+                               else "unset or blank"))
+
+    def _enter_file(self, filename):
+        # Jumps to the beginning of a sourced Kconfig file, saving the previous
+        # position and file object.
+        #
+        # filename:
+        #   Absolute path to file
+
+        # Path relative to $srctree, stored in e.g. self.filename (which makes
+        # it indirectly show up in MenuNode.filename). Equals 'filename' for
+        # absolute paths passed to 'source'.
+        if filename.startswith(self._srctree_prefix):
+            # Relative path (or a redundant absolute path to within $srctree,
+            # but it's probably fine to reduce those too)
+            rel_filename = filename[len(self._srctree_prefix):]
+        else:
+            # Absolute path
+            rel_filename = filename
+
+        self.kconfig_filenames.append(rel_filename)
+
+        # The parent Kconfig files are represented as a list of
+        # (<include path>, <Python 'file' object for Kconfig file>) tuples.
+        #
+        # <include path> is immutable and holds a *tuple* of
+        # (<filename>, <linenr>) tuples, giving the locations of the 'source'
+        # statements in the parent Kconfig files. The current include path is
+        # also available in Kconfig._include_path.
+        #
+        # The point of this redundant setup is to allow Kconfig._include_path
+        # to be assigned directly to MenuNode.include_path without having to
+        # copy it, sharing it wherever possible.
+
+        # Save include path and 'file' object (via its 'readline' function)
+        # before entering the file
+        self._filestack.append((self._include_path, self._readline))
+
+        # _include_path is a tuple, so this rebinds the variable instead of
+        # doing in-place modification
+        self._include_path += ((self.filename, self.linenr),)
+
+        # Check for recursive 'source'
+        for name, _ in self._include_path:
+            if name == rel_filename:
+                raise KconfigError(
+                    "\n{}:{}: recursive 'source' of '{}' detected. Check that "
+                    "environment variables are set correctly.\n"
+                    "Include path:\n{}"
+                    .format(self.filename, self.linenr, rel_filename,
+                            "\n".join("{}:{}".format(name, linenr)
+                                      for name, linenr in self._include_path)))
+
+        try:
+            self._readline = self._open(filename, "r").readline
+        except EnvironmentError as e:
+            # We already know that the file exists
+            raise _KconfigIOError(
+                e, "{}:{}: Could not open '{}' (in '{}') ({}: {})"
+                   .format(self.filename, self.linenr, filename,
+                           self._line.strip(),
+                           errno.errorcode[e.errno], e.strerror))
+
+        self.filename = rel_filename
+        self.linenr = 0
+
+    def _leave_file(self):
+        # Returns from a Kconfig file to the file that sourced it. See
+        # _enter_file().
+
+        # Restore location from parent Kconfig file
+        self.filename, self.linenr = self._include_path[-1]
+        # Restore include path and 'file' object
+        self._readline.__self__.close()  # __self__ fetches the 'file' object
+        self._include_path, self._readline = self._filestack.pop()
+
+    def _next_line(self):
+        # Fetches and tokenizes the next line from the current Kconfig file.
+        # Returns False at EOF and True otherwise.
+
+        # We might already have tokens from parsing a line and discovering that
+        # it's part of a different construct
+        if self._reuse_tokens:
+            self._reuse_tokens = False
+            # self._tokens_i is known to be 1 here, because _parse_props()
+            # leaves it like that when it can't recognize a line (or parses a
+            # help text)
+            return True
+
+        # readline() returns '' over and over at EOF, which we rely on for help
+        # texts at the end of files (see _line_after_help())
+        line = self._readline()
+        if not line:
+            return False
+        self.linenr += 1
+
+        # Handle line joining
+        while line.endswith("\\\n"):
+            line = line[:-2] + self._readline()
+            self.linenr += 1
+
+        self._tokens = self._tokenize(line)
+        # Initialize to 1 instead of 0 to factor out code from _parse_block()
+        # and _parse_props(). They immediately fetch self._tokens[0].
+        self._tokens_i = 1
+
+        return True
+
+    def _line_after_help(self, line):
+        # Tokenizes a line after a help text. This case is special in that the
+        # line has already been fetched (to discover that it isn't part of the
+        # help text).
+        #
+        # An earlier version used a _saved_line variable instead that was
+        # checked in _next_line(). This special-casing gets rid of it and makes
+        # _reuse_tokens alone sufficient to handle unget.
+
+        # Handle line joining
+        while line.endswith("\\\n"):
+            line = line[:-2] + self._readline()
+            self.linenr += 1
+
+        self._tokens = self._tokenize(line)
+        self._reuse_tokens = True
+
+    def _write_if_changed(self, filename, contents):
+        # Writes 'contents' into 'filename', but only if it differs from the
+        # current contents of the file.
+        #
+        # Another variant would be write a temporary file on the same
+        # filesystem, compare the files, and rename() the temporary file if it
+        # differs, but it breaks stuff like write_config("/dev/null"), which is
+        # used out there to force evaluation-related warnings to be generated.
+        # This simple version is pretty failsafe and portable.
+        #
+        # Returns True if the file has changed and is updated, and False
+        # otherwise.
+
+        if self._contents_eq(filename, contents):
+            return False
+        with self._open(filename, "w") as f:
+            f.write(contents)
+        return True
+
+    def _contents_eq(self, filename, contents):
+        # Returns True if the contents of 'filename' is 'contents' (a string),
+        # and False otherwise (including if 'filename' can't be opened/read)
+
+        try:
+            with self._open(filename, "r") as f:
+                # Robust re. things like encoding and line endings (mmap()
+                # trickery isn't)
+                return f.read(len(contents) + 1) == contents
+        except EnvironmentError:
+            # If the error here would prevent writing the file as well, we'll
+            # notice it later
+            return False
+
+    #
+    # Tokenization
+    #
+
+    def _lookup_sym(self, name):
+        # Fetches the symbol 'name' from the symbol table, creating and
+        # registering it if it does not exist. If '_parsing_kconfigs' is False,
+        # it means we're in eval_string(), and new symbols won't be registered.
+
+        if name in self.syms:
+            return self.syms[name]
+
+        sym = Symbol()
+        sym.kconfig = self
+        sym.name = name
+        sym.is_constant = False
+        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        if self._parsing_kconfigs:
+            self.syms[name] = sym
+        else:
+            self._warn("no symbol {} in configuration".format(name))
+
+        return sym
+
+    def _lookup_const_sym(self, name):
+        # Like _lookup_sym(), for constant (quoted) symbols
+
+        if name in self.const_syms:
+            return self.const_syms[name]
+
+        sym = Symbol()
+        sym.kconfig = self
+        sym.name = name
+        sym.is_constant = True
+        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        if self._parsing_kconfigs:
+            self.const_syms[name] = sym
+
+        return sym
+
+    def _tokenize(self, s):
+        # Parses 's', returning a None-terminated list of tokens. Registers any
+        # new symbols encountered with _lookup(_const)_sym().
+        #
+        # Tries to be reasonably speedy by processing chunks of text via
+        # regexes and string operations where possible. This is the biggest
+        # hotspot during parsing.
+        #
+        # It might be possible to rewrite this to 'yield' tokens instead,
+        # working across multiple lines. Lookback and compatibility with old
+        # janky versions of the C tools complicate things though.
+
+        self._line = s  # Used for error reporting
+
+        # Initial token on the line
+        match = _command_match(s)
+        if not match:
+            if s.isspace() or s.lstrip().startswith("#"):
+                return (None,)
+            self._parse_error("unknown token at start of line")
+
+        # Tricky implementation detail: While parsing a token, 'token' refers
+        # to the previous token. See _STRING_LEX for why this is needed.
+        token = _get_keyword(match.group(1))
+        if not token:
+            # Backwards compatibility with old versions of the C tools, which
+            # (accidentally) accepted stuff like "--help--" and "-help---".
+            # This was fixed in the C tools by commit c2264564 ("kconfig: warn
+            # of unhandled characters in Kconfig commands"), committed in July
+            # 2015, but it seems people still run Kconfiglib on older kernels.
+            if s.strip(" \t\n-") == "help":
+                return (_T_HELP, None)
+
+            # If the first token is not a keyword (and not a weird help token),
+            # we have a preprocessor variable assignment (or a bare macro on a
+            # line)
+            self._parse_assignment(s)
+            return (None,)
+
+        tokens = [token]
+        # The current index in the string being tokenized
+        i = match.end()
+
+        # Main tokenization loop (for tokens past the first one)
+        while i < len(s):
+            # Test for an identifier/keyword first. This is the most common
+            # case.
+            match = _id_keyword_match(s, i)
+            if match:
+                # We have an identifier or keyword
+
+                # Check what it is. lookup_sym() will take care of allocating
+                # new symbols for us the first time we see them. Note that
+                # 'token' still refers to the previous token.
+
+                name = match.group(1)
+                keyword = _get_keyword(name)
+                if keyword:
+                    # It's a keyword
+                    token = keyword
+                    # Jump past it
+                    i = match.end()
+
+                elif token not in _STRING_LEX:
+                    # It's a non-const symbol, except we translate n, m, and y
+                    # into the corresponding constant symbols, like the C
+                    # implementation
+
+                    if "$" in name:
+                        # Macro expansion within symbol name
+                        name, s, i = self._expand_name(s, i)
+                    else:
+                        i = match.end()
+
+                    token = self.const_syms[name] if name in STR_TO_TRI else \
+                        self._lookup_sym(name)
+
+                else:
+                    # It's a case of missing quotes. For example, the
+                    # following is accepted:
+                    #
+                    #   menu unquoted_title
+                    #
+                    #   config A
+                    #       tristate unquoted_prompt
+                    #
+                    #   endmenu
+                    #
+                    # Named choices ('choice FOO') also end up here.
+
+                    if token not in (_T_CHOICE, _T_CONTCHOICE):
+                        self._warn("style: quotes recommended around '{}' in '{}'"
+                                   .format(name, self._line.strip()),
+                                   self.filename, self.linenr)
+
+                    token = name
+                    i = match.end()
+
+            else:
+                # Neither a keyword nor a non-const symbol
+
+                # We always strip whitespace after tokens, so it is safe to
+                # assume that s[i] is the start of a token here.
+                c = s[i]
+
+                if c in "\"'":
+                    if "$" not in s and "\\" not in s:
+                        # Fast path for lines without $ and \. Find the
+                        # matching quote.
+                        end_i = s.find(c, i + 1) + 1
+                        if not end_i:
+                            self._parse_error("unterminated string")
+                        val = s[i + 1:end_i - 1]
+                        i = end_i
+                    else:
+                        # Slow path
+                        s, end_i = self._expand_str(s, i)
+
+                        # os.path.expandvars() and the $UNAME_RELEASE replace()
+                        # is a backwards compatibility hack, which should be
+                        # reasonably safe as expandvars() leaves references to
+                        # undefined env. vars. as is.
+                        #
+                        # The preprocessor functionality changed how
+                        # environment variables are referenced, to $(FOO).
+                        val = expandvars(s[i + 1:end_i - 1]
+                                         .replace("$UNAME_RELEASE",
+                                                  _UNAME_RELEASE))
+
+                        i = end_i
+
+                    # This is the only place where we don't survive with a
+                    # single token of lookback: 'option env="FOO"' does not
+                    # refer to a constant symbol named "FOO".
+                    token = \
+                        val if token in _STRING_LEX or tokens[0] is _T_OPTION \
+                        else self._lookup_const_sym(val)
+
+                elif s.startswith("&&", i):
+                    token = _T_AND
+                    i += 2
+
+                elif s.startswith("||", i):
+                    token = _T_OR
+                    i += 2
+
+                elif c == "=":
+                    token = _T_EQUAL
+                    i += 1
+
+                elif s.startswith("!=", i):
+                    token = _T_UNEQUAL
+                    i += 2
+
+                elif c == "!":
+                    token = _T_NOT
+                    i += 1
+
+                elif c == "(":
+                    token = _T_OPEN_PAREN
+                    i += 1
+
+                elif c == ")":
+                    token = _T_CLOSE_PAREN
+                    i += 1
+
+                elif c == "#":
+                    break
+
+
+                # Very rare
+
+                elif s.startswith("<=", i):
+                    token = _T_LESS_EQUAL
+                    i += 2
+
+                elif c == "<":
+                    token = _T_LESS
+                    i += 1
+
+                elif s.startswith(">=", i):
+                    token = _T_GREATER_EQUAL
+                    i += 2
+
+                elif c == ">":
+                    token = _T_GREATER
+                    i += 1
+
+
+                else:
+                    self._parse_error("unknown tokens in line")
+
+
+                # Skip trailing whitespace
+                while i < len(s) and s[i].isspace():
+                    i += 1
+
+
+            # Add the token
+            tokens.append(token)
+
+        # None-terminating the token list makes token fetching simpler/faster
+        tokens.append(None)
+
+        return tokens
+
+    # Helpers for syntax checking and token fetching. See the
+    # 'Intro to expressions' section for what a constant symbol is.
+    #
+    # More of these could be added, but the single-use cases are inlined as an
+    # optimization.
+
+    def _expect_sym(self):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is not Symbol:
+            self._parse_error("expected symbol")
+
+        return token
+
+    def _expect_nonconst_sym(self):
+        # Used for 'select' and 'imply' only. We know the token indices.
+
+        token = self._tokens[1]
+        self._tokens_i = 2
+
+        if token.__class__ is not Symbol or token.is_constant:
+            self._parse_error("expected nonconstant symbol")
+
+        return token
+
+    def _expect_str_and_eol(self):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is not str:
+            self._parse_error("expected string")
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return token
+
+    def _expect_expr_and_eol(self):
+        expr = self._parse_expr(True)
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return expr
+
+    def _check_token(self, token):
+        # If the next token is 'token', removes it and returns True
+
+        if self._tokens[self._tokens_i] is token:
+            self._tokens_i += 1
+            return True
+        return False
+
+    #
+    # Preprocessor logic
+    #
+
+    def _parse_assignment(self, s):
+        # Parses a preprocessor variable assignment, registering the variable
+        # if it doesn't already exist. Also takes care of bare macros on lines
+        # (which are allowed, and can be useful for their side effects).
+
+        # Expand any macros in the left-hand side of the assignment (the
+        # variable name)
+        s = s.lstrip()
+        i = 0
+        while 1:
+            i = _assignment_lhs_fragment_match(s, i).end()
+            if s.startswith("$(", i):
+                s, i = self._expand_macro(s, i, ())
+            else:
+                break
+
+        if s.isspace():
+            # We also accept a bare macro on a line (e.g.
+            # $(warning-if,$(foo),ops)), provided it expands to a blank string
+            return
+
+        # Assigned variable
+        name = s[:i]
+
+
+        # Extract assignment operator (=, :=, or +=) and value
+        rhs_match = _assignment_rhs_match(s, i)
+        if not rhs_match:
+            self._parse_error("syntax error")
+
+        op, val = rhs_match.groups()
+
+
+        if name in self.variables:
+            # Already seen variable
+            var = self.variables[name]
+        else:
+            # New variable
+            var = Variable()
+            var.kconfig = self
+            var.name = name
+            var._n_expansions = 0
+            self.variables[name] = var
+
+            # += acts like = on undefined variables (defines a recursive
+            # variable)
+            if op == "+=":
+                op = "="
+
+        if op == "=":
+            var.is_recursive = True
+            var.value = val
+        elif op == ":=":
+            var.is_recursive = False
+            var.value = self._expand_whole(val, ())
+        else:  # op == "+="
+            # += does immediate expansion if the variable was last set
+            # with :=
+            var.value += " " + (val if var.is_recursive else
+                                self._expand_whole(val, ()))
+
+    def _expand_whole(self, s, args):
+        # Expands preprocessor macros in all of 's'. Used whenever we don't
+        # have to worry about delimiters. See _expand_macro() re. the 'args'
+        # parameter.
+        #
+        # Returns the expanded string.
+
+        i = 0
+        while 1:
+            i = s.find("$(", i)
+            if i == -1:
+                break
+            s, i = self._expand_macro(s, i, args)
+        return s
+
+    def _expand_name(self, s, i):
+        # Expands a symbol name starting at index 'i' in 's'.
+        #
+        # Returns the expanded name, the expanded 's' (including the part
+        # before the name), and the index of the first character in the next
+        # token after the name.
+
+        s, end_i = self._expand_name_iter(s, i)
+        name = s[i:end_i]
+        # isspace() is False for empty strings
+        if not name.strip():
+            # Avoid creating a Kconfig symbol with a blank name. It's almost
+            # guaranteed to be an error.
+            self._parse_error("macro expanded to blank string")
+
+        # Skip trailing whitespace
+        while end_i < len(s) and s[end_i].isspace():
+            end_i += 1
+
+        return name, s, end_i
+
+    def _expand_name_iter(self, s, i):
+        # Expands a symbol name starting at index 'i' in 's'.
+        #
+        # Returns the expanded 's' (including the part before the name) and the
+        # index of the first character after the expanded name in 's'.
+
+        while 1:
+            match = _name_special_search(s, i)
+
+            if match.group() != "$(":
+                return (s, match.start())
+            s, i = self._expand_macro(s, match.start(), ())
+
+    def _expand_str(self, s, i):
+        # Expands a quoted string starting at index 'i' in 's'. Handles both
+        # backslash escapes and macro expansion.
+        #
+        # Returns the expanded 's' (including the part before the string) and
+        # the index of the first character after the expanded string in 's'.
+
+        quote = s[i]
+        i += 1  # Skip over initial "/'
+        while 1:
+            match = _string_special_search(s, i)
+            if not match:
+                self._parse_error("unterminated string")
+
+
+            if match.group() == quote:
+                # Found the end of the string
+                return (s, match.end())
+
+            elif match.group() == "\\":
+                # Replace '\x' with 'x'. 'i' ends up pointing to the character
+                # after 'x', which allows macros to be canceled with '\$(foo)'.
+                i = match.end()
+                s = s[:match.start()] + s[i:]
+
+            elif match.group() == "$(":
+                # A macro call within the string
+                s, i = self._expand_macro(s, match.start(), ())
+
+            else:
+                # A ' quote within " quotes or vice versa
+                i += 1
+
+    def _expand_macro(self, s, i, args):
+        # Expands a macro starting at index 'i' in 's'. If this macro resulted
+        # from the expansion of another macro, 'args' holds the arguments
+        # passed to that macro.
+        #
+        # Returns the expanded 's' (including the part before the macro) and
+        # the index of the first character after the expanded macro in 's'.
+
+        res = s[:i]
+        i += 2  # Skip over "$("
+
+        arg_start = i  # Start of current macro argument
+        new_args = []  # Arguments of this macro call
+        nesting = 0  # Current parentheses nesting level
+
+        while 1:
+            match = _macro_special_search(s, i)
+            if not match:
+                self._parse_error("missing end parenthesis in macro expansion")
+
+
+            if match.group() == "(":
+                nesting += 1
+                i = match.end()
+
+            elif match.group() == ")":
+                if nesting:
+                    nesting -= 1
+                    i = match.end()
+                    continue
+
+                # Found the end of the macro
+
+                new_args.append(s[arg_start:match.start()])
+
+                # $(1) is replaced by the first argument to the function, etc.,
+                # provided at least that many arguments were passed
+
+                try:
+                    # Does the macro look like an integer, with a corresponding
+                    # argument? If so, expand it to the value of the argument.
+                    res += args[int(new_args[0])]
+                except (ValueError, IndexError):
+                    # Regular variables are just functions without arguments,
+                    # and also go through the function value path
+                    res += self._fn_val(new_args)
+
+                return (res + s[match.end():], len(res))
+
+            elif match.group() == ",":
+                i = match.end()
+                if nesting:
+                    continue
+
+                # Found the end of a macro argument
+                new_args.append(s[arg_start:match.start()])
+                arg_start = i
+
+            else:  # match.group() == "$("
+                # A nested macro call within the macro
+                s, i = self._expand_macro(s, match.start(), args)
+
+    def _fn_val(self, args):
+        # Returns the result of calling the function args[0] with the arguments
+        # args[1..len(args)-1]. Plain variables are treated as functions
+        # without arguments.
+
+        fn = args[0]
+
+        if fn in self.variables:
+            var = self.variables[fn]
+
+            if len(args) == 1:
+                # Plain variable
+                if var._n_expansions:
+                    self._parse_error("Preprocessor variable {} recursively "
+                                      "references itself".format(var.name))
+            elif var._n_expansions > 100:
+                # Allow functions to call themselves, but guess that functions
+                # that are overly recursive are stuck
+                self._parse_error("Preprocessor function {} seems stuck "
+                                  "in infinite recursion".format(var.name))
+
+            var._n_expansions += 1
+            res = self._expand_whole(self.variables[fn].value, args)
+            var._n_expansions -= 1
+            return res
+
+        if fn in self._functions:
+            # Built-in or user-defined function
+
+            py_fn, min_arg, max_arg = self._functions[fn]
+
+            if len(args) - 1 < min_arg or \
+               (max_arg is not None and len(args) - 1 > max_arg):
+
+                if min_arg == max_arg:
+                    expected_args = min_arg
+                elif max_arg is None:
+                    expected_args = "{} or more".format(min_arg)
+                else:
+                    expected_args = "{}-{}".format(min_arg, max_arg)
+
+                raise KconfigError("{}:{}: bad number of arguments in call "
+                                   "to {}, expected {}, got {}"
+                                   .format(self.filename, self.linenr, fn,
+                                           expected_args, len(args) - 1))
+
+            return py_fn(self, *args)
+
+        # Environment variables are tried last
+        if fn in os.environ:
+            self.env_vars.add(fn)
+            return os.environ[fn]
+
+        return ""
+
+    #
+    # Parsing
+    #
+
+    def _make_and(self, e1, e2):
+        # Constructs an AND (&&) expression. Performs trivial simplification.
+
+        if e1 is self.y:
+            return e2
+
+        if e2 is self.y:
+            return e1
+
+        if e1 is self.n or e2 is self.n:
+            return self.n
+
+        return (AND, e1, e2)
+
+    def _make_or(self, e1, e2):
+        # Constructs an OR (||) expression. Performs trivial simplification.
+
+        if e1 is self.n:
+            return e2
+
+        if e2 is self.n:
+            return e1
+
+        if e1 is self.y or e2 is self.y:
+            return self.y
+
+        return (OR, e1, e2)
+
+    def _parse_block(self, end_token, parent, prev):
+        # Parses a block, which is the contents of either a file or an if,
+        # menu, or choice statement.
+        #
+        # end_token:
+        #   The token that ends the block, e.g. _T_ENDIF ("endif") for ifs.
+        #   None for files.
+        #
+        # parent:
+        #   The parent menu node, corresponding to a menu, Choice, or 'if'.
+        #   'if's are flattened after parsing.
+        #
+        # prev:
+        #   The previous menu node. New nodes will be added after this one (by
+        #   modifying 'next' pointers).
+        #
+        #   'prev' is reused to parse a list of child menu nodes (for a menu or
+        #   Choice): After parsing the children, the 'next' pointer is assigned
+        #   to the 'list' pointer to "tilt up" the children above the node.
+        #
+        # Returns the final menu node in the block (or 'prev' if the block is
+        # empty). This allows chaining.
+
+        while self._next_line():
+            t0 = self._tokens[0]
+
+            if t0 is _T_CONFIG or t0 is _T_MENUCONFIG:
+                # The tokenizer allocates Symbol objects for us
+                sym = self._tokens[1]
+
+                if sym.__class__ is not Symbol or sym.is_constant:
+                    self._parse_error("missing or bad symbol name")
+
+                if self._tokens[2] is not None:
+                    self._trailing_tokens_error()
+
+                self.defined_syms.append(sym)
+
+                node = MenuNode()
+                node.kconfig = self
+                node.item = sym
+                node.is_menuconfig = (t0 is _T_MENUCONFIG)
+                node.prompt = node.help = node.list = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                sym.nodes.append(node)
+
+                self._parse_props(node)
+
+                if node.is_menuconfig and not node.prompt:
+                    self._warn("the menuconfig symbol {} has no prompt"
+                               .format(sym.name_and_loc))
+
+                # Equivalent to
+                #
+                #   prev.next = node
+                #   prev = node
+                #
+                # due to tricky Python semantics. The order matters.
+                prev.next = prev = node
+
+            elif t0 is None:
+                # Blank line
+                continue
+
+            elif t0 in _SOURCE_TOKENS:
+                pattern = self._expect_str_and_eol()
+
+                if t0 in _REL_SOURCE_TOKENS:
+                    # Relative source
+                    pattern = join(dirname(self.filename), pattern)
+
+                # - glob() doesn't support globbing relative to a directory, so
+                #   we need to prepend $srctree to 'pattern'. Use join()
+                #   instead of '+' so that an absolute path in 'pattern' is
+                #   preserved.
+                #
+                # - Sort the glob results to ensure a consistent ordering of
+                #   Kconfig symbols, which indirectly ensures a consistent
+                #   ordering in e.g. .config files
+                filenames = sorted(iglob(join(self._srctree_prefix, pattern)))
+
+                if not filenames and t0 in _OBL_SOURCE_TOKENS:
+                    raise KconfigError(
+                        "{}:{}: '{}' not found (in '{}'). Check that "
+                        "environment variables are set correctly (e.g. "
+                        "$srctree, which is {}). Also note that unset "
+                        "environment variables expand to the empty string."
+                        .format(self.filename, self.linenr, pattern,
+                                self._line.strip(),
+                                "set to '{}'".format(self.srctree)
+                                    if self.srctree else "unset or blank"))
+
+                for filename in filenames:
+                    self._enter_file(filename)
+                    prev = self._parse_block(None, parent, prev)
+                    self._leave_file()
+
+            elif t0 is end_token:
+                # Reached the end of the block. Terminate the final node and
+                # return it.
+
+                if self._tokens[1] is not None:
+                    self._trailing_tokens_error()
+
+                prev.next = None
+                return prev
+
+            elif t0 is _T_IF:
+                node = MenuNode()
+                node.item = node.prompt = None
+                node.parent = parent
+                node.dep = self._expect_expr_and_eol()
+
+                self._parse_block(_T_ENDIF, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_MENU:
+                node = MenuNode()
+                node.kconfig = self
+                node.item = t0  # _T_MENU == MENU
+                node.is_menuconfig = True
+                node.prompt = (self._expect_str_and_eol(), self.y)
+                node.visibility = self.y
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                self.menus.append(node)
+
+                self._parse_props(node)
+                self._parse_block(_T_ENDMENU, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_COMMENT:
+                node = MenuNode()
+                node.kconfig = self
+                node.item = t0  # _T_COMMENT == COMMENT
+                node.is_menuconfig = False
+                node.prompt = (self._expect_str_and_eol(), self.y)
+                node.list = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                self.comments.append(node)
+
+                self._parse_props(node)
+
+                prev.next = prev = node
+
+            elif t0 is _T_CHOICE:
+                if self._tokens[1] is None:
+                    choice = Choice()
+                    choice.direct_dep = self.n
+                else:
+                    # Named choice
+                    name = self._expect_str_and_eol()
+                    choice = self.named_choices.get(name)
+                    if not choice:
+                        choice = Choice()
+                        choice.name = name
+                        choice.direct_dep = self.n
+                        self.named_choices[name] = choice
+
+                self.choices.append(choice)
+
+                node = MenuNode()
+                node.kconfig = choice.kconfig = self
+                node.item = choice
+                node.is_menuconfig = True
+                node.prompt = node.help = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                choice.nodes.append(node)
+
+                self._parse_props(node)
+                self._parse_block(_T_ENDCHOICE, node, node)
+
+                node.list = node.next
+                prev.next = prev = node
+
+            elif t0 is _T_CONTCHOICE:
+                # Named choice
+                name = self._expect_str_and_eol()
+                choice = self.named_choices.get(name)
+                if not choice:
+                    self._parse_error(f"can't continue choice '{name}'")
+
+                assert(len(choice.nodes))
+                # Add more to the earlier node.
+                node = choice.nodes[-1]
+
+                # Find the end of its list so we can add to it.
+                if node.list:
+                    sub_prev = node.list
+                    while sub_prev.next:
+                        sub_prev = sub_prev.next
+                else:
+                    # If we don't have a list at all, temporarily make one up.
+                    sub_prev = MenuNode()
+
+                # Parse any new properties.
+                self._parse_props(node)
+                # Read in new subnodes.
+                self._parse_block(_T_ENDCHOICE, node, sub_prev)
+
+                # If we made up a lead node, move the list to where it belongs.
+                if not node.list:
+                    node.list = sub_prev.next
+
+            elif t0 is _T_MAINMENU:
+                self.top_node.prompt = (self._expect_str_and_eol(), self.y)
+
+            else:
+                # A valid endchoice/endif/endmenu is caught by the 'end_token'
+                # check above
+                self._parse_error(
+                    "no corresponding 'choice'" if t0 is _T_ENDCHOICE else
+                    "no corresponding 'if'"     if t0 is _T_ENDIF else
+                    "no corresponding 'menu'"   if t0 is _T_ENDMENU else
+                    "unrecognized construct")
+
+        # End of file reached. Return the last node.
+
+        if end_token:
+            raise KconfigError(
+                "error: expected '{}' at end of '{}'"
+                .format("endchoice" if end_token is _T_ENDCHOICE else
+                        "endif"     if end_token is _T_ENDIF else
+                        "endmenu",
+                        self.filename))
+
+        return prev
+
+    def _parse_cond(self):
+        # Parses an optional 'if <expr>' construct and returns the parsed
+        # <expr>, or self.y if the next token is not _T_IF
+
+        expr = self._parse_expr(True) if self._check_token(_T_IF) else self.y
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return expr
+
+    def _parse_props(self, node):
+        # Parses and adds properties to the MenuNode 'node' (type, 'prompt',
+        # 'default's, etc.) Properties are later copied up to symbols and
+        # choices in a separate pass after parsing, in e.g.
+        # _add_props_to_sym().
+        #
+        # An older version of this code added properties directly to symbols
+        # and choices instead of to their menu nodes (and handled dependency
+        # propagation simultaneously), but that loses information on where a
+        # property is added when a symbol or choice is defined in multiple
+        # locations. Some Kconfig configuration systems rely heavily on such
+        # symbols, and better docs can be generated by keeping track of where
+        # properties are added.
+        #
+        # node:
+        #   The menu node we're parsing properties on
+
+        # Dependencies from 'depends on'. Will get propagated to the properties
+        # below.
+        node.dep = self.y
+
+        while self._next_line():
+            t0 = self._tokens[0]
+
+            if t0 in _TYPE_TOKENS:
+                # Relies on '_T_BOOL is BOOL', etc., to save a conversion
+                self._set_type(node.item, t0)
+                if self._tokens[1] is not None:
+                    self._parse_prompt(node)
+
+            elif t0 is _T_DEPENDS:
+                if not self._check_token(_T_ON):
+                    self._parse_error("expected 'on' after 'depends'")
+
+                node.dep = self._make_and(node.dep,
+                                          self._expect_expr_and_eol())
+
+            elif t0 is _T_HELP:
+                self._parse_help(node)
+
+            elif t0 is _T_SELECT:
+                if node.item.__class__ is not Symbol:
+                    self._parse_error("only symbols can select")
+
+                node.selects.append((self._expect_nonconst_sym(),
+                                     self._parse_cond()))
+
+            elif t0 is None:
+                # Blank line
+                continue
+
+            elif t0 is _T_DEFAULT:
+                node.defaults.append((self._parse_expr(False),
+                                      self._parse_cond()))
+
+            elif t0 in _DEF_TOKEN_TO_TYPE:
+                self._set_type(node.item, _DEF_TOKEN_TO_TYPE[t0])
+                node.defaults.append((self._parse_expr(False),
+                                      self._parse_cond()))
+
+            elif t0 is _T_PROMPT:
+                self._parse_prompt(node)
+
+            elif t0 is _T_RANGE:
+                node.ranges.append((self._expect_sym(), self._expect_sym(),
+                                    self._parse_cond()))
+
+            elif t0 is _T_IMPLY:
+                if node.item.__class__ is not Symbol:
+                    self._parse_error("only symbols can imply")
+
+                node.implies.append((self._expect_nonconst_sym(),
+                                     self._parse_cond()))
+
+            elif t0 is _T_VISIBLE:
+                if not self._check_token(_T_IF):
+                    self._parse_error("expected 'if' after 'visible'")
+
+                node.visibility = self._make_and(node.visibility,
+                                                 self._expect_expr_and_eol())
+
+            elif t0 is _T_OPTION:
+                if self._check_token(_T_ENV):
+                    if not self._check_token(_T_EQUAL):
+                        self._parse_error("expected '=' after 'env'")
+
+                    env_var = self._expect_str_and_eol()
+                    node.item.env_var = env_var
+
+                    if env_var in os.environ:
+                        node.defaults.append(
+                            (self._lookup_const_sym(os.environ[env_var]),
+                             self.y))
+                    else:
+                        self._warn("{1} has 'option env=\"{0}\"', "
+                                   "but the environment variable {0} is not "
+                                   "set".format(node.item.name, env_var),
+                                   self.filename, self.linenr)
+
+                    if env_var != node.item.name:
+                        self._warn("Kconfiglib expands environment variables "
+                                   "in strings directly, meaning you do not "
+                                   "need 'option env=...' \"bounce\" symbols. "
+                                   "For compatibility with the C tools, "
+                                   "rename {} to {} (so that the symbol name "
+                                   "matches the environment variable name)."
+                                   .format(node.item.name, env_var),
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_DEFCONFIG_LIST):
+                    if not self.defconfig_list:
+                        self.defconfig_list = node.item
+                    else:
+                        self._warn("'option defconfig_list' set on multiple "
+                                   "symbols ({0} and {1}). Only {0} will be "
+                                   "used.".format(self.defconfig_list.name,
+                                                  node.item.name),
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_MODULES):
+                    # To reduce warning spam, only warn if 'option modules' is
+                    # set on some symbol that isn't MODULES, which should be
+                    # safe. I haven't run into any projects that make use
+                    # modules besides the kernel yet, and there it's likely to
+                    # keep being called "MODULES".
+                    if node.item is not self.modules:
+                        self._warn("the 'modules' option is not supported. "
+                                   "Let me know if this is a problem for you, "
+                                   "as it wouldn't be that hard to implement. "
+                                   "Note that modules are supported -- "
+                                   "Kconfiglib just assumes the symbol name "
+                                   "MODULES, like older versions of the C "
+                                   "implementation did when 'option modules' "
+                                   "wasn't used.",
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_ALLNOCONFIG_Y):
+                    if node.item.__class__ is not Symbol:
+                        self._parse_error("the 'allnoconfig_y' option is only "
+                                          "valid for symbols")
+
+                    node.item.is_allnoconfig_y = True
+
+                else:
+                    self._parse_error("unrecognized option")
+
+            elif t0 is _T_OPTIONAL:
+                if node.item.__class__ is not Choice:
+                    self._parse_error('"optional" is only valid for choices')
+
+                node.item.is_optional = True
+
+            else:
+                # Reuse the tokens for the non-property line later
+                self._reuse_tokens = True
+                return
+
+    def _set_type(self, sc, new_type):
+        # Sets the type of 'sc' (symbol or choice) to 'new_type'
+
+        # UNKNOWN is falsy
+        if sc.orig_type and sc.orig_type is not new_type:
+            self._warn("{} defined with multiple types, {} will be used"
+                       .format(sc.name_and_loc, TYPE_TO_STR[new_type]))
+
+        sc.orig_type = new_type
+
+    def _parse_prompt(self, node):
+        # 'prompt' properties override each other within a single definition of
+        # a symbol, but additional prompts can be added by defining the symbol
+        # multiple times
+
+        if node.prompt:
+            self._warn(node.item.name_and_loc +
+                       " defined with multiple prompts in single location")
+
+        prompt = self._tokens[1]
+        self._tokens_i = 2
+
+        if prompt.__class__ is not str:
+            self._parse_error("expected prompt string")
+
+        if prompt != prompt.strip():
+            self._warn(node.item.name_and_loc +
+                       " has leading or trailing whitespace in its prompt")
+
+            # This avoid issues for e.g. reStructuredText documentation, where
+            # '*prompt *' is invalid
+            prompt = prompt.strip()
+
+        node.prompt = (prompt, self._parse_cond())
+
+    def _parse_help(self, node):
+        if node.help is not None:
+            self._warn(node.item.name_and_loc + " defined with more than "
+                       "one help text -- only the last one will be used")
+
+        # Micro-optimization. This code is pretty hot.
+        readline = self._readline
+
+        # Find first non-blank (not all-space) line and get its
+        # indentation
+
+        while 1:
+            line = readline()
+            self.linenr += 1
+            if not line:
+                self._empty_help(node, line)
+                return
+            if not line.isspace():
+                break
+
+        len_ = len  # Micro-optimization
+
+        # Use a separate 'expline' variable here and below to avoid stomping on
+        # any tabs people might've put deliberately into the first line after
+        # the help text
+        expline = line.expandtabs()
+        indent = len_(expline) - len_(expline.lstrip())
+        if not indent:
+            self._empty_help(node, line)
+            return
+
+        # The help text goes on till the first non-blank line with less indent
+        # than the first line
+
+        # Add the first line
+        lines = [expline[indent:]]
+        add_line = lines.append  # Micro-optimization
+
+        while 1:
+            line = readline()
+            if line.isspace():
+                # No need to preserve the exact whitespace in these
+                add_line("\n")
+            elif not line:
+                # End of file
+                break
+            else:
+                expline = line.expandtabs()
+                if len_(expline) - len_(expline.lstrip()) < indent:
+                    break
+                add_line(expline[indent:])
+
+        self.linenr += len_(lines)
+        node.help = "".join(lines).rstrip()
+        if line:
+            self._line_after_help(line)
+
+    def _empty_help(self, node, line):
+        self._warn(node.item.name_and_loc +
+                   " has 'help' but empty help text")
+        node.help = ""
+        if line:
+            self._line_after_help(line)
+
+    def _parse_expr(self, transform_m):
+        # Parses an expression from the tokens in Kconfig._tokens using a
+        # simple top-down approach. See the module docstring for the expression
+        # format.
+        #
+        # transform_m:
+        #   True if m should be rewritten to m && MODULES. See the
+        #   Kconfig.eval_string() documentation.
+
+        # Grammar:
+        #
+        #   expr:     and_expr ['||' expr]
+        #   and_expr: factor ['&&' and_expr]
+        #   factor:   <symbol> ['='/'!='/'<'/... <symbol>]
+        #             '!' factor
+        #             '(' expr ')'
+        #
+        # It helps to think of the 'expr: and_expr' case as a single-operand OR
+        # (no ||), and of the 'and_expr: factor' case as a single-operand AND
+        # (no &&). Parsing code is always a bit tricky.
+
+        # Mind dump: parse_factor() and two nested loops for OR and AND would
+        # work as well. The straightforward implementation there gives a
+        # (op, (op, (op, A, B), C), D) parse for A op B op C op D. Representing
+        # expressions as (op, [list of operands]) instead goes nicely with that
+        # version, but is wasteful for short expressions and complicates
+        # expression evaluation and other code that works on expressions (more
+        # complicated code likely offsets any performance gain from less
+        # recursion too). If we also try to optimize the list representation by
+        # merging lists when possible (e.g. when ANDing two AND expressions),
+        # we end up allocating a ton of lists instead of reusing expressions,
+        # which is bad.
+
+        and_expr = self._parse_and_expr(transform_m)
+
+        # Return 'and_expr' directly if we have a "single-operand" OR.
+        # Otherwise, parse the expression on the right and make an OR node.
+        # This turns A || B || C || D into (OR, A, (OR, B, (OR, C, D))).
+        return and_expr if not self._check_token(_T_OR) else \
+            (OR, and_expr, self._parse_expr(transform_m))
+
+    def _parse_and_expr(self, transform_m):
+        factor = self._parse_factor(transform_m)
+
+        # Return 'factor' directly if we have a "single-operand" AND.
+        # Otherwise, parse the right operand and make an AND node. This turns
+        # A && B && C && D into (AND, A, (AND, B, (AND, C, D))).
+        return factor if not self._check_token(_T_AND) else \
+            (AND, factor, self._parse_and_expr(transform_m))
+
+    def _parse_factor(self, transform_m):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is Symbol:
+            # Plain symbol or relation
+
+            if self._tokens[self._tokens_i] not in _RELATIONS:
+                # Plain symbol
+
+                # For conditional expressions ('depends on <expr>',
+                # '... if <expr>', etc.), m is rewritten to m && MODULES.
+                if transform_m and token is self.m:
+                    return (AND, self.m, self.modules)
+
+                return token
+
+            # Relation
+            #
+            # _T_EQUAL, _T_UNEQUAL, etc., deliberately have the same values as
+            # EQUAL, UNEQUAL, etc., so we can just use the token directly
+            self._tokens_i += 1
+            return (self._tokens[self._tokens_i - 1], token,
+                    self._expect_sym())
+
+        if token is _T_NOT:
+            # token == _T_NOT == NOT
+            return (token, self._parse_factor(transform_m))
+
+        if token is _T_OPEN_PAREN:
+            expr_parse = self._parse_expr(transform_m)
+            if self._check_token(_T_CLOSE_PAREN):
+                return expr_parse
+
+        self._parse_error("malformed expression")
+
+    #
+    # Caching and invalidation
+    #
+
+    def _build_dep(self):
+        # Populates the Symbol/Choice._dependents sets, which contain all other
+        # items (symbols and choices) that immediately depend on the item in
+        # the sense that changing the value of the item might affect the value
+        # of the dependent items. This is used for caching/invalidation.
+        #
+        # The calculated sets might be larger than necessary as we don't do any
+        # complex analysis of the expressions.
+
+        depend_on = _depend_on  # Micro-optimization
+
+        # Only calculate _dependents for defined symbols. Constant and
+        # undefined symbols could theoretically be selected/implied, but it
+        # wouldn't change their value, so it's not a true dependency.
+        for sym in self.unique_defined_syms:
+            # Symbols depend on the following:
+
+            # The prompt conditions
+            for node in sym.nodes:
+                if node.prompt:
+                    depend_on(sym, node.prompt[1])
+
+            # The default values and their conditions
+            for value, cond in sym.defaults:
+                depend_on(sym, value)
+                depend_on(sym, cond)
+
+            # The reverse and weak reverse dependencies
+            depend_on(sym, sym.rev_dep)
+            depend_on(sym, sym.weak_rev_dep)
+
+            # The ranges along with their conditions
+            for low, high, cond in sym.ranges:
+                depend_on(sym, low)
+                depend_on(sym, high)
+                depend_on(sym, cond)
+
+            # The direct dependencies. This is usually redundant, as the direct
+            # dependencies get propagated to properties, but it's needed to get
+            # invalidation solid for 'imply', which only checks the direct
+            # dependencies (even if there are no properties to propagate it
+            # to).
+            depend_on(sym, sym.direct_dep)
+
+            # In addition to the above, choice symbols depend on the choice
+            # they're in, but that's handled automatically since the Choice is
+            # propagated to the conditions of the properties before
+            # _build_dep() runs.
+
+        for choice in self.unique_choices:
+            # Choices depend on the following:
+
+            # The prompt conditions
+            for node in choice.nodes:
+                if node.prompt:
+                    depend_on(choice, node.prompt[1])
+
+            # The default symbol conditions
+            for _, cond in choice.defaults:
+                depend_on(choice, cond)
+
+    def _add_choice_deps(self):
+        # Choices also depend on the choice symbols themselves, because the
+        # y-mode selection of the choice might change if a choice symbol's
+        # visibility changes.
+        #
+        # We add these dependencies separately after dependency loop detection.
+        # The invalidation algorithm can handle the resulting
+        # <choice symbol> <-> <choice> dependency loops, but they make loop
+        # detection awkward.
+
+        for choice in self.unique_choices:
+            for sym in choice.syms:
+                sym._dependents.add(choice)
+
+    def _invalidate_all(self):
+        # Undefined symbols never change value and don't need to be
+        # invalidated, so we can just iterate over defined symbols.
+        # Invalidating constant symbols would break things horribly.
+        for sym in self.unique_defined_syms:
+            sym._invalidate()
+
+        for choice in self.unique_choices:
+            choice._invalidate()
+
+    #
+    # Post-parsing menu tree processing, including dependency propagation and
+    # implicit submenu creation
+    #
+
+    def _finalize_node(self, node, visible_if):
+        # Finalizes a menu node and its children:
+        #
+        #  - Copies properties from menu nodes up to their contained
+        #    symbols/choices
+        #
+        #  - Propagates dependencies from parent to child nodes
+        #
+        #  - Creates implicit menus (see kconfig-language.txt)
+        #
+        #  - Removes 'if' nodes
+        #
+        #  - Sets 'choice' types and registers choice symbols
+        #
+        # menu_finalize() in the C implementation is similar.
+        #
+        # node:
+        #   The menu node to finalize. This node and its children will have
+        #   been finalized when the function returns, and any implicit menus
+        #   will have been created.
+        #
+        # visible_if:
+        #   Dependencies from 'visible if' on parent menus. These are added to
+        #   the prompts of symbols and choices.
+
+        if node.item.__class__ is Symbol:
+            # Copy defaults, ranges, selects, and implies to the Symbol
+            self._add_props_to_sym(node)
+
+            # Find any items that should go in an implicit menu rooted at the
+            # symbol
+            cur = node
+            while cur.next and _auto_menu_dep(node, cur.next):
+                # This makes implicit submenu creation work recursively, with
+                # implicit menus inside implicit menus
+                self._finalize_node(cur.next, visible_if)
+                cur = cur.next
+                cur.parent = node
+
+            if cur is not node:
+                # Found symbols that should go in an implicit submenu. Tilt
+                # them up above us.
+                node.list = node.next
+                node.next = cur.next
+                cur.next = None
+
+        elif node.list:
+            # The menu node is a choice, menu, or if. Finalize each child node.
+
+            if node.item is MENU:
+                visible_if = self._make_and(visible_if, node.visibility)
+
+            # Propagate the menu node's dependencies to each child menu node.
+            #
+            # This needs to go before the recursive _finalize_node() call so
+            # that implicit submenu creation can look ahead at dependencies.
+            self._propagate_deps(node, visible_if)
+
+            # Finalize the children
+            cur = node.list
+            while cur:
+                self._finalize_node(cur, visible_if)
+                cur = cur.next
+
+        if node.list:
+            # node's children have been individually finalized. Do final steps
+            # to finalize this "level" in the menu tree.
+            _flatten(node.list)
+            _remove_ifs(node)
+
+        # Empty choices (node.list None) are possible, so this needs to go
+        # outside
+        if node.item.__class__ is Choice:
+            # Add the node's non-node-specific properties to the choice, like
+            # _add_props_to_sym() does
+            choice = node.item
+            choice.direct_dep = self._make_or(choice.direct_dep, node.dep)
+            choice.defaults += node.defaults
+
+            _finalize_choice(node)
+
+    def _propagate_deps(self, node, visible_if):
+        # Propagates 'node's dependencies to its child menu nodes
+
+        # If the parent node holds a Choice, we use the Choice itself as the
+        # parent dependency. This makes sense as the value (mode) of the choice
+        # limits the visibility of the contained choice symbols. The C
+        # implementation works the same way.
+        #
+        # Due to the similar interface, Choice works as a drop-in replacement
+        # for Symbol here.
+        basedep = node.item if node.item.__class__ is Choice else node.dep
+
+        cur = node.list
+        while cur:
+            dep = cur.dep = self._make_and(cur.dep, basedep)
+
+            if cur.item.__class__ in _SYMBOL_CHOICE:
+                # Propagate 'visible if' and dependencies to the prompt
+                if cur.prompt:
+                    cur.prompt = (cur.prompt[0],
+                                  self._make_and(
+                                      cur.prompt[1],
+                                      self._make_and(visible_if, dep)))
+
+                # Propagate dependencies to defaults
+                if cur.defaults:
+                    cur.defaults = [(default, self._make_and(cond, dep))
+                                    for default, cond in cur.defaults]
+
+                # Propagate dependencies to ranges
+                if cur.ranges:
+                    cur.ranges = [(low, high, self._make_and(cond, dep))
+                                  for low, high, cond in cur.ranges]
+
+                # Propagate dependencies to selects
+                if cur.selects:
+                    cur.selects = [(target, self._make_and(cond, dep))
+                                   for target, cond in cur.selects]
+
+                # Propagate dependencies to implies
+                if cur.implies:
+                    cur.implies = [(target, self._make_and(cond, dep))
+                                   for target, cond in cur.implies]
+
+            elif cur.prompt:  # Not a symbol/choice
+                # Propagate dependencies to the prompt. 'visible if' is only
+                # propagated to symbols/choices.
+                cur.prompt = (cur.prompt[0],
+                              self._make_and(cur.prompt[1], dep))
+
+            cur = cur.next
+
+    def _add_props_to_sym(self, node):
+        # Copies properties from the menu node 'node' up to its contained
+        # symbol, and adds (weak) reverse dependencies to selected/implied
+        # symbols.
+        #
+        # This can't be rolled into _propagate_deps(), because that function
+        # traverses the menu tree roughly breadth-first, meaning properties on
+        # symbols defined in multiple locations could end up in the wrong
+        # order.
+
+        sym = node.item
+
+        # See the Symbol class docstring
+        sym.direct_dep = self._make_or(sym.direct_dep, node.dep)
+
+        sym.defaults += node.defaults
+        sym.ranges += node.ranges
+        sym.selects += node.selects
+        sym.implies += node.implies
+
+        # Modify the reverse dependencies of the selected symbol
+        for target, cond in node.selects:
+            target.rev_dep = self._make_or(
+                target.rev_dep,
+                self._make_and(sym, cond))
+
+        # Modify the weak reverse dependencies of the implied
+        # symbol
+        for target, cond in node.implies:
+            target.weak_rev_dep = self._make_or(
+                target.weak_rev_dep,
+                self._make_and(sym, cond))
+
+    #
+    # Misc.
+    #
+
+    def _check_sym_sanity(self):
+        # Checks various symbol properties that are handiest to check after
+        # parsing. Only generates errors and warnings.
+
+        def num_ok(sym, type_):
+            # Returns True if the (possibly constant) symbol 'sym' is valid as a value
+            # for a symbol of type type_ (INT or HEX)
+
+            # 'not sym.nodes' implies a constant or undefined symbol, e.g. a plain
+            # "123"
+            if not sym.nodes:
+                return _is_base_n(sym.name, _TYPE_TO_BASE[type_])
+
+            return sym.orig_type is type_
+
+        for sym in self.unique_defined_syms:
+            if sym.orig_type in _BOOL_TRISTATE:
+                # A helper function could be factored out here, but keep it
+                # speedy/straightforward
+
+                for target_sym, _ in sym.selects:
+                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
+                        self._warn("{} selects the {} symbol {}, which is not "
+                                   "bool or tristate"
+                                   .format(sym.name_and_loc,
+                                           TYPE_TO_STR[target_sym.orig_type],
+                                           target_sym.name_and_loc))
+
+                for target_sym, _ in sym.implies:
+                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
+                        self._warn("{} implies the {} symbol {}, which is not "
+                                   "bool or tristate"
+                                   .format(sym.name_and_loc,
+                                           TYPE_TO_STR[target_sym.orig_type],
+                                           target_sym.name_and_loc))
+
+            elif sym.orig_type:  # STRING/INT/HEX
+                for default, _ in sym.defaults:
+                    if default.__class__ is not Symbol:
+                        raise KconfigError(
+                            "the {} symbol {} has a malformed default {} -- "
+                            "expected a single symbol"
+                            .format(TYPE_TO_STR[sym.orig_type],
+                                    sym.name_and_loc, expr_str(default)))
+
+                    if sym.orig_type is STRING:
+                        if not default.is_constant and not default.nodes and \
+                           not default.name.isupper():
+                            # 'default foo' on a string symbol could be either a symbol
+                            # reference or someone leaving out the quotes. Guess that
+                            # the quotes were left out if 'foo' isn't all-uppercase
+                            # (and no symbol named 'foo' exists).
+                            self._warn("style: quotes recommended around "
+                                       "default value for string symbol "
+                                       + sym.name_and_loc)
+
+                    elif not num_ok(default, sym.orig_type):  # INT/HEX
+                        self._warn("the {0} symbol {1} has a non-{0} default {2}"
+                                   .format(TYPE_TO_STR[sym.orig_type],
+                                           sym.name_and_loc,
+                                           default.name_and_loc))
+
+                if sym.selects or sym.implies:
+                    self._warn("the {} symbol {} has selects or implies"
+                               .format(TYPE_TO_STR[sym.orig_type],
+                                       sym.name_and_loc))
+
+            else:  # UNKNOWN
+                self._warn("{} defined without a type"
+                           .format(sym.name_and_loc))
+
+
+            if sym.ranges:
+                if sym.orig_type not in _INT_HEX:
+                    self._warn(
+                        "the {} symbol {} has ranges, but is not int or hex"
+                        .format(TYPE_TO_STR[sym.orig_type],
+                                sym.name_and_loc))
+                else:
+                    for low, high, _ in sym.ranges:
+                        if not num_ok(low, sym.orig_type) or \
+                           not num_ok(high, sym.orig_type):
+
+                            self._warn("the {0} symbol {1} has a non-{0} "
+                                       "range [{2}, {3}]"
+                                       .format(TYPE_TO_STR[sym.orig_type],
+                                               sym.name_and_loc,
+                                               low.name_and_loc,
+                                               high.name_and_loc))
+
+    def _check_choice_sanity(self):
+        # Checks various choice properties that are handiest to check after
+        # parsing. Only generates errors and warnings.
+
+        def warn_select_imply(sym, expr, expr_type):
+            msg = "the choice symbol {} is {} by the following symbols, but " \
+                  "select/imply has no effect on choice symbols" \
+                  .format(sym.name_and_loc, expr_type)
+
+            # si = select/imply
+            for si in split_expr(expr, OR):
+                msg += "\n - " + split_expr(si, AND)[0].name_and_loc
+
+            self._warn(msg)
+
+        for choice in self.unique_choices:
+            if choice.orig_type not in _BOOL_TRISTATE:
+                self._warn("{} defined with type {}"
+                           .format(choice.name_and_loc,
+                                   TYPE_TO_STR[choice.orig_type]))
+
+            for node in choice.nodes:
+                if node.prompt:
+                    break
+            else:
+                self._warn(choice.name_and_loc + " defined without a prompt")
+
+            for default, _ in choice.defaults:
+                if default.__class__ is not Symbol:
+                    raise KconfigError(
+                        "{} has a malformed default {}"
+                        .format(choice.name_and_loc, expr_str(default)))
+
+                if default.choice is not choice:
+                    self._warn("the default selection {} of {} is not "
+                               "contained in the choice"
+                               .format(default.name_and_loc,
+                                       choice.name_and_loc))
+
+            for sym in choice.syms:
+                if sym.defaults:
+                    self._warn("default on the choice symbol {} will have "
+                               "no effect, as defaults do not affect choice "
+                               "symbols".format(sym.name_and_loc))
+
+                if sym.rev_dep is not sym.kconfig.n:
+                    warn_select_imply(sym, sym.rev_dep, "selected")
+
+                if sym.weak_rev_dep is not sym.kconfig.n:
+                    warn_select_imply(sym, sym.weak_rev_dep, "implied")
+
+                for node in sym.nodes:
+                    if node.parent.item is choice:
+                        if not node.prompt:
+                            self._warn("the choice symbol {} has no prompt"
+                                       .format(sym.name_and_loc))
+
+                    elif node.prompt:
+                        self._warn("the choice symbol {} is defined with a "
+                                   "prompt outside the choice"
+                                   .format(sym.name_and_loc))
+
+    def _parse_error(self, msg):
+        raise KconfigError("{}error: couldn't parse '{}': {}".format(
+            "" if self.filename is None else
+                "{}:{}: ".format(self.filename, self.linenr),
+            self._line.strip(), msg))
+
+    def _trailing_tokens_error(self):
+        self._parse_error("extra tokens at end of line")
+
+    def _open(self, filename, mode):
+        # open() wrapper:
+        #
+        # - Enable universal newlines mode on Python 2 to ease
+        #   interoperability between Linux and Windows. It's already the
+        #   default on Python 3.
+        #
+        #   The "U" flag would currently work for both Python 2 and 3, but it's
+        #   deprecated on Python 3, so play it future-safe.
+        #
+        #   io.open() defaults to universal newlines on Python 2 (and is an
+        #   alias for open() on Python 3), but it returns 'unicode' strings and
+        #   slows things down:
+        #
+        #     Parsing x86 Kconfigs on Python 2
+        #
+        #     with open(..., "rU"):
+        #
+        #       real  0m0.930s
+        #       user  0m0.905s
+        #       sys   0m0.025s
+        #
+        #     with io.open():
+        #
+        #       real  0m1.069s
+        #       user  0m1.040s
+        #       sys   0m0.029s
+        #
+        #   There's no appreciable performance difference between "r" and
+        #   "rU" for parsing performance on Python 2.
+        #
+        # - For Python 3, force the encoding. Forcing the encoding on Python 2
+        #   turns strings into Unicode strings, which gets messy. Python 2
+        #   doesn't decode regular strings anyway.
+        return open(filename, "rU" if mode == "r" else mode) if _IS_PY2 else \
+               open(filename, mode, encoding=self._encoding)
+
+    def _check_undef_syms(self):
+        # Prints warnings for all references to undefined symbols within the
+        # Kconfig files
+
+        def is_num(s):
+            # Returns True if the string 's' looks like a number.
+            #
+            # Internally, all operands in Kconfig are symbols, only undefined symbols
+            # (which numbers usually are) get their name as their value.
+            #
+            # Only hex numbers that start with 0x/0X are classified as numbers.
+            # Otherwise, symbols whose names happen to contain only the letters A-F
+            # would trigger false positives.
+
+            try:
+                int(s)
+            except ValueError:
+                if not s.startswith(("0x", "0X")):
+                    return False
+
+                try:
+                    int(s, 16)
+                except ValueError:
+                    return False
+
+            return True
+
+        for sym in (self.syms.viewvalues if _IS_PY2 else self.syms.values)():
+            # - sym.nodes empty means the symbol is undefined (has no
+            #   definition locations)
+            #
+            # - Due to Kconfig internals, numbers show up as undefined Kconfig
+            #   symbols, but shouldn't be flagged
+            #
+            # - The MODULES symbol always exists
+            if not sym.nodes and not is_num(sym.name) and \
+               sym.name != "MODULES":
+
+                msg = "undefined symbol {}:".format(sym.name)
+                for node in self.node_iter():
+                    if sym in node.referenced:
+                        msg += "\n\n- Referenced at {}:{}:\n\n{}" \
+                               .format(node.filename, node.linenr, node)
+                self._warn(msg)
+
+    def _warn(self, msg, filename=None, linenr=None):
+        # For printing general warnings
+
+        if not self.warn:
+            return
+
+        msg = "warning: " + msg
+        if filename is not None:
+            msg = "{}:{}: {}".format(filename, linenr, msg)
+
+        self.warnings.append(msg)
+        if self.warn_to_stderr:
+            sys.stderr.write(msg + "\n")
+
+
+class Symbol(object):
+    """
+    Represents a configuration symbol:
+
+      (menu)config FOO
+          ...
+
+    The following attributes are available. They should be viewed as read-only,
+    and some are implemented through @property magic (but are still efficient
+    to access due to internal caching).
+
+    Note: Prompts, help texts, and locations are stored in the Symbol's
+    MenuNode(s) rather than in the Symbol itself. Check the MenuNode class and
+    the Symbol.nodes attribute. This organization matches the C tools.
+
+    name:
+      The name of the symbol, e.g. "FOO" for 'config FOO'.
+
+    type:
+      The type of the symbol. One of BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN.
+      UNKNOWN is for undefined symbols, (non-special) constant symbols, and
+      symbols defined without a type.
+
+      When running without modules (MODULES having the value n), TRISTATE
+      symbols magically change type to BOOL. This also happens for symbols
+      within choices in "y" mode. This matches the C tools, and makes sense for
+      menuconfig-like functionality.
+
+    orig_type:
+      The type as given in the Kconfig file, without any magic applied. Used
+      when printing the symbol.
+
+    tri_value:
+      The tristate value of the symbol as an integer. One of 0, 1, 2,
+      representing n, m, y. Always 0 (n) for non-bool/tristate symbols.
+
+      This is the symbol value that's used outside of relation expressions
+      (A, !A, A && B, A || B).
+
+    str_value:
+      The value of the symbol as a string. Gives the value for string/int/hex
+      symbols. For bool/tristate symbols, gives "n", "m", or "y".
+
+      This is the symbol value that's used in relational expressions
+      (A = B, A != B, etc.)
+
+      Gotcha: For int/hex symbols, the exact format of the value is often
+      preserved (e.g. when writing a .config file), hence why you can't get it
+      directly as an int. Do int(int_sym.str_value) or
+      int(hex_sym.str_value, 16) to get the integer value.
+
+    user_value:
+      The user value of the symbol. None if no user value has been assigned
+      (via Kconfig.load_config() or Symbol.set_value()).
+
+      Holds 0, 1, or 2 for bool/tristate symbols, and a string for the other
+      symbol types.
+
+      WARNING: Do not assign directly to this. It will break things. Use
+      Symbol.set_value().
+
+    assignable:
+      A tuple containing the tristate user values that can currently be
+      assigned to the symbol (that would be respected), ordered from lowest (0,
+      representing n) to highest (2, representing y). This corresponds to the
+      selections available in the menuconfig interface. The set of assignable
+      values is calculated from the symbol's visibility and selects/implies.
+
+      Returns the empty set for non-bool/tristate symbols and for symbols with
+      visibility n. The other possible values are (0, 2), (0, 1, 2), (1, 2),
+      (1,), and (2,). A (1,) or (2,) result means the symbol is visible but
+      "locked" to m or y through a select, perhaps in combination with the
+      visibility. menuconfig represents this as -M- and -*-, respectively.
+
+      For string/hex/int symbols, check if Symbol.visibility is non-0 (non-n)
+      instead to determine if the value can be changed.
+
+      Some handy 'assignable' idioms:
+
+        # Is 'sym' an assignable (visible) bool/tristate symbol?
+        if sym.assignable:
+            # What's the highest value it can be assigned? [-1] in Python
+            # gives the last element.
+            sym_high = sym.assignable[-1]
+
+            # The lowest?
+            sym_low = sym.assignable[0]
+
+            # Can the symbol be set to at least m?
+            if sym.assignable[-1] >= 1:
+                ...
+
+        # Can the symbol be set to m?
+        if 1 in sym.assignable:
+            ...
+
+    visibility:
+      The visibility of the symbol. One of 0, 1, 2, representing n, m, y. See
+      the module documentation for an overview of symbol values and visibility.
+
+    config_string:
+      The .config assignment string that would get written out for the symbol
+      by Kconfig.write_config(). Returns the empty string if no .config
+      assignment would get written out.
+
+      In general, visible symbols, symbols with (active) defaults, and selected
+      symbols get written out. This includes all non-n-valued bool/tristate
+      symbols, and all visible string/int/hex symbols.
+
+      Symbols with the (no longer needed) 'option env=...' option generate no
+      configuration output, and neither does the special
+      'option defconfig_list' symbol.
+
+      Tip: This field is useful when generating custom configuration output,
+      even for non-.config-like formats. To write just the symbols that would
+      get written out to .config files, do this:
+
+        if sym.config_string:
+            *Write symbol, e.g. by looking sym.str_value*
+
+      This is a superset of the symbols written out by write_autoconf().
+      That function skips all n-valued symbols.
+
+      There usually won't be any great harm in just writing all symbols either,
+      though you might get some special symbols and possibly some "redundant"
+      n-valued symbol entries in there.
+
+    name_and_loc:
+      Holds a string like
+
+        "MY_SYMBOL (defined at foo/Kconfig:12, bar/Kconfig:14)"
+
+      , giving the name of the symbol and its definition location(s).
+
+      If the symbol is undefined, the location is given as "(undefined)".
+
+    nodes:
+      A list of MenuNodes for this symbol. Will contain a single MenuNode for
+      most symbols. Undefined and constant symbols have an empty nodes list.
+      Symbols defined in multiple locations get one node for each location.
+
+    choice:
+      Holds the parent Choice for choice symbols, and None for non-choice
+      symbols. Doubles as a flag for whether a symbol is a choice symbol.
+
+    defaults:
+      List of (default, cond) tuples for the symbol's 'default' properties. For
+      example, 'default A && B if C || D' is represented as
+      ((AND, A, B), (OR, C, D)). If no condition was given, 'cond' is
+      self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to
+      'default' conditions.
+
+    selects:
+      List of (symbol, cond) tuples for the symbol's 'select' properties. For
+      example, 'select A if B && C' is represented as (A, (AND, B, C)). If no
+      condition was given, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to 'select'
+      conditions.
+
+    implies:
+      Like 'selects', for imply.
+
+    ranges:
+      List of (low, high, cond) tuples for the symbol's 'range' properties. For
+      example, 'range 1 2 if A' is represented as (1, 2, A). If there is no
+      condition, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to 'range'
+      conditions.
+
+      Gotcha: 1 and 2 above will be represented as (undefined) Symbols rather
+      than plain integers. Undefined symbols get their name as their string
+      value, so this works out. The C tools work the same way.
+
+    orig_defaults:
+    orig_selects:
+    orig_implies:
+    orig_ranges:
+      See the corresponding attributes on the MenuNode class.
+
+    rev_dep:
+      Reverse dependency expression from other symbols selecting this symbol.
+      Multiple selections get ORed together. A condition on a select is ANDed
+      with the selecting symbol.
+
+      For example, if A has 'select FOO' and B has 'select FOO if C', then
+      FOO's rev_dep will be (OR, A, (AND, B, C)).
+
+    weak_rev_dep:
+      Like rev_dep, for imply.
+
+    direct_dep:
+      The direct ('depends on') dependencies for the symbol, or self.kconfig.y
+      if there are no direct dependencies.
+
+      This attribute includes any dependencies from surrounding menus and ifs.
+      Those get propagated to the direct dependencies, and the resulting direct
+      dependencies in turn get propagated to the conditions of all properties.
+
+      If the symbol is defined in multiple locations, the dependencies from the
+      different locations get ORed together.
+
+    referenced:
+      A set() with all symbols and choices referenced in the properties and
+      property conditions of the symbol.
+
+      Also includes dependencies from surrounding menus and ifs, because those
+      get propagated to the symbol (see the 'Intro to symbol values' section in
+      the module docstring).
+
+      Choices appear in the dependencies of choice symbols.
+
+      For the following definitions, only B and not C appears in A's
+      'referenced'. To get transitive references, you'll have to recursively
+      expand 'references' until no new items appear.
+
+        config A
+                bool
+                depends on B
+
+        config B
+                bool
+                depends on C
+
+        config C
+                bool
+
+      See the Symbol.direct_dep attribute if you're only interested in the
+      direct dependencies of the symbol (its 'depends on'). You can extract the
+      symbols in it with the global expr_items() function.
+
+    env_var:
+      If the Symbol has an 'option env="FOO"' option, this contains the name
+      ("FOO") of the environment variable. None for symbols without no
+      'option env'.
+
+      'option env="FOO"' acts like a 'default' property whose value is the
+      value of $FOO.
+
+      Symbols with 'option env' are never written out to .config files, even if
+      they are visible. env_var corresponds to a flag called SYMBOL_AUTO in the
+      C implementation.
+
+    is_allnoconfig_y:
+      True if the symbol has 'option allnoconfig_y' set on it. This has no
+      effect internally (except when printing symbols), but can be checked by
+      scripts.
+
+    is_constant:
+      True if the symbol is a constant (quoted) symbol.
+
+    kconfig:
+      The Kconfig instance this symbol is from.
+    """
+    __slots__ = (
+        "_cached_assignable",
+        "_cached_str_val",
+        "_cached_tri_val",
+        "_cached_vis",
+        "_dependents",
+        "_old_val",
+        "_visited",
+        "_was_set",
+        "_write_to_conf",
+        "choice",
+        "defaults",
+        "direct_dep",
+        "env_var",
+        "implies",
+        "is_allnoconfig_y",
+        "is_constant",
+        "kconfig",
+        "name",
+        "nodes",
+        "orig_type",
+        "ranges",
+        "rev_dep",
+        "selects",
+        "user_value",
+        "weak_rev_dep",
+    )
+
+    #
+    # Public interface
+    #
+
+    @property
+    def type(self):
+        """
+        See the class documentation.
+        """
+        if self.orig_type is TRISTATE and \
+           (self.choice and self.choice.tri_value == 2 or
+            not self.kconfig.modules.tri_value):
+
+            return BOOL
+
+        return self.orig_type
+
+    @property
+    def str_value(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_str_val is not None:
+            return self._cached_str_val
+
+        if self.orig_type in _BOOL_TRISTATE:
+            # Also calculates the visibility, so invalidation safe
+            self._cached_str_val = TRI_TO_STR[self.tri_value]
+            return self._cached_str_val
+
+        # As a quirk of Kconfig, undefined symbols get their name as their
+        # string value. This is why things like "FOO = bar" work for seeing if
+        # FOO has the value "bar".
+        if not self.orig_type:  # UNKNOWN
+            self._cached_str_val = self.name
+            return self.name
+
+        val = ""
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+
+        self._write_to_conf = (vis != 0)
+
+        if self.orig_type in _INT_HEX:
+            # The C implementation checks the user value against the range in a
+            # separate code path (post-processing after loading a .config).
+            # Checking all values here instead makes more sense for us. It
+            # requires that we check for a range first.
+
+            base = _TYPE_TO_BASE[self.orig_type]
+
+            # Check if a range is in effect
+            for low_expr, high_expr, cond in self.ranges:
+                if expr_value(cond):
+                    has_active_range = True
+
+                    # The zeros are from the C implementation running strtoll()
+                    # on empty strings
+                    low = int(low_expr.str_value, base) if \
+                      _is_base_n(low_expr.str_value, base) else 0
+                    high = int(high_expr.str_value, base) if \
+                      _is_base_n(high_expr.str_value, base) else 0
+
+                    break
+            else:
+                has_active_range = False
+
+            # Defaults are used if the symbol is invisible, lacks a user value,
+            # or has an out-of-range user value
+            use_defaults = True
+
+            if vis and self.user_value:
+                user_val = int(self.user_value, base)
+                if has_active_range and not low <= user_val <= high:
+                    num2str = str if base == 10 else hex
+                    self.kconfig._warn(
+                        "user value {} on the {} symbol {} ignored due to "
+                        "being outside the active range ([{}, {}]) -- falling "
+                        "back on defaults"
+                        .format(num2str(user_val), TYPE_TO_STR[self.orig_type],
+                                self.name_and_loc,
+                                num2str(low), num2str(high)))
+                else:
+                    # If the user value is well-formed and satisfies range
+                    # contraints, it is stored in exactly the same form as
+                    # specified in the assignment (with or without "0x", etc.)
+                    val = self.user_value
+                    use_defaults = False
+
+            if use_defaults:
+                # No user value or invalid user value. Look at defaults.
+
+                # Used to implement the warning below
+                has_default = False
+
+                for sym, cond in self.defaults:
+                    if expr_value(cond):
+                        has_default = self._write_to_conf = True
+
+                        val = sym.str_value
+
+                        if _is_base_n(val, base):
+                            val_num = int(val, base)
+                        else:
+                            val_num = 0  # strtoll() on empty string
+
+                        break
+                else:
+                    val_num = 0  # strtoll() on empty string
+
+                # This clamping procedure runs even if there's no default
+                if has_active_range:
+                    clamp = None
+                    if val_num < low:
+                        clamp = low
+                    elif val_num > high:
+                        clamp = high
+
+                    if clamp is not None:
+                        # The value is rewritten to a standard form if it is
+                        # clamped
+                        val = str(clamp) \
+                              if self.orig_type is INT else \
+                              hex(clamp)
+
+                        if has_default:
+                            num2str = str if base == 10 else hex
+                            self.kconfig._warn(
+                                "default value {} on {} clamped to {} due to "
+                                "being outside the active range ([{}, {}])"
+                                .format(val_num, self.name_and_loc,
+                                        num2str(clamp), num2str(low),
+                                        num2str(high)))
+
+        elif self.orig_type is STRING:
+            if vis and self.user_value is not None:
+                # If the symbol is visible and has a user value, use that
+                val = self.user_value
+            else:
+                # Otherwise, look at defaults
+                for sym, cond in self.defaults:
+                    if expr_value(cond):
+                        val = sym.str_value
+                        self._write_to_conf = True
+                        break
+
+        # env_var corresponds to SYMBOL_AUTO in the C implementation, and is
+        # also set on the defconfig_list symbol there. Test for the
+        # defconfig_list symbol explicitly instead here, to avoid a nonsensical
+        # env_var setting and the defconfig_list symbol being printed
+        # incorrectly. This code is pretty cold anyway.
+        if self.env_var is not None or self is self.kconfig.defconfig_list:
+            self._write_to_conf = False
+
+        self._cached_str_val = val
+        return val
+
+    @property
+    def tri_value(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_tri_val is not None:
+            return self._cached_tri_val
+
+        if self.orig_type not in _BOOL_TRISTATE:
+            if self.orig_type:  # != UNKNOWN
+                # Would take some work to give the location here
+                self.kconfig._warn(
+                    "The {} symbol {} is being evaluated in a logical context "
+                    "somewhere. It will always evaluate to n."
+                    .format(TYPE_TO_STR[self.orig_type], self.name_and_loc))
+
+            self._cached_tri_val = 0
+            return 0
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+        self._write_to_conf = (vis != 0)
+
+        val = 0
+
+        if not self.choice:
+            # Non-choice symbol
+
+            if vis and self.user_value is not None:
+                # If the symbol is visible and has a user value, use that
+                val = min(self.user_value, vis)
+
+            else:
+                # Otherwise, look at defaults and weak reverse dependencies
+                # (implies)
+
+                for default, cond in self.defaults:
+                    dep_val = expr_value(cond)
+                    if dep_val:
+                        val = min(expr_value(default), dep_val)
+                        if val:
+                            self._write_to_conf = True
+                        break
+
+                # Weak reverse dependencies are only considered if our
+                # direct dependencies are met
+                dep_val = expr_value(self.weak_rev_dep)
+                if dep_val and expr_value(self.direct_dep):
+                    val = max(dep_val, val)
+                    self._write_to_conf = True
+
+            # Reverse (select-related) dependencies take precedence
+            dep_val = expr_value(self.rev_dep)
+            if dep_val:
+                if expr_value(self.direct_dep) < dep_val:
+                    self._warn_select_unsatisfied_deps()
+
+                val = max(dep_val, val)
+                self._write_to_conf = True
+
+            # m is promoted to y for (1) bool symbols and (2) symbols with a
+            # weak_rev_dep (from imply) of y
+            if val == 1 and \
+               (self.type is BOOL or expr_value(self.weak_rev_dep) == 2):
+                val = 2
+
+        elif vis == 2:
+            # Visible choice symbol in y-mode choice. The choice mode limits
+            # the visibility of choice symbols, so it's sufficient to just
+            # check the visibility of the choice symbols themselves.
+            val = 2 if self.choice.selection is self else 0
+
+        elif vis and self.user_value:
+            # Visible choice symbol in m-mode choice, with set non-0 user value
+            val = 1
+
+        self._cached_tri_val = val
+        return val
+
+    @property
+    def assignable(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_assignable is None:
+            self._cached_assignable = self._assignable()
+        return self._cached_assignable
+
+    @property
+    def visibility(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_vis is None:
+            self._cached_vis = _visibility(self)
+        return self._cached_vis
+
+    @property
+    def config_string(self):
+        """
+        See the class documentation.
+        """
+        # _write_to_conf is determined when the value is calculated. This is a
+        # hidden function call due to property magic.
+        val = self.str_value
+        if not self._write_to_conf:
+            return ""
+
+        if self.orig_type in _BOOL_TRISTATE:
+            return "{}{}={}\n" \
+                   .format(self.kconfig.config_prefix, self.name, val) \
+                   if val != "n" else \
+                   "# {}{} is not set\n" \
+                   .format(self.kconfig.config_prefix, self.name)
+
+        if self.orig_type in _INT_HEX:
+            return "{}{}={}\n" \
+                   .format(self.kconfig.config_prefix, self.name, val)
+
+        # sym.orig_type is STRING
+        return '{}{}="{}"\n' \
+               .format(self.kconfig.config_prefix, self.name, escape(val))
+
+    @property
+    def name_and_loc(self):
+        """
+        See the class documentation.
+        """
+        return self.name + " " + _locs(self)
+
+    def set_value(self, value):
+        """
+        Sets the user value of the symbol.
+
+        Equal in effect to assigning the value to the symbol within a .config
+        file. For bool and tristate symbols, use the 'assignable' attribute to
+        check which values can currently be assigned. Setting values outside
+        'assignable' will cause Symbol.user_value to differ from
+        Symbol.str/tri_value (be truncated down or up).
+
+        Setting a choice symbol to 2 (y) sets Choice.user_selection to the
+        choice symbol in addition to setting Symbol.user_value.
+        Choice.user_selection is considered when the choice is in y mode (the
+        "normal" mode).
+
+        Other symbols that depend (possibly indirectly) on this symbol are
+        automatically recalculated to reflect the assigned value.
+
+        value:
+          The user value to give to the symbol. For bool and tristate symbols,
+          n/m/y can be specified either as 0/1/2 (the usual format for tristate
+          values in Kconfiglib) or as one of the strings "n", "m", or "y". For
+          other symbol types, pass a string.
+
+          Note that the value for an int/hex symbol is passed as a string, e.g.
+          "123" or "0x0123". The format of this string is preserved in the
+          output.
+
+          Values that are invalid for the type (such as "foo" or 1 (m) for a
+          BOOL or "0x123" for an INT) are ignored and won't be stored in
+          Symbol.user_value. Kconfiglib will print a warning by default for
+          invalid assignments, and set_value() will return False.
+
+        Returns True if the value is valid for the type of the symbol, and
+        False otherwise. This only looks at the form of the value. For BOOL and
+        TRISTATE symbols, check the Symbol.assignable attribute to see what
+        values are currently in range and would actually be reflected in the
+        value of the symbol. For other symbol types, check whether the
+        visibility is non-n.
+        """
+        if self.orig_type in _BOOL_TRISTATE and value in STR_TO_TRI:
+            value = STR_TO_TRI[value]
+
+        # If the new user value matches the old, nothing changes, and we can
+        # avoid invalidating cached values.
+        #
+        # This optimization is skipped for choice symbols: Setting a choice
+        # symbol's user value to y might change the state of the choice, so it
+        # wouldn't be safe (symbol user values always match the values set in a
+        # .config file or via set_value(), and are never implicitly updated).
+        if value == self.user_value and not self.choice:
+            self._was_set = True
+            return True
+
+        # Check if the value is valid for our type
+        if not (self.orig_type is BOOL     and value in (2, 0)     or
+                self.orig_type is TRISTATE and value in TRI_TO_STR or
+                value.__class__ is str and
+                (self.orig_type is STRING                        or
+                 self.orig_type is INT and _is_base_n(value, 10) or
+                 self.orig_type is HEX and _is_base_n(value, 16)
+                                       and int(value, 16) >= 0)):
+
+            # Display tristate values as n, m, y in the warning
+            self.kconfig._warn(
+                "the value {} is invalid for {}, which has type {} -- "
+                "assignment ignored"
+                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
+                            "'{}'".format(value),
+                        self.name_and_loc, TYPE_TO_STR[self.orig_type]))
+
+            return False
+
+        self.user_value = value
+        self._was_set = True
+
+        if self.choice and value == 2:
+            # Setting a choice symbol to y makes it the user selection of the
+            # choice. Like for symbol user values, the user selection is not
+            # guaranteed to match the actual selection of the choice, as
+            # dependencies come into play.
+            self.choice.user_selection = self
+            self.choice._was_set = True
+            self.choice._rec_invalidate()
+        else:
+            self._rec_invalidate_if_has_prompt()
+
+        return True
+
+    def unset_value(self):
+        """
+        Removes any user value from the symbol, as if the symbol had never
+        gotten a user value via Kconfig.load_config() or Symbol.set_value().
+        """
+        if self.user_value is not None:
+            self.user_value = None
+            self._rec_invalidate_if_has_prompt()
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        return {item for node in self.nodes for item in node.referenced}
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [d for node in self.nodes for d in node.orig_defaults]
+
+    @property
+    def orig_selects(self):
+        """
+        See the class documentation.
+        """
+        return [s for node in self.nodes for s in node.orig_selects]
+
+    @property
+    def orig_implies(self):
+        """
+        See the class documentation.
+        """
+        return [i for node in self.nodes for i in node.orig_implies]
+
+    @property
+    def orig_ranges(self):
+        """
+        See the class documentation.
+        """
+        return [r for node in self.nodes for r in node.orig_ranges]
+
+    def __repr__(self):
+        """
+        Returns a string with information about the symbol (including its name,
+        value, visibility, and location(s)) when it is evaluated on e.g. the
+        interactive Python prompt.
+        """
+        fields = ["symbol " + self.name, TYPE_TO_STR[self.type]]
+        add = fields.append
+
+        for node in self.nodes:
+            if node.prompt:
+                add('"{}"'.format(node.prompt[0]))
+
+        # Only add quotes for non-bool/tristate symbols
+        add("value " + (self.str_value if self.orig_type in _BOOL_TRISTATE
+                        else '"{}"'.format(self.str_value)))
+
+        if not self.is_constant:
+            # These aren't helpful to show for constant symbols
+
+            if self.user_value is not None:
+                # Only add quotes for non-bool/tristate symbols
+                add("user value " + (TRI_TO_STR[self.user_value]
+                                     if self.orig_type in _BOOL_TRISTATE
+                                     else '"{}"'.format(self.user_value)))
+
+            add("visibility " + TRI_TO_STR[self.visibility])
+
+            if self.choice:
+                add("choice symbol")
+
+            if self.is_allnoconfig_y:
+                add("allnoconfig_y")
+
+            if self is self.kconfig.defconfig_list:
+                add("is the defconfig_list symbol")
+
+            if self.env_var is not None:
+                add("from environment variable " + self.env_var)
+
+            if self is self.kconfig.modules:
+                add("is the modules symbol")
+
+            add("direct deps " + TRI_TO_STR[expr_value(self.direct_dep)])
+
+        if self.nodes:
+            for node in self.nodes:
+                add("{}:{}".format(node.filename, node.linenr))
+        else:
+            add("constant" if self.is_constant else "undefined")
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the symbol when it is printed.
+        Matches the Kconfig format, with any parent dependencies propagated to
+        the 'depends on' condition.
+
+        The string is constructed by joining the strings returned by
+        MenuNode.__str__() for each of the symbol's menu nodes, so symbols
+        defined in multiple locations will return a string with all
+        definitions.
+
+        The returned string does not end in a newline. An empty string is
+        returned for undefined and constant symbols.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like Symbol.__str__(), but allows a custom format to be used for
+        all symbol/choice references. See expr_str().
+        """
+        return "\n\n".join(node.custom_str(sc_expr_str_fn)
+                           for node in self.nodes)
+
+    #
+    # Private methods
+    #
+
+    def __init__(self):
+        """
+        Symbol constructor -- not intended to be called directly by Kconfiglib
+        clients.
+        """
+        # These attributes are always set on the instance from outside and
+        # don't need defaults:
+        #   kconfig
+        #   direct_dep
+        #   is_constant
+        #   name
+        #   rev_dep
+        #   weak_rev_dep
+
+        # - UNKNOWN == 0
+        # - _visited is used during tree iteration and dep. loop detection
+        self.orig_type = self._visited = 0
+
+        self.nodes = []
+
+        self.defaults = []
+        self.selects = []
+        self.implies = []
+        self.ranges = []
+
+        self.user_value = \
+        self.choice = \
+        self.env_var = \
+        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
+        self._cached_assignable = None
+
+        # _write_to_conf is calculated along with the value. If True, the
+        # Symbol gets a .config entry.
+
+        self.is_allnoconfig_y = \
+        self._was_set = \
+        self._write_to_conf = False
+
+        # See Kconfig._build_dep()
+        self._dependents = set()
+
+    def _assignable(self):
+        # Worker function for the 'assignable' attribute
+
+        if self.orig_type not in _BOOL_TRISTATE:
+            return ()
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+        if not vis:
+            return ()
+
+        rev_dep_val = expr_value(self.rev_dep)
+
+        if vis == 2:
+            if self.choice:
+                return (2,)
+
+            if not rev_dep_val:
+                if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
+                    return (0, 2)
+                return (0, 1, 2)
+
+            if rev_dep_val == 2:
+                return (2,)
+
+            # rev_dep_val == 1
+
+            if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
+                return (2,)
+            return (1, 2)
+
+        # vis == 1
+
+        # Must be a tristate here, because bool m visibility gets promoted to y
+
+        if not rev_dep_val:
+            return (0, 1) if expr_value(self.weak_rev_dep) != 2 else (0, 2)
+
+        if rev_dep_val == 2:
+            return (2,)
+
+        # vis == rev_dep_val == 1
+
+        return (1,)
+
+    def _invalidate(self):
+        # Marks the symbol as needing to be recalculated
+
+        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
+        self._cached_assignable = None
+
+    def _rec_invalidate(self):
+        # Invalidates the symbol and all items that (possibly) depend on it
+
+        if self is self.kconfig.modules:
+            # Invalidating MODULES has wide-ranging effects
+            self.kconfig._invalidate_all()
+        else:
+            self._invalidate()
+
+            for item in self._dependents:
+                # _cached_vis doubles as a flag that tells us whether 'item'
+                # has cached values, because it's calculated as a side effect
+                # of calculating all other (non-constant) cached values.
+                #
+                # If item._cached_vis is None, it means there can't be cached
+                # values on other items that depend on 'item', because if there
+                # were, some value on 'item' would have been calculated and
+                # item._cached_vis set as a side effect. It's therefore safe to
+                # stop the invalidation at symbols with _cached_vis None.
+                #
+                # This approach massively speeds up scripts that set a lot of
+                # values, vs simply invalidating all possibly dependent symbols
+                # (even when you already have a list of all the dependent
+                # symbols, because some symbols get huge dependency trees).
+                #
+                # This gracefully handles dependency loops too, which is nice
+                # for choices, where the choice depends on the choice symbols
+                # and vice versa.
+                if item._cached_vis is not None:
+                    item._rec_invalidate()
+
+    def _rec_invalidate_if_has_prompt(self):
+        # Invalidates the symbol and its dependent symbols, but only if the
+        # symbol has a prompt. User values never have an effect on promptless
+        # symbols, so we skip invalidation for them as an optimization.
+        #
+        # This also prevents constant (quoted) symbols from being invalidated
+        # if set_value() is called on them, which would make them lose their
+        # value and break things.
+        #
+        # Prints a warning if the symbol has no prompt. In some contexts (e.g.
+        # when loading a .config files) assignments to promptless symbols are
+        # normal and expected, so the warning can be disabled.
+
+        for node in self.nodes:
+            if node.prompt:
+                self._rec_invalidate()
+                return
+
+        if self.kconfig._warn_assign_no_prompt:
+            self.kconfig._warn(self.name_and_loc + " has no prompt, meaning "
+                               "user values have no effect on it")
+
+    def _str_default(self):
+        # write_min_config() helper function. Returns the value the symbol
+        # would get from defaults if it didn't have a user value. Uses exactly
+        # the same algorithm as the C implementation (though a bit cleaned up),
+        # for compatibility.
+
+        if self.orig_type in _BOOL_TRISTATE:
+            val = 0
+
+            # Defaults, selects, and implies do not affect choice symbols
+            if not self.choice:
+                for default, cond in self.defaults:
+                    cond_val = expr_value(cond)
+                    if cond_val:
+                        val = min(expr_value(default), cond_val)
+                        break
+
+                val = max(expr_value(self.rev_dep),
+                          expr_value(self.weak_rev_dep),
+                          val)
+
+                # Transpose mod to yes if type is bool (possibly due to modules
+                # being disabled)
+                if val == 1 and self.type is BOOL:
+                    val = 2
+
+            return TRI_TO_STR[val]
+
+        if self.orig_type:  # STRING/INT/HEX
+            for default, cond in self.defaults:
+                if expr_value(cond):
+                    return default.str_value
+
+        return ""
+
+    def _warn_select_unsatisfied_deps(self):
+        # Helper for printing an informative warning when a symbol with
+        # unsatisfied direct dependencies (dependencies from 'depends on', ifs,
+        # and menus) is selected by some other symbol. Also warn if a symbol
+        # whose direct dependencies evaluate to m is selected to y.
+
+        msg = "{} has direct dependencies {} with value {}, but is " \
+              "currently being {}-selected by the following symbols:" \
+              .format(self.name_and_loc, expr_str(self.direct_dep),
+                      TRI_TO_STR[expr_value(self.direct_dep)],
+                      TRI_TO_STR[expr_value(self.rev_dep)])
+
+        # The reverse dependencies from each select are ORed together
+        for select in split_expr(self.rev_dep, OR):
+            if expr_value(select) <= expr_value(self.direct_dep):
+                # Only include selects that exceed the direct dependencies
+                continue
+
+            # - 'select A if B' turns into A && B
+            # - 'select A' just turns into A
+            #
+            # In both cases, we can split on AND and pick the first operand
+            selecting_sym = split_expr(select, AND)[0]
+
+            msg += "\n - {}, with value {}, direct dependencies {} " \
+                   "(value: {})" \
+                   .format(selecting_sym.name_and_loc,
+                           selecting_sym.str_value,
+                           expr_str(selecting_sym.direct_dep),
+                           TRI_TO_STR[expr_value(selecting_sym.direct_dep)])
+
+            if select.__class__ is tuple:
+                msg += ", and select condition {} (value: {})" \
+                       .format(expr_str(select[2]),
+                               TRI_TO_STR[expr_value(select[2])])
+
+        self.kconfig._warn(msg)
+
+
+class Choice(object):
+    """
+    Represents a choice statement:
+
+      choice
+          ...
+      endchoice
+
+    The following attributes are available on Choice instances. They should be
+    treated as read-only, and some are implemented through @property magic (but
+    are still efficient to access due to internal caching).
+
+    Note: Prompts, help texts, and locations are stored in the Choice's
+    MenuNode(s) rather than in the Choice itself. Check the MenuNode class and
+    the Choice.nodes attribute. This organization matches the C tools.
+
+    name:
+      The name of the choice, e.g. "FOO" for 'choice FOO', or None if the
+      Choice has no name.
+
+    type:
+      The type of the choice. One of BOOL, TRISTATE, UNKNOWN. UNKNOWN is for
+      choices defined without a type where none of the contained symbols have a
+      type either (otherwise the choice inherits the type of the first symbol
+      defined with a type).
+
+      When running without modules (CONFIG_MODULES=n), TRISTATE choices
+      magically change type to BOOL. This matches the C tools, and makes sense
+      for menuconfig-like functionality.
+
+    orig_type:
+      The type as given in the Kconfig file, without any magic applied. Used
+      when printing the choice.
+
+    tri_value:
+      The tristate value (mode) of the choice. A choice can be in one of three
+      modes:
+
+        0 (n) - The choice is disabled and no symbols can be selected. For
+                visible choices, this mode is only possible for choices with
+                the 'optional' flag set (see kconfig-language.txt).
+
+        1 (m) - Any number of choice symbols can be set to m, the rest will
+                be n.
+
+        2 (y) - One symbol will be y, the rest n.
+
+      Only tristate choices can be in m mode. The visibility of the choice is
+      an upper bound on the mode, and the mode in turn is an upper bound on the
+      visibility of the choice symbols.
+
+      To change the mode, use Choice.set_value().
+
+      Implementation note:
+        The C tools internally represent choices as a type of symbol, with
+        special-casing in many code paths. This is why there is a lot of
+        similarity to Symbol. The value (mode) of a choice is really just a
+        normal symbol value, and an implicit reverse dependency forces its
+        lower bound to m for visible non-optional choices (the reverse
+        dependency is 'm && <visibility>').
+
+        Symbols within choices get the choice propagated as a dependency to
+        their properties. This turns the mode of the choice into an upper bound
+        on e.g. the visibility of choice symbols, and explains the gotcha
+        related to printing choice symbols mentioned in the module docstring.
+
+        Kconfiglib uses a separate Choice class only because it makes the code
+        and interface less confusing (especially in a user-facing interface).
+        Corresponding attributes have the same name in the Symbol and Choice
+        classes, for consistency and compatibility.
+
+    str_value:
+      Like choice.tri_value, but gives the value as one of the strings
+      "n", "m", or "y"
+
+    user_value:
+      The value (mode) selected by the user through Choice.set_value(). Either
+      0, 1, or 2, or None if the user hasn't selected a mode. See
+      Symbol.user_value.
+
+      WARNING: Do not assign directly to this. It will break things. Use
+      Choice.set_value() instead.
+
+    assignable:
+      See the symbol class documentation. Gives the assignable values (modes).
+
+    selection:
+      The Symbol instance of the currently selected symbol. None if the Choice
+      is not in y mode or has no selected symbol (due to unsatisfied
+      dependencies on choice symbols).
+
+      WARNING: Do not assign directly to this. It will break things. Call
+      sym.set_value(2) on the choice symbol you want to select instead.
+
+    user_selection:
+      The symbol selected by the user (by setting it to y). Ignored if the
+      choice is not in y mode, but still remembered so that the choice "snaps
+      back" to the user selection if the mode is changed back to y. This might
+      differ from 'selection' due to unsatisfied dependencies.
+
+      WARNING: Do not assign directly to this. It will break things. Call
+      sym.set_value(2) on the choice symbol to be selected instead.
+
+    visibility:
+      See the Symbol class documentation. Acts on the value (mode).
+
+    name_and_loc:
+      Holds a string like
+
+        "<choice MY_CHOICE> (defined at foo/Kconfig:12)"
+
+      , giving the name of the choice and its definition location(s). If the
+      choice has no name (isn't defined with 'choice MY_CHOICE'), then it will
+      be shown as "<choice>" before the list of locations (always a single one
+      in that case).
+
+    syms:
+      List of symbols contained in the choice.
+
+      Obscure gotcha: If a symbol depends on the previous symbol within a
+      choice so that an implicit menu is created, it won't be a choice symbol,
+      and won't be included in 'syms'.
+
+    nodes:
+      A list of MenuNodes for this choice. In practice, the list will probably
+      always contain a single MenuNode, but it is possible to give a choice a
+      name and define it in multiple locations.
+
+    defaults:
+      List of (symbol, cond) tuples for the choice's 'defaults' properties. For
+      example, 'default A if B && C' is represented as (A, (AND, B, C)). If
+      there is no condition, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to
+      'default' conditions.
+
+    orig_defaults:
+      See the corresponding attribute on the MenuNode class.
+
+    direct_dep:
+      See Symbol.direct_dep.
+
+    referenced:
+      A set() with all symbols referenced in the properties and property
+      conditions of the choice.
+
+      Also includes dependencies from surrounding menus and ifs, because those
+      get propagated to the choice (see the 'Intro to symbol values' section in
+      the module docstring).
+
+    is_optional:
+      True if the choice has the 'optional' flag set on it and can be in
+      n mode.
+
+    kconfig:
+      The Kconfig instance this choice is from.
+    """
+    __slots__ = (
+        "_cached_assignable",
+        "_cached_selection",
+        "_cached_vis",
+        "_dependents",
+        "_visited",
+        "_was_set",
+        "defaults",
+        "direct_dep",
+        "is_constant",
+        "is_optional",
+        "kconfig",
+        "name",
+        "nodes",
+        "orig_type",
+        "syms",
+        "user_selection",
+        "user_value",
+    )
+
+    #
+    # Public interface
+    #
+
+    @property
+    def type(self):
+        """
+        Returns the type of the choice. See Symbol.type.
+        """
+        if self.orig_type is TRISTATE and not self.kconfig.modules.tri_value:
+            return BOOL
+        return self.orig_type
+
+    @property
+    def str_value(self):
+        """
+        See the class documentation.
+        """
+        return TRI_TO_STR[self.tri_value]
+
+    @property
+    def tri_value(self):
+        """
+        See the class documentation.
+        """
+        # This emulates a reverse dependency of 'm && visibility' for
+        # non-optional choices, which is how the C implementation does it
+
+        val = 0 if self.is_optional else 1
+
+        if self.user_value is not None:
+            val = max(val, self.user_value)
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        val = min(val, self.visibility)
+
+        # Promote m to y for boolean choices
+        return 2 if val == 1 and self.type is BOOL else val
+
+    @property
+    def assignable(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_assignable is None:
+            self._cached_assignable = self._assignable()
+        return self._cached_assignable
+
+    @property
+    def visibility(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_vis is None:
+            self._cached_vis = _visibility(self)
+        return self._cached_vis
+
+    @property
+    def name_and_loc(self):
+        """
+        See the class documentation.
+        """
+        # Reuse the expression format, which is '<choice (name, if any)>'.
+        return standard_sc_expr_str(self) + " " + _locs(self)
+
+    @property
+    def selection(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_selection is _NO_CACHED_SELECTION:
+            self._cached_selection = self._selection()
+        return self._cached_selection
+
+    def set_value(self, value):
+        """
+        Sets the user value (mode) of the choice. Like for Symbol.set_value(),
+        the visibility might truncate the value. Choices without the 'optional'
+        attribute (is_optional) can never be in n mode, but 0/"n" is still
+        accepted since it's not a malformed value (though it will have no
+        effect).
+
+        Returns True if the value is valid for the type of the choice, and
+        False otherwise. This only looks at the form of the value. Check the
+        Choice.assignable attribute to see what values are currently in range
+        and would actually be reflected in the mode of the choice.
+        """
+        if value in STR_TO_TRI:
+            value = STR_TO_TRI[value]
+
+        if value == self.user_value:
+            # We know the value must be valid if it was successfully set
+            # previously
+            self._was_set = True
+            return True
+
+        if not (self.orig_type is BOOL     and value in (2, 0) or
+                self.orig_type is TRISTATE and value in TRI_TO_STR):
+
+            # Display tristate values as n, m, y in the warning
+            self.kconfig._warn(
+                "the value {} is invalid for {}, which has type {} -- "
+                "assignment ignored"
+                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
+                            "'{}'".format(value),
+                        self.name_and_loc, TYPE_TO_STR[self.orig_type]))
+
+            return False
+
+        self.user_value = value
+        self._was_set = True
+        self._rec_invalidate()
+
+        return True
+
+    def unset_value(self):
+        """
+        Resets the user value (mode) and user selection of the Choice, as if
+        the user had never touched the mode or any of the choice symbols.
+        """
+        if self.user_value is not None or self.user_selection:
+            self.user_value = self.user_selection = None
+            self._rec_invalidate()
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        return {item for node in self.nodes for item in node.referenced}
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [d for node in self.nodes for d in node.orig_defaults]
+
+    def __repr__(self):
+        """
+        Returns a string with information about the choice when it is evaluated
+        on e.g. the interactive Python prompt.
+        """
+        fields = ["choice " + self.name if self.name else "choice",
+                  TYPE_TO_STR[self.type]]
+        add = fields.append
+
+        for node in self.nodes:
+            if node.prompt:
+                add('"{}"'.format(node.prompt[0]))
+
+        add("mode " + self.str_value)
+
+        if self.user_value is not None:
+            add('user mode {}'.format(TRI_TO_STR[self.user_value]))
+
+        if self.selection:
+            add("{} selected".format(self.selection.name))
+
+        if self.user_selection:
+            user_sel_str = "{} selected by user" \
+                           .format(self.user_selection.name)
+
+            if self.selection is not self.user_selection:
+                user_sel_str += " (overridden)"
+
+            add(user_sel_str)
+
+        add("visibility " + TRI_TO_STR[self.visibility])
+
+        if self.is_optional:
+            add("optional")
+
+        for node in self.nodes:
+            add("{}:{}".format(node.filename, node.linenr))
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the choice when it is printed.
+        Matches the Kconfig format (though without the contained choice
+        symbols), with any parent dependencies propagated to the 'depends on'
+        condition.
+
+        The returned string does not end in a newline.
+
+        See Symbol.__str__() as well.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like Choice.__str__(), but allows a custom format to be used for
+        all symbol/choice references. See expr_str().
+        """
+        return "\n\n".join(node.custom_str(sc_expr_str_fn)
+                           for node in self.nodes)
+
+    #
+    # Private methods
+    #
+
+    def __init__(self):
+        """
+        Choice constructor -- not intended to be called directly by Kconfiglib
+        clients.
+        """
+        # These attributes are always set on the instance from outside and
+        # don't need defaults:
+        #   direct_dep
+        #   kconfig
+
+        # - UNKNOWN == 0
+        # - _visited is used during dep. loop detection
+        self.orig_type = self._visited = 0
+
+        self.nodes = []
+
+        self.syms = []
+        self.defaults = []
+
+        self.name = \
+        self.user_value = self.user_selection = \
+        self._cached_vis = self._cached_assignable = None
+
+        self._cached_selection = _NO_CACHED_SELECTION
+
+        # is_constant is checked by _depend_on(). Just set it to avoid having
+        # to special-case choices.
+        self.is_constant = self.is_optional = False
+
+        # See Kconfig._build_dep()
+        self._dependents = set()
+
+    def _assignable(self):
+        # Worker function for the 'assignable' attribute
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+
+        if not vis:
+            return ()
+
+        if vis == 2:
+            if not self.is_optional:
+                return (2,) if self.type is BOOL else (1, 2)
+            return (0, 2) if self.type is BOOL else (0, 1, 2)
+
+        # vis == 1
+
+        return (0, 1) if self.is_optional else (1,)
+
+    def _selection(self):
+        # Worker function for the 'selection' attribute
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        if self.tri_value != 2:
+            # Not in y mode, so no selection
+            return None
+
+        # Use the user selection if it's visible
+        if self.user_selection and self.user_selection.visibility:
+            return self.user_selection
+
+        # Otherwise, check if we have a default
+        return self._selection_from_defaults()
+
+    def _selection_from_defaults(self):
+        # Check if we have a default
+        for sym, cond in self.defaults:
+            # The default symbol must be visible too
+            if expr_value(cond) and sym.visibility:
+                return sym
+
+        # Otherwise, pick the first visible symbol, if any
+        for sym in self.syms:
+            if sym.visibility:
+                return sym
+
+        # Couldn't find a selection
+        return None
+
+    def _invalidate(self):
+        self._cached_vis = self._cached_assignable = None
+        self._cached_selection = _NO_CACHED_SELECTION
+
+    def _rec_invalidate(self):
+        # See Symbol._rec_invalidate()
+
+        self._invalidate()
+
+        for item in self._dependents:
+            if item._cached_vis is not None:
+                item._rec_invalidate()
+
+
+class MenuNode(object):
+    """
+    Represents a menu node in the configuration. This corresponds to an entry
+    in e.g. the 'make menuconfig' interface, though non-visible choices, menus,
+    and comments also get menu nodes. If a symbol or choice is defined in
+    multiple locations, it gets one menu node for each location.
+
+    The top-level menu node, corresponding to the implicit top-level menu, is
+    available in Kconfig.top_node.
+
+    The menu nodes for a Symbol or Choice can be found in the
+    Symbol/Choice.nodes attribute. Menus and comments are represented as plain
+    menu nodes, with their text stored in the prompt attribute (prompt[0]).
+    This mirrors the C implementation.
+
+    The following attributes are available on MenuNode instances. They should
+    be viewed as read-only.
+
+    item:
+      Either a Symbol, a Choice, or one of the constants MENU and COMMENT.
+      Menus and comments are represented as plain menu nodes. Ifs are collapsed
+      (matching the C implementation) and do not appear in the final menu tree.
+
+    next:
+      The following menu node. None if there is no following node.
+
+    list:
+      The first child menu node. None if there are no children.
+
+      Choices and menus naturally have children, but Symbols can also have
+      children because of menus created automatically from dependencies (see
+      kconfig-language.txt).
+
+    parent:
+      The parent menu node. None if there is no parent.
+
+    prompt:
+      A (string, cond) tuple with the prompt for the menu node and its
+      conditional expression (which is self.kconfig.y if there is no
+      condition). None if there is no prompt.
+
+      For symbols and choices, the prompt is stored in the MenuNode rather than
+      the Symbol or Choice instance. For menus and comments, the prompt holds
+      the text.
+
+    defaults:
+      The 'default' properties for this particular menu node. See
+      symbol.defaults.
+
+      When evaluating defaults, you should use Symbol/Choice.defaults instead,
+      as it include properties from all menu nodes (a symbol/choice can have
+      multiple definition locations/menu nodes). MenuNode.defaults is meant for
+      documentation generation.
+
+    selects:
+      Like MenuNode.defaults, for selects.
+
+    implies:
+      Like MenuNode.defaults, for implies.
+
+    ranges:
+      Like MenuNode.defaults, for ranges.
+
+    orig_prompt:
+    orig_defaults:
+    orig_selects:
+    orig_implies:
+    orig_ranges:
+      These work the like the corresponding attributes without orig_*, but omit
+      any dependencies propagated from 'depends on' and surrounding 'if's (the
+      direct dependencies, stored in MenuNode.dep).
+
+      One use for this is generating less cluttered documentation, by only
+      showing the direct dependencies in one place.
+
+    help:
+      The help text for the menu node for Symbols and Choices. None if there is
+      no help text. Always stored in the node rather than the Symbol or Choice.
+      It is possible to have a separate help text at each location if a symbol
+      is defined in multiple locations.
+
+      Trailing whitespace (including a final newline) is stripped from the help
+      text. This was not the case before Kconfiglib 10.21.0, where the format
+      was undocumented.
+
+    dep:
+      The direct ('depends on') dependencies for the menu node, or
+      self.kconfig.y if there are no direct dependencies.
+
+      This attribute includes any dependencies from surrounding menus and ifs.
+      Those get propagated to the direct dependencies, and the resulting direct
+      dependencies in turn get propagated to the conditions of all properties.
+
+      If a symbol or choice is defined in multiple locations, only the
+      properties defined at a particular location get the corresponding
+      MenuNode.dep dependencies propagated to them.
+
+    visibility:
+      The 'visible if' dependencies for the menu node (which must represent a
+      menu), or self.kconfig.y if there are no 'visible if' dependencies.
+      'visible if' dependencies are recursively propagated to the prompts of
+      symbols and choices within the menu.
+
+    referenced:
+      A set() with all symbols and choices referenced in the properties and
+      property conditions of the menu node.
+
+      Also includes dependencies inherited from surrounding menus and ifs.
+      Choices appear in the dependencies of choice symbols.
+
+    is_menuconfig:
+      Set to True if the children of the menu node should be displayed in a
+      separate menu. This is the case for the following items:
+
+        - Menus (node.item == MENU)
+
+        - Choices
+
+        - Symbols defined with the 'menuconfig' keyword. The children come from
+          implicitly created submenus, and should be displayed in a separate
+          menu rather than being indented.
+
+      'is_menuconfig' is just a hint on how to display the menu node. It's
+      ignored internally by Kconfiglib, except when printing symbols.
+
+    filename/linenr:
+      The location where the menu node appears. The filename is relative to
+      $srctree (or to the current directory if $srctree isn't set), except
+      absolute paths are used for paths outside $srctree.
+
+    include_path:
+      A tuple of (filename, linenr) tuples, giving the locations of the
+      'source' statements via which the Kconfig file containing this menu node
+      was included. The first element is the location of the 'source' statement
+      in the top-level Kconfig file passed to Kconfig.__init__(), etc.
+
+      Note that the Kconfig file of the menu node itself isn't included. Check
+      'filename' and 'linenr' for that.
+
+    kconfig:
+      The Kconfig instance the menu node is from.
+    """
+    __slots__ = (
+        "dep",
+        "filename",
+        "help",
+        "include_path",
+        "is_menuconfig",
+        "item",
+        "kconfig",
+        "linenr",
+        "list",
+        "next",
+        "parent",
+        "prompt",
+        "visibility",
+
+        # Properties
+        "defaults",
+        "selects",
+        "implies",
+        "ranges",
+    )
+
+    def __init__(self):
+        # Properties defined on this particular menu node. A local 'depends on'
+        # only applies to these, in case a symbol is defined in multiple
+        # locations.
+        self.defaults = []
+        self.selects = []
+        self.implies = []
+        self.ranges = []
+
+    @property
+    def orig_prompt(self):
+        """
+        See the class documentation.
+        """
+        if not self.prompt:
+            return None
+        return (self.prompt[0], self._strip_dep(self.prompt[1]))
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [(default, self._strip_dep(cond))
+                for default, cond in self.defaults]
+
+    @property
+    def orig_selects(self):
+        """
+        See the class documentation.
+        """
+        return [(select, self._strip_dep(cond))
+                for select, cond in self.selects]
+
+    @property
+    def orig_implies(self):
+        """
+        See the class documentation.
+        """
+        return [(imply, self._strip_dep(cond))
+                for imply, cond in self.implies]
+
+    @property
+    def orig_ranges(self):
+        """
+        See the class documentation.
+        """
+        return [(low, high, self._strip_dep(cond))
+                for low, high, cond in self.ranges]
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        # self.dep is included to catch dependencies from a lone 'depends on'
+        # when there are no properties to propagate it to
+        res = expr_items(self.dep)
+
+        if self.prompt:
+            res |= expr_items(self.prompt[1])
+
+        if self.item is MENU:
+            res |= expr_items(self.visibility)
+
+        for value, cond in self.defaults:
+            res |= expr_items(value)
+            res |= expr_items(cond)
+
+        for value, cond in self.selects:
+            res.add(value)
+            res |= expr_items(cond)
+
+        for value, cond in self.implies:
+            res.add(value)
+            res |= expr_items(cond)
+
+        for low, high, cond in self.ranges:
+            res.add(low)
+            res.add(high)
+            res |= expr_items(cond)
+
+        return res
+
+    def __repr__(self):
+        """
+        Returns a string with information about the menu node when it is
+        evaluated on e.g. the interactive Python prompt.
+        """
+        fields = []
+        add = fields.append
+
+        if self.item.__class__ is Symbol:
+            add("menu node for symbol " + self.item.name)
+
+        elif self.item.__class__ is Choice:
+            s = "menu node for choice"
+            if self.item.name is not None:
+                s += " " + self.item.name
+            add(s)
+
+        elif self.item is MENU:
+            add("menu node for menu")
+
+        else:  # self.item is COMMENT
+            add("menu node for comment")
+
+        if self.prompt:
+            add('prompt "{}" (visibility {})'.format(
+                self.prompt[0], TRI_TO_STR[expr_value(self.prompt[1])]))
+
+        if self.item.__class__ is Symbol and self.is_menuconfig:
+            add("is menuconfig")
+
+        add("deps " + TRI_TO_STR[expr_value(self.dep)])
+
+        if self.item is MENU:
+            add("'visible if' deps " + TRI_TO_STR[expr_value(self.visibility)])
+
+        if self.item.__class__ in _SYMBOL_CHOICE and self.help is not None:
+            add("has help")
+
+        if self.list:
+            add("has child")
+
+        if self.next:
+            add("has next")
+
+        add("{}:{}".format(self.filename, self.linenr))
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the menu node. Matches the Kconfig
+        format, with any parent dependencies propagated to the 'depends on'
+        condition.
+
+        The output could (almost) be fed back into a Kconfig parser to redefine
+        the object associated with the menu node. See the module documentation
+        for a gotcha related to choice symbols.
+
+        For symbols and choices with multiple menu nodes (multiple definition
+        locations), properties that aren't associated with a particular menu
+        node are shown on all menu nodes ('option env=...', 'optional' for
+        choices, etc.).
+
+        The returned string does not end in a newline.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like MenuNode.__str__(), but allows a custom format to be used
+        for all symbol/choice references. See expr_str().
+        """
+        return self._menu_comment_node_str(sc_expr_str_fn) \
+               if self.item in _MENU_COMMENT else \
+               self._sym_choice_node_str(sc_expr_str_fn)
+
+    def _menu_comment_node_str(self, sc_expr_str_fn):
+        s = '{} "{}"'.format("menu" if self.item is MENU else "comment",
+                             self.prompt[0])
+
+        if self.dep is not self.kconfig.y:
+            s += "\n\tdepends on {}".format(expr_str(self.dep, sc_expr_str_fn))
+
+        if self.item is MENU and self.visibility is not self.kconfig.y:
+            s += "\n\tvisible if {}".format(expr_str(self.visibility,
+                                                     sc_expr_str_fn))
+
+        return s
+
+    def _sym_choice_node_str(self, sc_expr_str_fn):
+        def indent_add(s):
+            lines.append("\t" + s)
+
+        def indent_add_cond(s, cond):
+            if cond is not self.kconfig.y:
+                s += " if " + expr_str(cond, sc_expr_str_fn)
+            indent_add(s)
+
+        sc = self.item
+
+        if sc.__class__ is Symbol:
+            lines = [("menuconfig " if self.is_menuconfig else "config ")
+                     + sc.name]
+        else:
+            lines = ["choice " + sc.name if sc.name else "choice"]
+
+        if sc.orig_type and not self.prompt:  # sc.orig_type != UNKNOWN
+            # If there's a prompt, we'll use the '<type> "prompt"' shorthand
+            # instead
+            indent_add(TYPE_TO_STR[sc.orig_type])
+
+        if self.prompt:
+            if sc.orig_type:
+                prefix = TYPE_TO_STR[sc.orig_type]
+            else:
+                # Symbol defined without a type (which generates a warning)
+                prefix = "prompt"
+
+            indent_add_cond(prefix + ' "{}"'.format(escape(self.prompt[0])),
+                            self.orig_prompt[1])
+
+        if sc.__class__ is Symbol:
+            if sc.is_allnoconfig_y:
+                indent_add("option allnoconfig_y")
+
+            if sc is sc.kconfig.defconfig_list:
+                indent_add("option defconfig_list")
+
+            if sc.env_var is not None:
+                indent_add('option env="{}"'.format(sc.env_var))
+
+            if sc is sc.kconfig.modules:
+                indent_add("option modules")
+
+            for low, high, cond in self.orig_ranges:
+                indent_add_cond(
+                    "range {} {}".format(sc_expr_str_fn(low),
+                                         sc_expr_str_fn(high)),
+                    cond)
+
+        for default, cond in self.orig_defaults:
+            indent_add_cond("default " + expr_str(default, sc_expr_str_fn),
+                            cond)
+
+        if sc.__class__ is Choice and sc.is_optional:
+            indent_add("optional")
+
+        if sc.__class__ is Symbol:
+            for select, cond in self.orig_selects:
+                indent_add_cond("select " + sc_expr_str_fn(select), cond)
+
+            for imply, cond in self.orig_implies:
+                indent_add_cond("imply " + sc_expr_str_fn(imply), cond)
+
+        if self.dep is not sc.kconfig.y:
+            indent_add("depends on " + expr_str(self.dep, sc_expr_str_fn))
+
+        if self.help is not None:
+            indent_add("help")
+            for line in self.help.splitlines():
+                indent_add("  " + line)
+
+        return "\n".join(lines)
+
+    def _strip_dep(self, expr):
+        # Helper function for removing MenuNode.dep from 'expr'. Uses two
+        # pieces of internal knowledge: (1) Expressions are reused rather than
+        # copied, and (2) the direct dependencies always appear at the end.
+
+        # ... if dep -> ... if y
+        if self.dep is expr:
+            return self.kconfig.y
+
+        # (AND, X, dep) -> X
+        if expr.__class__ is tuple and expr[0] is AND and expr[2] is self.dep:
+            return expr[1]
+
+        return expr
+
+
+class Variable(object):
+    """
+    Represents a preprocessor variable/function.
+
+    The following attributes are available:
+
+    name:
+      The name of the variable.
+
+    value:
+      The unexpanded value of the variable.
+
+    expanded_value:
+      The expanded value of the variable. For simple variables (those defined
+      with :=), this will equal 'value'. Accessing this property will raise a
+      KconfigError if the expansion seems to be stuck in a loop.
+
+      Accessing this field is the same as calling expanded_value_w_args() with
+      no arguments. I hadn't considered function arguments when adding it. It
+      is retained for backwards compatibility though.
+
+    is_recursive:
+      True if the variable is recursive (defined with =).
+    """
+    __slots__ = (
+        "_n_expansions",
+        "is_recursive",
+        "kconfig",
+        "name",
+        "value",
+    )
+
+    @property
+    def expanded_value(self):
+        """
+        See the class documentation.
+        """
+        return self.expanded_value_w_args()
+
+    def expanded_value_w_args(self, *args):
+        """
+        Returns the expanded value of the variable/function. Any arguments
+        passed will be substituted for $(1), $(2), etc.
+
+        Raises a KconfigError if the expansion seems to be stuck in a loop.
+        """
+        return self.kconfig._fn_val((self.name,) + args)
+
+    def __repr__(self):
+        return "<variable {}, {}, value '{}'>" \
+               .format(self.name,
+                       "recursive" if self.is_recursive else "immediate",
+                       self.value)
+
+
+class KconfigError(Exception):
+    """
+    Exception raised for Kconfig-related errors.
+
+    KconfigError and KconfigSyntaxError are the same class. The
+    KconfigSyntaxError alias is only maintained for backwards compatibility.
+    """
+
+KconfigSyntaxError = KconfigError  # Backwards compatibility
+
+
+class InternalError(Exception):
+    "Never raised. Kept around for backwards compatibility."
+
+
+# Workaround:
+#
+# If 'errno' and 'strerror' are set on IOError, then __str__() always returns
+# "[Errno <errno>] <strerror>", ignoring any custom message passed to the
+# constructor. By defining our own subclass, we can use a custom message while
+# also providing 'errno', 'strerror', and 'filename' to scripts.
+class _KconfigIOError(IOError):
+    def __init__(self, ioerror, msg):
+        self.msg = msg
+        super(_KconfigIOError, self).__init__(
+            ioerror.errno, ioerror.strerror, ioerror.filename)
+
+    def __str__(self):
+        return self.msg
+
+
+#
+# Public functions
+#
+
+
+def expr_value(expr):
+    """
+    Evaluates the expression 'expr' to a tristate value. Returns 0 (n), 1 (m),
+    or 2 (y).
+
+    'expr' must be an already-parsed expression from a Symbol, Choice, or
+    MenuNode property. To evaluate an expression represented as a string, use
+    Kconfig.eval_string().
+
+    Passing subexpressions of expressions to this function works as expected.
+    """
+    if expr.__class__ is not tuple:
+        return expr.tri_value
+
+    if expr[0] is AND:
+        v1 = expr_value(expr[1])
+        # Short-circuit the n case as an optimization (~5% faster
+        # allnoconfig.py and allyesconfig.py, as of writing)
+        return 0 if not v1 else min(v1, expr_value(expr[2]))
+
+    if expr[0] is OR:
+        v1 = expr_value(expr[1])
+        # Short-circuit the y case as an optimization
+        return 2 if v1 == 2 else max(v1, expr_value(expr[2]))
+
+    if expr[0] is NOT:
+        return 2 - expr_value(expr[1])
+
+    # Relation
+    #
+    # Implements <, <=, >, >= comparisons as well. These were added to
+    # kconfig in 31847b67 (kconfig: allow use of relations other than
+    # (in)equality).
+
+    rel, v1, v2 = expr
+
+    # If both operands are strings...
+    if v1.orig_type is STRING and v2.orig_type is STRING:
+        # ...then compare them lexicographically
+        comp = _strcmp(v1.str_value, v2.str_value)
+    else:
+        # Otherwise, try to compare them as numbers
+        try:
+            comp = _sym_to_num(v1) - _sym_to_num(v2)
+        except ValueError:
+            # Fall back on a lexicographic comparison if the operands don't
+            # parse as numbers
+            comp = _strcmp(v1.str_value, v2.str_value)
+
+    return 2*(comp == 0 if rel is EQUAL else
+              comp != 0 if rel is UNEQUAL else
+              comp <  0 if rel is LESS else
+              comp <= 0 if rel is LESS_EQUAL else
+              comp >  0 if rel is GREATER else
+              comp >= 0)
+
+
+def standard_sc_expr_str(sc):
+    """
+    Standard symbol/choice printing function. Uses plain Kconfig syntax, and
+    displays choices as <choice> (or <choice NAME>, for named choices).
+
+    See expr_str().
+    """
+    if sc.__class__ is Symbol:
+        if sc.is_constant and sc.name not in STR_TO_TRI:
+            return '"{}"'.format(escape(sc.name))
+        return sc.name
+
+    return "<choice {}>".format(sc.name) if sc.name else "<choice>"
+
+
+def expr_str(expr, sc_expr_str_fn=standard_sc_expr_str):
+    """
+    Returns the string representation of the expression 'expr', as in a Kconfig
+    file.
+
+    Passing subexpressions of expressions to this function works as expected.
+
+    sc_expr_str_fn (default: standard_sc_expr_str):
+      This function is called for every symbol/choice (hence "sc") appearing in
+      the expression, with the symbol/choice as the argument. It is expected to
+      return a string to be used for the symbol/choice.
+
+      This can be used e.g. to turn symbols/choices into links when generating
+      documentation, or for printing the value of each symbol/choice after it.
+
+      Note that quoted values are represented as constants symbols
+      (Symbol.is_constant == True).
+    """
+    if expr.__class__ is not tuple:
+        return sc_expr_str_fn(expr)
+
+    if expr[0] is AND:
+        return "{} && {}".format(_parenthesize(expr[1], OR, sc_expr_str_fn),
+                                 _parenthesize(expr[2], OR, sc_expr_str_fn))
+
+    if expr[0] is OR:
+        # This turns A && B || C && D into "(A && B) || (C && D)", which is
+        # redundant, but more readable
+        return "{} || {}".format(_parenthesize(expr[1], AND, sc_expr_str_fn),
+                                 _parenthesize(expr[2], AND, sc_expr_str_fn))
+
+    if expr[0] is NOT:
+        if expr[1].__class__ is tuple:
+            return "!({})".format(expr_str(expr[1], sc_expr_str_fn))
+        return "!" + sc_expr_str_fn(expr[1])  # Symbol
+
+    # Relation
+    #
+    # Relation operands are always symbols (quoted strings are constant
+    # symbols)
+    return "{} {} {}".format(sc_expr_str_fn(expr[1]), REL_TO_STR[expr[0]],
+                             sc_expr_str_fn(expr[2]))
+
+
+def expr_items(expr):
+    """
+    Returns a set() of all items (symbols and choices) that appear in the
+    expression 'expr'.
+
+    Passing subexpressions of expressions to this function works as expected.
+    """
+    res = set()
+
+    def rec(subexpr):
+        if subexpr.__class__ is tuple:
+            # AND, OR, NOT, or relation
+
+            rec(subexpr[1])
+
+            # NOTs only have a single operand
+            if subexpr[0] is not NOT:
+                rec(subexpr[2])
+
+        else:
+            # Symbol or choice
+            res.add(subexpr)
+
+    rec(expr)
+    return res
+
+
+def split_expr(expr, op):
+    """
+    Returns a list containing the top-level AND or OR operands in the
+    expression 'expr', in the same (left-to-right) order as they appear in
+    the expression.
+
+    This can be handy e.g. for splitting (weak) reverse dependencies
+    from 'select' and 'imply' into individual selects/implies.
+
+    op:
+      Either AND to get AND operands, or OR to get OR operands.
+
+      (Having this as an operand might be more future-safe than having two
+      hardcoded functions.)
+
+
+    Pseudo-code examples:
+
+      split_expr( A                    , OR  )  ->  [A]
+      split_expr( A && B               , OR  )  ->  [A && B]
+      split_expr( A || B               , OR  )  ->  [A, B]
+      split_expr( A || B               , AND )  ->  [A || B]
+      split_expr( A || B || (C && D)   , OR  )  ->  [A, B, C && D]
+
+      # Second || is not at the top level
+      split_expr( A || (B && (C || D)) , OR )  ->  [A, B && (C || D)]
+
+      # Parentheses don't matter as long as we stay at the top level (don't
+      # encounter any non-'op' nodes)
+      split_expr( (A || B) || C        , OR )  ->  [A, B, C]
+      split_expr( A || (B || C)        , OR )  ->  [A, B, C]
+    """
+    res = []
+
+    def rec(subexpr):
+        if subexpr.__class__ is tuple and subexpr[0] is op:
+            rec(subexpr[1])
+            rec(subexpr[2])
+        else:
+            res.append(subexpr)
+
+    rec(expr)
+    return res
+
+
+def escape(s):
+    r"""
+    Escapes the string 's' in the same fashion as is done for display in
+    Kconfig format and when writing strings to a .config file. " and \ are
+    replaced by \" and \\, respectively.
+    """
+    # \ must be escaped before " to avoid double escaping
+    return s.replace("\\", r"\\").replace('"', r'\"')
+
+
+def unescape(s):
+    r"""
+    Unescapes the string 's'. \ followed by any character is replaced with just
+    that character. Used internally when reading .config files.
+    """
+    return _unescape_sub(r"\1", s)
+
+# unescape() helper
+_unescape_sub = re.compile(r"\\(.)").sub
+
+
+def standard_kconfig(description=None):
+    """
+    Argument parsing helper for tools that take a single optional Kconfig file
+    argument (default: Kconfig). Returns the Kconfig instance for the parsed
+    configuration. Uses argparse internally.
+
+    Exits with sys.exit() (which raises SystemExit) on errors.
+
+    description (default: None):
+      The 'description' passed to argparse.ArgumentParser().
+      argparse.RawDescriptionHelpFormatter is used, so formatting is preserved.
+    """
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=description)
+
+    parser.add_argument(
+        "kconfig",
+        metavar="KCONFIG",
+        default="Kconfig",
+        nargs="?",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    return Kconfig(parser.parse_args().kconfig, suppress_traceback=True)
+
+
+def standard_config_filename():
+    """
+    Helper for tools. Returns the value of KCONFIG_CONFIG (which specifies the
+    .config file to load/save) if it is set, and ".config" otherwise.
+
+    Calling load_config() with filename=None might give the behavior you want,
+    without having to use this function.
+    """
+    return os.getenv("KCONFIG_CONFIG", ".config")
+
+
+def load_allconfig(kconf, filename):
+    """
+    Use Kconfig.load_allconfig() instead, which was added in Kconfiglib 13.4.0.
+    Supported for backwards compatibility. Might be removed at some point after
+    a long period of deprecation warnings.
+    """
+    allconfig = os.getenv("KCONFIG_ALLCONFIG")
+    if allconfig is None:
+        return
+
+    def std_msg(e):
+        # "Upcasts" a _KconfigIOError to an IOError, removing the custom
+        # __str__() message. The standard message is better here.
+        #
+        # This might also convert an OSError to an IOError in obscure cases,
+        # but it's probably not a big deal. The distinction is shaky (see
+        # PEP-3151).
+        return IOError(e.errno, e.strerror, e.filename)
+
+    old_warn_assign_override = kconf.warn_assign_override
+    old_warn_assign_redun = kconf.warn_assign_redun
+    kconf.warn_assign_override = kconf.warn_assign_redun = False
+
+    if allconfig in ("", "1"):
+        try:
+            print(kconf.load_config(filename, False))
+        except EnvironmentError as e1:
+            try:
+                print(kconf.load_config("all.config", False))
+            except EnvironmentError as e2:
+                sys.exit("error: KCONFIG_ALLCONFIG is set, but neither {} "
+                         "nor all.config could be opened: {}, {}"
+                         .format(filename, std_msg(e1), std_msg(e2)))
+    else:
+        try:
+            print(kconf.load_config(allconfig, False))
+        except EnvironmentError as e:
+            sys.exit("error: KCONFIG_ALLCONFIG is set to '{}', which "
+                     "could not be opened: {}"
+                     .format(allconfig, std_msg(e)))
+
+    kconf.warn_assign_override = old_warn_assign_override
+    kconf.warn_assign_redun = old_warn_assign_redun
+
+
+#
+# Internal functions
+#
+
+
+def _visibility(sc):
+    # Symbols and Choices have a "visibility" that acts as an upper bound on
+    # the values a user can set for them, corresponding to the visibility in
+    # e.g. 'make menuconfig'. This function calculates the visibility for the
+    # Symbol or Choice 'sc' -- the logic is nearly identical.
+
+    vis = 0
+
+    for node in sc.nodes:
+        if node.prompt:
+            vis = max(vis, expr_value(node.prompt[1]))
+
+    if sc.__class__ is Symbol and sc.choice:
+        if sc.choice.orig_type is TRISTATE and \
+           sc.orig_type is not TRISTATE and sc.choice.tri_value != 2:
+            # Non-tristate choice symbols are only visible in y mode
+            return 0
+
+        if sc.orig_type is TRISTATE and vis == 1 and sc.choice.tri_value == 2:
+            # Choice symbols with m visibility are not visible in y mode
+            return 0
+
+    # Promote m to y if we're dealing with a non-tristate (possibly due to
+    # modules being disabled)
+    if vis == 1 and sc.type is not TRISTATE:
+        return 2
+
+    return vis
+
+
+def _depend_on(sc, expr):
+    # Adds 'sc' (symbol or choice) as a "dependee" to all symbols in 'expr'.
+    # Constant symbols in 'expr' are skipped as they can never change value
+    # anyway.
+
+    if expr.__class__ is tuple:
+        # AND, OR, NOT, or relation
+
+        _depend_on(sc, expr[1])
+
+        # NOTs only have a single operand
+        if expr[0] is not NOT:
+            _depend_on(sc, expr[2])
+
+    elif not expr.is_constant:
+        # Non-constant symbol, or choice
+        expr._dependents.add(sc)
+
+
+def _parenthesize(expr, type_, sc_expr_str_fn):
+    # expr_str() helper. Adds parentheses around expressions of type 'type_'.
+
+    if expr.__class__ is tuple and expr[0] is type_:
+        return "({})".format(expr_str(expr, sc_expr_str_fn))
+    return expr_str(expr, sc_expr_str_fn)
+
+
+def _ordered_unique(lst):
+    # Returns 'lst' with any duplicates removed, preserving order. This hacky
+    # version seems to be a common idiom. It relies on short-circuit evaluation
+    # and set.add() returning None, which is falsy.
+
+    seen = set()
+    seen_add = seen.add
+    return [x for x in lst if x not in seen and not seen_add(x)]
+
+
+def _is_base_n(s, n):
+    try:
+        int(s, n)
+        return True
+    except ValueError:
+        return False
+
+
+def _strcmp(s1, s2):
+    # strcmp()-alike that returns -1, 0, or 1
+
+    return (s1 > s2) - (s1 < s2)
+
+
+def _sym_to_num(sym):
+    # expr_value() helper for converting a symbol to a number. Raises
+    # ValueError for symbols that can't be converted.
+
+    # For BOOL and TRISTATE, n/m/y count as 0/1/2. This mirrors 9059a3493ef
+    # ("kconfig: fix relational operators for bool and tristate symbols") in
+    # the C implementation.
+    return sym.tri_value if sym.orig_type in _BOOL_TRISTATE else \
+           int(sym.str_value, _TYPE_TO_BASE[sym.orig_type])
+
+
+def _touch_dep_file(path, sym_name):
+    # If sym_name is MY_SYM_NAME, touches my/sym/name.h. See the sync_deps()
+    # docstring.
+
+    sym_path = path + os.sep + sym_name.lower().replace("_", os.sep) + ".h"
+    sym_path_dir = dirname(sym_path)
+    if not exists(sym_path_dir):
+        os.makedirs(sym_path_dir, 0o755)
+
+    # A kind of truncating touch, mirroring the C tools
+    os.close(os.open(
+        sym_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644))
+
+
+def _save_old(path):
+    # See write_config()
+
+    def copy(src, dst):
+        # Import as needed, to save some startup time
+        import shutil
+        shutil.copyfile(src, dst)
+
+    if islink(path):
+        # Preserve symlinks
+        copy_fn = copy
+    elif hasattr(os, "replace"):
+        # Python 3 (3.3+) only. Best choice when available, because it
+        # removes <filename>.old on both *nix and Windows.
+        copy_fn = os.replace
+    elif os.name == "posix":
+        # Removes <filename>.old on POSIX systems
+        copy_fn = os.rename
+    else:
+        # Fall back on copying
+        copy_fn = copy
+
+    try:
+        copy_fn(path, path + ".old")
+    except Exception:
+        # Ignore errors from 'path' missing as well as other errors.
+        # <filename>.old file is usually more of a nice-to-have, and not worth
+        # erroring out over e.g. if <filename>.old happens to be a directory or
+        # <filename> is something like /dev/null.
+        pass
+
+
+def _locs(sc):
+    # Symbol/Choice.name_and_loc helper. Returns the "(defined at ...)" part of
+    # the string. 'sc' is a Symbol or Choice.
+
+    if sc.nodes:
+        return "(defined at {})".format(
+            ", ".join("{0.filename}:{0.linenr}".format(node)
+                      for node in sc.nodes))
+
+    return "(undefined)"
+
+
+# Menu manipulation
+
+
+def _expr_depends_on(expr, sym):
+    # Reimplementation of expr_depends_symbol() from mconf.c. Used to determine
+    # if a submenu should be implicitly created. This also influences which
+    # items inside choice statements are considered choice items.
+
+    if expr.__class__ is not tuple:
+        return expr is sym
+
+    if expr[0] in _EQUAL_UNEQUAL:
+        # Check for one of the following:
+        # sym = m/y, m/y = sym, sym != n, n != sym
+
+        left, right = expr[1:]
+
+        if right is sym:
+            left, right = right, left
+        elif left is not sym:
+            return False
+
+        return (expr[0] is EQUAL and right is sym.kconfig.m or
+                                     right is sym.kconfig.y) or \
+               (expr[0] is UNEQUAL and right is sym.kconfig.n)
+
+    return expr[0] is AND and \
+           (_expr_depends_on(expr[1], sym) or
+            _expr_depends_on(expr[2], sym))
+
+
+def _auto_menu_dep(node1, node2):
+    # Returns True if node2 has an "automatic menu dependency" on node1. If
+    # node2 has a prompt, we check its condition. Otherwise, we look directly
+    # at node2.dep.
+
+    return _expr_depends_on(node2.prompt[1] if node2.prompt else node2.dep,
+                            node1.item)
+
+
+def _flatten(node):
+    # "Flattens" menu nodes without prompts (e.g. 'if' nodes and non-visible
+    # symbols with children from automatic menu creation) so that their
+    # children appear after them instead. This gives a clean menu structure
+    # with no unexpected "jumps" in the indentation.
+    #
+    # Do not flatten promptless choices (which can appear "legitimately" if a
+    # named choice is defined in multiple locations to add on symbols). It
+    # looks confusing, and the menuconfig already shows all choice symbols if
+    # you enter the choice at some location with a prompt.
+
+    while node:
+        if node.list and not node.prompt and \
+           node.item.__class__ is not Choice:
+
+            last_node = node.list
+            while 1:
+                last_node.parent = node.parent
+                if not last_node.next:
+                    break
+                last_node = last_node.next
+
+            last_node.next = node.next
+            node.next = node.list
+            node.list = None
+
+        node = node.next
+
+
+def _remove_ifs(node):
+    # Removes 'if' nodes (which can be recognized by MenuNode.item being None),
+    # which are assumed to already have been flattened. The C implementation
+    # doesn't bother to do this, but we expose the menu tree directly, and it
+    # makes it nicer to work with.
+
+    cur = node.list
+    while cur and not cur.item:
+        cur = cur.next
+
+    node.list = cur
+
+    while cur:
+        next = cur.next
+        while next and not next.item:
+            next = next.next
+
+        # Equivalent to
+        #
+        #   cur.next = next
+        #   cur = next
+        #
+        # due to tricky Python semantics. The order matters.
+        cur.next = cur = next
+
+
+def _finalize_choice(node):
+    # Finalizes a choice, marking each symbol whose menu node has the choice as
+    # the parent as a choice symbol, and automatically determining types if not
+    # specified.
+
+    choice = node.item
+
+    cur = node.list
+    while cur:
+        if cur.item.__class__ is Symbol:
+            cur.item.choice = choice
+            choice.syms.append(cur.item)
+        cur = cur.next
+
+    # If no type is specified for the choice, its type is that of
+    # the first choice item with a specified type
+    if not choice.orig_type:
+        for item in choice.syms:
+            if item.orig_type:
+                choice.orig_type = item.orig_type
+                break
+
+    # Each choice item of UNKNOWN type gets the type of the choice
+    for sym in choice.syms:
+        if not sym.orig_type:
+            sym.orig_type = choice.orig_type
+
+
+def _check_dep_loop_sym(sym, ignore_choice):
+    # Detects dependency loops using depth-first search on the dependency graph
+    # (which is calculated earlier in Kconfig._build_dep()).
+    #
+    # Algorithm:
+    #
+    #  1. Symbols/choices start out with _visited = 0, meaning unvisited.
+    #
+    #  2. When a symbol/choice is first visited, _visited is set to 1, meaning
+    #     "visited, potentially part of a dependency loop". The recursive
+    #     search then continues from the symbol/choice.
+    #
+    #  3. If we run into a symbol/choice X with _visited already set to 1,
+    #     there's a dependency loop. The loop is found on the call stack by
+    #     recording symbols while returning ("on the way back") until X is seen
+    #     again.
+    #
+    #  4. Once a symbol/choice and all its dependencies (or dependents in this
+    #     case) have been checked recursively without detecting any loops, its
+    #     _visited is set to 2, meaning "visited, not part of a dependency
+    #     loop".
+    #
+    #     This saves work if we run into the symbol/choice again in later calls
+    #     to _check_dep_loop_sym(). We just return immediately.
+    #
+    # Choices complicate things, as every choice symbol depends on every other
+    # choice symbol in a sense. When a choice is "entered" via a choice symbol
+    # X, we visit all choice symbols from the choice except X, and prevent
+    # immediately revisiting the choice with a flag (ignore_choice).
+    #
+    # Maybe there's a better way to handle this (different flags or the
+    # like...)
+
+    if not sym._visited:
+        # sym._visited == 0, unvisited
+
+        sym._visited = 1
+
+        for dep in sym._dependents:
+            # Choices show up in Symbol._dependents when the choice has the
+            # symbol in a 'prompt' or 'default' condition (e.g.
+            # 'default ... if SYM').
+            #
+            # Since we aren't entering the choice via a choice symbol, all
+            # choice symbols need to be checked, hence the None.
+            loop = _check_dep_loop_choice(dep, None) \
+                   if dep.__class__ is Choice \
+                   else _check_dep_loop_sym(dep, False)
+
+            if loop:
+                # Dependency loop found
+                return _found_dep_loop(loop, sym)
+
+        if sym.choice and not ignore_choice:
+            loop = _check_dep_loop_choice(sym.choice, sym)
+            if loop:
+                # Dependency loop found
+                return _found_dep_loop(loop, sym)
+
+        # The symbol is not part of a dependency loop
+        sym._visited = 2
+
+        # No dependency loop found
+        return None
+
+    if sym._visited == 2:
+        # The symbol was checked earlier and is already known to not be part of
+        # a dependency loop
+        return None
+
+    # sym._visited == 1, found a dependency loop. Return the symbol as the
+    # first element in it.
+    return (sym,)
+
+
+def _check_dep_loop_choice(choice, skip):
+    if not choice._visited:
+        # choice._visited == 0, unvisited
+
+        choice._visited = 1
+
+        # Check for loops involving choice symbols. If we came here via a
+        # choice symbol, skip that one, as we'd get a false positive
+        # '<sym FOO> -> <choice> -> <sym FOO>' loop otherwise.
+        for sym in choice.syms:
+            if sym is not skip:
+                # Prevent the choice from being immediately re-entered via the
+                # "is a choice symbol" path by passing True
+                loop = _check_dep_loop_sym(sym, True)
+                if loop:
+                    # Dependency loop found
+                    return _found_dep_loop(loop, choice)
+
+        # The choice is not part of a dependency loop
+        choice._visited = 2
+
+        # No dependency loop found
+        return None
+
+    if choice._visited == 2:
+        # The choice was checked earlier and is already known to not be part of
+        # a dependency loop
+        return None
+
+    # choice._visited == 1, found a dependency loop. Return the choice as the
+    # first element in it.
+    return (choice,)
+
+
+def _found_dep_loop(loop, cur):
+    # Called "on the way back" when we know we have a loop
+
+    # Is the symbol/choice 'cur' where the loop started?
+    if cur is not loop[0]:
+        # Nope, it's just a part of the loop
+        return loop + (cur,)
+
+    # Yep, we have the entire loop. Throw an exception that shows it.
+
+    msg = "\nDependency loop\n" \
+            "===============\n\n"
+
+    for item in loop:
+        if item is not loop[0]:
+            msg += "...depends on "
+            if item.__class__ is Symbol and item.choice:
+                msg += "the choice symbol "
+
+        msg += "{}, with definition...\n\n{}\n\n" \
+               .format(item.name_and_loc, item)
+
+        # Small wart: Since we reuse the already calculated
+        # Symbol/Choice._dependents sets for recursive dependency detection, we
+        # lose information on whether a dependency came from a 'select'/'imply'
+        # condition or e.g. a 'depends on'.
+        #
+        # This might cause selecting symbols to "disappear". For example,
+        # a symbol B having 'select A if C' gives a direct dependency from A to
+        # C, since it corresponds to a reverse dependency of B && C.
+        #
+        # Always print reverse dependencies for symbols that have them to make
+        # sure information isn't lost. I wonder if there's some neat way to
+        # improve this.
+
+        if item.__class__ is Symbol:
+            if item.rev_dep is not item.kconfig.n:
+                msg += "(select-related dependencies: {})\n\n" \
+                       .format(expr_str(item.rev_dep))
+
+            if item.weak_rev_dep is not item.kconfig.n:
+                msg += "(imply-related dependencies: {})\n\n" \
+                       .format(expr_str(item.rev_dep))
+
+    msg += "...depends again on " + loop[0].name_and_loc
+
+    raise KconfigError(msg)
+
+
+def _decoding_error(e, filename, macro_linenr=None):
+    # Gives the filename and context for UnicodeDecodeError's, which are a pain
+    # to debug otherwise. 'e' is the UnicodeDecodeError object.
+    #
+    # If the decoding error is for the output of a $(shell,...) command,
+    # macro_linenr holds the line number where it was run (the exact line
+    # number isn't available for decoding errors in files).
+
+    raise KconfigError(
+        "\n"
+        "Malformed {} in {}\n"
+        "Context: {}\n"
+        "Problematic data: {}\n"
+        "Reason: {}".format(
+            e.encoding,
+            "'{}'".format(filename) if macro_linenr is None else
+                "output from macro at {}:{}".format(filename, macro_linenr),
+            e.object[max(e.start - 40, 0):e.end + 40],
+            e.object[e.start:e.end],
+            e.reason))
+
+
+def _warn_verbose_deprecated(fn_name):
+    sys.stderr.write(
+        "Deprecation warning: {0}()'s 'verbose' argument has no effect. Since "
+        "Kconfiglib 12.0.0, the message is returned from {0}() instead, "
+        "and is always generated. Do e.g. print(kconf.{0}()) if you want to "
+        "want to show a message like \"Loaded configuration '.config'\" on "
+        "stdout. The old API required ugly hacks to reuse messages in "
+        "configuration interfaces.\n".format(fn_name))
+
+
+# Predefined preprocessor functions
+
+
+def _filename_fn(kconf, _):
+    return kconf.filename
+
+
+def _lineno_fn(kconf, _):
+    return str(kconf.linenr)
+
+
+def _info_fn(kconf, _, msg):
+    print("{}:{}: {}".format(kconf.filename, kconf.linenr, msg))
+
+    return ""
+
+
+def _warning_if_fn(kconf, _, cond, msg):
+    if cond == "y":
+        kconf._warn(msg, kconf.filename, kconf.linenr)
+
+    return ""
+
+
+def _error_if_fn(kconf, _, cond, msg):
+    if cond == "y":
+        raise KconfigError("{}:{}: {}".format(
+            kconf.filename, kconf.linenr, msg))
+
+    return ""
+
+
+def _shell_fn(kconf, _, command):
+    import subprocess  # Only import as needed, to save some startup time
+
+    stdout, stderr = subprocess.Popen(
+        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    ).communicate()
+
+    if not _IS_PY2:
+        try:
+            stdout = stdout.decode(kconf._encoding)
+            stderr = stderr.decode(kconf._encoding)
+        except UnicodeDecodeError as e:
+            _decoding_error(e, kconf.filename, kconf.linenr)
+
+    if stderr:
+        kconf._warn("'{}' wrote to stderr: {}".format(
+                        command, "\n".join(stderr.splitlines())),
+                    kconf.filename, kconf.linenr)
+
+    # Universal newlines with splitlines() (to prevent e.g. stray \r's in
+    # command output on Windows), trailing newline removal, and
+    # newline-to-space conversion.
+    #
+    # On Python 3 versions before 3.6, it's not possible to specify the
+    # encoding when passing universal_newlines=True to Popen() (the 'encoding'
+    # parameter was added in 3.6), so we do this manual version instead.
+    return "\n".join(stdout.splitlines()).rstrip("\n").replace("\n", " ")
+
+#
+# Global constants
+#
+
+TRI_TO_STR = {
+    0: "n",
+    1: "m",
+    2: "y",
+}
+
+STR_TO_TRI = {
+    "n": 0,
+    "m": 1,
+    "y": 2,
+}
+
+# Constant representing that there's no cached choice selection. This is
+# distinct from a cached None (no selection). Any object that's not None or a
+# Symbol will do. We test this with 'is'.
+_NO_CACHED_SELECTION = 0
+
+# Are we running on Python 2?
+_IS_PY2 = sys.version_info[0] < 3
+
+try:
+    _UNAME_RELEASE = os.uname()[2]
+except AttributeError:
+    # Only import as needed, to save some startup time
+    import platform
+    _UNAME_RELEASE = platform.uname()[2]
+
+# The token and type constants below are safe to test with 'is', which is a bit
+# faster (~30% faster on my machine, and a few % faster for total parsing
+# time), even without assuming Python's small integer optimization (which
+# caches small integer objects). The constants end up pointing to unique
+# integer objects, and since we consistently refer to them via the names below,
+# we always get the same object.
+#
+# Client code should use == though.
+
+# Tokens, with values 1, 2, ... . Avoiding 0 simplifies some checks by making
+# all tokens except empty strings truthy.
+(
+    _T_ALLNOCONFIG_Y,
+    _T_AND,
+    _T_BOOL,
+    _T_CHOICE,
+    _T_CLOSE_PAREN,
+    _T_COMMENT,
+    _T_CONFIG,
+    _T_CONTCHOICE,
+    _T_DEFAULT,
+    _T_DEFCONFIG_LIST,
+    _T_DEF_BOOL,
+    _T_DEF_HEX,
+    _T_DEF_INT,
+    _T_DEF_STRING,
+    _T_DEF_TRISTATE,
+    _T_DEPENDS,
+    _T_ENDCHOICE,
+    _T_ENDIF,
+    _T_ENDMENU,
+    _T_ENV,
+    _T_EQUAL,
+    _T_GREATER,
+    _T_GREATER_EQUAL,
+    _T_HELP,
+    _T_HEX,
+    _T_IF,
+    _T_IMPLY,
+    _T_INT,
+    _T_LESS,
+    _T_LESS_EQUAL,
+    _T_MAINMENU,
+    _T_MENU,
+    _T_MENUCONFIG,
+    _T_MODULES,
+    _T_NOT,
+    _T_ON,
+    _T_OPEN_PAREN,
+    _T_OPTION,
+    _T_OPTIONAL,
+    _T_OR,
+    _T_ORSOURCE,
+    _T_OSOURCE,
+    _T_PROMPT,
+    _T_RANGE,
+    _T_RSOURCE,
+    _T_SELECT,
+    _T_SOURCE,
+    _T_STRING,
+    _T_TRISTATE,
+    _T_UNEQUAL,
+    _T_VISIBLE,
+) = range(1, 52)
+
+# Keyword to token map, with the get() method assigned directly as a small
+# optimization
+_get_keyword = {
+    "---help---":     _T_HELP,
+    "allnoconfig_y":  _T_ALLNOCONFIG_Y,
+    "bool":           _T_BOOL,
+    "boolean":        _T_BOOL,
+    "choice":         _T_CHOICE,
+    "comment":        _T_COMMENT,
+    "config":         _T_CONFIG,
+    "cont_choice":    _T_CONTCHOICE,
+    "def_bool":       _T_DEF_BOOL,
+    "def_hex":        _T_DEF_HEX,
+    "def_int":        _T_DEF_INT,
+    "def_string":     _T_DEF_STRING,
+    "def_tristate":   _T_DEF_TRISTATE,
+    "default":        _T_DEFAULT,
+    "defconfig_list": _T_DEFCONFIG_LIST,
+    "depends":        _T_DEPENDS,
+    "endchoice":      _T_ENDCHOICE,
+    "endif":          _T_ENDIF,
+    "endmenu":        _T_ENDMENU,
+    "env":            _T_ENV,
+    "grsource":       _T_ORSOURCE,  # Backwards compatibility
+    "gsource":        _T_OSOURCE,   # Backwards compatibility
+    "help":           _T_HELP,
+    "hex":            _T_HEX,
+    "if":             _T_IF,
+    "imply":          _T_IMPLY,
+    "int":            _T_INT,
+    "mainmenu":       _T_MAINMENU,
+    "menu":           _T_MENU,
+    "menuconfig":     _T_MENUCONFIG,
+    "modules":        _T_MODULES,
+    "on":             _T_ON,
+    "option":         _T_OPTION,
+    "optional":       _T_OPTIONAL,
+    "orsource":       _T_ORSOURCE,
+    "osource":        _T_OSOURCE,
+    "prompt":         _T_PROMPT,
+    "range":          _T_RANGE,
+    "rsource":        _T_RSOURCE,
+    "select":         _T_SELECT,
+    "source":         _T_SOURCE,
+    "string":         _T_STRING,
+    "tristate":       _T_TRISTATE,
+    "visible":        _T_VISIBLE,
+}.get
+
+# The constants below match the value of the corresponding tokens to remove the
+# need for conversion
+
+# Node types
+MENU    = _T_MENU
+COMMENT = _T_COMMENT
+
+# Expression types
+AND           = _T_AND
+OR            = _T_OR
+NOT           = _T_NOT
+EQUAL         = _T_EQUAL
+UNEQUAL       = _T_UNEQUAL
+LESS          = _T_LESS
+LESS_EQUAL    = _T_LESS_EQUAL
+GREATER       = _T_GREATER
+GREATER_EQUAL = _T_GREATER_EQUAL
+
+REL_TO_STR = {
+    EQUAL:         "=",
+    UNEQUAL:       "!=",
+    LESS:          "<",
+    LESS_EQUAL:    "<=",
+    GREATER:       ">",
+    GREATER_EQUAL: ">=",
+}
+
+# Symbol/choice types. UNKNOWN is 0 (falsy) to simplify some checks.
+# Client code shouldn't rely on it though, as it was non-zero in
+# older versions.
+UNKNOWN  = 0
+BOOL     = _T_BOOL
+TRISTATE = _T_TRISTATE
+STRING   = _T_STRING
+INT      = _T_INT
+HEX      = _T_HEX
+
+TYPE_TO_STR = {
+    UNKNOWN:  "unknown",
+    BOOL:     "bool",
+    TRISTATE: "tristate",
+    STRING:   "string",
+    INT:      "int",
+    HEX:      "hex",
+}
+
+# Used in comparisons. 0 means the base is inferred from the format of the
+# string.
+_TYPE_TO_BASE = {
+    HEX:      16,
+    INT:      10,
+    STRING:   0,
+    UNKNOWN:  0,
+}
+
+# def_bool -> BOOL, etc.
+_DEF_TOKEN_TO_TYPE = {
+    _T_DEF_BOOL:     BOOL,
+    _T_DEF_HEX:      HEX,
+    _T_DEF_INT:      INT,
+    _T_DEF_STRING:   STRING,
+    _T_DEF_TRISTATE: TRISTATE,
+}
+
+# Tokens after which strings are expected. This is used to tell strings from
+# constant symbol references during tokenization, both of which are enclosed in
+# quotes.
+#
+# Identifier-like lexemes ("missing quotes") are also treated as strings after
+# these tokens. _T_CHOICE is included to avoid symbols being registered for
+# named choices.
+_STRING_LEX = frozenset({
+    _T_BOOL,
+    _T_CHOICE,
+    _T_COMMENT,
+    _T_CONTCHOICE,
+    _T_HEX,
+    _T_INT,
+    _T_MAINMENU,
+    _T_MENU,
+    _T_ORSOURCE,
+    _T_OSOURCE,
+    _T_PROMPT,
+    _T_RSOURCE,
+    _T_SOURCE,
+    _T_STRING,
+    _T_TRISTATE,
+})
+
+# Various sets for quick membership tests. Gives a single global lookup and
+# avoids creating temporary dicts/tuples.
+
+_TYPE_TOKENS = frozenset({
+    _T_BOOL,
+    _T_TRISTATE,
+    _T_INT,
+    _T_HEX,
+    _T_STRING,
+})
+
+_SOURCE_TOKENS = frozenset({
+    _T_SOURCE,
+    _T_RSOURCE,
+    _T_OSOURCE,
+    _T_ORSOURCE,
+})
+
+_REL_SOURCE_TOKENS = frozenset({
+    _T_RSOURCE,
+    _T_ORSOURCE,
+})
+
+# Obligatory (non-optional) sources
+_OBL_SOURCE_TOKENS = frozenset({
+    _T_SOURCE,
+    _T_RSOURCE,
+})
+
+_BOOL_TRISTATE = frozenset({
+    BOOL,
+    TRISTATE,
+})
+
+_BOOL_TRISTATE_UNKNOWN = frozenset({
+    BOOL,
+    TRISTATE,
+    UNKNOWN,
+})
+
+_INT_HEX = frozenset({
+    INT,
+    HEX,
+})
+
+_SYMBOL_CHOICE = frozenset({
+    Symbol,
+    Choice,
+})
+
+_MENU_COMMENT = frozenset({
+    MENU,
+    COMMENT,
+})
+
+_EQUAL_UNEQUAL = frozenset({
+    EQUAL,
+    UNEQUAL,
+})
+
+_RELATIONS = frozenset({
+    EQUAL,
+    UNEQUAL,
+    LESS,
+    LESS_EQUAL,
+    GREATER,
+    GREATER_EQUAL,
+})
+
+# Helper functions for getting compiled regular expressions, with the needed
+# matching function returned directly as a small optimization.
+#
+# Use ASCII regex matching on Python 3. It's already the default on Python 2.
+
+
+def _re_match(regex):
+    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).match
+
+
+def _re_search(regex):
+    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).search
+
+
+# Various regular expressions used during parsing
+
+# The initial token on a line. Also eats leading and trailing whitespace, so
+# that we can jump straight to the next token (or to the end of the line if
+# there is only one token).
+#
+# This regex will also fail to match for empty lines and comment lines.
+#
+# '$' is included to detect preprocessor variable assignments with macro
+# expansions in the left-hand side.
+_command_match = _re_match(r"\s*([A-Za-z0-9_$-]+)\s*")
+
+# An identifier/keyword after the first token. Also eats trailing whitespace.
+# '$' is included to detect identifiers containing macro expansions.
+_id_keyword_match = _re_match(r"([A-Za-z0-9_$/.-]+)\s*")
+
+# A fragment in the left-hand side of a preprocessor variable assignment. These
+# are the portions between macro expansions ($(foo)). Macros are supported in
+# the LHS (variable name).
+_assignment_lhs_fragment_match = _re_match("[A-Za-z0-9_-]*")
+
+# The assignment operator and value (right-hand side) in a preprocessor
+# variable assignment
+_assignment_rhs_match = _re_match(r"\s*(=|:=|\+=)\s*(.*)")
+
+# Special characters/strings while expanding a macro ('(', ')', ',', and '$(')
+_macro_special_search = _re_search(r"\(|\)|,|\$\(")
+
+# Special characters/strings while expanding a string (quotes, '\', and '$(')
+_string_special_search = _re_search(r'"|\'|\\|\$\(')
+
+# Special characters/strings while expanding a symbol name. Also includes
+# end-of-line, in case the macro is the last thing on the line.
+_name_special_search = _re_search(r'[^A-Za-z0-9_$/.-]|\$\(|$')
+
+# A valid right-hand side for an assignment to a string symbol in a .config
+# file, including escaped characters. Extracts the contents.
+_conf_string_match = _re_match(r'"((?:[^\\"]|\\.)*)"')
diff --git a/ext/Kconfiglib/listnewconfig.py b/ext/Kconfiglib/listnewconfig.py
new file mode 100755
index 0000000..8276de1
--- /dev/null
+++ b/ext/Kconfiglib/listnewconfig.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Lists all user-modifiable symbols that are not given a value in the
+configuration file. Usually, these are new symbols that have been added to the
+Kconfig files.
+
+The default configuration filename is '.config'. A different filename can be
+passed in the KCONFIG_CONFIG environment variable.
+"""
+from __future__ import print_function
+
+import argparse
+import sys
+
+from kconfiglib import Kconfig, BOOL, TRISTATE, INT, HEX, STRING, TRI_TO_STR
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--show-help", "-l",
+        action="store_true",
+        help="Show any help texts as well")
+
+    parser.add_argument(
+        "kconfig",
+        metavar="KCONFIG",
+        nargs="?",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    args = parser.parse_args()
+
+    kconf = Kconfig(args.kconfig, suppress_traceback=True)
+    # Make it possible to filter this message out
+    print(kconf.load_config(), file=sys.stderr)
+
+    for sym in kconf.unique_defined_syms:
+        # Only show symbols that can be toggled. Choice symbols are a special
+        # case in that sym.assignable will be (2,) (length 1) for visible
+        # symbols in choices in y mode, but they can still be toggled by
+        # selecting some other symbol.
+        if sym.user_value is None and \
+           (len(sym.assignable) > 1 or
+            (sym.visibility and (sym.orig_type in (INT, HEX, STRING) or
+                                 sym.choice))):
+
+            # Don't reuse the 'config_string' format for bool/tristate symbols,
+            # to show n-valued symbols as 'CONFIG_FOO=n' instead of
+            # '# CONFIG_FOO is not set'. This matches the C tools.
+            if sym.orig_type in (BOOL, TRISTATE):
+                s = "{}{}={}\n".format(kconf.config_prefix, sym.name,
+                                       TRI_TO_STR[sym.tri_value])
+            else:
+                s = sym.config_string
+
+            print(s, end="")
+            if args.show_help:
+                for node in sym.nodes:
+                    if node.help is not None:
+                        # Indent by two spaces. textwrap.indent() is not
+                        # available in Python 2 (it's 3.3+).
+                        print("\n".join("  " + line
+                                        for line in node.help.split("\n")))
+                        break
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/makefile.patch b/ext/Kconfiglib/makefile.patch
new file mode 100644
index 0000000..a617ebd
--- /dev/null
+++ b/ext/Kconfiglib/makefile.patch
@@ -0,0 +1,48 @@
+From 93daf46f309b0c8f86149ef58c4906387d054c22 Mon Sep 17 00:00:00 2001
+From: Ulf Magnusson <ulfalizer@gmail.com>
+Date: Tue, 9 Jun 2015 13:01:34 +0200
+Subject: [PATCH] Kconfiglib scripts/kconfig/Makefile patch
+
+---
+ scripts/kconfig/Makefile | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
+index 3f327e21f60e..8b7dd1292005 100644
+--- a/scripts/kconfig/Makefile
++++ b/scripts/kconfig/Makefile
+@@ -27,2 +27,31 @@ gconfig: $(obj)/gconf
+ 
++PHONY += scriptconfig iscriptconfig kmenuconfig guiconfig dumpvarsconfig
++
++PYTHONCMD ?= python
++kpython := PYTHONPATH=$(srctree)/Kconfiglib:$$PYTHONPATH $(PYTHONCMD)
++
++ifneq ($(filter scriptconfig,$(MAKECMDGOALS)),)
++ifndef SCRIPT
++$(error Use "make scriptconfig SCRIPT=<path to script> [SCRIPT_ARG=<argument>]")
++endif
++endif
++
++scriptconfig:
++	$(Q)$(kpython) $(SCRIPT) $(Kconfig) $(if $(SCRIPT_ARG),"$(SCRIPT_ARG)")
++
++iscriptconfig:
++	$(Q)$(kpython) -i -c \
++	  "import kconfiglib; \
++	   kconf = kconfiglib.Kconfig('$(Kconfig)'); \
++	   print('A Kconfig instance \'kconf\' for the architecture $(ARCH) has been created.')"
++
++kmenuconfig:
++	$(Q)$(kpython) $(srctree)/Kconfiglib/menuconfig.py $(Kconfig)
++
++guiconfig:
++	$(Q)$(kpython) $(srctree)/Kconfiglib/guiconfig.py $(Kconfig)
++
++dumpvarsconfig:
++	$(Q)$(kpython) $(srctree)/Kconfiglib/examples/dumpvars.py $(Kconfig)
++
+ menuconfig: $(obj)/mconf
+-- 
+2.20.1
+
diff --git a/ext/Kconfiglib/menuconfig.py b/ext/Kconfiglib/menuconfig.py
new file mode 100755
index 0000000..7e765d3
--- /dev/null
+++ b/ext/Kconfiglib/menuconfig.py
@@ -0,0 +1,3278 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Nordic Semiconductor ASA and Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Overview
+========
+
+A curses-based Python 2/3 menuconfig implementation. The interface should feel
+familiar to people used to mconf ('make menuconfig').
+
+Supports the same keys as mconf, and also supports a set of keybindings
+inspired by Vi:
+
+  J/K     : Down/Up
+  L       : Enter menu/Toggle item
+  H       : Leave menu
+  Ctrl-D/U: Page Down/Page Up
+  G/End   : Jump to end of list
+  g/Home  : Jump to beginning of list
+
+[Space] toggles values if possible, and enters menus otherwise. [Enter] works
+the other way around.
+
+The mconf feature where pressing a key jumps to a menu entry with that
+character in it in the current menu isn't supported. A jump-to feature for
+jumping directly to any symbol (including invisible symbols), choice, menu or
+comment (as in a Kconfig 'comment "Foo"') is available instead.
+
+A few different modes are available:
+
+  F: Toggle show-help mode, which shows the help text of the currently selected
+  item in the window at the bottom of the menu display. This is handy when
+  browsing through options.
+
+  C: Toggle show-name mode, which shows the symbol name before each symbol menu
+  entry
+
+  A: Toggle show-all mode, which shows all items, including currently invisible
+  items and items that lack a prompt. Invisible items are drawn in a different
+  style to make them stand out.
+
+
+Running
+=======
+
+menuconfig.py can be run either as a standalone executable or by calling the
+menuconfig() function with an existing Kconfig instance. The second option is a
+bit inflexible in that it will still load and save .config, etc.
+
+When run in standalone mode, the top-level Kconfig file to load can be passed
+as a command-line argument. With no argument, it defaults to "Kconfig".
+
+The KCONFIG_CONFIG environment variable specifies the .config file to load (if
+it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+
+$srctree is supported through Kconfiglib.
+
+
+Color schemes
+=============
+
+It is possible to customize the color scheme by setting the MENUCONFIG_STYLE
+environment variable. For example, setting it to 'aquatic' will enable an
+alternative, less yellow, more 'make menuconfig'-like color scheme, contributed
+by Mitja Horvat (pinkfluid).
+
+This is the current list of built-in styles:
+    - default       classic Kconfiglib theme with a yellow accent
+    - monochrome    colorless theme (uses only bold and standout) attributes,
+                    this style is used if the terminal doesn't support colors
+    - aquatic       blue-tinted style loosely resembling the lxdialog theme
+
+It is possible to customize the current style by changing colors of UI
+elements on the screen. This is the list of elements that can be stylized:
+
+    - path          Top row in the main display, with the menu path
+    - separator     Separator lines between windows. Also used for the top line
+                    in the symbol information display.
+    - list          List of items, e.g. the main display
+    - selection     Style for the selected item
+    - inv-list      Like list, but for invisible items. Used in show-all mode.
+    - inv-selection Like selection, but for invisible items. Used in show-all
+                    mode.
+    - help          Help text windows at the bottom of various fullscreen
+                    dialogs
+    - show-help     Window showing the help text in show-help mode
+    - frame         Frame around dialog boxes
+    - body          Body of dialog boxes
+    - edit          Edit box in pop-up dialogs
+    - jump-edit     Edit box in jump-to dialog
+    - text          Symbol information text
+
+The color definition is a comma separated list of attributes:
+
+    - fg:COLOR      Set the foreground/background colors. COLOR can be one of
+      * or *        the basic 16 colors (black, red, green, yellow, blue,
+    - bg:COLOR      magenta, cyan, white and brighter versions, for example,
+                    brightred). On terminals that support more than 8 colors,
+                    you can also directly put in a color number, e.g. fg:123
+                    (hexadecimal and octal constants are accepted as well).
+                    Colors outside the range -1..curses.COLORS-1 (which is
+                    terminal-dependent) are ignored (with a warning). The COLOR
+                    can be also specified using a RGB value in the HTML
+                    notation, for example #RRGGBB. If the terminal supports
+                    color changing, the color is rendered accurately.
+                    Otherwise, the visually nearest color is used.
+
+                    If the background or foreground color of an element is not
+                    specified, it defaults to -1, representing the default
+                    terminal foreground or background color.
+
+                    Note: On some terminals a bright version of the color
+                    implies bold.
+    - bold          Use bold text
+    - underline     Use underline text
+    - standout      Standout text attribute (reverse color)
+
+More often than not, some UI elements share the same color definition. In such
+cases the right value may specify an UI element from which the color definition
+will be copied. For example, "separator=help" will apply the current color
+definition for "help" to "separator".
+
+A keyword without the '=' is assumed to be a style template. The template name
+is looked up in the built-in styles list and the style definition is expanded
+in-place. With this, built-in styles can be used as basis for new styles.
+
+For example, take the aquatic theme and give it a red selection bar:
+
+MENUCONFIG_STYLE="aquatic selection=fg:white,bg:red"
+
+If there's an error in the style definition or if a missing style is assigned
+to, the assignment will be ignored, along with a warning being printed on
+stderr.
+
+The 'default' theme is always implicitly parsed first, so the following two
+settings have the same effect:
+
+    MENUCONFIG_STYLE="selection=fg:white,bg:red"
+    MENUCONFIG_STYLE="default selection=fg:white,bg:red"
+
+If the terminal doesn't support colors, the 'monochrome' theme is used, and
+MENUCONFIG_STYLE is ignored. The assumption is that the environment is broken
+somehow, and that the important thing is to get something usable.
+
+
+Other features
+==============
+
+  - Seamless terminal resizing
+
+  - No dependencies on *nix, as the 'curses' module is in the Python standard
+    library
+
+  - Unicode text entry
+
+  - Improved information screen compared to mconf:
+
+      * Expressions are split up by their top-level &&/|| operands to improve
+        readability
+
+      * Undefined symbols in expressions are pointed out
+
+      * Menus and comments have information displays
+
+      * Kconfig definitions are printed
+
+      * The include path is shown, listing the locations of the 'source'
+        statements that included the Kconfig file of the symbol (or other
+        item)
+
+
+Limitations
+===========
+
+Doesn't work out of the box on Windows, but can be made to work with
+
+    pip install windows-curses
+
+See the https://github.com/zephyrproject-rtos/windows-curses repository.
+"""
+from __future__ import print_function
+
+import os
+import sys
+
+_IS_WINDOWS = os.name == "nt"  # Are we running on Windows?
+
+try:
+    import curses
+except ImportError as e:
+    if not _IS_WINDOWS:
+        raise
+    sys.exit("""\
+menuconfig failed to import the standard Python 'curses' library. Try
+installing a package like windows-curses
+(https://github.com/zephyrproject-rtos/windows-curses) by running this command
+in cmd.exe:
+
+    pip install windows-curses
+
+Starting with Kconfiglib 13.0.0, windows-curses is no longer automatically
+installed when installing Kconfiglib via pip on Windows (because it breaks
+installation on MSYS2).
+
+Exception:
+{}: {}""".format(type(e).__name__, e))
+
+import errno
+import locale
+import re
+import textwrap
+
+from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \
+                       BOOL, TRISTATE, STRING, INT, HEX, \
+                       AND, OR, \
+                       expr_str, expr_value, split_expr, \
+                       standard_sc_expr_str, \
+                       TRI_TO_STR, TYPE_TO_STR, \
+                       standard_kconfig, standard_config_filename
+
+
+#
+# Configuration variables
+#
+
+# If True, try to change LC_CTYPE to a UTF-8 locale if it is set to the C
+# locale (which implies ASCII). This fixes curses Unicode I/O issues on systems
+# with bad defaults. ncurses configures itself from the locale settings.
+#
+# Related PEP: https://www.python.org/dev/peps/pep-0538/
+_CHANGE_C_LC_CTYPE_TO_UTF8 = True
+
+# How many steps an implicit submenu will be indented. Implicit submenus are
+# created when an item depends on the symbol before it. Note that symbols
+# defined with 'menuconfig' create a separate menu instead of indenting.
+_SUBMENU_INDENT = 4
+
+# Number of steps for Page Up/Down to jump
+_PG_JUMP = 6
+
+# Height of the help window in show-help mode
+_SHOW_HELP_HEIGHT = 8
+
+# How far the cursor needs to be from the edge of the window before it starts
+# to scroll. Used for the main menu display, the information display, the
+# search display, and for text boxes.
+_SCROLL_OFFSET = 5
+
+# Minimum width of dialogs that ask for text input
+_INPUT_DIALOG_MIN_WIDTH = 30
+
+# Number of arrows pointing up/down to draw when a window is scrolled
+_N_SCROLL_ARROWS = 14
+
+# Lines of help text shown at the bottom of the "main" display
+_MAIN_HELP_LINES = """
+[Space/Enter] Toggle/enter  [ESC] Leave menu           [S] Save
+[O] Load                    [?] Symbol info            [/] Jump to symbol
+[F] Toggle show-help mode   [C] Toggle show-name mode  [A] Toggle show-all mode
+[Q] Quit (prompts for save) [D] Save minimal config (advanced)
+"""[1:-1].split("\n")
+
+# Lines of help text shown at the bottom of the information dialog
+_INFO_HELP_LINES = """
+[ESC/q] Return to menu      [/] Jump to symbol
+"""[1:-1].split("\n")
+
+# Lines of help text shown at the bottom of the search dialog
+_JUMP_TO_HELP_LINES = """
+Type text to narrow the search. Regexes are supported (via Python's 're'
+module). The up/down cursor keys step in the list. [Enter] jumps to the
+selected symbol. [ESC] aborts the search. Type multiple space-separated
+strings/regexes to find entries that match all of them. Type Ctrl-F to
+view the help of the selected item without leaving the dialog.
+"""[1:-1].split("\n")
+
+#
+# Styling
+#
+
+_STYLES = {
+    "default": """
+    path=fg:black,bg:white,bold
+    separator=fg:black,bg:yellow,bold
+    list=fg:black,bg:white
+    selection=fg:white,bg:blue,bold
+    inv-list=fg:red,bg:white
+    inv-selection=fg:red,bg:blue
+    help=path
+    show-help=list
+    frame=fg:black,bg:yellow,bold
+    body=fg:white,bg:black
+    edit=fg:white,bg:blue
+    jump-edit=edit
+    text=list
+    """,
+
+    # This style is forced on terminals that do no support colors
+    "monochrome": """
+    path=bold
+    separator=bold,standout
+    list=
+    selection=bold,standout
+    inv-list=bold
+    inv-selection=bold,standout
+    help=bold
+    show-help=
+    frame=bold,standout
+    body=
+    edit=standout
+    jump-edit=
+    text=
+    """,
+
+    # Blue-tinted style loosely resembling lxdialog
+    "aquatic": """
+    path=fg:white,bg:blue
+    separator=fg:white,bg:cyan
+    help=path
+    frame=fg:white,bg:cyan
+    body=fg:white,bg:blue
+    edit=fg:black,bg:white
+    """
+}
+
+_NAMED_COLORS = {
+    # Basic colors
+    "black":         curses.COLOR_BLACK,
+    "red":           curses.COLOR_RED,
+    "green":         curses.COLOR_GREEN,
+    "yellow":        curses.COLOR_YELLOW,
+    "blue":          curses.COLOR_BLUE,
+    "magenta":       curses.COLOR_MAGENTA,
+    "cyan":          curses.COLOR_CYAN,
+    "white":         curses.COLOR_WHITE,
+
+    # Bright versions
+    "brightblack":   curses.COLOR_BLACK + 8,
+    "brightred":     curses.COLOR_RED + 8,
+    "brightgreen":   curses.COLOR_GREEN + 8,
+    "brightyellow":  curses.COLOR_YELLOW + 8,
+    "brightblue":    curses.COLOR_BLUE + 8,
+    "brightmagenta": curses.COLOR_MAGENTA + 8,
+    "brightcyan":    curses.COLOR_CYAN + 8,
+    "brightwhite":   curses.COLOR_WHITE + 8,
+
+    # Aliases
+    "purple":        curses.COLOR_MAGENTA,
+    "brightpurple":  curses.COLOR_MAGENTA + 8,
+}
+
+
+def _rgb_to_6cube(rgb):
+    # Converts an 888 RGB color to a 3-tuple (nice in that it's hashable)
+    # representing the closest xterm 256-color 6x6x6 color cube color.
+    #
+    # The xterm 256-color extension uses a RGB color palette with components in
+    # the range 0-5 (a 6x6x6 cube). The catch is that the mapping is nonlinear.
+    # Index 0 in the 6x6x6 cube is mapped to 0, index 1 to 95, then 135, 175,
+    # etc., in increments of 40. See the links below:
+    #
+    #   https://commons.wikimedia.org/wiki/File:Xterm_256color_chart.svg
+    #   https://github.com/tmux/tmux/blob/master/colour.c
+
+    # 48 is the middle ground between 0 and 95.
+    return tuple(0 if x < 48 else int(round(max(1, (x - 55)/40))) for x in rgb)
+
+
+def _6cube_to_rgb(r6g6b6):
+    # Returns the 888 RGB color for a 666 xterm color cube index
+
+    return tuple(0 if x == 0 else 40*x + 55 for x in r6g6b6)
+
+
+def _rgb_to_gray(rgb):
+    # Converts an 888 RGB color to the index of an xterm 256-color grayscale
+    # color with approx. the same perceived brightness
+
+    # Calculate the luminance (gray intensity) of the color. See
+    #   https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
+    # and
+    #   https://www.w3.org/TR/AERT/#color-contrast
+    luma = 0.299*rgb[0] + 0.587*rgb[1] + 0.114*rgb[2]
+
+    # Closest index in the grayscale palette, which starts at RGB 0x080808,
+    # with stepping 0x0A0A0A
+    index = int(round((luma - 8)/10))
+
+    # Clamp the index to 0-23, corresponding to 232-255
+    return max(0, min(index, 23))
+
+
+def _gray_to_rgb(index):
+    # Convert a grayscale index to its closet single RGB component
+
+    return 3*(10*index + 8,)  # Returns a 3-tuple
+
+
+# Obscure Python: We never pass a value for rgb2index, and it keeps pointing to
+# the same dict. This avoids a global.
+def _alloc_rgb(rgb, rgb2index={}):
+    # Initialize a new entry in the xterm palette to the given RGB color,
+    # returning its index. If the color has already been initialized, the index
+    # of the existing entry is returned.
+    #
+    # ncurses is palette-based, so we need to overwrite palette entries to make
+    # new colors.
+    #
+    # The colors from 0 to 15 are user-defined, and there's no way to query
+    # their RGB values, so we better leave them untouched. Also leave any
+    # hypothetical colors above 255 untouched (though we're unlikely to
+    # allocate that many colors anyway).
+
+    if rgb in rgb2index:
+        return rgb2index[rgb]
+
+    # Many terminals allow the user to customize the first 16 colors. Avoid
+    # changing their values.
+    color_index = 16 + len(rgb2index)
+    if color_index >= 256:
+        _warn("Unable to allocate new RGB color ", rgb, ". Too many colors "
+              "allocated.")
+        return 0
+
+    # Map each RGB component from the range 0-255 to the range 0-1000, which is
+    # what curses uses
+    curses.init_color(color_index, *(int(round(1000*x/255)) for x in rgb))
+    rgb2index[rgb] = color_index
+
+    return color_index
+
+
+def _color_from_num(num):
+    # Returns the index of a color that looks like color 'num' in the xterm
+    # 256-color palette (but that might not be 'num', if we're redefining
+    # colors)
+
+    # - _alloc_rgb() won't touch the first 16 colors or any (hypothetical)
+    #   colors above 255, so we can always return them as-is
+    #
+    # - If the terminal doesn't support changing color definitions, or if
+    #   curses.COLORS < 256, _alloc_rgb() won't touch any color, and all colors
+    #   can be returned as-is
+    if num < 16 or num > 255 or not curses.can_change_color() or \
+       curses.COLORS < 256:
+        return num
+
+    # _alloc_rgb() might redefine colors, so emulate the xterm 256-color
+    # palette by allocating new colors instead of returning color numbers
+    # directly
+
+    if num < 232:
+        num -= 16
+        return _alloc_rgb(_6cube_to_rgb(((num//36)%6, (num//6)%6, num%6)))
+
+    return _alloc_rgb(_gray_to_rgb(num - 232))
+
+
+def _color_from_rgb(rgb):
+    # Returns the index of a color matching the 888 RGB color 'rgb'. The
+    # returned color might be an ~exact match or an approximation, depending on
+    # terminal capabilities.
+
+    # Calculates the Euclidean distance between two RGB colors
+    def dist(r1, r2): return sum((x - y)**2 for x, y in zip(r1, r2))
+
+    if curses.COLORS >= 256:
+        # Assume we're dealing with xterm's 256-color extension
+
+        if curses.can_change_color():
+            # Best case -- the terminal supports changing palette entries via
+            # curses.init_color(). Initialize an unused palette entry and
+            # return it.
+            return _alloc_rgb(rgb)
+
+        # Second best case -- pick between the xterm 256-color extension colors
+
+        # Closest 6-cube "color" color
+        c6 = _rgb_to_6cube(rgb)
+        # Closest gray color
+        gray = _rgb_to_gray(rgb)
+
+        if dist(rgb, _6cube_to_rgb(c6)) < dist(rgb, _gray_to_rgb(gray)):
+            # Use the "color" color from the 6x6x6 color palette. Calculate the
+            # color number from the 6-cube index triplet.
+            return 16 + 36*c6[0] + 6*c6[1] + c6[2]
+
+        # Use the color from the gray palette
+        return 232 + gray
+
+    # Terminal not in xterm 256-color mode. This is probably the best we can
+    # do, or is it? Submit patches. :)
+    min_dist = float('inf')
+    best = -1
+    for color in range(curses.COLORS):
+        # ncurses uses the range 0..1000. Scale that down to 0..255.
+        d = dist(rgb, tuple(int(round(255*c/1000))
+                            for c in curses.color_content(color)))
+        if d < min_dist:
+            min_dist = d
+            best = color
+
+    return best
+
+
+def _parse_style(style_str, parsing_default):
+    # Parses a string with '<element>=<style>' assignments. Anything not
+    # containing '=' is assumed to be a reference to a built-in style, which is
+    # treated as if all the assignments from the style were inserted at that
+    # point in the string.
+    #
+    # The parsing_default flag is set to True when we're implicitly parsing the
+    # 'default'/'monochrome' style, to prevent warnings.
+
+    for sline in style_str.split():
+        # Words without a "=" character represents a style template
+        if "=" in sline:
+            key, data = sline.split("=", 1)
+
+            # The 'default' style template is assumed to define all keys. We
+            # run _style_to_curses() for non-existing keys as well, so that we
+            # print warnings for errors to the right of '=' for those too.
+            if key not in _style and not parsing_default:
+                _warn("Ignoring non-existent style", key)
+
+            # If data is a reference to another key, copy its style
+            if data in _style:
+                _style[key] = _style[data]
+            else:
+                _style[key] = _style_to_curses(data)
+
+        elif sline in _STYLES:
+            # Recursively parse style template. Ignore styles that don't exist,
+            # for backwards/forwards compatibility.
+            _parse_style(_STYLES[sline], parsing_default)
+
+        else:
+            _warn("Ignoring non-existent style template", sline)
+
+# Dictionary mapping element types to the curses attributes used to display
+# them
+_style = {}
+
+
+def _style_to_curses(style_def):
+    # Parses a style definition string (<element>=<style>), returning
+    # a (fg_color, bg_color, attributes) tuple.
+
+    def parse_color(color_def):
+        color_def = color_def.split(":", 1)[1]
+
+        # HTML format, #RRGGBB
+        if re.match("#[A-Fa-f0-9]{6}", color_def):
+            return _color_from_rgb((
+                int(color_def[1:3], 16),
+                int(color_def[3:5], 16),
+                int(color_def[5:7], 16)))
+
+        if color_def in _NAMED_COLORS:
+            color_num = _color_from_num(_NAMED_COLORS[color_def])
+        else:
+            try:
+                color_num = _color_from_num(int(color_def, 0))
+            except ValueError:
+                _warn("Ignoring color", color_def, "that's neither "
+                      "predefined nor a number")
+                return -1
+
+        if not -1 <= color_num < curses.COLORS:
+            _warn("Ignoring color {}, which is outside the range "
+                  "-1..curses.COLORS-1 (-1..{})"
+                  .format(color_def, curses.COLORS - 1))
+            return -1
+
+        return color_num
+
+    fg_color = -1
+    bg_color = -1
+    attrs = 0
+
+    if style_def:
+        for field in style_def.split(","):
+            if field.startswith("fg:"):
+                fg_color = parse_color(field)
+            elif field.startswith("bg:"):
+                bg_color = parse_color(field)
+            elif field == "bold":
+                # A_BOLD tends to produce faint and hard-to-read text on the
+                # Windows console, especially with the old color scheme, before
+                # the introduction of
+                # https://blogs.msdn.microsoft.com/commandline/2017/08/02/updating-the-windows-console-colors/
+                attrs |= curses.A_NORMAL if _IS_WINDOWS else curses.A_BOLD
+            elif field == "standout":
+                attrs |= curses.A_STANDOUT
+            elif field == "underline":
+                attrs |= curses.A_UNDERLINE
+            else:
+                _warn("Ignoring unknown style attribute", field)
+
+    return _style_attr(fg_color, bg_color, attrs)
+
+
+def _init_styles():
+    if curses.has_colors():
+        try:
+            curses.use_default_colors()
+        except curses.error:
+            # Ignore errors on funky terminals that support colors but not
+            # using default colors. Worst it can do is break transparency and
+            # the like. Ran across this with the MSYS2/winpty setup in
+            # https://github.com/msys2/MINGW-packages/issues/5823, though there
+            # seems to be a lot of general brokenness there.
+            pass
+
+        # Use the 'default' theme as the base, and add any user-defined style
+        # settings from the environment
+        _parse_style("default", True)
+        if "MENUCONFIG_STYLE" in os.environ:
+            _parse_style(os.environ["MENUCONFIG_STYLE"], False)
+    else:
+        # Force the 'monochrome' theme if the terminal doesn't support colors.
+        # MENUCONFIG_STYLE is likely to mess things up here (though any colors
+        # would be ignored), so ignore it.
+        _parse_style("monochrome", True)
+
+
+# color_attribs holds the color pairs we've already created, indexed by a
+# (<foreground color>, <background color>) tuple.
+#
+# Obscure Python: We never pass a value for color_attribs, and it keeps
+# pointing to the same dict. This avoids a global.
+def _style_attr(fg_color, bg_color, attribs, color_attribs={}):
+    # Returns an attribute with the specified foreground and background color
+    # and the attributes in 'attribs'. Reuses color pairs already created if
+    # possible, and creates a new color pair otherwise.
+    #
+    # Returns 'attribs' if colors aren't supported.
+
+    if not curses.has_colors():
+        return attribs
+
+    if (fg_color, bg_color) not in color_attribs:
+        # Create new color pair. Color pair number 0 is hardcoded and cannot be
+        # changed, hence the +1s.
+        curses.init_pair(len(color_attribs) + 1, fg_color, bg_color)
+        color_attribs[(fg_color, bg_color)] = \
+            curses.color_pair(len(color_attribs) + 1)
+
+    return color_attribs[(fg_color, bg_color)] | attribs
+
+
+#
+# Main application
+#
+
+
+def _main():
+    menuconfig(standard_kconfig(__doc__))
+
+
+def menuconfig(kconf):
+    """
+    Launches the configuration interface, returning after the user exits.
+
+    kconf:
+      Kconfig instance to be configured
+    """
+    global _kconf
+    global _conf_filename
+    global _conf_changed
+    global _minconf_filename
+    global _show_all
+
+    _kconf = kconf
+
+    # Filename to save configuration to
+    _conf_filename = standard_config_filename()
+
+    # Load existing configuration and set _conf_changed True if it is outdated
+    _conf_changed = _load_config()
+
+    # Filename to save minimal configuration to
+    _minconf_filename = "defconfig"
+
+    # Any visible items in the top menu?
+    _show_all = False
+    if not _shown_nodes(kconf.top_node):
+        # Nothing visible. Start in show-all mode and try again.
+        _show_all = True
+        if not _shown_nodes(kconf.top_node):
+            # Give up. The implementation relies on always having a selected
+            # node.
+            print("Empty configuration -- nothing to configure.\n"
+                  "Check that environment variables are set properly.")
+            return
+
+    # Disable warnings. They get mangled in curses mode, and we deal with
+    # errors ourselves.
+    kconf.warn = False
+
+    # Make curses use the locale settings specified in the environment
+    locale.setlocale(locale.LC_ALL, "")
+
+    # Try to fix Unicode issues on systems with bad defaults
+    if _CHANGE_C_LC_CTYPE_TO_UTF8:
+        _change_c_lc_ctype_to_utf8()
+
+    # Get rid of the delay between pressing ESC and jumping to the parent menu,
+    # unless the user has set ESCDELAY (see ncurses(3)). This makes the UI much
+    # smoother to work with.
+    #
+    # Note: This is strictly pretty iffy, since escape codes for e.g. cursor
+    # keys start with ESC, but I've never seen it cause problems in practice
+    # (probably because it's unlikely that the escape code for a key would get
+    # split up across read()s, at least with a terminal emulator). Please
+    # report if you run into issues. Some suitable small default value could be
+    # used here instead in that case. Maybe it's silly to not put in the
+    # smallest imperceptible delay here already, though I don't like guessing.
+    #
+    # (From a quick glance at the ncurses source code, ESCDELAY might only be
+    # relevant for mouse events there, so maybe escapes are assumed to arrive
+    # in one piece already...)
+    os.environ.setdefault("ESCDELAY", "0")
+
+    # Enter curses mode. _menuconfig() returns a string to print on exit, after
+    # curses has been de-initialized.
+    print(curses.wrapper(_menuconfig))
+
+
+def _load_config():
+    # Loads any existing .config file. See the Kconfig.load_config() docstring.
+    #
+    # Returns True if .config is missing or outdated. We always prompt for
+    # saving the configuration in that case.
+
+    print(_kconf.load_config())
+    if not os.path.exists(_conf_filename):
+        # No .config
+        return True
+
+    return _needs_save()
+
+
+def _needs_save():
+    # Returns True if a just-loaded .config file is outdated (would get
+    # modified when saving)
+
+    if _kconf.missing_syms:
+        # Assignments to undefined symbols in the .config
+        return True
+
+    for sym in _kconf.unique_defined_syms:
+        if sym.user_value is None:
+            if sym.config_string:
+                # Unwritten symbol
+                return True
+        elif sym.orig_type in (BOOL, TRISTATE):
+            if sym.tri_value != sym.user_value:
+                # Written bool/tristate symbol, new value
+                return True
+        elif sym.str_value != sym.user_value:
+            # Written string/int/hex symbol, new value
+            return True
+
+    # No need to prompt for save
+    return False
+
+
+# Global variables used below:
+#
+#   _stdscr:
+#     stdscr from curses
+#
+#   _cur_menu:
+#     Menu node of the menu (or menuconfig symbol, or choice) currently being
+#     shown
+#
+#   _shown:
+#     List of items in _cur_menu that are shown (ignoring scrolling). In
+#     show-all mode, this list contains all items in _cur_menu. Otherwise, it
+#     contains just the visible items.
+#
+#   _sel_node_i:
+#     Index in _shown of the currently selected node
+#
+#   _menu_scroll:
+#     Index in _shown of the top row of the main display
+#
+#   _parent_screen_rows:
+#     List/stack of the row numbers that the selections in the parent menus
+#     appeared on. This is used to prevent the scrolling from jumping around
+#     when going in and out of menus.
+#
+#   _show_help/_show_name/_show_all:
+#     If True, the corresponding mode is on. See the module docstring.
+#
+#   _conf_filename:
+#     File to save the configuration to
+#
+#   _minconf_filename:
+#     File to save minimal configurations to
+#
+#   _conf_changed:
+#     True if the configuration has been changed. If False, we don't bother
+#     showing the save-and-quit dialog.
+#
+#     We reset this to False whenever the configuration is saved explicitly
+#     from the save dialog.
+
+
+def _menuconfig(stdscr):
+    # Logic for the main display, with the list of symbols, etc.
+
+    global _stdscr
+    global _conf_filename
+    global _conf_changed
+    global _minconf_filename
+    global _show_help
+    global _show_name
+
+    _stdscr = stdscr
+
+    _init()
+
+    while True:
+        _draw_main()
+        curses.doupdate()
+
+
+        c = _getch_compat(_menu_win)
+
+        if c == curses.KEY_RESIZE:
+            _resize_main()
+
+        elif c in (curses.KEY_DOWN, "j", "J"):
+            _select_next_menu_entry()
+
+        elif c in (curses.KEY_UP, "k", "K"):
+            _select_prev_menu_entry()
+
+        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
+            # Keep it simple. This way we get sane behavior for small windows,
+            # etc., for free.
+            for _ in range(_PG_JUMP):
+                _select_next_menu_entry()
+
+        elif c in (curses.KEY_PPAGE, "\x15"):  # Page Up/Ctrl-U
+            for _ in range(_PG_JUMP):
+                _select_prev_menu_entry()
+
+        elif c in (curses.KEY_END, "G"):
+            _select_last_menu_entry()
+
+        elif c in (curses.KEY_HOME, "g"):
+            _select_first_menu_entry()
+
+        elif c == " ":
+            # Toggle the node if possible
+            sel_node = _shown[_sel_node_i]
+            if not _change_node(sel_node):
+                _enter_menu(sel_node)
+
+        elif c in (curses.KEY_RIGHT, "\n", "l", "L"):
+            # Enter the node if possible
+            sel_node = _shown[_sel_node_i]
+            if not _enter_menu(sel_node):
+                _change_node(sel_node)
+
+        elif c in ("n", "N"):
+            _set_sel_node_tri_val(0)
+
+        elif c in ("m", "M"):
+            _set_sel_node_tri_val(1)
+
+        elif c in ("y", "Y"):
+            _set_sel_node_tri_val(2)
+
+        elif c in (curses.KEY_LEFT, curses.KEY_BACKSPACE, _ERASE_CHAR,
+                   "\x1B", "h", "H"):  # \x1B = ESC
+
+            if c == "\x1B" and _cur_menu is _kconf.top_node:
+                res = _quit_dialog()
+                if res:
+                    return res
+            else:
+                _leave_menu()
+
+        elif c in ("o", "O"):
+            _load_dialog()
+
+        elif c in ("s", "S"):
+            filename = _save_dialog(_kconf.write_config, _conf_filename,
+                                    "configuration")
+            if filename:
+                _conf_filename = filename
+                _conf_changed = False
+
+        elif c in ("d", "D"):
+            filename = _save_dialog(_kconf.write_min_config, _minconf_filename,
+                                    "minimal configuration")
+            if filename:
+                _minconf_filename = filename
+
+        elif c == "/":
+            _jump_to_dialog()
+            # The terminal might have been resized while the fullscreen jump-to
+            # dialog was open
+            _resize_main()
+
+        elif c == "?":
+            _info_dialog(_shown[_sel_node_i], False)
+            # The terminal might have been resized while the fullscreen info
+            # dialog was open
+            _resize_main()
+
+        elif c in ("f", "F"):
+            _show_help = not _show_help
+            _set_style(_help_win, "show-help" if _show_help else "help")
+            _resize_main()
+
+        elif c in ("c", "C"):
+            _show_name = not _show_name
+
+        elif c in ("a", "A"):
+            _toggle_show_all()
+
+        elif c in ("q", "Q"):
+            res = _quit_dialog()
+            if res:
+                return res
+
+
+def _quit_dialog():
+    if not _conf_changed:
+        return "No changes to save (for '{}')".format(_conf_filename)
+
+    while True:
+        c = _key_dialog(
+            "Quit",
+            " Save configuration?\n"
+            "\n"
+            "(Y)es  (N)o  (C)ancel",
+            "ync")
+
+        if c is None or c == "c":
+            return None
+
+        if c == "y":
+            # Returns a message to print
+            msg = _try_save(_kconf.write_config, _conf_filename, "configuration")
+            if msg:
+                return msg
+
+        elif c == "n":
+            return "Configuration ({}) was not saved".format(_conf_filename)
+
+
+def _init():
+    # Initializes the main display with the list of symbols, etc. Also does
+    # misc. global initialization that needs to happen after initializing
+    # curses.
+
+    global _ERASE_CHAR
+
+    global _path_win
+    global _top_sep_win
+    global _menu_win
+    global _bot_sep_win
+    global _help_win
+
+    global _parent_screen_rows
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    global _show_help
+    global _show_name
+
+    # Looking for this in addition to KEY_BACKSPACE (which is unreliable) makes
+    # backspace work with TERM=vt100. That makes it likely to work in sane
+    # environments.
+    _ERASE_CHAR = curses.erasechar()
+    if sys.version_info[0] >= 3:
+        # erasechar() returns a one-byte bytes object on Python 3. This sets
+        # _ERASE_CHAR to a blank string if it can't be decoded, which should be
+        # harmless.
+        _ERASE_CHAR = _ERASE_CHAR.decode("utf-8", "ignore")
+
+    _init_styles()
+
+    # Hide the cursor
+    _safe_curs_set(0)
+
+    # Initialize windows
+
+    # Top row, with menu path
+    _path_win = _styled_win("path")
+
+    # Separator below menu path, with title and arrows pointing up
+    _top_sep_win = _styled_win("separator")
+
+    # List of menu entries with symbols, etc.
+    _menu_win = _styled_win("list")
+    _menu_win.keypad(True)
+
+    # Row below menu list, with arrows pointing down
+    _bot_sep_win = _styled_win("separator")
+
+    # Help window with keys at the bottom. Shows help texts in show-help mode.
+    _help_win = _styled_win("help")
+
+    # The rows we'd like the nodes in the parent menus to appear on. This
+    # prevents the scroll from jumping around when going in and out of menus.
+    _parent_screen_rows = []
+
+    # Initial state
+
+    _cur_menu = _kconf.top_node
+    _shown = _shown_nodes(_cur_menu)
+    _sel_node_i = _menu_scroll = 0
+
+    _show_help = _show_name = False
+
+    # Give windows their initial size
+    _resize_main()
+
+
+def _resize_main():
+    # Resizes the main display, with the list of symbols, etc., to fill the
+    # terminal
+
+    global _menu_scroll
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    _path_win.resize(1, screen_width)
+    _top_sep_win.resize(1, screen_width)
+    _bot_sep_win.resize(1, screen_width)
+
+    help_win_height = _SHOW_HELP_HEIGHT if _show_help else \
+        len(_MAIN_HELP_LINES)
+
+    menu_win_height = screen_height - help_win_height - 3
+
+    if menu_win_height >= 1:
+        _menu_win.resize(menu_win_height, screen_width)
+        _help_win.resize(help_win_height, screen_width)
+
+        _top_sep_win.mvwin(1, 0)
+        _menu_win.mvwin(2, 0)
+        _bot_sep_win.mvwin(2 + menu_win_height, 0)
+        _help_win.mvwin(2 + menu_win_height + 1, 0)
+    else:
+        # Degenerate case. Give up on nice rendering and just prevent errors.
+
+        menu_win_height = 1
+
+        _menu_win.resize(1, screen_width)
+        _help_win.resize(1, screen_width)
+
+        for win in _top_sep_win, _menu_win, _bot_sep_win, _help_win:
+            win.mvwin(0, 0)
+
+    # Adjust the scroll so that the selected node is still within the window,
+    # if needed
+    if _sel_node_i - _menu_scroll >= menu_win_height:
+        _menu_scroll = _sel_node_i - menu_win_height + 1
+
+
+def _height(win):
+    # Returns the height of 'win'
+
+    return win.getmaxyx()[0]
+
+
+def _width(win):
+    # Returns the width of 'win'
+
+    return win.getmaxyx()[1]
+
+
+def _enter_menu(menu):
+    # Makes 'menu' the currently displayed menu. In addition to actual 'menu's,
+    # "menu" here includes choices and symbols defined with the 'menuconfig'
+    # keyword.
+    #
+    # Returns False if 'menu' can't be entered.
+
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    if not menu.is_menuconfig:
+        return False  # Not a menu
+
+    shown_sub = _shown_nodes(menu)
+    # Never enter empty menus. We depend on having a current node.
+    if not shown_sub:
+        return False
+
+    # Remember where the current node appears on the screen, so we can try
+    # to get it to appear in the same place when we leave the menu
+    _parent_screen_rows.append(_sel_node_i - _menu_scroll)
+
+    # Jump into menu
+    _cur_menu = menu
+    _shown = shown_sub
+    _sel_node_i = _menu_scroll = 0
+
+    if isinstance(menu.item, Choice):
+        _select_selected_choice_sym()
+
+    return True
+
+
+def _select_selected_choice_sym():
+    # Puts the cursor on the currently selected (y-valued) choice symbol, if
+    # any. Does nothing if if the choice has no selection (is not visible/in y
+    # mode).
+
+    global _sel_node_i
+
+    choice = _cur_menu.item
+    if choice.selection:
+        # Search through all menu nodes to handle choice symbols being defined
+        # in multiple locations
+        for node in choice.selection.nodes:
+            if node in _shown:
+                _sel_node_i = _shown.index(node)
+                _center_vertically()
+                return
+
+
+def _jump_to(node):
+    # Jumps directly to the menu node 'node'
+
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+    global _show_all
+    global _parent_screen_rows
+
+    # Clear remembered menu locations. We might not even have been in the
+    # parent menus before.
+    _parent_screen_rows = []
+
+    old_show_all = _show_all
+    jump_into = (isinstance(node.item, Choice) or node.item == MENU) and \
+                node.list
+
+    # If we're jumping to a non-empty choice or menu, jump to the first entry
+    # in it instead of jumping to its menu node
+    if jump_into:
+        _cur_menu = node
+        node = node.list
+    else:
+        _cur_menu = _parent_menu(node)
+
+    _shown = _shown_nodes(_cur_menu)
+    if node not in _shown:
+        # The node wouldn't be shown. Turn on show-all to show it.
+        _show_all = True
+        _shown = _shown_nodes(_cur_menu)
+
+    _sel_node_i = _shown.index(node)
+
+    if jump_into and not old_show_all and _show_all:
+        # If we're jumping into a choice or menu and were forced to turn on
+        # show-all because the first entry wasn't visible, try turning it off.
+        # That will land us at the first visible node if there are visible
+        # nodes, and is a no-op otherwise.
+        _toggle_show_all()
+
+    _center_vertically()
+
+    # If we're jumping to a non-empty choice, jump to the selected symbol, if
+    # any
+    if jump_into and isinstance(_cur_menu.item, Choice):
+        _select_selected_choice_sym()
+
+
+def _leave_menu():
+    # Jumps to the parent menu of the current menu. Does nothing if we're in
+    # the top menu.
+
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    if _cur_menu is _kconf.top_node:
+        return
+
+    # Jump to parent menu
+    parent = _parent_menu(_cur_menu)
+    _shown = _shown_nodes(parent)
+    _sel_node_i = _shown.index(_cur_menu)
+    _cur_menu = parent
+
+    # Try to make the menu entry appear on the same row on the screen as it did
+    # before we entered the menu.
+
+    if _parent_screen_rows:
+        # The terminal might have shrunk since we were last in the parent menu
+        screen_row = min(_parent_screen_rows.pop(), _height(_menu_win) - 1)
+        _menu_scroll = max(_sel_node_i - screen_row, 0)
+    else:
+        # No saved parent menu locations, meaning we jumped directly to some
+        # node earlier
+        _center_vertically()
+
+
+def _select_next_menu_entry():
+    # Selects the menu entry after the current one, adjusting the scroll if
+    # necessary. Does nothing if we're already at the last menu entry.
+
+    global _sel_node_i
+    global _menu_scroll
+
+    if _sel_node_i < len(_shown) - 1:
+        # Jump to the next node
+        _sel_node_i += 1
+
+        # If the new node is sufficiently close to the edge of the menu window
+        # (as determined by _SCROLL_OFFSET), increase the scroll by one. This
+        # gives nice and non-jumpy behavior even when
+        # _SCROLL_OFFSET >= _height(_menu_win).
+        if _sel_node_i >= _menu_scroll + _height(_menu_win) - _SCROLL_OFFSET \
+           and _menu_scroll < _max_scroll(_shown, _menu_win):
+
+            _menu_scroll += 1
+
+
+def _select_prev_menu_entry():
+    # Selects the menu entry before the current one, adjusting the scroll if
+    # necessary. Does nothing if we're already at the first menu entry.
+
+    global _sel_node_i
+    global _menu_scroll
+
+    if _sel_node_i > 0:
+        # Jump to the previous node
+        _sel_node_i -= 1
+
+        # See _select_next_menu_entry()
+        if _sel_node_i < _menu_scroll + _SCROLL_OFFSET:
+            _menu_scroll = max(_menu_scroll - 1, 0)
+
+
+def _select_last_menu_entry():
+    # Selects the last menu entry in the current menu
+
+    global _sel_node_i
+    global _menu_scroll
+
+    _sel_node_i = len(_shown) - 1
+    _menu_scroll = _max_scroll(_shown, _menu_win)
+
+
+def _select_first_menu_entry():
+    # Selects the first menu entry in the current menu
+
+    global _sel_node_i
+    global _menu_scroll
+
+    _sel_node_i = _menu_scroll = 0
+
+
+def _toggle_show_all():
+    # Toggles show-all mode on/off. If turning it off would give no visible
+    # items in the current menu, it is left on.
+
+    global _show_all
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    # Row on the screen the cursor is on. Preferably we want the same row to
+    # stay highlighted.
+    old_row = _sel_node_i - _menu_scroll
+
+    _show_all = not _show_all
+    # List of new nodes to be shown after toggling _show_all
+    new_shown = _shown_nodes(_cur_menu)
+
+    # Find a good node to select. The selected node might disappear if show-all
+    # mode is turned off.
+
+    # Select the previously selected node itself if it is still visible. If
+    # there are visible nodes before it, select the closest one.
+    for node in _shown[_sel_node_i::-1]:
+        if node in new_shown:
+            _sel_node_i = new_shown.index(node)
+            break
+    else:
+        # No visible nodes before the previously selected node. Select the
+        # closest visible node after it instead.
+        for node in _shown[_sel_node_i + 1:]:
+            if node in new_shown:
+                _sel_node_i = new_shown.index(node)
+                break
+        else:
+            # No visible nodes at all, meaning show-all was turned off inside
+            # an invisible menu. Don't allow that, as the implementation relies
+            # on always having a selected node.
+            _show_all = True
+            return
+
+    _shown = new_shown
+
+    # Try to make the cursor stay on the same row in the menu window. This
+    # might be impossible if too many nodes have disappeared above the node.
+    _menu_scroll = max(_sel_node_i - old_row, 0)
+
+
+def _center_vertically():
+    # Centers the selected node vertically, if possible
+
+    global _menu_scroll
+
+    _menu_scroll = min(max(_sel_node_i - _height(_menu_win)//2, 0),
+                       _max_scroll(_shown, _menu_win))
+
+
+def _draw_main():
+    # Draws the "main" display, with the list of symbols, the header, and the
+    # footer.
+    #
+    # This could be optimized to only update the windows that have actually
+    # changed, but keep it simple for now and let curses sort it out.
+
+    term_width = _width(_stdscr)
+
+    #
+    # Update the separator row below the menu path
+    #
+
+    _top_sep_win.erase()
+
+    # Draw arrows pointing up if the symbol window is scrolled down. Draw them
+    # before drawing the title, so the title ends up on top for small windows.
+    if _menu_scroll > 0:
+        _safe_hline(_top_sep_win, 0, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS)
+
+    # Add the 'mainmenu' text as the title, centered at the top
+    _safe_addstr(_top_sep_win,
+                 0, max((term_width - len(_kconf.mainmenu_text))//2, 0),
+                 _kconf.mainmenu_text)
+
+    _top_sep_win.noutrefresh()
+
+    # Note: The menu path at the top is deliberately updated last. See below.
+
+    #
+    # Update the symbol window
+    #
+
+    _menu_win.erase()
+
+    # Draw the _shown nodes starting from index _menu_scroll up to either as
+    # many as fit in the window, or to the end of _shown
+    for i in range(_menu_scroll,
+                   min(_menu_scroll + _height(_menu_win), len(_shown))):
+
+        node = _shown[i]
+
+        # The 'not _show_all' test avoids showing invisible items in red
+        # outside show-all mode, which could look confusing/broken. Invisible
+        # symbols show up outside show-all mode if an invisible symbol has
+        # visible children in an implicit (indented) menu.
+        if _visible(node) or not _show_all:
+            style = _style["selection" if i == _sel_node_i else "list"]
+        else:
+            style = _style["inv-selection" if i == _sel_node_i else "inv-list"]
+
+        _safe_addstr(_menu_win, i - _menu_scroll, 0, _node_str(node), style)
+
+    _menu_win.noutrefresh()
+
+    #
+    # Update the bottom separator window
+    #
+
+    _bot_sep_win.erase()
+
+    # Draw arrows pointing down if the symbol window is scrolled up
+    if _menu_scroll < _max_scroll(_shown, _menu_win):
+        _safe_hline(_bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
+
+    # Indicate when show-name/show-help/show-all mode is enabled
+    enabled_modes = []
+    if _show_help:
+        enabled_modes.append("show-help (toggle with [F])")
+    if _show_name:
+        enabled_modes.append("show-name")
+    if _show_all:
+        enabled_modes.append("show-all")
+    if enabled_modes:
+        s = " and ".join(enabled_modes) + " mode enabled"
+        _safe_addstr(_bot_sep_win, 0, max(term_width - len(s) - 2, 0), s)
+
+    _bot_sep_win.noutrefresh()
+
+    #
+    # Update the help window, which shows either key bindings or help texts
+    #
+
+    _help_win.erase()
+
+    if _show_help:
+        node = _shown[_sel_node_i]
+        if isinstance(node.item, (Symbol, Choice)) and node.help:
+            help_lines = textwrap.wrap(node.help, _width(_help_win))
+            for i in range(min(_height(_help_win), len(help_lines))):
+                _safe_addstr(_help_win, i, 0, help_lines[i])
+        else:
+            _safe_addstr(_help_win, 0, 0, "(no help)")
+    else:
+        for i, line in enumerate(_MAIN_HELP_LINES):
+            _safe_addstr(_help_win, i, 0, line)
+
+    _help_win.noutrefresh()
+
+    #
+    # Update the top row with the menu path.
+    #
+    # Doing this last leaves the cursor on the top row, which avoids some minor
+    # annoying jumpiness in gnome-terminal when reducing the height of the
+    # terminal. It seems to happen whenever the row with the cursor on it
+    # disappears.
+    #
+
+    _path_win.erase()
+
+    # Draw the menu path ("(Top) -> Menu -> Submenu -> ...")
+
+    menu_prompts = []
+
+    menu = _cur_menu
+    while menu is not _kconf.top_node:
+        # Promptless choices can be entered in show-all mode. Use
+        # standard_sc_expr_str() for them, so they show up as
+        # '<choice (name if any)>'.
+        menu_prompts.append(menu.prompt[0] if menu.prompt else
+                            standard_sc_expr_str(menu.item))
+        menu = menu.parent
+    menu_prompts.append("(Top)")
+    menu_prompts.reverse()
+
+    # Hack: We can't put ACS_RARROW directly in the string. Temporarily
+    # represent it with NULL.
+    menu_path_str = " \0 ".join(menu_prompts)
+
+    # Scroll the menu path to the right if needed to make the current menu's
+    # title visible
+    if len(menu_path_str) > term_width:
+        menu_path_str = menu_path_str[len(menu_path_str) - term_width:]
+
+    # Print the path with the arrows reinserted
+    split_path = menu_path_str.split("\0")
+    _safe_addstr(_path_win, split_path[0])
+    for s in split_path[1:]:
+        _safe_addch(_path_win, curses.ACS_RARROW)
+        _safe_addstr(_path_win, s)
+
+    _path_win.noutrefresh()
+
+
+def _parent_menu(node):
+    # Returns the menu node of the menu that contains 'node'. In addition to
+    # proper 'menu's, this might also be a 'menuconfig' symbol or a 'choice'.
+    # "Menu" here means a menu in the interface.
+
+    menu = node.parent
+    while not menu.is_menuconfig:
+        menu = menu.parent
+    return menu
+
+
+def _shown_nodes(menu):
+    # Returns the list of menu nodes from 'menu' (see _parent_menu()) that
+    # would be shown when entering it
+
+    def rec(node):
+        res = []
+
+        while node:
+            if _visible(node) or _show_all:
+                res.append(node)
+                if node.list and not node.is_menuconfig:
+                    # Nodes from implicit menu created from dependencies. Will
+                    # be shown indented. Note that is_menuconfig is True for
+                    # menus and choices as well as 'menuconfig' symbols.
+                    res += rec(node.list)
+
+            elif node.list and isinstance(node.item, Symbol):
+                # Show invisible symbols if they have visible children. This
+                # can happen for an m/y-valued symbol with an optional prompt
+                # ('prompt "foo" is COND') that is currently disabled. Note
+                # that it applies to both 'config' and 'menuconfig' symbols.
+                shown_children = rec(node.list)
+                if shown_children:
+                    res.append(node)
+                    if not node.is_menuconfig:
+                        res += shown_children
+
+            node = node.next
+
+        return res
+
+    if isinstance(menu.item, Choice):
+        # For named choices defined in multiple locations, entering the choice
+        # at a particular menu node would normally only show the choice symbols
+        # defined there (because that's what the MenuNode tree looks like).
+        #
+        # That might look confusing, and makes extending choices by defining
+        # them in multiple locations less useful. Instead, gather all the child
+        # menu nodes for all the choices whenever a choice is entered. That
+        # makes all choice symbols visible at all locations.
+        #
+        # Choices can contain non-symbol items (people do all sorts of weird
+        # stuff with them), hence the generality here. We really need to
+        # preserve the menu tree at each choice location.
+        #
+        # Note: Named choices are pretty broken in the C tools, and this is
+        # super obscure, so you probably won't find much that relies on this.
+        # This whole 'if' could be deleted if you don't care about defining
+        # choices in multiple locations to add symbols (which will still work,
+        # just with things being displayed in a way that might be unexpected).
+
+        # Do some additional work to avoid listing choice symbols twice if all
+        # or part of the choice is copied in multiple locations (e.g. by
+        # including some Kconfig file multiple times). We give the prompts at
+        # the current location precedence.
+        seen_syms = {node.item for node in rec(menu.list)
+                     if isinstance(node.item, Symbol)}
+        res = []
+        for choice_node in menu.item.nodes:
+            for node in rec(choice_node.list):
+                # 'choice_node is menu' checks if we're dealing with the
+                # current location
+                if node.item not in seen_syms or choice_node is menu:
+                    res.append(node)
+                    if isinstance(node.item, Symbol):
+                        seen_syms.add(node.item)
+        return res
+
+    return rec(menu.list)
+
+
+def _visible(node):
+    # Returns True if the node should appear in the menu (outside show-all
+    # mode)
+
+    return node.prompt and expr_value(node.prompt[1]) and not \
+        (node.item == MENU and not expr_value(node.visibility))
+
+
+def _change_node(node):
+    # Changes the value of the menu node 'node' if it is a symbol. Bools and
+    # tristates are toggled, while other symbol types pop up a text entry
+    # dialog.
+    #
+    # Returns False if the value of 'node' can't be changed.
+
+    if not _changeable(node):
+        return False
+
+    # sc = symbol/choice
+    sc = node.item
+
+    if sc.orig_type in (INT, HEX, STRING):
+        s = sc.str_value
+
+        while True:
+            s = _input_dialog(
+                "{} ({})".format(node.prompt[0], TYPE_TO_STR[sc.orig_type]),
+                s, _range_info(sc))
+
+            if s is None:
+                break
+
+            if sc.orig_type in (INT, HEX):
+                s = s.strip()
+
+                # 'make menuconfig' does this too. Hex values not starting with
+                # '0x' are accepted when loading .config files though.
+                if sc.orig_type == HEX and not s.startswith(("0x", "0X")):
+                    s = "0x" + s
+
+            if _check_valid(sc, s):
+                _set_val(sc, s)
+                break
+
+    elif len(sc.assignable) == 1:
+        # Handles choice symbols for choices in y mode, which are a special
+        # case: .assignable can be (2,) while .tri_value is 0.
+        _set_val(sc, sc.assignable[0])
+
+    else:
+        # Set the symbol to the value after the current value in
+        # sc.assignable, with wrapping
+        val_index = sc.assignable.index(sc.tri_value)
+        _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)])
+
+
+    if _is_y_mode_choice_sym(sc) and not node.list:
+        # Immediately jump to the parent menu after making a choice selection,
+        # like 'make menuconfig' does, except if the menu node has children
+        # (which can happen if a symbol 'depends on' a choice symbol that
+        # immediately precedes it).
+        _leave_menu()
+
+
+    return True
+
+
+def _changeable(node):
+    # Returns True if the value if 'node' can be changed
+
+    sc = node.item
+
+    if not isinstance(sc, (Symbol, Choice)):
+        return False
+
+    # This will hit for invisible symbols, which appear in show-all mode and
+    # when an invisible symbol has visible children (which can happen e.g. for
+    # symbols with optional prompts)
+    if not (node.prompt and expr_value(node.prompt[1])):
+        return False
+
+    return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \
+        or _is_y_mode_choice_sym(sc)
+
+
+def _set_sel_node_tri_val(tri_val):
+    # Sets the value of the currently selected menu entry to 'tri_val', if that
+    # value can be assigned
+
+    sc = _shown[_sel_node_i].item
+    if isinstance(sc, (Symbol, Choice)) and tri_val in sc.assignable:
+        _set_val(sc, tri_val)
+
+
+def _set_val(sc, val):
+    # Wrapper around Symbol/Choice.set_value() for updating the menu state and
+    # _conf_changed
+
+    global _conf_changed
+
+    # Use the string representation of tristate values. This makes the format
+    # consistent for all symbol types.
+    if val in TRI_TO_STR:
+        val = TRI_TO_STR[val]
+
+    if val != sc.str_value:
+        sc.set_value(val)
+        _conf_changed = True
+
+        # Changing the value of the symbol might have changed what items in the
+        # current menu are visible. Recalculate the state.
+        _update_menu()
+
+
+def _update_menu():
+    # Updates the current menu after the value of a symbol or choice has been
+    # changed. Changing a value might change which items in the menu are
+    # visible.
+    #
+    # If possible, preserves the location of the cursor on the screen when
+    # items are added/removed above the selected item.
+
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    # Row on the screen the cursor was on
+    old_row = _sel_node_i - _menu_scroll
+
+    sel_node = _shown[_sel_node_i]
+
+    # New visible nodes
+    _shown = _shown_nodes(_cur_menu)
+
+    # New index of selected node
+    _sel_node_i = _shown.index(sel_node)
+
+    # Try to make the cursor stay on the same row in the menu window. This
+    # might be impossible if too many nodes have disappeared above the node.
+    _menu_scroll = max(_sel_node_i - old_row, 0)
+
+
+def _input_dialog(title, initial_text, info_text=None):
+    # Pops up a dialog that prompts the user for a string
+    #
+    # title:
+    #   Title to display at the top of the dialog window's border
+    #
+    # initial_text:
+    #   Initial text to prefill the input field with
+    #
+    # info_text:
+    #   String to show next to the input field. If None, just the input field
+    #   is shown.
+
+    win = _styled_win("body")
+    win.keypad(True)
+
+    info_lines = info_text.split("\n") if info_text else []
+
+    # Give the input dialog its initial size
+    _resize_input_dialog(win, title, info_lines)
+
+    _safe_curs_set(2)
+
+    # Input field text
+    s = initial_text
+
+    # Cursor position
+    i = len(initial_text)
+
+    def edit_width():
+        return _width(win) - 4
+
+    # Horizontal scroll offset
+    hscroll = max(i - edit_width() + 1, 0)
+
+    while True:
+        # Draw the "main" display with the menu, etc., so that resizing still
+        # works properly. This is like a stack of windows, only hardcoded for
+        # now.
+        _draw_main()
+        _draw_input_dialog(win, title, info_lines, s, i, hscroll)
+        curses.doupdate()
+
+
+        c = _getch_compat(win)
+
+        if c == curses.KEY_RESIZE:
+            # Resize the main display too. The dialog floats above it.
+            _resize_main()
+            _resize_input_dialog(win, title, info_lines)
+
+        elif c == "\n":
+            _safe_curs_set(0)
+            return s
+
+        elif c == "\x1B":  # \x1B = ESC
+            _safe_curs_set(0)
+            return None
+
+        else:
+            s, i, hscroll = _edit_text(c, s, i, hscroll, edit_width())
+
+
+def _resize_input_dialog(win, title, info_lines):
+    # Resizes the input dialog to a size appropriate for the terminal size
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    win_height = 5
+    if info_lines:
+        win_height += len(info_lines) + 1
+    win_height = min(win_height, screen_height)
+
+    win_width = max(_INPUT_DIALOG_MIN_WIDTH,
+                    len(title) + 4,
+                    *(len(line) + 4 for line in info_lines))
+    win_width = min(win_width, screen_width)
+
+    win.resize(win_height, win_width)
+    win.mvwin((screen_height - win_height)//2,
+              (screen_width - win_width)//2)
+
+
+def _draw_input_dialog(win, title, info_lines, s, i, hscroll):
+    edit_width = _width(win) - 4
+
+    win.erase()
+
+    # Note: Perhaps having a separate window for the input field would be nicer
+    visible_s = s[hscroll:hscroll + edit_width]
+    _safe_addstr(win, 2, 2, visible_s + " "*(edit_width - len(visible_s)),
+                 _style["edit"])
+
+    for linenr, line in enumerate(info_lines):
+        _safe_addstr(win, 4 + linenr, 2, line)
+
+    # Draw the frame last so that it overwrites the body text for small windows
+    _draw_frame(win, title)
+
+    _safe_move(win, 2, 2 + i - hscroll)
+
+    win.noutrefresh()
+
+
+def _load_dialog():
+    # Dialog for loading a new configuration
+
+    global _conf_changed
+    global _conf_filename
+    global _show_all
+
+    if _conf_changed:
+        c = _key_dialog(
+            "Load",
+            "You have unsaved changes. Load new\n"
+            "configuration anyway?\n"
+            "\n"
+            "         (O)K  (C)ancel",
+            "oc")
+
+        if c is None or c == "c":
+            return
+
+    filename = _conf_filename
+    while True:
+        filename = _input_dialog("File to load", filename, _load_save_info())
+        if filename is None:
+            return
+
+        filename = os.path.expanduser(filename)
+
+        if _try_load(filename):
+            _conf_filename = filename
+            _conf_changed = _needs_save()
+
+            # Turn on show-all mode if the selected node is not visible after
+            # loading the new configuration. _shown still holds the old state.
+            if _shown[_sel_node_i] not in _shown_nodes(_cur_menu):
+                _show_all = True
+
+            _update_menu()
+
+            # The message dialog indirectly updates the menu display, so _msg()
+            # must be called after the new state has been initialized
+            _msg("Success", "Loaded " + filename)
+            return
+
+
+def _try_load(filename):
+    # Tries to load a configuration file. Pops up an error and returns False on
+    # failure.
+    #
+    # filename:
+    #   Configuration file to load
+
+    try:
+        _kconf.load_config(filename)
+        return True
+    except EnvironmentError as e:
+        _error("Error loading '{}'\n\n{} (errno: {})"
+               .format(filename, e.strerror, errno.errorcode[e.errno]))
+        return False
+
+
+def _save_dialog(save_fn, default_filename, description):
+    # Dialog for saving the current configuration
+    #
+    # save_fn:
+    #   Function to call with 'filename' to save the file
+    #
+    # default_filename:
+    #   Prefilled filename in the input field
+    #
+    # description:
+    #   String describing the thing being saved
+    #
+    # Return value:
+    #   The path to the saved file, or None if no file was saved
+
+    filename = default_filename
+    while True:
+        filename = _input_dialog("Filename to save {} to".format(description),
+                                 filename, _load_save_info())
+        if filename is None:
+            return None
+
+        filename = os.path.expanduser(filename)
+
+        msg = _try_save(save_fn, filename, description)
+        if msg:
+            _msg("Success", msg)
+            return filename
+
+
+def _try_save(save_fn, filename, description):
+    # Tries to save a configuration file. Returns a message to print on
+    # success.
+    #
+    # save_fn:
+    #   Function to call with 'filename' to save the file
+    #
+    # description:
+    #   String describing the thing being saved
+    #
+    # Return value:
+    #   A message to print on success, and None on failure
+
+    try:
+        # save_fn() returns a message to print
+        return save_fn(filename)
+    except EnvironmentError as e:
+        _error("Error saving {} to '{}'\n\n{} (errno: {})"
+               .format(description, e.filename, e.strerror,
+                       errno.errorcode[e.errno]))
+        return None
+
+
+def _key_dialog(title, text, keys):
+    # Pops up a dialog that can be closed by pressing a key
+    #
+    # title:
+    #   Title to display at the top of the dialog window's border
+    #
+    # text:
+    #   Text to show in the dialog
+    #
+    # keys:
+    #   List of keys that will close the dialog. Other keys (besides ESC) are
+    #   ignored. The caller is responsible for providing a hint about which
+    #   keys can be pressed in 'text'.
+    #
+    # Return value:
+    #   The key that was pressed to close the dialog. Uppercase characters are
+    #   converted to lowercase. ESC will always close the dialog, and returns
+    #   None.
+
+    win = _styled_win("body")
+    win.keypad(True)
+
+    _resize_key_dialog(win, text)
+
+    while True:
+        # See _input_dialog()
+        _draw_main()
+        _draw_key_dialog(win, title, text)
+        curses.doupdate()
+
+
+        c = _getch_compat(win)
+
+        if c == curses.KEY_RESIZE:
+            # Resize the main display too. The dialog floats above it.
+            _resize_main()
+            _resize_key_dialog(win, text)
+
+        elif c == "\x1B":  # \x1B = ESC
+            return None
+
+        elif isinstance(c, str):
+            c = c.lower()
+            if c in keys:
+                return c
+
+
+def _resize_key_dialog(win, text):
+    # Resizes the key dialog to a size appropriate for the terminal size
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    lines = text.split("\n")
+
+    win_height = min(len(lines) + 4, screen_height)
+    win_width = min(max(len(line) for line in lines) + 4, screen_width)
+
+    win.resize(win_height, win_width)
+    win.mvwin((screen_height - win_height)//2,
+              (screen_width - win_width)//2)
+
+
+def _draw_key_dialog(win, title, text):
+    win.erase()
+
+    for i, line in enumerate(text.split("\n")):
+        _safe_addstr(win, 2 + i, 2, line)
+
+    # Draw the frame last so that it overwrites the body text for small windows
+    _draw_frame(win, title)
+
+    win.noutrefresh()
+
+
+def _draw_frame(win, title):
+    # Draw a frame around the inner edges of 'win', with 'title' at the top
+
+    win_height, win_width = win.getmaxyx()
+
+    win.attron(_style["frame"])
+
+    # Draw top/bottom edge
+    _safe_hline(win,              0, 0, " ", win_width)
+    _safe_hline(win, win_height - 1, 0, " ", win_width)
+
+    # Draw left/right edge
+    _safe_vline(win, 0,             0, " ", win_height)
+    _safe_vline(win, 0, win_width - 1, " ", win_height)
+
+    # Draw title
+    _safe_addstr(win, 0, max((win_width - len(title))//2, 0), title)
+
+    win.attroff(_style["frame"])
+
+
+def _jump_to_dialog():
+    # Implements the jump-to dialog, where symbols can be looked up via
+    # incremental search and jumped to.
+    #
+    # Returns True if the user jumped to a symbol, and False if the dialog was
+    # canceled.
+
+    s = ""  # Search text
+    prev_s = None  # Previous search text
+    s_i = 0  # Search text cursor position
+    hscroll = 0  # Horizontal scroll offset
+
+    sel_node_i = 0  # Index of selected row
+    scroll = 0  # Index in 'matches' of the top row of the list
+
+    # Edit box at the top
+    edit_box = _styled_win("jump-edit")
+    edit_box.keypad(True)
+
+    # List of matches
+    matches_win = _styled_win("list")
+
+    # Bottom separator, with arrows pointing down
+    bot_sep_win = _styled_win("separator")
+
+    # Help window with instructions at the bottom
+    help_win = _styled_win("help")
+
+    # Give windows their initial size
+    _resize_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                           sel_node_i, scroll)
+
+    _safe_curs_set(2)
+
+    # Logic duplication with _select_{next,prev}_menu_entry(), except we do a
+    # functional variant that returns the new (sel_node_i, scroll) values to
+    # avoid 'nonlocal'. TODO: Can this be factored out in some nice way?
+
+    def select_next_match():
+        if sel_node_i == len(matches) - 1:
+            return sel_node_i, scroll
+
+        if sel_node_i + 1 >= scroll + _height(matches_win) - _SCROLL_OFFSET \
+           and scroll < _max_scroll(matches, matches_win):
+
+            return sel_node_i + 1, scroll + 1
+
+        return sel_node_i + 1, scroll
+
+    def select_prev_match():
+        if sel_node_i == 0:
+            return sel_node_i, scroll
+
+        if sel_node_i - 1 < scroll + _SCROLL_OFFSET:
+            return sel_node_i - 1, max(scroll - 1, 0)
+
+        return sel_node_i - 1, scroll
+
+    while True:
+        if s != prev_s:
+            # The search text changed. Find new matching nodes.
+
+            prev_s = s
+
+            try:
+                # We could use re.IGNORECASE here instead of lower(), but this
+                # is noticeably less jerky while inputting regexes like
+                # '.*debug$' (though the '.*' is redundant there). Those
+                # probably have bad interactions with re.search(), which
+                # matches anywhere in the string.
+                #
+                # It's not horrible either way. Just a bit smoother.
+                regex_searches = [re.compile(regex).search
+                                  for regex in s.lower().split()]
+
+                # No exception thrown, so the regexes are okay
+                bad_re = None
+
+                # List of matching nodes
+                matches = []
+                add_match = matches.append
+
+                # Search symbols and choices
+
+                for node in _sorted_sc_nodes():
+                    # Symbol/choice
+                    sc = node.item
+
+                    for search in regex_searches:
+                        # Both the name and the prompt might be missing, since
+                        # we're searching both symbols and choices
+
+                        # Does the regex match either the symbol name or the
+                        # prompt (if any)?
+                        if not (sc.name and search(sc.name.lower()) or
+                                node.prompt and search(node.prompt[0].lower())):
+
+                            # Give up on the first regex that doesn't match, to
+                            # speed things up a bit when multiple regexes are
+                            # entered
+                            break
+
+                    else:
+                        add_match(node)
+
+                # Search menus and comments
+
+                for node in _sorted_menu_comment_nodes():
+                    for search in regex_searches:
+                        if not search(node.prompt[0].lower()):
+                            break
+                    else:
+                        add_match(node)
+
+            except re.error as e:
+                # Bad regex. Remember the error message so we can show it.
+                bad_re = "Bad regular expression"
+                # re.error.msg was added in Python 3.5
+                if hasattr(e, "msg"):
+                    bad_re += ": " + e.msg
+
+                matches = []
+
+            # Reset scroll and jump to the top of the list of matches
+            sel_node_i = scroll = 0
+
+        _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                             s, s_i, hscroll,
+                             bad_re, matches, sel_node_i, scroll)
+        curses.doupdate()
+
+
+        c = _getch_compat(edit_box)
+
+        if c == "\n":
+            if matches:
+                _jump_to(matches[sel_node_i])
+                _safe_curs_set(0)
+                return True
+
+        elif c == "\x1B":  # \x1B = ESC
+            _safe_curs_set(0)
+            return False
+
+        elif c == curses.KEY_RESIZE:
+            # We adjust the scroll so that the selected node stays visible in
+            # the list when the terminal is resized, hence the 'scroll'
+            # assignment
+            scroll = _resize_jump_to_dialog(
+                edit_box, matches_win, bot_sep_win, help_win,
+                sel_node_i, scroll)
+
+        elif c == "\x06":  # \x06 = Ctrl-F
+            if matches:
+                _safe_curs_set(0)
+                _info_dialog(matches[sel_node_i], True)
+                _safe_curs_set(2)
+
+                scroll = _resize_jump_to_dialog(
+                    edit_box, matches_win, bot_sep_win, help_win,
+                    sel_node_i, scroll)
+
+        elif c == curses.KEY_DOWN:
+            sel_node_i, scroll = select_next_match()
+
+        elif c == curses.KEY_UP:
+            sel_node_i, scroll = select_prev_match()
+
+        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
+            # Keep it simple. This way we get sane behavior for small windows,
+            # etc., for free.
+            for _ in range(_PG_JUMP):
+                sel_node_i, scroll = select_next_match()
+
+        # Page Up (no Ctrl-U, as it's already used by the edit box)
+        elif c == curses.KEY_PPAGE:
+            for _ in range(_PG_JUMP):
+                sel_node_i, scroll = select_prev_match()
+
+        elif c == curses.KEY_END:
+            sel_node_i = len(matches) - 1
+            scroll = _max_scroll(matches, matches_win)
+
+        elif c == curses.KEY_HOME:
+            sel_node_i = scroll = 0
+
+        else:
+            s, s_i, hscroll = _edit_text(c, s, s_i, hscroll,
+                                         _width(edit_box) - 2)
+
+
+# Obscure Python: We never pass a value for cached_nodes, and it keeps pointing
+# to the same list. This avoids a global.
+def _sorted_sc_nodes(cached_nodes=[]):
+    # Returns a sorted list of symbol and choice nodes to search. The symbol
+    # nodes appear first, sorted by name, and then the choice nodes, sorted by
+    # prompt and (secondarily) name.
+
+    if not cached_nodes:
+        # Add symbol nodes
+        for sym in sorted(_kconf.unique_defined_syms,
+                          key=lambda sym: sym.name):
+            # += is in-place for lists
+            cached_nodes += sym.nodes
+
+        # Add choice nodes
+
+        choices = sorted(_kconf.unique_choices,
+                         key=lambda choice: choice.name or "")
+
+        cached_nodes += sorted(
+            [node for choice in choices for node in choice.nodes],
+            key=lambda node: node.prompt[0] if node.prompt else "")
+
+    return cached_nodes
+
+
+def _sorted_menu_comment_nodes(cached_nodes=[]):
+    # Returns a list of menu and comment nodes to search, sorted by prompt,
+    # with the menus first
+
+    if not cached_nodes:
+        def prompt_text(mc):
+            return mc.prompt[0]
+
+        cached_nodes += sorted(_kconf.menus, key=prompt_text)
+        cached_nodes += sorted(_kconf.comments, key=prompt_text)
+
+    return cached_nodes
+
+
+def _resize_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                           sel_node_i, scroll):
+    # Resizes the jump-to dialog to fill the terminal.
+    #
+    # Returns the new scroll index. We adjust the scroll if needed so that the
+    # selected node stays visible.
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    bot_sep_win.resize(1, screen_width)
+
+    help_win_height = len(_JUMP_TO_HELP_LINES)
+    matches_win_height = screen_height - help_win_height - 4
+
+    if matches_win_height >= 1:
+        edit_box.resize(3, screen_width)
+        matches_win.resize(matches_win_height, screen_width)
+        help_win.resize(help_win_height, screen_width)
+
+        matches_win.mvwin(3, 0)
+        bot_sep_win.mvwin(3 + matches_win_height, 0)
+        help_win.mvwin(3 + matches_win_height + 1, 0)
+    else:
+        # Degenerate case. Give up on nice rendering and just prevent errors.
+
+        matches_win_height = 1
+
+        edit_box.resize(screen_height, screen_width)
+        matches_win.resize(1, screen_width)
+        help_win.resize(1, screen_width)
+
+        for win in matches_win, bot_sep_win, help_win:
+            win.mvwin(0, 0)
+
+    # Adjust the scroll so that the selected row is still within the window, if
+    # needed
+    if sel_node_i - scroll >= matches_win_height:
+        return sel_node_i - matches_win_height + 1
+    return scroll
+
+
+def _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                         s, s_i, hscroll,
+                         bad_re, matches, sel_node_i, scroll):
+
+    edit_width = _width(edit_box) - 2
+
+    #
+    # Update list of matches
+    #
+
+    matches_win.erase()
+
+    if matches:
+        for i in range(scroll,
+                       min(scroll + _height(matches_win), len(matches))):
+
+            node = matches[i]
+
+            if isinstance(node.item, (Symbol, Choice)):
+                node_str = _name_and_val_str(node.item)
+                if node.prompt:
+                    node_str += ' "{}"'.format(node.prompt[0])
+            elif node.item == MENU:
+                node_str = 'menu "{}"'.format(node.prompt[0])
+            else:  # node.item == COMMENT
+                node_str = 'comment "{}"'.format(node.prompt[0])
+
+            _safe_addstr(matches_win, i - scroll, 0, node_str,
+                         _style["selection" if i == sel_node_i else "list"])
+
+    else:
+        # bad_re holds the error message from the re.error exception on errors
+        _safe_addstr(matches_win, 0, 0, bad_re or "No matches")
+
+    matches_win.noutrefresh()
+
+    #
+    # Update bottom separator line
+    #
+
+    bot_sep_win.erase()
+
+    # Draw arrows pointing down if the symbol list is scrolled up
+    if scroll < _max_scroll(matches, matches_win):
+        _safe_hline(bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
+
+    bot_sep_win.noutrefresh()
+
+    #
+    # Update help window at bottom
+    #
+
+    help_win.erase()
+
+    for i, line in enumerate(_JUMP_TO_HELP_LINES):
+        _safe_addstr(help_win, i, 0, line)
+
+    help_win.noutrefresh()
+
+    #
+    # Update edit box. We do this last since it makes it handy to position the
+    # cursor.
+    #
+
+    edit_box.erase()
+
+    _draw_frame(edit_box, "Jump to symbol/choice/menu/comment")
+
+    # Draw arrows pointing up if the symbol list is scrolled down
+    if scroll > 0:
+        # TODO: Bit ugly that _style["frame"] is repeated here
+        _safe_hline(edit_box, 2, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS,
+                    _style["frame"])
+
+    visible_s = s[hscroll:hscroll + edit_width]
+    _safe_addstr(edit_box, 1, 1, visible_s)
+
+    _safe_move(edit_box, 1, 1 + s_i - hscroll)
+
+    edit_box.noutrefresh()
+
+
+def _info_dialog(node, from_jump_to_dialog):
+    # Shows a fullscreen window with information about 'node'.
+    #
+    # If 'from_jump_to_dialog' is True, the information dialog was opened from
+    # within the jump-to-dialog. In this case, we make '/' from within the
+    # information dialog just return, to avoid a confusing recursive invocation
+    # of the jump-to-dialog.
+
+    # Top row, with title and arrows point up
+    top_line_win = _styled_win("separator")
+
+    # Text display
+    text_win = _styled_win("text")
+    text_win.keypad(True)
+
+    # Bottom separator, with arrows pointing down
+    bot_sep_win = _styled_win("separator")
+
+    # Help window with keys at the bottom
+    help_win = _styled_win("help")
+
+    # Give windows their initial size
+    _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
+
+
+    # Get lines of help text
+    lines = _info_str(node).split("\n")
+
+    # Index of first row in 'lines' to show
+    scroll = 0
+
+    while True:
+        _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
+                          bot_sep_win, help_win)
+        curses.doupdate()
+
+
+        c = _getch_compat(text_win)
+
+        if c == curses.KEY_RESIZE:
+            _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
+
+        elif c in (curses.KEY_DOWN, "j", "J"):
+            if scroll < _max_scroll(lines, text_win):
+                scroll += 1
+
+        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
+            scroll = min(scroll + _PG_JUMP, _max_scroll(lines, text_win))
+
+        elif c in (curses.KEY_PPAGE, "\x15"):  # Page Up/Ctrl-U
+            scroll = max(scroll - _PG_JUMP, 0)
+
+        elif c in (curses.KEY_END, "G"):
+            scroll = _max_scroll(lines, text_win)
+
+        elif c in (curses.KEY_HOME, "g"):
+            scroll = 0
+
+        elif c in (curses.KEY_UP, "k", "K"):
+            if scroll > 0:
+                scroll -= 1
+
+        elif c == "/":
+            # Support starting a search from within the information dialog
+
+            if from_jump_to_dialog:
+                return  # Avoid recursion
+
+            if _jump_to_dialog():
+                return  # Jumped to a symbol. Cancel the information dialog.
+
+            # Stay in the information dialog if the jump-to dialog was
+            # canceled. Resize it in case the terminal was resized while the
+            # fullscreen jump-to dialog was open.
+            _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
+
+        elif c in (curses.KEY_LEFT, curses.KEY_BACKSPACE, _ERASE_CHAR,
+                   "\x1B",  # \x1B = ESC
+                   "q", "Q", "h", "H"):
+
+            return
+
+
+def _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win):
+    # Resizes the info dialog to fill the terminal
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    top_line_win.resize(1, screen_width)
+    bot_sep_win.resize(1, screen_width)
+
+    help_win_height = len(_INFO_HELP_LINES)
+    text_win_height = screen_height - help_win_height - 2
+
+    if text_win_height >= 1:
+        text_win.resize(text_win_height, screen_width)
+        help_win.resize(help_win_height, screen_width)
+
+        text_win.mvwin(1, 0)
+        bot_sep_win.mvwin(1 + text_win_height, 0)
+        help_win.mvwin(1 + text_win_height + 1, 0)
+    else:
+        # Degenerate case. Give up on nice rendering and just prevent errors.
+
+        text_win.resize(1, screen_width)
+        help_win.resize(1, screen_width)
+
+        for win in text_win, bot_sep_win, help_win:
+            win.mvwin(0, 0)
+
+
+def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
+                      bot_sep_win, help_win):
+
+    text_win_height, text_win_width = text_win.getmaxyx()
+
+    # Note: The top row is deliberately updated last. See _draw_main().
+
+    #
+    # Update text display
+    #
+
+    text_win.erase()
+
+    for i, line in enumerate(lines[scroll:scroll + text_win_height]):
+        _safe_addstr(text_win, i, 0, line)
+
+    text_win.noutrefresh()
+
+    #
+    # Update bottom separator line
+    #
+
+    bot_sep_win.erase()
+
+    # Draw arrows pointing down if the symbol window is scrolled up
+    if scroll < _max_scroll(lines, text_win):
+        _safe_hline(bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
+
+    bot_sep_win.noutrefresh()
+
+    #
+    # Update help window at bottom
+    #
+
+    help_win.erase()
+
+    for i, line in enumerate(_INFO_HELP_LINES):
+        _safe_addstr(help_win, i, 0, line)
+
+    help_win.noutrefresh()
+
+    #
+    # Update top row
+    #
+
+    top_line_win.erase()
+
+    # Draw arrows pointing up if the information window is scrolled down. Draw
+    # them before drawing the title, so the title ends up on top for small
+    # windows.
+    if scroll > 0:
+        _safe_hline(top_line_win, 0, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS)
+
+    title = ("Symbol" if isinstance(node.item, Symbol) else
+             "Choice" if isinstance(node.item, Choice) else
+             "Menu"   if node.item == MENU else
+             "Comment") + " information"
+    _safe_addstr(top_line_win, 0, max((text_win_width - len(title))//2, 0),
+                 title)
+
+    top_line_win.noutrefresh()
+
+
+def _info_str(node):
+    # Returns information about the menu node 'node' as a string.
+    #
+    # The helper functions are responsible for adding newlines. This allows
+    # them to return "" if they don't want to add any output.
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+
+        return (
+            _name_info(sym) +
+            _prompt_info(sym) +
+            "Type: {}\n".format(TYPE_TO_STR[sym.type]) +
+            _value_info(sym) +
+            _help_info(sym) +
+            _direct_dep_info(sym) +
+            _defaults_info(sym) +
+            _select_imply_info(sym) +
+            _kconfig_def_info(sym)
+        )
+
+    if isinstance(node.item, Choice):
+        choice = node.item
+
+        return (
+            _name_info(choice) +
+            _prompt_info(choice) +
+            "Type: {}\n".format(TYPE_TO_STR[choice.type]) +
+            'Mode: {}\n'.format(choice.str_value) +
+            _help_info(choice) +
+            _choice_syms_info(choice) +
+            _direct_dep_info(choice) +
+            _defaults_info(choice) +
+            _kconfig_def_info(choice)
+        )
+
+    return _kconfig_def_info(node)  # node.item in (MENU, COMMENT)
+
+
+def _name_info(sc):
+    # Returns a string with the name of the symbol/choice. Names are optional
+    # for choices.
+
+    return "Name: {}\n".format(sc.name) if sc.name else ""
+
+
+def _prompt_info(sc):
+    # Returns a string listing the prompts of 'sc' (Symbol or Choice)
+
+    s = ""
+
+    for node in sc.nodes:
+        if node.prompt:
+            s += "Prompt: {}\n".format(node.prompt[0])
+
+    return s
+
+
+def _value_info(sym):
+    # Returns a string showing 'sym's value
+
+    # Only put quotes around the value for string symbols
+    return "Value: {}\n".format(
+        '"{}"'.format(sym.str_value)
+        if sym.orig_type == STRING
+        else sym.str_value)
+
+
+def _choice_syms_info(choice):
+    # Returns a string listing the choice symbols in 'choice'. Adds
+    # "(selected)" next to the selected one.
+
+    s = "Choice symbols:\n"
+
+    for sym in choice.syms:
+        s += "  - " + sym.name
+        if sym is choice.selection:
+            s += " (selected)"
+        s += "\n"
+
+    return s + "\n"
+
+
+def _help_info(sc):
+    # Returns a string with the help text(s) of 'sc' (Symbol or Choice).
+    # Symbols and choices defined in multiple locations can have multiple help
+    # texts.
+
+    s = "\n"
+
+    for node in sc.nodes:
+        if node.help is not None:
+            s += "Help:\n\n{}\n\n".format(_indent(node.help, 2))
+
+    return s
+
+
+def _direct_dep_info(sc):
+    # Returns a string describing the direct dependencies of 'sc' (Symbol or
+    # Choice). The direct dependencies are the OR of the dependencies from each
+    # definition location. The dependencies at each definition location come
+    # from 'depends on' and dependencies inherited from parent items.
+
+    return "" if sc.direct_dep is _kconf.y else \
+        'Direct dependencies (={}):\n{}\n' \
+        .format(TRI_TO_STR[expr_value(sc.direct_dep)],
+                _split_expr_info(sc.direct_dep, 2))
+
+
+def _defaults_info(sc):
+    # Returns a string describing the defaults of 'sc' (Symbol or Choice)
+
+    if not sc.defaults:
+        return ""
+
+    s = "Default"
+    if len(sc.defaults) > 1:
+        s += "s"
+    s += ":\n"
+
+    for val, cond in sc.orig_defaults:
+        s += "  - "
+        if isinstance(sc, Symbol):
+            s += _expr_str(val)
+
+            # Skip the tristate value hint if the expression is just a single
+            # symbol. _expr_str() already shows its value as a string.
+            #
+            # This also avoids showing the tristate value for string/int/hex
+            # defaults, which wouldn't make any sense.
+            if isinstance(val, tuple):
+                s += '  (={})'.format(TRI_TO_STR[expr_value(val)])
+        else:
+            # Don't print the value next to the symbol name for choice
+            # defaults, as it looks a bit confusing
+            s += val.name
+        s += "\n"
+
+        if cond is not _kconf.y:
+            s += "    Condition (={}):\n{}" \
+                 .format(TRI_TO_STR[expr_value(cond)],
+                         _split_expr_info(cond, 4))
+
+    return s + "\n"
+
+
+def _split_expr_info(expr, indent):
+    # Returns a string with 'expr' split into its top-level && or || operands,
+    # with one operand per line, together with the operand's value. This is
+    # usually enough to get something readable for long expressions. A fancier
+    # recursive thingy would be possible too.
+    #
+    # indent:
+    #   Number of leading spaces to add before the split expression.
+
+    if len(split_expr(expr, AND)) > 1:
+        split_op = AND
+        op_str = "&&"
+    else:
+        split_op = OR
+        op_str = "||"
+
+    s = ""
+    for i, term in enumerate(split_expr(expr, split_op)):
+        s += "{}{} {}".format(indent*" ",
+                              "  " if i == 0 else op_str,
+                              _expr_str(term))
+
+        # Don't bother showing the value hint if the expression is just a
+        # single symbol. _expr_str() already shows its value.
+        if isinstance(term, tuple):
+            s += "  (={})".format(TRI_TO_STR[expr_value(term)])
+
+        s += "\n"
+
+    return s
+
+
+def _select_imply_info(sym):
+    # Returns a string with information about which symbols 'select' or 'imply'
+    # 'sym'. The selecting/implying symbols are grouped according to which
+    # value they select/imply 'sym' to (n/m/y).
+
+    def sis(expr, val, title):
+        # sis = selects/implies
+        sis = [si for si in split_expr(expr, OR) if expr_value(si) == val]
+        if not sis:
+            return ""
+
+        res = title
+        for si in sis:
+            res += "  - {}\n".format(split_expr(si, AND)[0].name)
+        return res + "\n"
+
+    s = ""
+
+    if sym.rev_dep is not _kconf.n:
+        s += sis(sym.rev_dep, 2,
+                 "Symbols currently y-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 1,
+                 "Symbols currently m-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 0,
+                 "Symbols currently n-selecting this symbol (no effect):\n")
+
+    if sym.weak_rev_dep is not _kconf.n:
+        s += sis(sym.weak_rev_dep, 2,
+                 "Symbols currently y-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 1,
+                 "Symbols currently m-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 0,
+                 "Symbols currently n-implying this symbol (no effect):\n")
+
+    return s
+
+
+def _kconfig_def_info(item):
+    # Returns a string with the definition of 'item' in Kconfig syntax,
+    # together with the definition location(s) and their include and menu paths
+
+    nodes = [item] if isinstance(item, MenuNode) else item.nodes
+
+    s = "Kconfig definition{}, with parent deps. propagated to 'depends on'\n" \
+        .format("s" if len(nodes) > 1 else "")
+    s += (len(s) - 1)*"="
+
+    for node in nodes:
+        s += "\n\n" \
+             "At {}:{}\n" \
+             "{}" \
+             "Menu path: {}\n\n" \
+             "{}" \
+             .format(node.filename, node.linenr,
+                     _include_path_info(node),
+                     _menu_path_info(node),
+                     _indent(node.custom_str(_name_and_val_str), 2))
+
+    return s
+
+
+def _include_path_info(node):
+    if not node.include_path:
+        # In the top-level Kconfig file
+        return ""
+
+    return "Included via {}\n".format(
+        " -> ".join("{}:{}".format(filename, linenr)
+                    for filename, linenr in node.include_path))
+
+
+def _menu_path_info(node):
+    # Returns a string describing the menu path leading up to 'node'
+
+    path = ""
+
+    while node.parent is not _kconf.top_node:
+        node = node.parent
+
+        # Promptless choices might appear among the parents. Use
+        # standard_sc_expr_str() for them, so that they show up as
+        # '<choice (name if any)>'.
+        path = " -> " + (node.prompt[0] if node.prompt else
+                         standard_sc_expr_str(node.item)) + path
+
+    return "(Top)" + path
+
+
+def _indent(s, n):
+    # Returns 's' with each line indented 'n' spaces. textwrap.indent() is not
+    # available in Python 2 (it's 3.3+).
+
+    return "\n".join(n*" " + line for line in s.split("\n"))
+
+
+def _name_and_val_str(sc):
+    # Custom symbol/choice printer that shows symbol values after symbols
+
+    # Show the values of non-constant (non-quoted) symbols that don't look like
+    # numbers. Things like 123 are actually symbol references, and only work as
+    # expected due to undefined symbols getting their name as their value.
+    # Showing the symbol value for those isn't helpful though.
+    if isinstance(sc, Symbol) and not sc.is_constant and not _is_num(sc.name):
+        if not sc.nodes:
+            # Undefined symbol reference
+            return "{}(undefined/n)".format(sc.name)
+
+        return '{}(={})'.format(sc.name, sc.str_value)
+
+    # For other items, use the standard format
+    return standard_sc_expr_str(sc)
+
+
+def _expr_str(expr):
+    # Custom expression printer that shows symbol values
+    return expr_str(expr, _name_and_val_str)
+
+
+def _styled_win(style):
+    # Returns a new curses window with style 'style' and space as the fill
+    # character. The initial dimensions are (1, 1), so the window needs to be
+    # sized and positioned separately.
+
+    win = curses.newwin(1, 1)
+    _set_style(win, style)
+    return win
+
+
+def _set_style(win, style):
+    # Changes the style of an existing window
+
+    win.bkgdset(" ", _style[style])
+
+
+def _max_scroll(lst, win):
+    # Assuming 'lst' is a list of items to be displayed in 'win',
+    # returns the maximum number of steps 'win' can be scrolled down.
+    # We stop scrolling when the bottom item is visible.
+
+    return max(0, len(lst) - _height(win))
+
+
+def _edit_text(c, s, i, hscroll, width):
+    # Implements text editing commands for edit boxes. Takes a character (which
+    # could also be e.g. curses.KEY_LEFT) and the edit box state, and returns
+    # the new state after the character has been processed.
+    #
+    # c:
+    #   Character from user
+    #
+    # s:
+    #   Current contents of string
+    #
+    # i:
+    #   Current cursor index in string
+    #
+    # hscroll:
+    #   Index in s of the leftmost character in the edit box, for horizontal
+    #   scrolling
+    #
+    # width:
+    #   Width in characters of the edit box
+    #
+    # Return value:
+    #   An (s, i, hscroll) tuple for the new state
+
+    if c == curses.KEY_LEFT:
+        if i > 0:
+            i -= 1
+
+    elif c == curses.KEY_RIGHT:
+        if i < len(s):
+            i += 1
+
+    elif c in (curses.KEY_HOME, "\x01"):  # \x01 = CTRL-A
+        i = 0
+
+    elif c in (curses.KEY_END, "\x05"):  # \x05 = CTRL-E
+        i = len(s)
+
+    elif c in (curses.KEY_BACKSPACE, _ERASE_CHAR):
+        if i > 0:
+            s = s[:i-1] + s[i:]
+            i -= 1
+
+    elif c == curses.KEY_DC:
+        s = s[:i] + s[i+1:]
+
+    elif c == "\x17":  # \x17 = CTRL-W
+        # The \W removes characters like ',' one at a time
+        new_i = re.search(r"(?:\w*|\W)\s*$", s[:i]).start()
+        s = s[:new_i] + s[i:]
+        i = new_i
+
+    elif c == "\x0B":  # \x0B = CTRL-K
+        s = s[:i]
+
+    elif c == "\x15":  # \x15 = CTRL-U
+        s = s[i:]
+        i = 0
+
+    elif isinstance(c, str):
+        # Insert character
+        s = s[:i] + c + s[i:]
+        i += 1
+
+    # Adjust the horizontal scroll so that the cursor never touches the left or
+    # right edges of the edit box, except when it's at the beginning or the end
+    # of the string
+    if i < hscroll + _SCROLL_OFFSET:
+        hscroll = max(i - _SCROLL_OFFSET, 0)
+    elif i >= hscroll + width - _SCROLL_OFFSET:
+        max_scroll = max(len(s) - width + 1, 0)
+        hscroll = min(i - width + _SCROLL_OFFSET + 1, max_scroll)
+
+    return s, i, hscroll
+
+
+def _load_save_info():
+    # Returns an information string for load/save dialog boxes
+
+    return "(Relative to {})\n\nRefer to your home directory with ~" \
+           .format(os.path.join(os.getcwd(), ""))
+
+
+def _msg(title, text):
+    # Pops up a message dialog that can be dismissed with Space/Enter/ESC
+
+    _key_dialog(title, text, " \n")
+
+
+def _error(text):
+    # Pops up an error dialog that can be dismissed with Space/Enter/ESC
+
+    _msg("Error", text)
+
+
+def _node_str(node):
+    # Returns the complete menu entry text for a menu node.
+    #
+    # Example return value: "[*] Support for X"
+
+    # Calculate the indent to print the item with by checking how many levels
+    # above it the closest 'menuconfig' item is (this includes menus and
+    # choices as well as menuconfig symbols)
+    indent = 0
+    parent = node.parent
+    while not parent.is_menuconfig:
+        indent += _SUBMENU_INDENT
+        parent = parent.parent
+
+    # This approach gives nice alignment for empty string symbols ("()  Foo")
+    s = "{:{}}".format(_value_str(node), 3 + indent)
+
+    if _should_show_name(node):
+        if isinstance(node.item, Symbol):
+            s += " <{}>".format(node.item.name)
+        else:
+            # For choices, use standard_sc_expr_str(). That way they show up as
+            # '<choice (name if any)>'.
+            s += " " + standard_sc_expr_str(node.item)
+
+    if node.prompt:
+        if node.item == COMMENT:
+            s += " *** {} ***".format(node.prompt[0])
+        else:
+            s += " " + node.prompt[0]
+
+        if isinstance(node.item, Symbol):
+            sym = node.item
+
+            # Print "(NEW)" next to symbols without a user value (from e.g. a
+            # .config), but skip it for choice symbols in choices in y mode,
+            # and for symbols of UNKNOWN type (which generate a warning though)
+            if sym.user_value is None and sym.orig_type and \
+               not (sym.choice and sym.choice.tri_value == 2):
+
+                s += " (NEW)"
+
+    if isinstance(node.item, Choice) and node.item.tri_value == 2:
+        # Print the prompt of the selected symbol after the choice for
+        # choices in y mode
+        sym = node.item.selection
+        if sym:
+            for sym_node in sym.nodes:
+                # Use the prompt used at this choice location, in case the
+                # choice symbol is defined in multiple locations
+                if sym_node.parent is node and sym_node.prompt:
+                    s += " ({})".format(sym_node.prompt[0])
+                    break
+            else:
+                # If the symbol isn't defined at this choice location, then
+                # just use whatever prompt we can find for it
+                for sym_node in sym.nodes:
+                    if sym_node.prompt:
+                        s += " ({})".format(sym_node.prompt[0])
+                        break
+
+    # Print "--->" next to nodes that have menus that can potentially be
+    # entered. Print "----" if the menu is empty. We don't allow those to be
+    # entered.
+    if node.is_menuconfig:
+        s += "  --->" if _shown_nodes(node) else "  ----"
+
+    return s
+
+
+def _should_show_name(node):
+    # Returns True if 'node' is a symbol or choice whose name should shown (if
+    # any, as names are optional for choices)
+
+    # The 'not node.prompt' case only hits in show-all mode, for promptless
+    # symbols and choices
+    return not node.prompt or \
+           (_show_name and isinstance(node.item, (Symbol, Choice)))
+
+
+def _value_str(node):
+    # Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu node
+
+    item = node.item
+
+    if item in (MENU, COMMENT):
+        return ""
+
+    # Wouldn't normally happen, and generates a warning
+    if not item.orig_type:
+        return ""
+
+    if item.orig_type in (STRING, INT, HEX):
+        return "({})".format(item.str_value)
+
+    # BOOL or TRISTATE
+
+    if _is_y_mode_choice_sym(item):
+        return "(X)" if item.choice.selection is item else "( )"
+
+    tri_val_str = (" ", "M", "*")[item.tri_value]
+
+    if len(item.assignable) <= 1:
+        # Pinned to a single value
+        return "" if isinstance(item, Choice) else "-{}-".format(tri_val_str)
+
+    if item.type == BOOL:
+        return "[{}]".format(tri_val_str)
+
+    # item.type == TRISTATE
+    if item.assignable == (1, 2):
+        return "{{{}}}".format(tri_val_str)  # {M}/{*}
+    return "<{}>".format(tri_val_str)
+
+
+def _is_y_mode_choice_sym(item):
+    # The choice mode is an upper bound on the visibility of choice symbols, so
+    # we can check the choice symbols' own visibility to see if the choice is
+    # in y mode
+    return isinstance(item, Symbol) and item.choice and item.visibility == 2
+
+
+def _check_valid(sym, s):
+    # Returns True if the string 's' is a well-formed value for 'sym'.
+    # Otherwise, displays an error and returns False.
+
+    if sym.orig_type not in (INT, HEX):
+        return True  # Anything goes for non-int/hex symbols
+
+    base = 10 if sym.orig_type == INT else 16
+    try:
+        int(s, base)
+    except ValueError:
+        _error("'{}' is a malformed {} value"
+               .format(s, TYPE_TO_STR[sym.orig_type]))
+        return False
+
+    for low_sym, high_sym, cond in sym.ranges:
+        if expr_value(cond):
+            low_s = low_sym.str_value
+            high_s = high_sym.str_value
+
+            if not int(low_s, base) <= int(s, base) <= int(high_s, base):
+                _error("{} is outside the range {}-{}"
+                       .format(s, low_s, high_s))
+                return False
+
+            break
+
+    return True
+
+
+def _range_info(sym):
+    # Returns a string with information about the valid range for the symbol
+    # 'sym', or None if 'sym' doesn't have a range
+
+    if sym.orig_type in (INT, HEX):
+        for low, high, cond in sym.ranges:
+            if expr_value(cond):
+                return "Range: {}-{}".format(low.str_value, high.str_value)
+
+    return None
+
+
+def _is_num(name):
+    # Heuristic to see if a symbol name looks like a number, for nicer output
+    # when printing expressions. Things like 16 are actually symbol names, only
+    # they get their name as their value when the symbol is undefined.
+
+    try:
+        int(name)
+    except ValueError:
+        if not name.startswith(("0x", "0X")):
+            return False
+
+        try:
+            int(name, 16)
+        except ValueError:
+            return False
+
+    return True
+
+
+def _getch_compat(win):
+    # Uses get_wch() if available (Python 3.3+) and getch() otherwise.
+    #
+    # Also falls back on getch() if get_wch() raises curses.error, to work
+    # around an issue when resizing the terminal on at least macOS Catalina.
+    # See https://github.com/ulfalizer/Kconfiglib/issues/84.
+    #
+    # Also handles a PDCurses resizing quirk.
+
+    try:
+        c = win.get_wch()
+    except (AttributeError, curses.error):
+        c = win.getch()
+        if 0 <= c <= 255:
+            c = chr(c)
+
+    # Decent resizing behavior on PDCurses requires calling resize_term(0, 0)
+    # after receiving KEY_RESIZE, while ncurses (usually) handles terminal
+    # resizing automatically in get(_w)ch() (see the end of the
+    # resizeterm(3NCURSES) man page).
+    #
+    # resize_term(0, 0) reliably fails and does nothing on ncurses, so this
+    # hack gives ncurses/PDCurses compatibility for resizing. I don't know
+    # whether it would cause trouble for other implementations.
+    if c == curses.KEY_RESIZE:
+        try:
+            curses.resize_term(0, 0)
+        except curses.error:
+            pass
+
+    return c
+
+
+def _warn(*args):
+    # Temporarily returns from curses to shell mode and prints a warning to
+    # stderr. The warning would get lost in curses mode.
+    curses.endwin()
+    print("menuconfig warning: ", end="", file=sys.stderr)
+    print(*args, file=sys.stderr)
+    curses.doupdate()
+
+
+# Ignore exceptions from some functions that might fail, e.g. for small
+# windows. They usually do reasonable things anyway.
+
+
+def _safe_curs_set(visibility):
+    try:
+        curses.curs_set(visibility)
+    except curses.error:
+        pass
+
+
+def _safe_addstr(win, *args):
+    # Clip the line to avoid wrapping to the next line, which looks glitchy.
+    # addchstr() would do it for us, but it's not available in the 'curses'
+    # module.
+
+    attr = None
+    if isinstance(args[0], str):
+        y, x = win.getyx()
+        s = args[0]
+        if len(args) == 2:
+            attr = args[1]
+    else:
+        y, x, s = args[:3]
+        if len(args) == 4:
+            attr = args[3]
+
+    maxlen = _width(win) - x
+    s = s.expandtabs()
+
+    try:
+        # The 'curses' module uses wattr_set() internally if you pass 'attr',
+        # overwriting the background style, so setting 'attr' to 0 in the first
+        # case won't do the right thing
+        if attr is None:
+            win.addnstr(y, x, s, maxlen)
+        else:
+            win.addnstr(y, x, s, maxlen, attr)
+    except curses.error:
+        pass
+
+
+def _safe_addch(win, *args):
+    try:
+        win.addch(*args)
+    except curses.error:
+        pass
+
+
+def _safe_hline(win, *args):
+    try:
+        win.hline(*args)
+    except curses.error:
+        pass
+
+
+def _safe_vline(win, *args):
+    try:
+        win.vline(*args)
+    except curses.error:
+        pass
+
+
+def _safe_move(win, *args):
+    try:
+        win.move(*args)
+    except curses.error:
+        pass
+
+
+def _change_c_lc_ctype_to_utf8():
+    # See _CHANGE_C_LC_CTYPE_TO_UTF8
+
+    if _IS_WINDOWS:
+        # Windows rarely has issues here, and the PEP 538 implementation avoids
+        # changing the locale on it. None of the UTF-8 locales below were
+        # supported from some quick testing either. Play it safe.
+        return
+
+    def try_set_locale(loc):
+        try:
+            locale.setlocale(locale.LC_CTYPE, loc)
+            return True
+        except locale.Error:
+            return False
+
+    # Is LC_CTYPE set to the C locale?
+    if locale.setlocale(locale.LC_CTYPE) == "C":
+        # This list was taken from the PEP 538 implementation in the CPython
+        # code, in Python/pylifecycle.c
+        for loc in "C.UTF-8", "C.utf8", "UTF-8":
+            if try_set_locale(loc):
+                # LC_CTYPE successfully changed
+                return
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/ext/Kconfiglib/oldconfig.py b/ext/Kconfiglib/oldconfig.py
new file mode 100755
index 0000000..53434b2
--- /dev/null
+++ b/ext/Kconfiglib/oldconfig.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Implements oldconfig functionality.
+
+  1. Loads existing .config
+  2. Prompts for the value of all modifiable symbols/choices that
+     aren't already set in the .config
+  3. Writes an updated .config
+
+The default input/output filename is '.config'. A different filename can be
+passed in the KCONFIG_CONFIG environment variable.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+
+Entering '?' displays the help text of the symbol/choice, if any.
+
+Unlike 'make oldconfig', this script doesn't print menu titles and comments,
+but gives Kconfig definition locations. Printing menus and comments would be
+pretty easy to add: Look at the parents of each item, and print all menu
+prompts and comments unless they have already been printed (assuming you want
+to skip "irrelevant" menus).
+"""
+from __future__ import print_function
+
+import sys
+
+from kconfiglib import Symbol, Choice, BOOL, TRISTATE, HEX, standard_kconfig
+
+
+# Python 2/3 compatibility hack
+if sys.version_info[0] < 3:
+    input = raw_input
+
+
+def _main():
+    # Earlier symbols in Kconfig files might depend on later symbols and become
+    # visible if their values change. This flag is set to True if the value of
+    # any symbol changes, in which case we rerun the oldconfig to check for new
+    # visible symbols.
+    global conf_changed
+
+    kconf = standard_kconfig(__doc__)
+    print(kconf.load_config())
+
+    while True:
+        conf_changed = False
+
+        for node in kconf.node_iter():
+            oldconfig(node)
+
+        if not conf_changed:
+            break
+
+    print(kconf.write_config())
+
+
+def oldconfig(node):
+    """
+    Prompts the user for a value if node.item is a visible symbol/choice with
+    no user value.
+    """
+    # See main()
+    global conf_changed
+
+    # Only symbols and choices can be configured
+    if not isinstance(node.item, (Symbol, Choice)):
+        return
+
+    # Skip symbols and choices that aren't visible
+    if not node.item.visibility:
+        return
+
+    # Skip symbols and choices that don't have a prompt (at this location)
+    if not node.prompt:
+        return
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+
+        # Skip symbols that already have a user value
+        if sym.user_value is not None:
+            return
+
+        # Skip symbols that can only have a single value, due to selects
+        if len(sym.assignable) == 1:
+            return
+
+        # Skip symbols in choices in y mode. We ask once for the entire choice
+        # instead.
+        if sym.choice and sym.choice.tri_value == 2:
+            return
+
+        # Loop until the user enters a valid value or enters a blank string
+        # (for the default value)
+        while True:
+            val = input("{} ({}) [{}] ".format(
+                node.prompt[0], _name_and_loc_str(sym),
+                _default_value_str(sym)))
+
+            if val == "?":
+                _print_help(node)
+                continue
+
+            # Substitute a blank string with the default value the symbol
+            # would get
+            if not val:
+                val = sym.str_value
+
+            # Automatically add a "0x" prefix for hex symbols, like the
+            # menuconfig interface does. This isn't done when loading .config
+            # files, hence why set_value() doesn't do it automatically.
+            if sym.type == HEX and not val.startswith(("0x", "0X")):
+                val = "0x" + val
+
+            old_str_val = sym.str_value
+
+            # Kconfiglib itself will print a warning here if the value
+            # is invalid, so we don't need to bother
+            if sym.set_value(val):
+                # Valid value input. We're done with this node.
+
+                if sym.str_value != old_str_val:
+                    conf_changed = True
+
+                return
+
+    else:
+        choice = node.item
+
+        # Skip choices that already have a visible user selection...
+        if choice.user_selection and choice.user_selection.visibility == 2:
+            # ...unless there are new visible symbols in the choice. (We know
+            # they have y (2) visibility in that case, because m-visible
+            # symbols get demoted to n-visibility in y-mode choices, and the
+            # user-selected symbol had visibility y.)
+            for sym in choice.syms:
+                if sym is not choice.user_selection and sym.visibility and \
+                   sym.user_value is None:
+                    # New visible symbols in the choice
+                    break
+            else:
+                # No new visible symbols in the choice
+                return
+
+        # Get a list of available selections. The mode of the choice limits
+        # the visibility of the choice value symbols, so this will indirectly
+        # skip choices in n and m mode.
+        options = [sym for sym in choice.syms if sym.visibility == 2]
+
+        if not options:
+            # No y-visible choice value symbols
+            return
+
+        # Loop until the user enters a valid selection or a blank string (for
+        # the default selection)
+        while True:
+            print("{} ({})".format(node.prompt[0], _name_and_loc_str(choice)))
+
+            for i, sym in enumerate(options, 1):
+                print("{} {}. {} ({})".format(
+                    ">" if sym is choice.selection else " ",
+                    i,
+                    # Assume people don't define choice symbols with multiple
+                    # prompts. That generates a warning anyway.
+                    sym.nodes[0].prompt[0],
+                    sym.name))
+
+            sel_index = input("choice[1-{}]: ".format(len(options)))
+
+            if sel_index == "?":
+                _print_help(node)
+                continue
+
+            # Pick the default selection if the string is blank
+            if not sel_index:
+                choice.selection.set_value(2)
+                break
+
+            try:
+                sel_index = int(sel_index)
+            except ValueError:
+                print("Bad index", file=sys.stderr)
+                continue
+
+            if not 1 <= sel_index <= len(options):
+                print("Bad index", file=sys.stderr)
+                continue
+
+            # Valid selection
+
+            if options[sel_index - 1].tri_value != 2:
+                conf_changed = True
+
+            options[sel_index - 1].set_value(2)
+            break
+
+        # Give all of the non-selected visible choice symbols the user value n.
+        # This makes it so that the choice is no longer considered new once we
+        # do additional passes, if the reason that it was considered new was
+        # that it had new visible choice symbols.
+        #
+        # Only giving visible choice symbols the user value n means we will
+        # prompt for the choice again if later user selections make more new
+        # choice symbols visible, which is correct.
+        for sym in choice.syms:
+            if sym is not choice.user_selection and sym.visibility:
+                sym.set_value(0)
+
+
+def _name_and_loc_str(sc):
+    # Helper for printing the name of the symbol/choice 'sc' along with the
+    # location(s) in the Kconfig files where it is defined. Unnamed choices
+    # return "choice" instead of the name.
+
+    return "{}, defined at {}".format(
+        sc.name or "choice",
+        ", ".join("{}:{}".format(node.filename, node.linenr)
+                  for node in sc.nodes))
+
+
+def _print_help(node):
+    print("\n" + (node.help or "No help text\n"))
+
+
+def _default_value_str(sym):
+    # Returns the "m/M/y" string in e.g.
+    #
+    #   TRISTATE_SYM prompt (TRISTATE_SYM, defined at Kconfig:9) [n/M/y]:
+    #
+    # For string/int/hex, returns the default value as-is.
+
+    if sym.type in (BOOL, TRISTATE):
+        return "/".join(("NMY" if sym.tri_value == tri else "nmy")[tri]
+                        for tri in sym.assignable)
+
+    # string/int/hex
+    return sym.str_value
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/ext/Kconfiglib/olddefconfig.py b/ext/Kconfiglib/olddefconfig.py
new file mode 100755
index 0000000..2dadfb4
--- /dev/null
+++ b/ext/Kconfiglib/olddefconfig.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Updates an old .config file or creates a new one, by filling in default values
+for all new symbols. This is the same as picking the default selection for all
+symbols in oldconfig, or entering the menuconfig interface and immediately
+saving.
+
+The default input/output filename is '.config'. A different filename can be
+passed in the KCONFIG_CONFIG environment variable.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+    print(kconf.load_config())
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/savedefconfig.py b/ext/Kconfiglib/savedefconfig.py
new file mode 100755
index 0000000..0f36bde
--- /dev/null
+++ b/ext/Kconfiglib/savedefconfig.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Saves a minimal configuration file that only lists symbols that differ in value
+from their defaults. Loading such a configuration file is equivalent to loading
+the "full" configuration file.
+
+Minimal configuration files are handy to start from when editing configuration
+files by hand.
+
+The default input configuration file is '.config'. A different input filename
+can be passed in the KCONFIG_CONFIG environment variable.
+
+Note: Minimal configurations can also be generated from within the menuconfig
+interface.
+"""
+import argparse
+
+import kconfiglib
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--kconfig",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    parser.add_argument(
+        "--out",
+        metavar="MINIMAL_CONFIGURATION",
+        default="defconfig",
+        help="Output filename for minimal configuration (default: defconfig)")
+
+    args = parser.parse_args()
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    print(kconf.load_config())
+    print(kconf.write_min_config(args.out))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/setconfig.py b/ext/Kconfiglib/setconfig.py
new file mode 100755
index 0000000..f9cf5cd
--- /dev/null
+++ b/ext/Kconfiglib/setconfig.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Simple utility for setting configuration values from the command line.
+
+Sample usage:
+
+  $ setconfig FOO_SUPPORT=y BAR_BITS=8
+
+Note: Symbol names should not be prefixed with 'CONFIG_'.
+
+The exit status on errors is 1.
+
+The default input/output configuration file is '.config'. A different filename
+can be passed in the KCONFIG_CONFIG environment variable.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+"""
+import argparse
+import sys
+
+import kconfiglib
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--kconfig",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    parser.add_argument(
+        "--no-check-exists",
+        dest="check_exists",
+        action="store_false",
+        help="Ignore assignments to non-existent symbols instead of erroring "
+             "out")
+
+    parser.add_argument(
+        "--no-check-value",
+        dest="check_value",
+        action="store_false",
+        help="Ignore assignments that didn't \"take\" (where the symbol got a "
+             "different value, e.g. due to unsatisfied dependencies) instead "
+             "of erroring out")
+
+    parser.add_argument(
+        "assignments",
+        metavar="ASSIGNMENT",
+        nargs="*",
+        help="A 'NAME=value' assignment")
+
+    args = parser.parse_args()
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    print(kconf.load_config())
+
+    for arg in args.assignments:
+        if "=" not in arg:
+            sys.exit("error: no '=' in assignment: '{}'".format(arg))
+        name, value = arg.split("=", 1)
+
+        if name not in kconf.syms:
+            if not args.check_exists:
+                continue
+            sys.exit("error: no symbol '{}' in configuration".format(name))
+
+        sym = kconf.syms[name]
+
+        if not sym.set_value(value):
+            sys.exit("error: '{}' is an invalid value for the {} symbol {}"
+                     .format(value, kconfiglib.TYPE_TO_STR[sym.orig_type],
+                             name))
+
+        if args.check_value and sym.str_value != value:
+            sys.exit("error: {} was assigned the value '{}', but got the "
+                     "value '{}'. Check the symbol's dependencies, and make "
+                     "sure that it has a prompt."
+                     .format(name, value, sym.str_value))
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/ext/Kconfiglib/setup.cfg b/ext/Kconfiglib/setup.cfg
new file mode 100644
index 0000000..185845e
--- /dev/null
+++ b/ext/Kconfiglib/setup.cfg
@@ -0,0 +1,7 @@
+[bdist_wheel]
+# We support both Python 2 and Python 3
+universal = 1
+
+[metadata]
+# Include the license file in wheels
+license_file = LICENSE.txt
diff --git a/ext/Kconfiglib/setup.py b/ext/Kconfiglib/setup.py
new file mode 100644
index 0000000..8aa2cc4
--- /dev/null
+++ b/ext/Kconfiglib/setup.py
@@ -0,0 +1,96 @@
+import io
+import os
+
+import setuptools
+
+
+setuptools.setup(
+    name="kconfiglib",
+    # MAJOR.MINOR.PATCH, per http://semver.org
+    version="14.1.0",
+    description="A flexible Python Kconfig implementation",
+
+    # Make sure that README.rst decodes on Python 3 in environments that use
+    # the C locale (which implies ASCII), by explicitly giving the encoding.
+    #
+    # io.open() has the 'encoding' parameter on both Python 2 and 3. open()
+    # doesn't have it on Python 2. This lets us use the same code for both.
+    long_description=io.open(
+        os.path.join(os.path.dirname(__file__), "README.rst"),
+        encoding="utf-8"
+    ).read(),
+
+    url="https://github.com/ulfalizer/Kconfiglib",
+    author='Ulf "Ulfalizer" Magnusson',
+    author_email="ulfalizer@gmail.com",
+    keywords="kconfig, kbuild, menuconfig, configuration-management",
+    license="ISC",
+
+    py_modules=(
+        "kconfiglib",
+        "menuconfig",
+        "guiconfig",
+        "genconfig",
+        "oldconfig",
+        "olddefconfig",
+        "savedefconfig",
+        "defconfig",
+        "alldefconfig",
+        "allnoconfig",
+        "allmodconfig",
+        "allyesconfig",
+        "listnewconfig",
+        "setconfig",
+    ),
+
+    entry_points={
+        "console_scripts": (
+            "menuconfig = menuconfig:_main",
+            "guiconfig = guiconfig:_main",
+            "genconfig = genconfig:main",
+            "oldconfig = oldconfig:_main",
+            "olddefconfig = olddefconfig:main",
+            "savedefconfig = savedefconfig:main",
+            "defconfig = defconfig:main",
+            "alldefconfig = alldefconfig:main",
+            "allnoconfig = allnoconfig:main",
+            "allmodconfig = allmodconfig:main",
+            "allyesconfig = allyesconfig:main",
+            "listnewconfig = listnewconfig:main",
+            "setconfig = setconfig:main",
+        )
+    },
+
+    # Note: windows-curses is not automatically installed on Windows anymore,
+    # because it made Kconfiglib impossible to install on MSYS2 with pip
+
+    # Needs support for unnumbered {} in format() and argparse
+    python_requires=">=2.7,!=3.0.*,!=3.1.*",
+
+    project_urls={
+        "GitHub repository": "https://github.com/ulfalizer/Kconfiglib",
+        "Examples": "https://github.com/ulfalizer/Kconfiglib/tree/master/examples",
+    },
+
+    classifiers=[
+        "Development Status :: 5 - Production/Stable",
+        "Intended Audience :: Developers",
+        "Topic :: Software Development :: Build Tools",
+        "Topic :: System :: Operating System Kernels :: Linux",
+        "License :: OSI Approved :: ISC License (ISCL)",
+        "Operating System :: POSIX",
+        "Operating System :: Microsoft :: Windows",
+        "Programming Language :: Python :: 2",
+        "Programming Language :: Python :: 2.7",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.2",
+        "Programming Language :: Python :: 3.3",
+        "Programming Language :: Python :: 3.4",
+        "Programming Language :: Python :: 3.5",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: Implementation :: CPython",
+        "Programming Language :: Python :: Implementation :: PyPy",
+    ]
+)
diff --git a/ext/Kconfiglib/tests/Kappend b/ext/Kconfiglib/tests/Kappend
new file mode 100644
index 0000000..ce1478f
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kappend
@@ -0,0 +1,12 @@
+config MODULES
+    def_bool y
+
+config BOOL
+    bool "bool 1"
+
+config STRING
+    string "string"
+
+config IGNOREME
+    bool "ignore me"
+    default y
diff --git a/ext/Kconfiglib/tests/Kassignable b/ext/Kconfiglib/tests/Kassignable
new file mode 100644
index 0000000..26b4177
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kassignable
@@ -0,0 +1,230 @@
+config MODULES
+	bool "modules"
+	option modules
+
+
+# Things that should never be .assignable
+
+if UNDEFINED && "const"
+endif
+
+config NO_PROMPT
+	bool
+
+config STRING
+	string "string"
+
+config INT
+	int "int"
+
+config HEX
+	hex "hex"
+
+
+# Non-selected symbols
+
+config Y_VIS_BOOL
+	bool "y-vis bool"
+
+config M_VIS_BOOL
+	bool "m-vis bool" if m
+
+config N_VIS_BOOL
+	bool "n-vis bool" if n
+
+config Y_VIS_TRI
+	tristate "y-vis tri"
+
+config M_VIS_TRI
+	tristate "m-vis tri" if m
+
+config N_VIS_TRI
+	tristate "n-vis tri" if n
+
+
+# Symbols selected to y
+
+config Y_SELECTOR
+	def_tristate y
+
+	select Y_SEL_Y_VIS_BOOL
+	select Y_SEL_M_VIS_BOOL
+	select Y_SEL_N_VIS_BOOL
+
+	select Y_SEL_Y_VIS_TRI
+	select Y_SEL_M_VIS_TRI
+	select Y_SEL_N_VIS_TRI
+
+config Y_SEL_Y_VIS_BOOL
+	bool "y-sel y-vis bool"
+
+config Y_SEL_M_VIS_BOOL
+	bool "y-sel m-vis bool" if m
+
+config Y_SEL_N_VIS_BOOL
+	bool "y-sel n-vis bool" if n
+
+config Y_SEL_Y_VIS_TRI
+	tristate "y-sel y-vis tri"
+
+config Y_SEL_M_VIS_TRI
+	tristate "y-sel m-vis tri" if m
+
+config Y_SEL_N_VIS_TRI
+	tristate "y-sel n-vis tri" if n
+
+
+# Symbols selected to m
+
+config M_SELECTOR
+	def_tristate m
+
+	select M_SEL_Y_VIS_BOOL
+	select M_SEL_M_VIS_BOOL
+	select M_SEL_N_VIS_BOOL
+
+	select M_SEL_Y_VIS_TRI
+	select M_SEL_M_VIS_TRI
+	select M_SEL_N_VIS_TRI
+
+config M_SEL_Y_VIS_BOOL
+	bool "m-sel y-vis bool"
+
+config M_SEL_M_VIS_BOOL
+	bool "m-sel m-vis bool" if m
+
+config M_SEL_N_VIS_BOOL
+	bool "m-sel n-vis bool" if n
+
+config M_SEL_Y_VIS_TRI
+	tristate "m-sel y-vis tri"
+
+config M_SEL_M_VIS_TRI
+	tristate "m-sel m-vis tri" if m
+
+config M_SEL_N_VIS_TRI
+	tristate "m-sel n-vis tri" if n
+
+
+# Symbols implied to y
+
+config Y_IMPLIER
+	def_tristate y
+
+	imply Y_IMP_Y_VIS_BOOL
+	imply Y_IMP_M_VIS_BOOL
+	imply Y_IMP_N_VIS_BOOL
+
+	imply Y_IMP_Y_VIS_TRI
+	imply Y_IMP_M_VIS_TRI
+	imply Y_IMP_N_VIS_TRI
+
+config Y_IMP_Y_VIS_BOOL
+	bool "y-imp y-vis bool"
+
+config Y_IMP_M_VIS_BOOL
+	bool "y-imp m-vis bool" if m
+
+config Y_IMP_N_VIS_BOOL
+	bool "y-imp n-vis bool" if n
+
+config Y_IMP_Y_VIS_TRI
+	tristate "y-imp y-vis tri"
+
+config Y_IMP_M_VIS_TRI
+	tristate "y-imp m-vis tri" if m
+
+config Y_IMP_N_VIS_TRI
+	tristate "y-imp n-vis tri" if n
+
+
+# Symbols implied to m (never affects assignable values)
+
+config M_IMPLIER
+	def_tristate m
+
+	imply M_IMP_Y_VIS_BOOL
+	imply M_IMP_M_VIS_BOOL
+	imply M_IMP_N_VIS_BOOL
+
+	imply M_IMP_Y_VIS_TRI
+	imply M_IMP_M_VIS_TRI
+	imply M_IMP_N_VIS_TRI
+
+config M_IMP_Y_VIS_BOOL
+	bool "m-imp y-vis bool"
+
+config M_IMP_M_VIS_BOOL
+	bool "m-imp m-vis bool" if m
+
+config M_IMP_N_VIS_BOOL
+	bool "m-imp n-vis bool" if n
+
+config M_IMP_Y_VIS_TRI
+	tristate "m-imp y-vis tri"
+
+config M_IMP_M_VIS_TRI
+	tristate "m-imp m-vis tri" if m
+
+config M_IMP_N_VIS_TRI
+	tristate "m-imp n-vis tri" if n
+
+
+# Symbols in y-mode choice
+
+choice Y_CHOICE
+	bool "y-mode choice"
+
+config Y_CHOICE_BOOL
+	bool "y-mode choice bool"
+
+config Y_CHOICE_TRISTATE
+	tristate "y-mode choice tristate"
+
+config Y_CHOICE_N_VIS_TRISTATE
+	tristate "y-mode choice tristate invisible" if n
+
+endchoice
+
+
+# Symbols in m/y-mode choice
+
+choice MY_CHOICE
+	tristate "m/y-mode choice"
+
+config MY_CHOICE_BOOL
+	bool "m/y-mode choice bool"
+
+config MY_CHOICE_TRISTATE
+	tristate "m/y-mode choice tristate"
+
+config MY_CHOICE_N_VIS_TRISTATE
+	tristate "m/y-mode choice tristate invisible" if n
+
+endchoice
+
+
+# Choices with some other possible modes
+
+choice NMY_CHOICE
+	tristate "n/m/y-mode choice"
+	optional
+endchoice
+
+choice NY_CHOICE
+	bool "n/y-mode choice"
+	optional
+endchoice
+
+choice NM_CHOICE
+	tristate "n/m-mode choice" if m
+	optional
+endchoice
+
+choice M_CHOICE
+	tristate "m-mode choice" if m
+endchoice
+
+choice N_CHOICE
+	tristate "n-mode choice" if n
+endchoice
diff --git a/ext/Kconfiglib/tests/Kchoice b/ext/Kconfiglib/tests/Kchoice
new file mode 100644
index 0000000..16b38d4
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kchoice
@@ -0,0 +1,198 @@
+config MODULES
+    bool "modules"
+
+# bool/tristate and optional
+
+choice BOOL
+    bool "bool"
+config B_1
+    tristate "B_1"
+config B_2
+    tristate "B_2"
+endchoice
+
+choice BOOL_OPT
+    bool "bool optional"
+    optional
+config BO_1
+    tristate "BO_1"
+config BO_2
+    tristate "BO_2"
+endchoice
+
+choice TRISTATE
+    tristate "tristate"
+config T_1
+    tristate "T_1"
+config T_2
+    tristate "T_2"
+endchoice
+
+choice TRISTATE_OPT
+    tristate "tristate optional"
+    optional
+config TO_1
+    tristate "TO_1"
+config TO_2
+    tristate "TO_2"
+endchoice
+
+# m-visibility
+
+choice BOOL_M
+    bool "bool m" if m
+config BM_1
+    tristate "BM_1"
+config BM_2
+    tristate "BM_2"
+endchoice
+
+choice TRISTATE_M
+    tristate "tristate m" if m
+config TM_1
+    tristate "TM_1"
+config TM_2
+    tristate "TM_2"
+endchoice
+
+# Defaults
+
+config TRISTATE_SYM
+    tristate "tristate"
+
+choice DEFAULTS
+    bool "defaults"
+    default OPT_1 if n
+    default OPT_2 if TRISTATE_SYM
+    default OPT_4
+config OPT_1
+    tristate "OPT_1"
+config OPT_2
+    tristate "OPT_2"
+config OPT_3
+    tristate "OPT_3"
+config OPT_4
+    tristate "OPT_4"
+endchoice
+
+choice DEFAULTS_NOT_VISIBLE
+    bool "defaults not visible"
+    # Skipped due to condition
+    default OPT_6 if n
+    # Skipped because OPT_7 is not visible
+    default OPT_7
+    # This one should apply
+    default OPT_8
+config OPT_5
+    tristate "OPT_5"
+config OPT_6
+    tristate "OPT_6"
+config OPT_7
+    tristate "OPT_7" if n
+config OPT_8
+    tristate "OPT_8"
+config OPT_9
+    tristate "OPT_9"
+endchoice
+
+# Choices without an explicitly specified type should get the type of the first
+# symbol with a type
+
+choice NO_TYPE_BOOL
+    prompt "no type bool"
+config NTB_1
+    bool "NTB_1"
+config NTB_2
+    tristate "NTB_2"
+endchoice
+
+choice NO_TYPE_TRISTATE
+    prompt "no type tristate"
+config NTT_1
+config NTT_2
+    tristate "NTB_2"
+config NTT_3
+    bool "NTT_3"
+endchoice
+
+# Choice items without an explicitly specified type should get the type of the
+# choice
+
+choice MISSING_MEMBER_TYPES_1
+    bool "missing member types"
+config MMT_1
+config MMT_2
+config MMT_3
+    tristate
+endchoice
+
+choice MISSING_MEMBER_TYPES_2
+config MMT_4
+config MMT_5
+    bool
+endchoice
+
+# Choice where the default selection (the first symbol) depends on another
+# symbol. If that symbol becomes 'n', the default selection should change to
+# the first visible symbol in the choice.
+
+choice DEFAULT_WITH_DEP
+    bool "default with dep"
+
+config A
+    bool "A"
+    depends on DEP
+
+config B
+    bool "B"
+
+endchoice
+
+config DEP
+    bool "dep"
+
+# Choice with symbols that shouldn't be considered choice symbols because they
+# depend on the preceding symbol. This might be a kconfig bug, but some things
+# use it, so we need to emulate it.
+
+choice WEIRD_SYMS
+    bool "weird symbols that aren't considered part of the choice"
+
+# Only WS1 is part of the choice
+config WS1
+    bool "WS1"
+
+config WS2
+    bool "WS2"
+    depends on WS1
+
+config WS3
+    bool
+    depends on WS2
+
+config WS4
+    bool
+    depends on WS1
+
+config WS5
+    bool "WS5" if WS1
+
+# 'if' has the same effect, so only WS6 is part of the choice
+config WS6
+    bool "WS6"
+
+if WS6
+
+config WS7
+    bool
+
+config WS8
+    bool "WS8"
+
+endif
+
+# Should also be part of the choice
+config WS9
+    bool "WS9"
+
+endchoice
diff --git a/ext/Kconfiglib/tests/Kdefconfig_existent b/ext/Kconfiglib/tests/Kdefconfig_existent
new file mode 100644
index 0000000..304cae6
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdefconfig_existent
@@ -0,0 +1,8 @@
+# $FOO is "defconfig_2"
+
+config A
+    string
+    option defconfig_list
+    default "Kconfiglib/tests/defconfig_1" if y && !n && n
+    default "Kconfiglib/tests/$FOO"
+    default "Kconfiglib/tests/defconfig_1"
diff --git a/ext/Kconfiglib/tests/Kdefconfig_existent_but_n b/ext/Kconfiglib/tests/Kdefconfig_existent_but_n
new file mode 100644
index 0000000..2fdaaa9
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdefconfig_existent_but_n
@@ -0,0 +1,10 @@
+# $FOO is "defconfig_2"
+# Should produce None due to the "depends on n"
+
+config A
+    string
+    depends on n
+    option defconfig_list
+    default "Kconfiglib/tests/defconfig_1" if y && !n && n
+    default "Kconfiglib/tests/$FOO"
+    default "Kconfiglib/tests/defconfig_1"
diff --git a/ext/Kconfiglib/tests/Kdefconfig_nonexistent b/ext/Kconfiglib/tests/Kdefconfig_nonexistent
new file mode 100644
index 0000000..5b7230f
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdefconfig_nonexistent
@@ -0,0 +1,5 @@
+config A
+    string
+    option defconfig_list
+    default "Kconfiglib/tests/non_existent_1"
+    default "Kconfiglib/tests/non_existent_2"
diff --git a/ext/Kconfiglib/tests/Kdefconfig_srctree b/ext/Kconfiglib/tests/Kdefconfig_srctree
new file mode 100644
index 0000000..d2591fa
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdefconfig_srctree
@@ -0,0 +1,5 @@
+config A
+    string
+    option defconfig_list
+    default "sub/defconfig_in_sub" # Assume this doesn't exist
+    default "Kconfiglib/tests/defconfig_2"
diff --git a/ext/Kconfiglib/tests/Kdepcopy b/ext/Kconfiglib/tests/Kdepcopy
new file mode 100644
index 0000000..6abe898
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdepcopy
@@ -0,0 +1,173 @@
+# We verify that the properties below end up in definition order
+
+config MULTIDEF
+    bool
+    default A
+    default B
+    select AA
+    imply AA
+
+if FOO
+
+config MULTIDEF
+    default C
+    default D
+    select BB
+    imply BB
+
+if BAR
+
+config MULTIDEF
+    default E
+    default F
+    select CC
+    imply CC
+
+menu "menu"
+
+config MULTIDEF
+    default G
+    default H
+    select DD
+    imply DD
+
+config MULTIDEF
+    default I
+    default J
+    select EE
+    imply EE
+
+endmenu
+
+config MULTIDEF
+    default K
+    default L
+    select FF
+    imply FF
+
+config MULTIDEF
+    default M
+    default N
+    select GG
+    imply GG
+
+endif
+
+config MULTIDEF
+    default O
+    default P
+    select HH
+    select II
+    imply HH
+    imply II
+
+endif
+
+config MULTIDEF
+    default Q
+    default R
+    select JJ
+    imply JJ
+
+
+# Same test with choice symbols involved
+
+config MULTIDEF_CHOICE
+    bool
+    select A
+
+choice
+    bool "choice"
+
+config MULTIDEF_CHOICE
+    bool "multidef choice"
+    select B
+
+endchoice
+
+config MULTIDEF_CHOICE
+    bool
+    select C
+
+
+# Same test with ranges involved
+
+config MULTIDEF_RANGE
+    int
+    range A _
+
+menu "menu"
+
+config MULTIDEF_RANGE
+    int
+    range B _
+
+if FOO
+
+config MULTIDEF_RANGE
+    int
+    range C _
+
+endif
+
+config MULTIDEF_RANGE
+    int
+    range D _
+
+endmenu
+
+config MULTIDEF_RANGE
+    int
+    range E _
+
+config MULTIDEF_RANGE
+    int
+    range F _
+
+
+# Same test for a choice
+
+choice MULTICHOICE
+    bool "choice"
+    default A
+
+config A
+    bool "A"
+
+config B
+    bool "B"
+
+config C
+    bool "C"
+
+config D
+    bool "C"
+
+config E
+    bool "C"
+
+endchoice
+
+if FOO
+
+choice MULTICHOICE
+    default B
+endchoice
+
+menu "menu"
+
+choice MULTICHOICE
+    default C
+endchoice
+
+endmenu
+
+choice MULTICHOICE
+    default D
+endchoice
+
+endif
+
+choice MULTICHOICE
+    default E
+endchoice
diff --git a/ext/Kconfiglib/tests/Kdeploop0 b/ext/Kconfiglib/tests/Kdeploop0
new file mode 100644
index 0000000..98d3e3c
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop0
@@ -0,0 +1,3 @@
+config FOO
+    bool
+    depends on FOO
diff --git a/ext/Kconfiglib/tests/Kdeploop1 b/ext/Kconfiglib/tests/Kdeploop1
new file mode 100644
index 0000000..134cd29
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop1
@@ -0,0 +1,3 @@
+config FOO
+    bool
+    select FOO
diff --git a/ext/Kconfiglib/tests/Kdeploop10 b/ext/Kconfiglib/tests/Kdeploop10
new file mode 100644
index 0000000..2e616ae
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop10
@@ -0,0 +1,48 @@
+config A
+    bool
+    depends on B
+
+config B
+    bool
+    depends on C = 7
+
+config C
+    int
+    range D 8
+
+config D
+    int
+    default 3 if E
+    default 8
+
+config E
+    bool
+
+config F
+    bool
+    select E if G
+
+config G
+    bool
+    depends on H
+
+choice
+    bool "choice"
+
+config H
+    bool "H"
+    depends on I
+
+endchoice
+
+choice
+    bool "choice" if J
+
+config I
+    bool "I"
+
+endchoice
+
+config J
+    bool
+    depends on A
diff --git a/ext/Kconfiglib/tests/Kdeploop2 b/ext/Kconfiglib/tests/Kdeploop2
new file mode 100644
index 0000000..c997243
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop2
@@ -0,0 +1,3 @@
+config FOO
+    bool
+    default FOO
diff --git a/ext/Kconfiglib/tests/Kdeploop3 b/ext/Kconfiglib/tests/Kdeploop3
new file mode 100644
index 0000000..90c83d5
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop3
@@ -0,0 +1,3 @@
+config FOO
+    bool
+    default y if FOO
diff --git a/ext/Kconfiglib/tests/Kdeploop4 b/ext/Kconfiglib/tests/Kdeploop4
new file mode 100644
index 0000000..789d8b7
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop4
@@ -0,0 +1,7 @@
+config FOO
+    bool
+    depends on BAR
+
+config BAR
+    bool
+    depends on FOO
diff --git a/ext/Kconfiglib/tests/Kdeploop5 b/ext/Kconfiglib/tests/Kdeploop5
new file mode 100644
index 0000000..f12fe6b
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop5
@@ -0,0 +1,7 @@
+config FOO
+    bool
+    select BAR
+
+config BAR
+    bool
+    select FOO
diff --git a/ext/Kconfiglib/tests/Kdeploop6 b/ext/Kconfiglib/tests/Kdeploop6
new file mode 100644
index 0000000..cb1e701
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop6
@@ -0,0 +1,6 @@
+config FOO
+    bool
+
+config BAR
+    bool
+    select FOO if FOO
diff --git a/ext/Kconfiglib/tests/Kdeploop7 b/ext/Kconfiglib/tests/Kdeploop7
new file mode 100644
index 0000000..63d2c57
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop7
@@ -0,0 +1,11 @@
+choice
+    bool "choice"
+
+config FOO
+    bool "foo"
+    depends on BAR
+
+config BAR
+    bool "bar"
+
+endchoice
diff --git a/ext/Kconfiglib/tests/Kdeploop8 b/ext/Kconfiglib/tests/Kdeploop8
new file mode 100644
index 0000000..84efd8d
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop8
@@ -0,0 +1,8 @@
+choice
+    bool "choice"
+    default FOO if FOO
+
+config FOO
+    bool "foo"
+
+endchoice
diff --git a/ext/Kconfiglib/tests/Kdeploop9 b/ext/Kconfiglib/tests/Kdeploop9
new file mode 100644
index 0000000..939f7f4
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdeploop9
@@ -0,0 +1,7 @@
+choice
+    bool "choice" if FOO
+
+config FOO
+    bool "foo"
+
+endchoice
diff --git a/ext/Kconfiglib/tests/Kdirdep b/ext/Kconfiglib/tests/Kdirdep
new file mode 100644
index 0000000..cbb88b9
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kdirdep
@@ -0,0 +1,30 @@
+config NO_DEP_SYM
+    bool
+
+config DEP_SYM
+    bool
+    depends on A
+
+config DEP_SYM
+    depends on B && C
+
+config DEP_SYM
+    depends on !D
+
+
+choice NO_DEP_CHOICE
+    bool "no dep. choice"
+endchoice
+
+choice DEP_CHOICE
+    bool "dep. choice"
+    depends on A
+endchoice
+
+choice DEP_CHOICE
+    depends on B
+endchoice
+
+choice DEP_CHOICE
+    depends on C
+endchoice
diff --git a/ext/Kconfiglib/tests/Kescape b/ext/Kconfiglib/tests/Kescape
new file mode 100644
index 0000000..ee6b2b6
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kescape
@@ -0,0 +1,3 @@
+config STRING
+    string "string"
+    default "\"\\"
diff --git a/ext/Kconfiglib/tests/Keval b/ext/Kconfiglib/tests/Keval
new file mode 100644
index 0000000..1e64b83
--- /dev/null
+++ b/ext/Kconfiglib/tests/Keval
@@ -0,0 +1,34 @@
+# Enabled/disabled in the test
+config MODULES
+    bool "modules"
+    option modules
+
+config N
+    def_tristate n
+
+config M
+    def_tristate m
+
+menuconfig Y
+    def_tristate y
+    prompt "foo"
+
+config Y_STRING
+    string
+    default "y"
+
+config FOO_BAR_STRING
+    string
+    default "foo bar"
+
+config INT_37
+    int
+    default 37
+
+config HEX_0X37
+    hex
+    default 0x37
+
+config HEX_37
+    hex
+    default 37
diff --git a/ext/Kconfiglib/tests/Kexpr_items b/ext/Kconfiglib/tests/Kexpr_items
new file mode 100644
index 0000000..fb97ace
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kexpr_items
@@ -0,0 +1,11 @@
+config TEST
+    bool
+    default A && (B || !C && D = "E") || F > G || !!!H
+
+choice CHOICE
+    bool "choice"
+
+config TEST_CHOICE
+    bool "test choice" if A
+
+endchoice
diff --git a/ext/Kconfiglib/tests/Kheader b/ext/Kconfiglib/tests/Kheader
new file mode 100644
index 0000000..495afc6
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kheader
@@ -0,0 +1,5 @@
+# Used to test headers in .config and header files
+
+config FOO
+	bool "foo"
+	default y
diff --git a/ext/Kconfiglib/tests/Khelp b/ext/Kconfiglib/tests/Khelp
new file mode 100644
index 0000000..b80c2eb
--- /dev/null
+++ b/ext/Kconfiglib/tests/Khelp
@@ -0,0 +1,50 @@
+config TWO_HELP_STRINGS
+    bool
+    help
+      first help string
+
+
+
+
+config TWO_HELP_STRINGS
+    help
+      second help string
+
+config NO_BLANK_AFTER_HELP
+    bool
+    help
+      help for
+      NO_BLANK_AFTER_HELP
+choice CHOICE_HELP
+    bool "choice with help"
+    help
+  help for
+  CHOICE_HELP
+endchoice
+
+config HELP_TERMINATED_BY_COMMENT
+  bool
+  help
+ a
+ b
+ c
+#
+
+config TRICKY_HELP
+  bool
+  -help---
+
+
+  a
+   b
+    c
+
+   d
+    e
+     f
+
+
+  g
+   h
+    i
+#
diff --git a/ext/Kconfiglib/tests/Kifremoval b/ext/Kconfiglib/tests/Kifremoval
new file mode 100644
index 0000000..f94472c
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kifremoval
@@ -0,0 +1,99 @@
+# Test some tricky cases that give consecutive 'if' nodes even after
+# flattening. Simple cases are exercised a ton elsewhere.
+
+if X
+endif
+if X
+endif
+
+config A
+
+if X
+endif
+if X
+endif
+
+config B
+
+if X
+endif
+if X
+endif
+if X
+endif
+
+config C
+
+if X
+  if X
+    if X
+    endif
+    if X
+    endif
+  endif
+  if X
+    if X
+    endif
+    if X
+    endif
+  endif
+  config D
+endif
+if X
+endif
+
+menu "E"
+  if X
+    if X
+    endif
+  endif
+  if X
+    if X
+    endif
+  endif
+endmenu
+
+menu "F"
+  if X
+  endif
+  if X
+  endif
+  if X
+    if X
+    endif
+    if X
+    endif
+    menu "G"
+    endmenu
+  endif
+endmenu
+
+choice H
+  if X
+    if X
+    endif
+  endif
+  if X
+    if X
+    endif
+  endif
+endchoice
+
+choice I
+  if X
+  endif
+  if X
+  endif
+  if X
+    if X
+    endif
+    if X
+    endif
+    config J
+  endif
+endchoice
+
+if X
+endif
+if X
+endif
diff --git a/ext/Kconfiglib/tests/Kimply b/ext/Kconfiglib/tests/Kimply
new file mode 100644
index 0000000..3ce346f
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kimply
@@ -0,0 +1,145 @@
+config MODULES
+    def_bool y
+    option modules
+
+#
+# Implied symbols with unmet and met direct dependencies
+#
+
+config IMPLY_DIRECT_DEPS
+    def_tristate y
+    imply UNMET_DIRECT_1
+    imply UNMET_DIRECT_2
+    imply UNMET_DIRECT_3
+    imply MET_DIRECT_1
+    imply MET_DIRECT_2
+    imply MET_DIRECT_3
+    imply MET_DIRECT_4
+
+config UNMET_DIRECT_1
+    tristate
+    depends on n
+
+if n
+config UNMET_DIRECT_2
+    tristate
+endif
+
+menu "menu"
+    depends on n
+
+config UNMET_DIRECT_3
+    tristate
+
+endmenu
+
+config MET_DIRECT_1
+    tristate
+
+config MET_DIRECT_2
+    depends on y
+    tristate
+
+if y
+config MET_DIRECT_3
+    tristate
+endif
+
+menu "menu"
+    depends on y
+
+config MET_DIRECT_4
+    tristate
+
+endmenu
+
+#
+# 'imply' with condition
+#
+
+config IMPLY_COND
+    def_tristate y
+    tristate
+    imply IMPLIED_N_COND if n
+    imply IMPLIED_M_COND if m
+    imply IMPLIED_Y_COND if y
+
+config IMPLIED_N_COND
+    tristate
+
+config IMPLIED_M_COND
+    tristate
+
+config IMPLIED_Y_COND
+    tristate
+
+#
+# Implying from symbol with value n
+#
+
+# Will default to 'n'
+config IMPLY_N_1
+    tristate
+    imply IMPLIED_FROM_N_1
+
+# This test also disables the imply, so it's kinda redundant, but why not
+if n
+config IMPLY_N_2
+    tristate
+    imply IMPLIED_FROM_N_2
+endif
+
+config IMPLIED_FROM_N_1
+    tristate
+
+config IMPLIED_FROM_N_2
+    tristate
+
+#
+# Implying from symbol with value m
+#
+
+config IMPLY_M
+    def_tristate m
+    imply IMPLIED_M
+    # Implying a bool to 'm' makes it default to 'y'
+    imply IMPLIED_M_BOOL
+
+config IMPLIED_M
+    tristate
+
+config IMPLIED_M_BOOL
+    bool
+
+#
+# 'imply' which should raise an 'm' default to 'y'
+#
+
+config IMPLY_M_TO_Y
+    tristate
+    default y
+    imply IMPLIED_M_TO_Y
+
+config IMPLIED_M_TO_Y
+    tristate
+    default m
+
+#
+# Used for testing user values
+#
+
+config DIRECT_DEP
+    tristate "direct dep"
+
+config IMPLY
+    tristate "imply"
+    imply IMPLIED_TRISTATE
+    imply IMPLIED_BOOL
+
+config IMPLIED_TRISTATE
+    tristate "implied tristate"
+    depends on DIRECT_DEP
+
+config IMPLIED_BOOL
+    bool "implied bool"
+    depends on DIRECT_DEP
diff --git a/ext/Kconfiglib/tests/Kinclude_path b/ext/Kconfiglib/tests/Kinclude_path
new file mode 100644
index 0000000..7a3badb
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kinclude_path
@@ -0,0 +1,12 @@
+config TOP
+	bool
+
+source "Kinclude_path_sourced_1"
+
+config TOP
+	bool
+
+source "Kinclude_path_sourced_1"
+
+config TOP
+	bool
diff --git a/ext/Kconfiglib/tests/Kinclude_path_sourced_1 b/ext/Kconfiglib/tests/Kinclude_path_sourced_1
new file mode 100644
index 0000000..f4dee98
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kinclude_path_sourced_1
@@ -0,0 +1,12 @@
+config ONE_DOWN
+	bool
+
+source "Kinclude_path_sourced_2"
+
+config ONE_DOWN
+	bool
+
+source "Kinclude_path_sourced_2"
+
+config ONE_DOWN
+	bool
diff --git a/ext/Kconfiglib/tests/Kinclude_path_sourced_2 b/ext/Kconfiglib/tests/Kinclude_path_sourced_2
new file mode 100644
index 0000000..068f18d
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kinclude_path_sourced_2
@@ -0,0 +1,11 @@
+config TWO_DOWN
+	bool
+
+menu "menu"
+endmenu
+
+comment "comment"
+
+choice
+	bool "choice"
+endchoice
diff --git a/ext/Kconfiglib/tests/Kitemlists b/ext/Kconfiglib/tests/Kitemlists
new file mode 100644
index 0000000..8aa7107
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kitemlists
@@ -0,0 +1,36 @@
+comment "comment 1"
+
+choice
+    bool "choice 1"
+endchoice
+
+menu "menu 1"
+
+choice NAMED
+    bool "choice 2"
+endchoice
+
+menu "menu 2"
+menu "menu 3"
+comment "comment 2"
+endmenu
+
+choice
+    bool "choice 3"
+endchoice
+
+choice NAMED
+    bool
+endchoice
+
+endmenu
+
+menu "menu 4"
+endmenu
+
+comment "comment 3"
+
+endmenu
+
+menu "menu 5"
+endmenu
diff --git a/ext/Kconfiglib/tests/Klocation b/ext/Kconfiglib/tests/Klocation
new file mode 100644
index 0000000..3820a7b
--- /dev/null
+++ b/ext/Kconfiglib/tests/Klocation
@@ -0,0 +1,78 @@
+if UNDEFINED
+endif
+
+config ONE_DEF
+    bool
+
+config TWO_DEF
+    bool
+
+config TWO_DEF
+    bool
+
+config MANY_DEF
+    bool
+
+# Throw in some line continuations too to make sure it doesn't mess up the line
+# numbers
+if y && \
+   y
+if y && \
+   y && \
+   y
+
+# Throw in some help texts too
+
+config HELP_1
+    bool "help 1"
+    help
+config HELP_2
+    bool "help 2"
+    help
+      foo
+      bar
+
+        baz
+
+config HELP_3
+    help
+      foo
+      bar
+    bool
+config \
+MANY_DEF
+
+config MANY_DEF
+
+endif
+endif
+
+# Expands to "tests/Klocation_sourced"
+source "$TESTS_DIR_FROM_ENV/Klocation$_SOURCED"
+
+# Expands to "sub/Klocation_rsourced"
+rsource "$SUB_DIR_FROM_ENV/Klocation$_RSOURCED"
+
+# Expands to "tests/*ub/Klocation_gsourced[12]", matching
+# tests/sub/Klocation_gsourced{1,2}
+source "$TESTS_DIR_FROM_ENV/*ub/Klocation$_GSOURCED[12]"
+# Test old syntax too
+gsource "$TESTS_DIR_FROM_ENV/*ub/Klocation$_GSOURCED[12]"
+
+# Expands to "sub/Klocation_grsourced[12]", matching
+# tests/sub/Klocation_grsourced{1,2}
+rsource "$SUB_DIR_FROM_ENV/Klocation$_GRSOURCED[12]"
+# Test old syntax too
+grsource "$SUB_DIR_FROM_ENV/Klocation$_GRSOURCED[12]"
+
+# No-ops
+osource "nonexistent"
+osource "nonexistent*"
+gsource "nonexistent"
+gsource "nonexistent*"
+orsource "nonexistent"
+orsource "nonexistent*"
+grsource "nonexistent"
+grsource "nonexistent*"
+
+config MANY_DEF
diff --git a/ext/Kconfiglib/tests/Klocation_sourced b/ext/Kconfiglib/tests/Klocation_sourced
new file mode 100644
index 0000000..2ad8481
--- /dev/null
+++ b/ext/Kconfiglib/tests/Klocation_sourced
@@ -0,0 +1,26 @@
+
+
+config MANY_DEF
+
+choice CHOICE_ONE_DEF
+	bool "one-def choice"
+endchoice
+
+choice CHOICE_TWO_DEF
+	bool "two-def choice 1"
+endchoice
+
+choice CHOICE_TWO_DEF
+	bool "two-def choice 2"
+endchoice
+
+config MENU_HOOK
+	bool
+
+menu "menu"
+endmenu
+
+config COMMENT_HOOK
+	bool
+
+comment "comment"
diff --git a/ext/Kconfiglib/tests/Kmainmenu b/ext/Kconfiglib/tests/Kmainmenu
new file mode 100644
index 0000000..80713c0
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kmainmenu
@@ -0,0 +1,5 @@
+config FOO
+    string
+    option env="FOO"
+
+mainmenu "---$FOO---"
diff --git a/ext/Kconfiglib/tests/Kmenuconfig b/ext/Kconfiglib/tests/Kmenuconfig
new file mode 100644
index 0000000..9a4cc11
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kmenuconfig
@@ -0,0 +1,37 @@
+# Menu nodes with is_menuconfig False
+
+config NOT_MENUCONFIG_1
+	bool
+
+config NOT_MENUCONFIG_2
+	bool "not menuconfig 2"
+
+config MENUCONFIG_MULTI_DEF
+	bool "menuconfig multi def 1"
+
+config COMMENT_HOOK
+	bool
+
+comment "not menuconfig 3"
+
+
+# Menu nodes with is_menuconfig True
+
+menuconfig MENUCONFIG_1
+	bool "menuconfig 1"
+
+menuconfig MENUCONFIG_MULTI_DEF
+	bool "menuconfig multi def 2"
+
+config MENU_HOOK
+	bool
+
+menu "menuconfig 2"
+endmenu
+
+config CHOICE_HOOK
+	bool
+
+choice
+	bool "menuconfig 3"
+endchoice
diff --git a/ext/Kconfiglib/tests/Kmisc b/ext/Kconfiglib/tests/Kmisc
new file mode 100644
index 0000000..d9a799d
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kmisc
@@ -0,0 +1,82 @@
+# For testing various minor APIs
+
+# optional choices
+
+choice NOT_OPTIONAL
+    bool "not optional"
+config A
+    bool "A"
+config B
+    bool "B"
+endchoice
+
+choice OPTIONAL
+    tristate "optional"
+    optional
+config C
+    tristate "C"
+config D
+    tristate "D"
+# Quirky symbols - not proper choice symbol
+
+config Q1
+    tristate "Q1"
+    depends on D
+
+config Q2
+    tristate "Q2"
+    depends on Q1
+
+config Q3
+    tristate "Q3"
+    depends on D
+
+endchoice
+
+# User values
+
+config BOOL
+    bool "bool" if NOT_DEFINED_1
+
+config TRISTATE
+    tristate # Visibility should not affect user value
+
+config STRING
+    string "string"
+
+config INT
+    int # Visibility should not affect user value
+
+config HEX
+    hex "hex"
+    depends on NOT_DEFINED_2
+
+config COMMENT_HOOK
+comment "comment"
+
+config MENU_HOOK
+menu "menu"
+    depends on NOT_DEFINED_3 || NOT_DEFINED_2
+    depends on !NOT_DEFINED_4
+endmenu
+
+config FROM_ENV
+    string "from env"
+    option env="ENV_VAR"
+
+config FROM_ENV_MISSING
+    string "from env missing"
+    option env="MISSING_ENV_VAR"
+    default "missing"
+
+config FROM_ENV_WEIRD
+    string
+    default "weird"
+    option env="ENV_VAR"
+
+config NOT_ALLNOCONFIG_Y
+    bool "not allnoconfig_y"
+
+config ALLNOCONFIG_Y
+    bool "allnoconfig_y"
+    option allnoconfig_y
diff --git a/ext/Kconfiglib/tests/Kmissingrsource b/ext/Kconfiglib/tests/Kmissingrsource
new file mode 100644
index 0000000..924b0b3
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kmissingrsource
@@ -0,0 +1 @@
+rsource "nonexistent"
diff --git a/ext/Kconfiglib/tests/Kmissingsource b/ext/Kconfiglib/tests/Kmissingsource
new file mode 100644
index 0000000..a3a6c25
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kmissingsource
@@ -0,0 +1 @@
+source "nonexistent"
diff --git a/ext/Kconfiglib/tests/Korder b/ext/Kconfiglib/tests/Korder
new file mode 100644
index 0000000..3a8dffa
--- /dev/null
+++ b/ext/Kconfiglib/tests/Korder
@@ -0,0 +1,35 @@
+config O
+	int "O"
+	default 0
+
+config R
+	int "R"
+	default 1
+
+config D
+	int "D"
+	default 2
+
+config E
+	int "E"
+	default 3
+
+# Defined twice
+config R
+	int "R"
+
+config R2
+	int "R2"
+	default 4
+
+config I
+	int "I"
+	default 5
+
+config N
+	int "N"
+	default 6
+
+config G
+	int "G"
+	default 7
diff --git a/ext/Kconfiglib/tests/Kpreprocess b/ext/Kconfiglib/tests/Kpreprocess
new file mode 100644
index 0000000..283a988
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kpreprocess
@@ -0,0 +1,151 @@
+# Simple assignments (with bad formatting, as an additional test)
+
+simple-recursive=foo
+simple-immediate:=bar
+# Should become recursive
+simple-recursive-2+=baz
+
+	 whitespaced	 =	 foo
+
+
+# Simple += test. += should preserve the flavor of the variable (simple vs.
+# recursive).
+
+preserve-recursive = foo
+preserve-recursive += bar
+
+preserve-immediate := foo
+preserve-immediate += bar
+
+
+# Recursive substitution
+
+recursive = $(foo) $(bar) $($(b-char)a$(z-char))
+recursive += $(indir)
+
+foo = abc
+bar = def
+baz = ghi
+
+b-char = b
+z-char = z
+
+indir = jkl $(indir-2)
+indir-2 = mno
+
+
+# Immediate substitution
+
+def = foo
+immediate := $(undef)$(def)$(undef)$(def)
+def = bar
+undef = bar
+
+
+# Function calls
+
+# Chained function call
+quote = "$(1)" "$(2)"
+rev-quote = $(quote,$(2),$(1))
+surround-rev-quote = $(0) $(rev-quote,$(1),$(2)) $(0)
+surround-rev-quote-unused-arg = $(surround-rev-quote,$(1),$(2)) $(3)
+# No value is passed for $(3), so it expands to nothing
+fn-indir = surround-rev-quote
+messy-fn-res = $($(fn-indir)-unused-arg, a  b (,) , c  d )
+
+# Special characters in function call
+comma = ,
+right-paren = )
+dollar = $
+left-paren = (
+fn = "$(1)"
+special-chars-fn-res = $(fn,$(comma)$(dollar)$(left-paren)foo$(right-paren))
+
+
+# Variable expansions in various locations (verified by checking how the symbol
+# prints)
+
+qaz = QAZ
+echo = $(1)
+ignore-first = $(2)
+
+config PRINT_ME
+	string "$(ENV_1)" if ($(echo,FOO) && $(echo,BAR)) || !$(echo,BAZ) || !(($(qaz)))
+	default "$(echo,"foo")" if "foo $(echo,"bar") baz" = "$(undefined)"
+
+# Expansion within a symbol token, with deliberate sloppiness
+config PRINT_$(ignore-first,  ,ME)_TOO
+	bool "foo"
+	default FOO$(ignore-first,    ,BAR)BAZ$(qaz) if $(qaz)&&$(qaz)FOO&&x$(ignore-first,  ,xx)
+
+
+# Recursive expansion (throws an exception)
+
+rec-1 = x $(rec-2) y
+rec-2 = x $(rec-3) y
+rec-3 = x $(rec-1) y
+
+# Functions are allowed to reference themselves, but an exception is thrown if
+# the function seems to be stuck (the recursion gets too deep)
+safe-fn-rec = $($(1))
+safe-fn-rec-2 = $(safe-fn-rec,safe-fn-rec-3)
+safe-fn-rec-3 = foo
+safe-fn-rec-res = $(safe-fn-rec,safe-fn-rec-2)
+
+unsafe-fn-rec = $(unsafe-fn-rec,$(1))
+
+
+# Expansion in the left-hand side of assignments
+
+dummy-arg-fn = bar
+lhs-indir-1 = lhs-indir-2
+lhs-indir-2 = -baz
+rhs = value
+# LHS expands to foo-bar-baz
+foo-$(dummy-arg-fn, ignored argument )$($(lhs-indir-1)) = $(rhs)
+# Expands to empty string, accepted
+  $(undefined)  
+
+# Variable with a space in its name
+empty =
+space = $(empty) $(empty)
+foo$(space)bar = value
+space-var-res = $(foo bar)
+
+
+# Built-in functions
+
+# Expands to "baz qaz"
+shell-res = $(shell,false && echo foo bar || echo baz qaz)
+
+# Warns about output on stderr, expands to nothing
+shell-stderr-res := $(shell,echo message on stderr >&2)
+
+# Nested parens in macro call. Should give a single argument. Test it with
+# $(shell) to get a free argument number check.
+parens-res = pre-$(shell,echo '(a,$(b-char),(c,d),e)')-post
+
+# Expands to the current location
+location-res := $(filename):$(lineno)
+
+# Adds one warning, expands to nothing
+$(warning-if,,no warning)
+$(warning-if,n,no warning)
+warning-res := $(warning-if,y,a warning)
+
+# Does not cause an error, expands to nothing
+error-n-res := $(error-if,n,oops)
+
+# Causes an error when expanded
+error-y-res = $(error-if,y,oops)
+
+
+# Environment variables (for testing Kconfig.env_vars). ENV_1 is already
+# referenced above.
+env_ref_1 := xxx $(ENV_2) xxx
+env_ref_2 := $(shell,echo $(ENV_3))
+env_ref_3 :=
+env_ref_3 += $(ENV_4)
+$(warning-if,$(ENV_5),$(ENV_UNDEFINED))
+source "$(ENV_6)"
+env_ref_4 = $(ENV_7)  # Never evaluated
diff --git a/ext/Kconfiglib/tests/Krange b/ext/Kconfiglib/tests/Krange
new file mode 100644
index 0000000..3057483
--- /dev/null
+++ b/ext/Kconfiglib/tests/Krange
@@ -0,0 +1,133 @@
+#
+# No ranges
+#
+
+config HEX_NO_RANGE
+    hex "hex no range"
+
+config INT_NO_RANGE
+    int "int no range"
+
+#
+# Disabled ranges
+#
+
+config HEX_ALL_RANGES_DISABLED
+    hex "hex all ranges disabled"
+    range 0x10 0x20 if n
+    range 0x30 0x40 if n
+
+config INT_ALL_RANGES_DISABLED
+    int "int all ranges disabled"
+    range 10 20 if n
+    range 30 40 if n
+
+#
+# Ranges with defaults
+#
+
+# hex
+
+config HEX_RANGE_10_20_LOW_DEFAULT
+    hex "int range 10-20 low default"
+    range 0x10 0x20
+    default 0x9
+
+config HEX_RANGE_10_20_HIGH_DEFAULT
+    hex "int range 10-20 high default"
+    range 0x10 0x20
+    default 0x21
+
+config HEX_RANGE_10_20_OK_DEFAULT
+    hex "int range 10-20 ok default"
+    range 0x10 0x20
+    default 0x15
+
+config HEX_RANGE_10_20_OK_DEFAULT_ALTERNATE
+    hex "int range 10-20 ok default alternate"
+    range 0x10 0x20
+    default 15
+
+# int
+
+config INT_RANGE_10_20_LOW_DEFAULT
+    int "int range 10-20 low default"
+    range 10 20
+    default 9
+
+config INT_RANGE_10_20_HIGH_DEFAULT
+    int "int range 10-20 high default"
+    range 10 20
+    default 21
+
+config INT_RANGE_10_20_OK_DEFAULT
+    int "int range 10-20 ok default"
+    range 10 20
+    default 15
+
+#
+# Ranges with no defaults (should default to low end of range if > 0)
+#
+
+config HEX_RANGE_10_20
+    hex "hex range 10-20"    
+    range 0x10 0x20
+
+config HEX_RANGE_0_10
+    hex "hex range 0-10"
+    range 0x0 0x10
+
+config INT_RANGE_10_20
+    int "int range 10-20"    
+    range 10 20
+
+config INT_RANGE_0_10
+    int "int range 0-10"
+    range 0 10
+
+config INT_RANGE_NEG_10_10
+    int "int range -10-10"
+    range -10 10
+
+#
+# Dependent ranges
+#
+
+config HEX_40
+    hex
+    default 40
+
+config HEX_RANGE_10_40_DEPENDENT
+    hex "hex range 10-40 dependent"
+    range HEX_RANGE_10_20 HEX_40
+
+config INT_40
+    int
+    default 40
+
+config INT_RANGE_10_40_DEPENDENT
+    int "int range 10-40 dependent"
+    range INT_RANGE_10_20 INT_40
+
+#
+# Ranges on symbols defined in multiple locations
+#
+
+if n
+config INACTIVE_RANGE
+    range 0 1
+endif
+
+config INACTIVE_RANGE
+    int
+    # Default will apply and should not get clamped,
+    # because the range does not apply
+    default 2
+
+config ACTIVE_RANGE
+    range 0 1
+
+config ACTIVE_RANGE
+    int
+    # Default will apply and should be clamped to 1
+    default 2
diff --git a/ext/Kconfiglib/tests/Krecursive1 b/ext/Kconfiglib/tests/Krecursive1
new file mode 100644
index 0000000..35b521a
--- /dev/null
+++ b/ext/Kconfiglib/tests/Krecursive1
@@ -0,0 +1 @@
+source "tests/Krecursive2"
diff --git a/ext/Kconfiglib/tests/Krecursive2 b/ext/Kconfiglib/tests/Krecursive2
new file mode 100644
index 0000000..9f9f00e
--- /dev/null
+++ b/ext/Kconfiglib/tests/Krecursive2
@@ -0,0 +1 @@
+source "tests/Krecursive1"
diff --git a/ext/Kconfiglib/tests/Kreferenced b/ext/Kconfiglib/tests/Kreferenced
new file mode 100644
index 0000000..9da94c8
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kreferenced
@@ -0,0 +1,63 @@
+config NO_REFS
+    bool
+
+config JUST_DEPENDS_ON_REFS
+    bool
+    depends on A && B
+
+if A
+
+menu "menu"
+    depends on B
+    visible if C
+    visible if D
+
+config LOTS_OF_REFS
+    bool "lots" if C || D
+    default E || F if G || H
+    default I || J if K || L
+    select M if N || O
+    select P if Q || R
+    imply S if T || U
+    imply V if W || X
+    depends on Y || Z
+
+endmenu
+
+endif
+
+config INT_REFS
+    int "int"
+    range A B if C && D
+    range E F if G && H
+
+choice CHOICE
+    bool "choice"
+
+config CHOICE_REF
+    bool "choice ref"
+
+endchoice
+
+comment "comment"
+    depends on A || B
+
+
+config MULTI_DEF_SYM
+    def_bool A && B
+
+config MULTI_DEF_SYM
+    depends on C
+
+
+choice MULTI_DEF_CHOICE
+    bool "choice"
+    depends on A && B
+
+endchoice
+
+choice MULTI_DEF_CHOICE
+    bool "choice"
+    depends on C
+
+endchoice
diff --git a/ext/Kconfiglib/tests/Krelation b/ext/Kconfiglib/tests/Krelation
new file mode 100644
index 0000000..940eff8
--- /dev/null
+++ b/ext/Kconfiglib/tests/Krelation
@@ -0,0 +1,36 @@
+config A
+    bool
+    depends on UNDEFINED
+
+choice CHOICE_1
+    bool "C"
+config B
+    bool "B"
+config C
+    bool "C" if B
+config D
+    bool "D"
+endchoice
+
+menu "m1"
+config E
+    bool
+menu "m2"
+config F
+    bool
+choice CHOICE_2
+    tristate "foo"
+config G
+    bool "g"
+config H
+    bool "h"
+endchoice
+endmenu
+config I
+    bool
+endmenu
+
+menu "m3"
+endmenu
+menu "m4"
+endmenu
diff --git a/ext/Kconfiglib/tests/Krepr b/ext/Kconfiglib/tests/Krepr
new file mode 100644
index 0000000..fe6d8f0
--- /dev/null
+++ b/ext/Kconfiglib/tests/Krepr
@@ -0,0 +1,64 @@
+config MODULES
+    bool
+    option modules
+    default y
+
+if UNDEFINED
+endif
+
+config BASIC
+    bool
+    default y
+    ---help---
+
+config VISIBLE
+    bool "visible"
+
+config STRING
+    string "visible"
+
+config DIR_DEP_N
+    depends on n
+
+config OPTIONS
+    option allnoconfig_y
+    option defconfig_list
+    option env="ENV"
+
+config MULTI_DEF
+config MULTI_DEF
+
+menuconfig MENUCONFIG
+
+choice CHOICE
+    tristate "choice"
+
+config CHOICE_1
+    tristate "choice sym"
+
+config CHOICE_2
+    tristate "choice sym"
+
+endchoice
+
+config CHOICE_HOOK
+
+choice
+    tristate "optional choice" if n
+    optional
+endchoice
+
+config NO_VISIBLE_IF_HOOK
+
+menu "no visible if"
+endmenu
+
+config VISIBLE_IF_HOOK
+
+menu "visible if"
+    visible if m
+endmenu
+
+config COMMENT_HOOK
+
+comment "comment"
diff --git a/ext/Kconfiglib/tests/Kstr b/ext/Kconfiglib/tests/Kstr
new file mode 100644
index 0000000..f55c830
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kstr
@@ -0,0 +1,293 @@
+if UNDEFINED
+endif
+
+config NO_TYPE
+
+config BASIC_NO_PROMPT
+    bool
+    help
+      blah blah
+
+        blah blah blah
+
+       blah
+
+config BASIC_PROMPT
+    bool "basic"
+
+config ADVANCED
+    tristate "prompt" if DEP
+    default DEFAULT_1
+    default DEFAULT_2 if DEP
+    select SELECTED_1
+    select SELECTED_2 if DEP
+    imply IMPLIED_1
+    imply IMPLIED_2 if DEP
+    help
+      first help text
+
+config ADVANCED
+    prompt "prompt 2"
+
+menuconfig ADVANCED
+    prompt "prompt 3"
+
+if DEP3
+
+menu "foo"
+    depends on DEP4
+    visible if VIS
+
+config ADVANCED
+    help
+      second help text
+    depends on A || !B || (C && D) || !(E && F) || G = H || \
+              (I && !J && (K || L) && !(M || N) && O = P)
+
+config ADVANCED
+    # Used to verify that the direct dependencies appear to the right of VIS
+    prompt "prompt 4"
+
+endmenu
+
+endif
+
+config ONLY_DIRECT_DEPS
+    int
+    depends on DEP1
+    depends on DEP2
+
+config STRING
+    def_string "foo"
+    default "bar" if DEP
+    default STRING2
+    default STRING3 if DEP
+
+config INT
+    def_int 7 if DEP
+    range 1 2
+    range FOO BAR
+    range BAZ QAZ if DEP
+
+config HEX
+    def_hex 0x123
+    range 0x100 0x200
+    range FOO BAR
+    range BAZ QAZ if DEP
+
+config MODULES
+    bool "MODULES"
+    option modules
+
+config OPTIONS
+    option allnoconfig_y
+    option defconfig_list
+    option env="ENV"
+
+if LOC_1
+config CORRECT_PROP_LOCS_BOOL
+    prompt "prompt 1"
+    default DEFAULT_1
+    default DEFAULT_2
+    select SELECT_1
+    select SELECT_2
+    imply IMPLY_1
+    imply IMPLY_2
+    help
+      help 1
+endif
+
+if LOC_2
+menuconfig CORRECT_PROP_LOCS_BOOL
+    bool "prompt 2"
+    default DEFAULT_3
+    default DEFAULT_4
+    select SELECT_3
+    select SELECT_4
+    imply IMPLY_3
+    imply IMPLY_4
+    help
+      help 2
+endif
+
+if LOC_3
+config CORRECT_PROP_LOCS_BOOL
+    prompt "prompt 3"
+    default DEFAULT_5
+    default DEFAULT_6
+    select SELECT_5
+    select SELECT_6
+    imply IMPLY_5
+    imply IMPLY_6
+    help
+      help 2
+endif
+
+if LOC_1
+config CORRECT_PROP_LOCS_INT
+    int
+    range 1 2
+    range 3 4
+endif
+
+if LOC_2
+config CORRECT_PROP_LOCS_INT
+    range 5 6
+    range 7 8
+endif
+
+choice CHOICE
+    tristate "foo"
+    default CHOICE_1
+    default CHOICE_2 if dep
+
+config CHOICE_1
+    tristate "choice 1"
+
+config CHOICE_2
+    tristate "choice 2"
+
+endchoice
+
+choice
+    tristate "no name"
+    optional
+endchoice
+
+if LOC_1
+choice CORRECT_PROP_LOCS_CHOICE
+    bool
+    default CHOICE_3
+
+config CHOICE_3
+    bool "choice 3"
+
+config CHOICE_4
+    bool "choice 3"
+
+config CHOICE_5
+    bool "choice 3"
+
+endchoice
+endif
+
+if LOC_2
+choice CORRECT_PROP_LOCS_CHOICE
+    default CHOICE_4
+endchoice
+endif
+
+if LOC_3
+choice CORRECT_PROP_LOCS_CHOICE
+    default CHOICE_5
+endchoice
+endif
+
+config SIMPLE_MENU_HOOK
+menu "simple menu"
+endmenu
+
+config ADVANCED_MENU_HOOK
+menu "advanced menu"
+    depends on A
+    visible if B
+    visible if C || D
+endmenu
+
+config SIMPLE_COMMENT_HOOK
+comment "simple comment"
+
+config ADVANCED_COMMENT_HOOK
+comment "advanced comment"
+    depends on A
+    depends on B
+
+# Corner cases when removing direct dependencies
+
+config DEP_REM_CORNER_CASES
+    bool
+    default A
+    depends on n
+
+config DEP_REM_CORNER_CASES
+    default B if n
+
+config DEP_REM_CORNER_CASES
+    default C
+    depends on m
+
+config DEP_REM_CORNER_CASES
+    default D if A && y
+    depends on y
+
+config DEP_REM_CORNER_CASES
+    default E if !E1
+    default F if F1 = F2
+    default G if G1 || H1
+    depends on !H
+
+config DEP_REM_CORNER_CASES
+    default H
+    depends on "foo" = "bar"
+
+menu "menu"
+    visible if FOO || BAR
+
+config DEP_REM_CORNER_CASES
+    prompt "prompt"
+    depends on BAZ && QAZ
+
+endmenu
+
+# Only prompt, no type
+config PROMPT_ONLY
+    prompt "prompt only"
+
+# {Symbol,Choice}.orig_*
+
+if BASE_DEP
+
+config BOOL_SYM_ORIG
+    bool
+    default D1 if DEP
+    default D2
+    select S1
+    select S2 if DEP
+    imply I1
+    imply I1
+
+config BOOL_SYM_ORIG
+    default D3
+    select S3
+    imply I3 if DEP
+
+config INT_SYM_ORIG
+    int
+    range 1 2 if DEP
+    range 3 4
+
+config INT_SYM_ORIG
+    range 5 6 if DEP
+
+choice CHOICE_ORIG
+    bool "choice orig"
+    default A
+    default B if DEP
+
+config A
+    bool
+
+config B
+    bool
+
+endchoice
+
+choice CHOICE_ORIG
+    default C if DEP
+
+config C
+    bool
+
+endchoice
+
+endif
diff --git a/ext/Kconfiglib/tests/Kundef b/ext/Kconfiglib/tests/Kundef
new file mode 100644
index 0000000..fae521a
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kundef
@@ -0,0 +1,23 @@
+config DEF
+	bool
+
+config BOOL
+	bool "foo" if DEF || !UNDEF_1
+	default UNDEF_2
+
+config INT
+	int
+	range UNDEF_2 8
+	default 10
+	range 5 15
+
+config HEX
+	hex
+	range 0x123 0X456
+	default 0x200
+
+menu "menu"
+	depends on UNDEF_1
+	visible if UNDEF_3
+
+endmenu
diff --git a/ext/Kconfiglib/tests/Kuserfunctions b/ext/Kconfiglib/tests/Kuserfunctions
new file mode 100644
index 0000000..b0bf630
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kuserfunctions
@@ -0,0 +1,14 @@
+add-zero  = $(add)
+add-one   = $(add,1)
+add-three = $(add,1,-1,2,1)
+
+one-zero = $(one)
+one-one  = $(one,foo bar)
+one-two  = $(one,foo bar,baz)
+
+one-or-more-zero  = $(one-or-more)
+one-or-more-one   = $(one-or-more,foo)
+one-or-more-three = $(one-or-more,foo,bar,baz)
+
+location-1 := $(location)
+location-2 := $(location)
diff --git a/ext/Kconfiglib/tests/Kvisibility b/ext/Kconfiglib/tests/Kvisibility
new file mode 100644
index 0000000..91def0a
--- /dev/null
+++ b/ext/Kconfiglib/tests/Kvisibility
@@ -0,0 +1,342 @@
+config MODULES
+    bool "MODULES"
+    option modules
+
+#
+# Symbol visibility
+#
+
+config NO_PROMPT
+    bool
+
+# Not rewritten, so MOD will have the value 'y' when running without modules
+config MOD
+    def_tristate m
+
+config BOOL_N
+    bool "bool n" if n
+
+config BOOL_M
+    # Rewritten to m && MODULES
+    bool "bool m" if m
+
+config BOOL_MOD
+    bool "bool MOD"
+    # Not rewritten
+    depends on MOD
+
+config BOOL_Y
+    bool "bool y"
+    # Rewritten to m && MODULES
+    depends on y || m
+
+config TRISTATE_N
+    tristate "tristate n" if n
+
+config TRISTATE_M
+    # Rewritten to m && MODULES
+    tristate "tristate m" if m
+
+config TRISTATE_MOD
+    tristate "tristate MOD"
+    # Not rewritten
+    depends on MOD
+
+config TRISTATE_Y
+    bool "tristate y"
+    # Rewritten to m && MODULES
+    depends on y || m
+
+# Symbols nested in 'if'
+
+if n
+
+config BOOL_IF_N
+    bool "bool if n"
+
+config TRISTATE_IF_N
+    tristate "tristate if n"
+
+endif
+
+if m
+
+config BOOL_IF_M
+    bool "bool if m"
+
+config TRISTATE_IF_M
+    tristate "tristate if n"
+
+endif
+
+if y
+
+config BOOL_IF_Y
+    bool "bool if y"
+
+config TRISTATE_IF_Y
+    tristate "tristate if y"
+
+endif
+
+# Symbols nested in 'menu'
+
+menu "menu 1"
+    depends on n
+
+config BOOL_MENU_N
+    bool "bool menu n"
+
+config TRISTATE_MENU_N
+    tristate "tristate menu n"
+
+endmenu
+
+menu "menu 2"
+    depends on m
+
+config BOOL_MENU_M
+    bool "bool menu m"
+
+config TRISTATE_MENU_M
+    tristate "tristate menu n"
+
+endmenu
+
+menu "menu 3"
+    depends on y
+
+config BOOL_MENU_Y
+    bool "bool menu y"
+
+config TRISTATE_MENU_Y
+    tristate "tristate menu y"
+
+endmenu
+
+# Symbols nested in choices
+
+choice C1
+    tristate "choice n" if n
+
+config BOOL_CHOICE_N
+    bool "bool choice n"
+
+config TRISTATE_CHOICE_N
+    tristate "tristate choice n"
+
+endchoice
+
+choice C2
+    tristate "choice m" if m
+
+config BOOL_CHOICE_M
+    bool "bool choice m"
+
+config TRISTATE_CHOICE_M
+    tristate "tristate choice n"
+
+endchoice
+
+choice C3
+    tristate "choice y" if y
+
+config BOOL_CHOICE_Y
+    bool "bool choice y"
+
+config TRISTATE_CHOICE_Y
+    tristate "tristate choice y"
+
+endchoice
+
+#
+# Choice visibility
+#
+
+choice BOOL_CHOICE_N
+    bool "bool choice n" if n
+endchoice
+
+choice BOOL_CHOICE_M
+    bool "bool choice m" if m
+endchoice
+
+choice BOOL_CHOICE_Y
+    bool "bool choice y" if y
+endchoice
+
+choice TRISTATE_CHOICE_N
+    tristate "tristate choice n" if n
+endchoice
+
+choice TRISTATE_CHOICE_M
+    tristate "tristate choice m" if m
+endchoice
+
+choice TRISTATE_CHOICE_Y
+    tristate "tristate choice y" if y
+
+config K
+    tristate "K"
+
+config L
+    tristate "L"
+
+endchoice
+
+if m
+choice TRISTATE_CHOICE_IF_M_AND_Y
+    tristate "tristate choice if m and y" if y
+
+config M
+    bool "M"
+
+config N
+    bool "N"
+
+endchoice
+endif
+
+menu "choice-containing menu"
+    depends on n && y
+
+choice TRISTATE_CHOICE_MENU_N_AND_Y
+    tristate "tristate choice if n and y"
+
+config O
+    tristate "O"
+
+config P
+    tristate "P"
+
+endchoice
+
+endmenu
+
+#
+# Menu visibility
+#
+
+menu "menu n"
+    depends on n
+endmenu
+
+menu "menu m"
+    depends on m
+endmenu
+
+menu "menu y"
+    depends on y
+endmenu
+
+if n
+menu "menu if n"
+endmenu
+endif
+
+if m
+menu "menu if m"
+endmenu
+endif
+
+if y
+menu "menu if y"
+endmenu
+endif
+
+if m
+menu "menu if m and y"
+    depends on y
+endmenu
+endif
+
+#
+# Comment visibility
+#
+
+comment "comment n"
+    depends on n
+comment "comment m"
+    depends on m
+comment "comment y"
+    depends on y
+
+if n
+comment "comment if n"
+endif
+
+if m
+comment "comment if m"
+endif
+
+if y
+comment "comment if y"
+endif
+
+if "y"
+
+menu "menu with comment"
+    depends on m
+
+comment "double-nested m comment"
+    depends on y
+
+endmenu
+
+endif
+
+# Used to verify that string/int/hex symbols with m visibility accept a user
+# value
+
+if m
+
+config STRING_m
+    string "string"
+
+config INT_m
+    int "int"
+
+config HEX_m
+    hex "hex"
+
+endif
+
+# Menu 'visible if' visibility
+
+menu "n-visible menu"
+    visible if n
+
+config VISIBLE_IF_N
+    tristate "visible if n"
+
+endmenu
+
+menu "m-visible menu"
+    visible if m
+
+config VISIBLE_IF_M
+    tristate "visible if m"
+
+endmenu
+
+menu "y-visible menu"
+    visible if y
+
+config VISIBLE_IF_Y
+    tristate "visible if m"
+
+endmenu
+
+menu "m-visible menu 2"
+    visible if y || n
+    visible if m && y
+    visible if y
+
+if y
+
+config VISIBLE_IF_M_2
+    tristate "visible if m 2"
+
+endif
+
+endmenu
diff --git a/ext/Kconfiglib/tests/config_indented b/ext/Kconfiglib/tests/config_indented
new file mode 100644
index 0000000..d57d8b4
--- /dev/null
+++ b/ext/Kconfiglib/tests/config_indented
@@ -0,0 +1,3 @@
+# Indented assignments should be ignored to be compatible with the C
+# implementation
+ CONFIG_IGNOREME=n
diff --git a/ext/Kconfiglib/tests/config_set_bool b/ext/Kconfiglib/tests/config_set_bool
new file mode 100644
index 0000000..d2dccbf
--- /dev/null
+++ b/ext/Kconfiglib/tests/config_set_bool
@@ -0,0 +1 @@
+CONFIG_BOOL=y
diff --git a/ext/Kconfiglib/tests/config_set_string b/ext/Kconfiglib/tests/config_set_string
new file mode 100644
index 0000000..3a1250a
--- /dev/null
+++ b/ext/Kconfiglib/tests/config_set_string
@@ -0,0 +1 @@
+CONFIG_STRING="foo bar"
diff --git a/ext/Kconfiglib/tests/defconfig_1 b/ext/Kconfiglib/tests/defconfig_1
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/Kconfiglib/tests/defconfig_1
diff --git a/ext/Kconfiglib/tests/defconfig_2 b/ext/Kconfiglib/tests/defconfig_2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/Kconfiglib/tests/defconfig_2
diff --git a/ext/Kconfiglib/tests/empty b/ext/Kconfiglib/tests/empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/Kconfiglib/tests/empty
diff --git a/ext/Kconfiglib/tests/kconfigfunctions.py b/ext/Kconfiglib/tests/kconfigfunctions.py
new file mode 100644
index 0000000..8f35511
--- /dev/null
+++ b/ext/Kconfiglib/tests/kconfigfunctions.py
@@ -0,0 +1,22 @@
+def add(kconf, name, *args):
+    return str(sum(map(int, args)))
+
+
+def one(kconf, name, s):
+    return name + 2*s
+
+
+def one_or_more(kconf, name, arg, *args):
+    return arg + " + " + ",".join(args)
+
+
+def location(kconf, name):
+    return "{}:{}".format(kconf.filename, kconf.linenr)
+
+
+functions = {
+    "add":         (add,         0, None),
+    "one":         (one,         1,    1),
+    "one-or-more": (one_or_more, 1, None),
+    "location":    (location,    0,    0),
+}
diff --git a/ext/Kconfiglib/tests/reltest b/ext/Kconfiglib/tests/reltest
new file mode 100755
index 0000000..3e09e5f
--- /dev/null
+++ b/ext/Kconfiglib/tests/reltest
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+# Runs the test suite and all examples scripts with Python 2 and Python 3,
+# bailing immediately if anything fails. For the examples that aren't tested in
+# the test suite, we just confirm that they at least run.
+#
+# Should be run from the kernel root with  $ Kconfiglib/tests/reltest
+
+test_script() {
+    echo -e "\n================= $1 with $py =================\n"
+    if (($# == 1)); then
+        make scriptconfig PYTHONCMD=$py SCRIPT=$1
+    else
+        make scriptconfig PYTHONCMD=$py SCRIPT=$1 SCRIPT_ARG="$2"
+    fi
+
+    if (($?)); then
+        echo "$1 failed to run with $py"
+        exit 1
+    fi
+}
+
+for py in python2 python3; do
+    echo -e "\n================= Test suite with $py =================\n"
+
+    if ! $py Kconfiglib/testsuite.py; then
+        echo "test suite failed for $py"
+        exit 1
+    fi
+
+    # Check that the example scripts that aren't tested by the test suite run
+    # at least
+
+    test_script Kconfiglib/examples/defconfig_oldconfig.py
+    test_script Kconfiglib/examples/eval_expr.py MODULES
+    test_script Kconfiglib/examples/find_symbol.py X86
+    test_script Kconfiglib/examples/help_grep.py general
+    test_script Kconfiglib/examples/print_sym_info.py MODULES
+    test_script Kconfiglib/examples/print_tree.py
+
+    $py Kconfiglib/examples/menuconfig_example.py Kconfiglib/examples/Kmenuconfig <<END
+BOOL
+n
+END
+    if (($?)); then
+        echo "menuconfig_example.py failed with $py"
+        exit 1
+    fi
+done
+
+echo "everything okay"
diff --git a/ext/Kconfiglib/tests/sub/Kconfig_symlink_2 b/ext/Kconfiglib/tests/sub/Kconfig_symlink_2
new file mode 100644
index 0000000..aeba985
--- /dev/null
+++ b/ext/Kconfiglib/tests/sub/Kconfig_symlink_2
@@ -0,0 +1 @@
+rsource "Kconfig_symlink_3"
diff --git a/ext/Kconfiglib/tests/sub/Kconfig_symlink_3 b/ext/Kconfiglib/tests/sub/Kconfig_symlink_3
new file mode 100644
index 0000000..20b4e06
--- /dev/null
+++ b/ext/Kconfiglib/tests/sub/Kconfig_symlink_3
@@ -0,0 +1,2 @@
+config FOUNDME
+    bool
diff --git a/ext/Kconfiglib/tests/sub/Klocation_grsourced1 b/ext/Kconfiglib/tests/sub/Klocation_grsourced1
new file mode 100644
index 0000000..1e04ad6
--- /dev/null
+++ b/ext/Kconfiglib/tests/sub/Klocation_grsourced1
@@ -0,0 +1 @@
+config MANY_DEF
diff --git a/ext/Kconfiglib/tests/sub/Klocation_grsourced2 b/ext/Kconfiglib/tests/sub/Klocation_grsourced2
new file mode 100644
index 0000000..1e04ad6
--- /dev/null
+++ b/ext/Kconfiglib/tests/sub/Klocation_grsourced2
@@ -0,0 +1 @@
+config MANY_DEF
diff --git a/ext/Kconfiglib/tests/sub/Klocation_gsourced1 b/ext/Kconfiglib/tests/sub/Klocation_gsourced1
new file mode 100644
index 0000000..1e04ad6
--- /dev/null
+++ b/ext/Kconfiglib/tests/sub/Klocation_gsourced1
@@ -0,0 +1 @@
+config MANY_DEF
diff --git a/ext/Kconfiglib/tests/sub/Klocation_gsourced2 b/ext/Kconfiglib/tests/sub/Klocation_gsourced2
new file mode 100644
index 0000000..1e04ad6
--- /dev/null
+++ b/ext/Kconfiglib/tests/sub/Klocation_gsourced2
@@ -0,0 +1 @@
+config MANY_DEF
diff --git a/ext/Kconfiglib/tests/sub/Klocation_rsourced b/ext/Kconfiglib/tests/sub/Klocation_rsourced
new file mode 100644
index 0000000..12fdbc2
--- /dev/null
+++ b/ext/Kconfiglib/tests/sub/Klocation_rsourced
@@ -0,0 +1,2 @@
+
+config MANY_DEF
diff --git a/ext/Kconfiglib/tests/sub/defconfig_in_sub b/ext/Kconfiglib/tests/sub/defconfig_in_sub
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/Kconfiglib/tests/sub/defconfig_in_sub
diff --git a/ext/Kconfiglib/tests/sub/sub/Kconfig_symlink_1 b/ext/Kconfiglib/tests/sub/sub/Kconfig_symlink_1
new file mode 100644
index 0000000..bceec8b
--- /dev/null
+++ b/ext/Kconfiglib/tests/sub/sub/Kconfig_symlink_1
@@ -0,0 +1,2 @@
+# Sources tests/sub/Kconfig_symlink_2, with an absolute path
+source "$(KCONFIG_SYMLINK_2)"
diff --git a/ext/Kconfiglib/testsuite.py b/ext/Kconfiglib/testsuite.py
new file mode 100644
index 0000000..fc61a16
--- /dev/null
+++ b/ext/Kconfiglib/testsuite.py
@@ -0,0 +1,3203 @@
+# Copyright (c) 2011-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+# This is the Kconfiglib test suite. It runs selftests on Kconfigs provided by
+# us and tests compatibility with the C Kconfig implementation by comparing the
+# output of Kconfiglib with the output of the scripts/kconfig/*conf utilities
+# for different targets and defconfigs. It should be run from the top-level
+# kernel directory with
+#
+#   $ python Kconfiglib/testsuite.py
+#
+# Some additional options can be turned on by passing them as arguments. They
+# default to off.
+#
+#  - obsessive:
+#    By default, only valid arch/defconfig pairs are tested. In obsessive mode,
+#    every arch will be tested with every defconfig. Increases the testing time
+#    by an order of magnitude. Occasionally finds (usually obscure) bugs, and I
+#    make sure everything passes with it.
+#
+#  - obsessive-min-config:
+#    Like obsessive, for the minimal configuation (defconfig) tests.
+#
+#  - log:
+#    Log timestamped defconfig test failures to the file test_defconfig_fails.
+#    Handy in obsessive mode.
+#
+# For example, this commands runs the test suite in obsessive mode with logging
+# enabled:
+#
+#   $ python(3) Kconfiglib/testsuite.py obsessive log
+#
+# pypy works too, and runs most tests much faster than CPython.
+#
+# All tests should pass. Report regressions to ulfalizer a.t Google's email
+# service.
+
+import difflib
+import errno
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import textwrap
+
+from kconfiglib import Kconfig, Symbol, Choice, COMMENT, MENU, MenuNode, \
+                       BOOL, TRISTATE, HEX, \
+                       TRI_TO_STR, \
+                       escape, unescape, \
+                       expr_str, expr_items, split_expr, \
+                       _ordered_unique, \
+                       OR, AND, \
+                       KconfigError
+
+
+def shell(cmd):
+    with open(os.devnull, "w") as devnull:
+        subprocess.call(cmd, shell=True, stdout=devnull, stderr=devnull)
+
+
+all_passed = True
+
+
+def fail(msg=None):
+    global all_passed
+    all_passed = False
+    if msg is not None:
+        print("fail: " + msg)
+
+
+def verify(cond, msg):
+    if not cond:
+        fail(msg)
+
+
+def verify_equal(x, y):
+    if x != y:
+        fail("'{}' does not equal '{}'".format(x, y))
+
+
+# Prevent accidental loading of configuration files by removing
+# KCONFIG_ALLCONFIG from the environment
+os.environ.pop("KCONFIG_ALLCONFIG", None)
+
+obsessive = False
+obsessive_min_config = False
+log = False
+
+
+def run_tests():
+    global obsessive, log
+    for s in sys.argv[1:]:
+        if s == "obsessive":
+            obsessive = True
+            print("Obsessive mode enabled")
+        elif s == "obsessive-min-config":
+            obsessive_min_config = True
+            print("Obsessive minimal config mode enabled")
+        elif s == "log":
+            log = True
+            print("Log mode enabled")
+        else:
+            print("Unrecognized option '{}'".format(s))
+            return
+
+    run_selftests()
+    run_compatibility_tests()
+
+
+def run_selftests():
+    #
+    # Common helper functions. These all expect 'c' to hold the current
+    # configuration.
+    #
+
+    def verify_value(sym_name, val):
+        # Verifies that a symbol has a particular value.
+
+        if isinstance(val, int):
+            val = TRI_TO_STR[val]
+
+        sym = c.syms[sym_name]
+        verify(sym.str_value == val,
+               'expected {} to have the value "{}", had the value "{}"'
+               .format(sym_name, val, sym.str_value))
+
+    def assign_and_verify_value(sym_name, val, new_val):
+        # Assigns 'val' to a symbol and verifies that its value becomes
+        # 'new_val'. Assumes (and tests) that 'val' is valid for the
+        # symbol type.
+
+        if isinstance(new_val, int):
+            new_val = TRI_TO_STR[new_val]
+
+        sym = c.syms[sym_name]
+        old_val = sym.str_value
+        verify(sym.set_value(val),
+               "assigning '{}' to {} unexpectedly failed"
+               .format(val, sym_name))
+        verify(sym.str_value == new_val,
+               "expected {} to have the value '{}' after being assigned the "
+               "value '{}'. Instead, the value is '{}'. The old value was "
+               "'{}'."
+               .format(sym_name, new_val, val, sym.str_value, old_val))
+
+    def assign_and_verify(sym_name, user_val):
+        # Like assign_and_verify_value(), with the expected value being the
+        # value just set.
+
+        assign_and_verify_value(sym_name, user_val, user_val)
+
+    def assign_and_verify_user_value(sym_name, val, user_val, valid):
+        # Assigns a user value to the symbol and verifies the new user value.
+        # If valid is True, the user value is valid for the type, otherwise
+        # not. This is used to test the set_value() return value.
+
+        sym = c.syms[sym_name]
+        sym_old_user_val = sym.user_value
+
+        verify(sym.set_value(val) == valid,
+               "expected the user value '{}' to be {} for {}, was not"
+               .format(val, "valid" if valid else "invalid", sym_name))
+        verify(sym.user_value == user_val,
+               "the assigned user value '{}' wasn't reflected in user_value "
+               "on the symbol {}. Instead, the new user_value was '{}'. The "
+               "old user value was '{}'."
+               .format(user_val, sym_name, sym.user_value, sym_old_user_val))
+
+    #
+    # Selftests
+    #
+
+    print("Testing string literal lexing")
+
+    # Dummy empty configuration just to get a Kconfig object
+    c = Kconfig("Kconfiglib/tests/empty")
+
+    def verify_string_lex(s, expected):
+        # Verifies that a constant symbol with the name 'res' is produced from
+        # lexing 's'
+
+        res = c._tokenize("if " + s)[1].name
+        verify(res == expected,
+               "expected <{}> to produced the constant symbol <{}>, "
+               'produced <{}>'.format(s[1:-1], expected, res))
+
+    verify_string_lex(r""" "" """, "")
+    verify_string_lex(r""" '' """, "")
+
+    verify_string_lex(r""" "a" """, "a")
+    verify_string_lex(r""" 'a' """, "a")
+    verify_string_lex(r""" "ab" """, "ab")
+    verify_string_lex(r""" 'ab' """, "ab")
+    verify_string_lex(r""" "abc" """, "abc")
+    verify_string_lex(r""" 'abc' """, "abc")
+
+    verify_string_lex(r""" "'" """, "'")
+    verify_string_lex(r""" '"' """, '"')
+
+    verify_string_lex(r""" "\"" """, '"')
+    verify_string_lex(r""" '\'' """, "'")
+
+    verify_string_lex(r""" "\"\"" """, '""')
+    verify_string_lex(r""" '\'\'' """, "''")
+
+    verify_string_lex(r""" "\'" """, "'")
+    verify_string_lex(r""" '\"' """, '"')
+
+    verify_string_lex(r""" "\\" """, "\\")
+    verify_string_lex(r""" '\\' """, "\\")
+
+    verify_string_lex(r""" "\a\\'\b\c\"'d" """, 'a\\\'bc"\'d')
+    verify_string_lex(r""" '\a\\"\b\c\'"d' """, "a\\\"bc'\"d")
+
+    def verify_string_bad(s):
+        # Verifies that tokenizing 's' throws a KconfigError. Strips the first
+        # and last characters from 's' so we can use readable raw strings as
+        # input.
+
+        try:
+            c.eval_string(s)
+        except KconfigError:
+            pass
+        else:
+            fail("expected tokenization of {} to fail, didn't".format(s[1:-1]))
+
+    verify_string_bad(r""" " """)
+    verify_string_bad(r""" ' """)
+    verify_string_bad(r""" "' """)
+    verify_string_bad(r""" '" """)
+    verify_string_bad(r""" "\" """)
+    verify_string_bad(r""" '\' """)
+    verify_string_bad(r""" "foo """)
+    verify_string_bad(r""" 'foo """)
+
+
+    print("Testing escape() and unescape()")
+
+    def verify_escape_unescape(s, sesc):
+        # Verify that 's' escapes to 'sesc' and that 'sesc' unescapes to 's'
+        verify_equal(escape(s), sesc)
+        verify_equal(unescape(sesc), s)
+
+    verify_escape_unescape(r''          , r''              )
+    verify_escape_unescape(r'foo'       , r'foo'           )
+    verify_escape_unescape(r'"'         , r'\"'            )
+    verify_escape_unescape(r'""'        , r'\"\"'          )
+    verify_escape_unescape('\\'         , r'\\'            )
+    verify_escape_unescape(r'\\'        , r'\\\\'          )
+    verify_escape_unescape(r'\"'        , r'\\\"'          )
+    verify_escape_unescape(r'"ab\cd"ef"', r'\"ab\\cd\"ef\"')
+
+    # Backslashes before any character should be unescaped, not just before "
+    # and \
+    verify_equal(unescape(r"\afoo\b\c\\d\\\e\\\\f"), r"afoobc\d\e\\f")
+
+
+    print("Testing _ordered_unique()")
+
+    verify_equal(_ordered_unique([]), [])
+    verify_equal(_ordered_unique([1]), [1])
+    verify_equal(_ordered_unique([1, 2]), [1, 2])
+    verify_equal(_ordered_unique([1, 1]), [1])
+    verify_equal(_ordered_unique([1, 1, 2]), [1, 2])
+    verify_equal(_ordered_unique([1, 2, 1]), [1, 2])
+    verify_equal(_ordered_unique([1, 2, 2]), [1, 2])
+    verify_equal(_ordered_unique([1, 2, 3, 2, 1, 2, 3, 4, 3, 2, 1, 0]),
+                                 [1, 2, 3, 4, 0])
+
+
+    print("Testing expression evaluation")
+
+    c = Kconfig("Kconfiglib/tests/Keval", warn=False)
+
+    def verify_eval(expr, val):
+        res = c.eval_string(expr)
+        verify(res == val,
+               "'{}' evaluated to {}, expected {}".format(expr, res, val))
+
+    # No modules
+    verify_eval("n", 0)
+    verify_eval("m", 0)
+    verify_eval("y", 2)
+    verify_eval("'n'", 0)
+    verify_eval("'m'", 0)
+    verify_eval("'y'", 2)
+    verify_eval("M", 2)
+
+    # Modules
+    c.modules.set_value(2)
+    verify_eval("n", 0)
+    verify_eval("m", 1)
+    verify_eval("y", 2)
+    verify_eval("'n'", 0)
+    verify_eval("'m'", 1)
+    verify_eval("'y'", 2)
+    verify_eval("M", 1)
+    verify_eval("(Y || N) && (m && y)", 1)
+
+    # Non-bool/non-tristate symbols are always n in a tristate sense
+    verify_eval("Y_STRING", 0)
+    verify_eval("Y_STRING || m", 1)
+
+    # As are all constants besides y and m
+    verify_eval('"foo"', 0)
+    verify_eval('"foo" || "bar"', 0)
+    verify_eval('"foo" || m', 1)
+
+    # Test equality for symbols
+
+    verify_eval("N = N", 2)
+    verify_eval("N = n", 2)
+    verify_eval("N = 'n'", 2)
+    verify_eval("N != N", 0)
+    verify_eval("N != n", 0)
+    verify_eval("N != 'n'", 0)
+
+    verify_eval("M = M", 2)
+    verify_eval("M = m", 2)
+    verify_eval("M = 'm'", 2)
+    verify_eval("M != M", 0)
+    verify_eval("M != m", 0)
+    verify_eval("M != 'm'", 0)
+
+    verify_eval("Y = Y", 2)
+    verify_eval("Y = y", 2)
+    verify_eval("Y = 'y'", 2)
+    verify_eval("Y != Y", 0)
+    verify_eval("Y != y", 0)
+    verify_eval("Y != 'y'", 0)
+
+    verify_eval("N != M", 2)
+    verify_eval("N != Y", 2)
+    verify_eval("M != Y", 2)
+
+    verify_eval("Y_STRING = y", 2)
+    verify_eval("Y_STRING = 'y'", 2)
+    verify_eval('FOO_BAR_STRING = "foo bar"', 2)
+    verify_eval('FOO_BAR_STRING != "foo bar baz"', 2)
+    verify_eval('INT_37 = 37', 2)
+    verify_eval("INT_37 = '37'", 2)
+    verify_eval('HEX_0X37 = 0x37', 2)
+    verify_eval("HEX_0X37 = '0x37'", 2)
+
+    # These should also hold after 31847b67 (kconfig: allow use of relations
+    # other than (in)equality)
+    verify_eval("HEX_0X37 = '0x037'", 2)
+    verify_eval("HEX_0X37 = '0x0037'", 2)
+
+    # Constant symbol comparisons
+    verify_eval('"foo" != "bar"', 2)
+    verify_eval('"foo" = "bar"', 0)
+    verify_eval('"foo" = "foo"', 2)
+
+    # Undefined symbols get their name as their value
+    c.warn = False
+    verify_eval("'not_defined' = not_defined", 2)
+    verify_eval("not_defined_2 = not_defined_2", 2)
+    verify_eval("not_defined_1 != not_defined_2", 2)
+
+    # Test less than/greater than
+
+    # Basic evaluation
+    verify_eval("INT_37 < 38", 2)
+    verify_eval("38 < INT_37", 0)
+    verify_eval("INT_37 < '38'", 2)
+    verify_eval("'38' < INT_37", 0)
+    verify_eval("INT_37 < 138", 2)
+    verify_eval("138 < INT_37", 0)
+    verify_eval("INT_37 < '138'", 2)
+    verify_eval("'138' < INT_37", 0)
+    verify_eval("INT_37 < -138", 0)
+    verify_eval("-138 < INT_37", 2)
+    verify_eval("INT_37 < '-138'", 0)
+    verify_eval("'-138' < INT_37", 2)
+    verify_eval("INT_37 < 37", 0)
+    verify_eval("37 < INT_37", 0)
+    verify_eval("INT_37 < 36", 0)
+    verify_eval("36 < INT_37", 2)
+
+    # Different formats in comparison
+    verify_eval("INT_37 < 0x26", 2) # 38
+    verify_eval("INT_37 < 0x25", 0) # 37
+    verify_eval("INT_37 < 0x24", 0) # 36
+    verify_eval("HEX_0X37 < 56", 2) # 0x38
+    verify_eval("HEX_0X37 < 55", 0) # 0x37
+    verify_eval("HEX_0X37 < 54", 0) # 0x36
+
+    # Other int comparisons
+    verify_eval("INT_37 <= 38", 2)
+    verify_eval("INT_37 <= 37", 2)
+    verify_eval("INT_37 <= 36", 0)
+    verify_eval("INT_37 >  38", 0)
+    verify_eval("INT_37 >  37", 0)
+    verify_eval("INT_37 >  36", 2)
+    verify_eval("INT_37 >= 38", 0)
+    verify_eval("INT_37 >= 37", 2)
+    verify_eval("INT_37 >= 36", 2)
+
+    # Other hex comparisons
+    verify_eval("HEX_0X37 <= 0x38", 2)
+    verify_eval("HEX_0X37 <= 0x37", 2)
+    verify_eval("HEX_0X37 <= 0x36", 0)
+    verify_eval("HEX_0X37 >  0x38", 0)
+    verify_eval("HEX_0X37 >  0x37", 0)
+    verify_eval("HEX_0X37 >  0x36", 2)
+    verify_eval("HEX_0X37 >= 0x38", 0)
+    verify_eval("HEX_0X37 >= 0x37", 2)
+    verify_eval("HEX_0X37 >= 0x36", 2)
+
+    # A hex holding a value without a "0x" prefix should still be treated as
+    # hexadecimal
+    verify_eval("HEX_37 < 0x38", 2)
+    verify_eval("HEX_37 < 0x37", 0)
+    verify_eval("HEX_37 < 0x36", 0)
+
+    # Symbol comparisons
+    verify_eval("INT_37   <  HEX_0X37", 2)
+    verify_eval("INT_37   >  HEX_0X37", 0)
+    verify_eval("HEX_0X37 <  INT_37  ", 0)
+    verify_eval("HEX_0X37 >  INT_37  ", 2)
+    verify_eval("INT_37   <  INT_37  ", 0)
+    verify_eval("INT_37   <= INT_37  ", 2)
+    verify_eval("INT_37   >  INT_37  ", 0)
+    verify_eval("INT_37   <= INT_37  ", 2)
+
+    # Tristate value comparisons
+    verify_eval("n < n", 0)
+    verify_eval("n < m", 2)
+    verify_eval("n < y", 2)
+    verify_eval("n < N", 0)
+    verify_eval("n < M", 2)
+    verify_eval("n < Y", 2)
+    verify_eval("0 > n", 0)
+    verify_eval("1 > n", 2)
+    verify_eval("2 > n", 2)
+    verify_eval("m < n", 0)
+    verify_eval("m < m", 0)
+    verify_eval("m < y", 2)
+
+    # Strings compare lexicographically
+    verify_eval("'aa' < 'ab'", 2)
+    verify_eval("'aa' > 'ab'", 0)
+    verify_eval("'ab' < 'aa'", 0)
+    verify_eval("'ab' > 'aa'", 2)
+
+    # Comparisons where one of the operands doesn't parse as a number also give
+    # a lexicographic comparison
+    verify_eval("INT_37 <  '37a' ", 2)
+    verify_eval("'37a'  >  INT_37", 2)
+    verify_eval("INT_37 <= '37a' ", 2)
+    verify_eval("'37a'  >= INT_37", 2)
+    verify_eval("INT_37 >= '37a' ", 0)
+    verify_eval("INT_37 >  '37a' ", 0)
+    verify_eval("'37a'  <  INT_37", 0)
+    verify_eval("'37a'  <= INT_37", 0)
+
+    def verify_eval_bad(expr):
+        try:
+            c.eval_string(expr)
+        except KconfigError:
+            pass
+        else:
+            fail('expected eval_string("{}") to throw KconfigError, '
+                 "didn't".format(expr))
+
+    # Verify that some bad stuff throws KconfigError's
+    verify_eval_bad("")
+    verify_eval_bad("&")
+    verify_eval_bad("|")
+    verify_eval_bad("!")
+    verify_eval_bad("(")
+    verify_eval_bad(")")
+    verify_eval_bad("=")
+    verify_eval_bad("(X")
+    verify_eval_bad("X)")
+    verify_eval_bad("X X")
+    verify_eval_bad("!X X")
+    verify_eval_bad("X !X")
+    verify_eval_bad("(X) X")
+    verify_eval_bad("X &&")
+    verify_eval_bad("&& X")
+    verify_eval_bad("X && && X")
+    verify_eval_bad("X && !&&")
+    verify_eval_bad("X ||")
+    verify_eval_bad("|| X")
+
+
+    print("Testing Symbol.__str__()/custom_str() and def_{int,hex,string}")
+
+    def verify_str(item, s):
+        verify_equal(str(item), s[1:-1])
+
+    def verify_custom_str(item, s):
+        verify_equal(item.custom_str(lambda sc: "[{}]".format(sc.name)),
+                     s[1:-1])
+
+    c = Kconfig("Kconfiglib/tests/Kstr", warn=False)
+
+    c.modules.set_value(2)
+
+    verify_str(c.syms["UNDEFINED"], """
+""")
+
+    verify_str(c.syms["BASIC_NO_PROMPT"], """
+config BASIC_NO_PROMPT
+	bool
+	help
+	  blah blah
+	  
+	    blah blah blah
+	  
+	   blah
+""")
+
+    verify_str(c.syms["BASIC_PROMPT"], """
+config BASIC_PROMPT
+	bool "basic"
+""")
+
+    verify_str(c.syms["ADVANCED"], """
+config ADVANCED
+	tristate "prompt" if DEP
+	default DEFAULT_1
+	default DEFAULT_2 if DEP
+	select SELECTED_1
+	select SELECTED_2 if DEP
+	imply IMPLIED_1
+	imply IMPLIED_2 if DEP
+	help
+	  first help text
+
+config ADVANCED
+	tristate "prompt 2"
+
+menuconfig ADVANCED
+	tristate "prompt 3"
+
+config ADVANCED
+	tristate
+	depends on (A || !B || (C && D) || !(E && F) || G = H || (I && !J && (K || L) && !(M || N) && O = P)) && DEP4 && DEP3
+	help
+	  second help text
+
+config ADVANCED
+	tristate "prompt 4" if VIS
+	depends on DEP4 && DEP3
+""")
+
+    verify_custom_str(c.syms["ADVANCED"], """
+config ADVANCED
+	tristate "prompt" if [DEP]
+	default [DEFAULT_1]
+	default [DEFAULT_2] if [DEP]
+	select [SELECTED_1]
+	select [SELECTED_2] if [DEP]
+	imply [IMPLIED_1]
+	imply [IMPLIED_2] if [DEP]
+	help
+	  first help text
+
+config ADVANCED
+	tristate "prompt 2"
+
+menuconfig ADVANCED
+	tristate "prompt 3"
+
+config ADVANCED
+	tristate
+	depends on ([A] || ![B] || ([C] && [D]) || !([E] && [F]) || [G] = [H] || ([I] && ![J] && ([K] || [L]) && !([M] || [N]) && [O] = [P])) && [DEP4] && [DEP3]
+	help
+	  second help text
+
+config ADVANCED
+	tristate "prompt 4" if [VIS]
+	depends on [DEP4] && [DEP3]
+""")
+
+
+    verify_str(c.syms["ONLY_DIRECT_DEPS"], """
+config ONLY_DIRECT_DEPS
+	int
+	depends on DEP1 && DEP2
+""")
+
+    verify_str(c.syms["STRING"], """
+config STRING
+	string
+	default "foo"
+	default "bar" if DEP
+	default STRING2
+	default STRING3 if DEP
+""")
+
+    verify_str(c.syms["INT"], """
+config INT
+	int
+	range 1 2
+	range FOO BAR
+	range BAZ QAZ if DEP
+	default 7 if DEP
+""")
+
+    verify_str(c.syms["HEX"], """
+config HEX
+	hex
+	range 0x100 0x200
+	range FOO BAR
+	range BAZ QAZ if DEP
+	default 0x123
+""")
+
+    verify_str(c.modules, """
+config MODULES
+	bool "MODULES"
+	option modules
+""")
+
+    verify_str(c.syms["OPTIONS"], """
+config OPTIONS
+	option allnoconfig_y
+	option defconfig_list
+	option env="ENV"
+""")
+
+    verify_str(c.syms["CORRECT_PROP_LOCS_BOOL"], """
+config CORRECT_PROP_LOCS_BOOL
+	bool "prompt 1"
+	default DEFAULT_1
+	default DEFAULT_2
+	select SELECT_1
+	select SELECT_2
+	imply IMPLY_1
+	imply IMPLY_2
+	depends on LOC_1
+	help
+	  help 1
+
+menuconfig CORRECT_PROP_LOCS_BOOL
+	bool "prompt 2"
+	default DEFAULT_3
+	default DEFAULT_4
+	select SELECT_3
+	select SELECT_4
+	imply IMPLY_3
+	imply IMPLY_4
+	depends on LOC_2
+	help
+	  help 2
+
+config CORRECT_PROP_LOCS_BOOL
+	bool "prompt 3"
+	default DEFAULT_5
+	default DEFAULT_6
+	select SELECT_5
+	select SELECT_6
+	imply IMPLY_5
+	imply IMPLY_6
+	depends on LOC_3
+	help
+	  help 2
+""")
+
+    verify_str(c.syms["CORRECT_PROP_LOCS_INT"], """
+config CORRECT_PROP_LOCS_INT
+	int
+	range 1 2
+	range 3 4
+	depends on LOC_1
+
+config CORRECT_PROP_LOCS_INT
+	int
+	range 5 6
+	range 7 8
+	depends on LOC_2
+""")
+
+    verify_str(c.syms["PROMPT_ONLY"], """
+config PROMPT_ONLY
+	prompt "prompt only"
+""")
+
+    verify_custom_str(c.syms["CORRECT_PROP_LOCS_INT"], """
+config CORRECT_PROP_LOCS_INT
+	int
+	range [1] [2]
+	range [3] [4]
+	depends on [LOC_1]
+
+config CORRECT_PROP_LOCS_INT
+	int
+	range [5] [6]
+	range [7] [8]
+	depends on [LOC_2]
+""")
+
+
+
+    print("Testing Choice.__str__()/custom_str()")
+
+    verify_str(c.named_choices["CHOICE"], """
+choice CHOICE
+	tristate "foo"
+	default CHOICE_1
+	default CHOICE_2 if dep
+""")
+
+    verify_str(c.named_choices["CHOICE"].nodes[0].next.item, """
+choice
+	tristate "no name"
+	optional
+""")
+
+    verify_str(c.named_choices["CORRECT_PROP_LOCS_CHOICE"], """
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default CHOICE_3
+	depends on LOC_1
+
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default CHOICE_4
+	depends on LOC_2
+
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default CHOICE_5
+	depends on LOC_3
+""")
+
+    verify_custom_str(c.named_choices["CORRECT_PROP_LOCS_CHOICE"], """
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default [CHOICE_3]
+	depends on [LOC_1]
+
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default [CHOICE_4]
+	depends on [LOC_2]
+
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default [CHOICE_5]
+	depends on [LOC_3]
+""")
+
+
+    print("Testing MenuNode.__str__()/custom_str() for menus and comments")
+
+    verify_str(c.syms["SIMPLE_MENU_HOOK"].nodes[0].next, """
+menu "simple menu"
+""")
+
+    verify_str(c.syms["ADVANCED_MENU_HOOK"].nodes[0].next, """
+menu "advanced menu"
+	depends on A
+	visible if B && (C || D)
+""")
+
+    verify_custom_str(c.syms["ADVANCED_MENU_HOOK"].nodes[0].next, """
+menu "advanced menu"
+	depends on [A]
+	visible if [B] && ([C] || [D])
+""")
+
+    verify_str(c.syms["SIMPLE_COMMENT_HOOK"].nodes[0].next, """
+comment "simple comment"
+""")
+
+    verify_str(c.syms["ADVANCED_COMMENT_HOOK"].nodes[0].next, """
+comment "advanced comment"
+	depends on A && B
+""")
+
+    verify_custom_str(c.syms["ADVANCED_COMMENT_HOOK"].nodes[0].next, """
+comment "advanced comment"
+	depends on [A] && [B]
+""")
+
+
+    print("Testing {MenuNode,Symbol,Choice}.orig_*")
+
+    # Just test some corner cases here re. MenuNode.orig_*. They are already
+    # indirectly tested above. Use MenuNode.__str__() as a proxy.
+
+    verify_str(c.syms["DEP_REM_CORNER_CASES"], """
+config DEP_REM_CORNER_CASES
+	bool
+	default A
+	depends on n
+
+config DEP_REM_CORNER_CASES
+	bool
+	default B if n
+
+config DEP_REM_CORNER_CASES
+	bool
+	default C
+	depends on m && MODULES
+
+config DEP_REM_CORNER_CASES
+	bool
+	default D if A
+
+config DEP_REM_CORNER_CASES
+	bool
+	default E if !E1
+	default F if F1 = F2
+	default G if G1 || H1
+	depends on !H
+
+config DEP_REM_CORNER_CASES
+	bool
+	default H
+	depends on "foo" = "bar"
+
+config DEP_REM_CORNER_CASES
+	bool "prompt" if FOO || BAR
+	depends on BAZ && QAZ
+""")
+
+    # Test {Symbol,Choice}.orig_*
+
+    def verify_deps(elms, dep_index, expected):
+        verify_equal(" ".join(expr_str(elm[dep_index]) for elm in elms),
+                     expected)
+
+    verify_deps(c.syms["BOOL_SYM_ORIG"].orig_defaults,        1, "DEP y y")
+    verify_deps(c.syms["BOOL_SYM_ORIG"].orig_selects,         1, "y DEP y")
+    verify_deps(c.syms["BOOL_SYM_ORIG"].orig_implies,         1, "y y DEP")
+    verify_deps(c.syms["INT_SYM_ORIG"].orig_ranges,           2, "DEP y DEP")
+    verify_deps(c.named_choices["CHOICE_ORIG"].orig_defaults, 1, "y DEP DEP")
+
+
+    print("Testing Symbol.__repr__()")
+
+    def verify_repr(item, s):
+        verify_equal(repr(item) + "\n", s[1:])
+
+    c = Kconfig("Kconfiglib/tests/Krepr", warn=False)
+
+    verify_repr(c.n, """
+<symbol n, tristate, value n, constant>
+""")
+
+    verify_repr(c.m, """
+<symbol m, tristate, value m, constant>
+""")
+
+    verify_repr(c.y, """
+<symbol y, tristate, value y, constant>
+""")
+
+    verify_repr(c.syms["UNDEFINED"], """
+<symbol UNDEFINED, unknown, value "UNDEFINED", visibility n, direct deps n, undefined>
+""")
+
+    verify_repr(c.syms["BASIC"], """
+<symbol BASIC, bool, value y, visibility n, direct deps y, Kconfiglib/tests/Krepr:9>
+""")
+
+    verify_repr(c.syms["VISIBLE"], """
+<symbol VISIBLE, bool, "visible", value n, visibility y, direct deps y, Kconfiglib/tests/Krepr:14>
+""")
+
+    c.syms["VISIBLE"].set_value(2)
+    c.syms["STRING"].set_value("foo")
+
+    verify_repr(c.syms["VISIBLE"], """
+<symbol VISIBLE, bool, "visible", value y, user value y, visibility y, direct deps y, Kconfiglib/tests/Krepr:14>
+""")
+
+    verify_repr(c.syms["STRING"], """
+<symbol STRING, string, "visible", value "foo", user value "foo", visibility y, direct deps y, Kconfiglib/tests/Krepr:17>
+""")
+
+    verify_repr(c.syms["DIR_DEP_N"], """
+<symbol DIR_DEP_N, unknown, value "DIR_DEP_N", visibility n, direct deps n, Kconfiglib/tests/Krepr:20>
+""")
+
+    verify_repr(c.syms["OPTIONS"], """
+<symbol OPTIONS, unknown, value "OPTIONS", visibility n, allnoconfig_y, is the defconfig_list symbol, from environment variable ENV, direct deps y, Kconfiglib/tests/Krepr:23>
+""")
+
+    verify_repr(c.syms["MULTI_DEF"], """
+<symbol MULTI_DEF, unknown, value "MULTI_DEF", visibility n, direct deps y, Kconfiglib/tests/Krepr:28, Kconfiglib/tests/Krepr:29>
+""")
+
+    verify_repr(c.syms["CHOICE_1"], """
+<symbol CHOICE_1, tristate, "choice sym", value n, visibility m, choice symbol, direct deps m, Kconfiglib/tests/Krepr:36>
+""")
+
+    verify_repr(c.modules, """
+<symbol MODULES, bool, value y, visibility n, is the modules symbol, direct deps y, Kconfiglib/tests/Krepr:1>
+""")
+
+
+    print("Testing Choice.__repr__()")
+
+    verify_repr(c.named_choices["CHOICE"], """
+<choice CHOICE, tristate, "choice", mode m, visibility y, Kconfiglib/tests/Krepr:33>
+""")
+
+    c.named_choices["CHOICE"].set_value(2)
+
+    verify_repr(c.named_choices["CHOICE"], """
+<choice CHOICE, tristate, "choice", mode y, user mode y, CHOICE_1 selected, visibility y, Kconfiglib/tests/Krepr:33>
+""")
+
+    c.syms["CHOICE_2"].set_value(2)
+
+    verify_repr(c.named_choices["CHOICE"], """
+<choice CHOICE, tristate, "choice", mode y, user mode y, CHOICE_2 selected, CHOICE_2 selected by user, visibility y, Kconfiglib/tests/Krepr:33>
+""")
+
+    c.named_choices["CHOICE"].set_value(1)
+
+    verify_repr(c.named_choices["CHOICE"], """
+<choice CHOICE, tristate, "choice", mode m, user mode m, CHOICE_2 selected by user (overridden), visibility y, Kconfiglib/tests/Krepr:33>
+""")
+
+    verify_repr(c.syms["CHOICE_HOOK"].nodes[0].next.item, """
+<choice, tristate, "optional choice", mode n, visibility n, optional, Kconfiglib/tests/Krepr:46>
+""")
+
+
+    print("Testing MenuNode.__repr__()")
+
+    verify_repr(c.syms["BASIC"].nodes[0], """
+<menu node for symbol BASIC, deps y, has help, has next, Kconfiglib/tests/Krepr:9>
+""")
+
+    verify_repr(c.syms["DIR_DEP_N"].nodes[0], """
+<menu node for symbol DIR_DEP_N, deps n, has next, Kconfiglib/tests/Krepr:20>
+""")
+
+    verify_repr(c.syms["MULTI_DEF"].nodes[0], """
+<menu node for symbol MULTI_DEF, deps y, has next, Kconfiglib/tests/Krepr:28>
+""")
+
+    verify_repr(c.syms["MULTI_DEF"].nodes[1], """
+<menu node for symbol MULTI_DEF, deps y, has next, Kconfiglib/tests/Krepr:29>
+""")
+
+    verify_repr(c.syms["MENUCONFIG"].nodes[0], """
+<menu node for symbol MENUCONFIG, is menuconfig, deps y, has next, Kconfiglib/tests/Krepr:31>
+""")
+
+    verify_repr(c.named_choices["CHOICE"].nodes[0], """
+<menu node for choice CHOICE, prompt "choice" (visibility y), deps y, has child, has next, Kconfiglib/tests/Krepr:33>
+""")
+
+    verify_repr(c.syms["CHOICE_HOOK"].nodes[0].next, """
+<menu node for choice, prompt "optional choice" (visibility n), deps y, has next, Kconfiglib/tests/Krepr:46>
+""")
+
+    verify_repr(c.syms["NO_VISIBLE_IF_HOOK"].nodes[0].next, """
+<menu node for menu, prompt "no visible if" (visibility y), deps y, 'visible if' deps y, has next, Kconfiglib/tests/Krepr:53>
+""")
+
+    verify_repr(c.syms["VISIBLE_IF_HOOK"].nodes[0].next, """
+<menu node for menu, prompt "visible if" (visibility y), deps y, 'visible if' deps m, has next, Kconfiglib/tests/Krepr:58>
+""")
+
+    verify_repr(c.syms["COMMENT_HOOK"].nodes[0].next, """
+<menu node for comment, prompt "comment" (visibility y), deps y, Kconfiglib/tests/Krepr:64>
+""")
+
+
+    print("Testing Kconfig.__repr__()")
+
+    verify_repr(c, """
+<configuration with 15 symbols, main menu prompt "Main menu", srctree is current directory, config symbol prefix "CONFIG_", warnings disabled, printing of warnings to stderr enabled, undef. symbol assignment warnings disabled, overriding symbol assignment warnings enabled, redundant symbol assignment warnings enabled>
+""")
+
+    os.environ["srctree"] = "Kconfiglib"
+    os.environ["CONFIG_"] = "CONFIG_ value"
+
+    c = Kconfig("tests/Krepr", warn=False)
+    c.warn = True
+    c.warn_to_stderr = False
+    c.warn_assign_override = False
+    c.warn_assign_redun = False
+    c.warn_assign_undef = True
+
+    verify_repr(c, """
+<configuration with 15 symbols, main menu prompt "Main menu", srctree "Kconfiglib", config symbol prefix "CONFIG_ value", warnings enabled, printing of warnings to stderr disabled, undef. symbol assignment warnings enabled, overriding symbol assignment warnings disabled, redundant symbol assignment warnings disabled>
+""")
+
+    os.environ.pop("srctree", None)
+    os.environ.pop("CONFIG_", None)
+
+
+    print("Testing tricky help strings")
+
+    c = Kconfig("Kconfiglib/tests/Khelp")
+
+    def verify_help(node, s):
+        verify_equal(node.help, s[1:-1])
+
+    verify_help(c.syms["TWO_HELP_STRINGS"].nodes[0], """
+first help string
+""")
+
+    verify_help(c.syms["TWO_HELP_STRINGS"].nodes[1], """
+second help string
+""")
+
+    verify_help(c.syms["NO_BLANK_AFTER_HELP"].nodes[0], """
+help for
+NO_BLANK_AFTER_HELP
+""")
+
+    verify_help(c.named_choices["CHOICE_HELP"].nodes[0], """
+help for
+CHOICE_HELP
+""")
+
+    verify_help(c.syms["HELP_TERMINATED_BY_COMMENT"].nodes[0], """
+a
+b
+c
+""")
+
+    verify_help(c.syms["TRICKY_HELP"].nodes[0], """
+a
+ b
+  c
+
+ d
+  e
+   f
+
+
+g
+ h
+  i
+""")
+
+
+    print("Testing locations, source/rsource/gsource/grsource, and "
+          "Kconfig.kconfig_filenames")
+
+    def verify_locations(nodes, *expected_locs):
+        verify(len(nodes) == len(expected_locs),
+               "Wrong number of locations for " + repr(nodes))
+
+        for node, expected_loc in zip(nodes, expected_locs):
+            node_loc = "{}:{}".format(node.filename, node.linenr)
+            verify(node_loc == expected_loc,
+                   "expected {} to have the location {}, had the location {}"
+                   .format(repr(node), expected_loc, node_loc))
+
+    # Expanded in the 'source' statement in Klocation
+
+    os.environ["TESTS_DIR_FROM_ENV"] = "tests"
+    os.environ["SUB_DIR_FROM_ENV"] = "sub"
+
+    os.environ["_SOURCED"] = "_sourced"
+    os.environ["_RSOURCED"] = "_rsourced"
+    os.environ["_GSOURCED"] = "_gsourced"
+    os.environ["_GRSOURCED"] = "_grsourced"
+
+    # Test twice, with $srctree as a relative and an absolute path,
+    # respectively
+    for srctree in "Kconfiglib", os.path.abspath("Kconfiglib"):
+        os.environ["srctree"] = srctree
+
+        # Has symbol with empty help text, so disable warnings
+        c = Kconfig("tests/Klocation", warn=False)
+
+        verify_locations(c.syms["UNDEFINED"].nodes)
+        verify_equal(c.syms["UNDEFINED"].name_and_loc, "UNDEFINED (undefined)")
+
+        verify_locations(c.syms["ONE_DEF"].nodes, "tests/Klocation:4")
+        verify_equal(c.syms["ONE_DEF"].name_and_loc,
+                     "ONE_DEF (defined at tests/Klocation:4)")
+
+        verify_locations(c.syms["TWO_DEF"].nodes,
+                         "tests/Klocation:7",
+                         "tests/Klocation:10")
+        verify_equal(c.syms["TWO_DEF"].name_and_loc,
+                     "TWO_DEF (defined at tests/Klocation:7, tests/Klocation:10)")
+
+        verify_locations(c.syms["MANY_DEF"].nodes,
+                         "tests/Klocation:13",
+                         "tests/Klocation:43",
+                         "tests/Klocation:45",
+                         "tests/Klocation_sourced:3",
+                         "tests/sub/Klocation_rsourced:2",
+                         "tests/sub/Klocation_gsourced1:1",
+                         "tests/sub/Klocation_gsourced2:1",
+                         "tests/sub/Klocation_gsourced1:1",
+                         "tests/sub/Klocation_gsourced2:1",
+                         "tests/sub/Klocation_grsourced1:1",
+                         "tests/sub/Klocation_grsourced2:1",
+                         "tests/sub/Klocation_grsourced1:1",
+                         "tests/sub/Klocation_grsourced2:1",
+                         "tests/Klocation:78")
+
+        verify_locations(c.named_choices["CHOICE_ONE_DEF"].nodes,
+                         "tests/Klocation_sourced:5")
+        verify_equal(c.named_choices["CHOICE_ONE_DEF"].name_and_loc,
+                     "<choice CHOICE_ONE_DEF> (defined at tests/Klocation_sourced:5)")
+
+        verify_locations(c.named_choices["CHOICE_TWO_DEF"].nodes,
+                         "tests/Klocation_sourced:9",
+                         "tests/Klocation_sourced:13")
+        verify_equal(c.named_choices["CHOICE_TWO_DEF"].name_and_loc,
+                     "<choice CHOICE_TWO_DEF> (defined at tests/Klocation_sourced:9, tests/Klocation_sourced:13)")
+
+        verify_locations([c.syms["MENU_HOOK"].nodes[0].next],
+                         "tests/Klocation_sourced:20")
+
+        verify_locations([c.syms["COMMENT_HOOK"].nodes[0].next],
+                         "tests/Klocation_sourced:26")
+
+        # Test Kconfig.kconfig_filenames
+
+        verify_equal(c.kconfig_filenames, [
+            "tests/Klocation",
+            "tests/Klocation_sourced",
+            "tests/sub/Klocation_rsourced",
+            "tests/sub/Klocation_gsourced1",
+            "tests/sub/Klocation_gsourced2",
+            "tests/sub/Klocation_gsourced1",
+            "tests/sub/Klocation_gsourced2",
+            "tests/sub/Klocation_grsourced1",
+            "tests/sub/Klocation_grsourced2",
+            "tests/sub/Klocation_grsourced1",
+            "tests/sub/Klocation_grsourced2"
+        ])
+
+        # Test recursive 'source' detection
+
+        try:
+            Kconfig("tests/Krecursive1")
+        except KconfigError as e:
+            verify_equal(str(e), """
+tests/Krecursive2:1: recursive 'source' of 'tests/Krecursive1' detected. Check that environment variables are set correctly.
+Include path:
+tests/Krecursive1:1
+tests/Krecursive2:1
+"""[:-1])
+        except:
+            fail("recursive 'source' raised wrong exception")
+        else:
+            fail("recursive 'source' did not raise exception")
+
+        # Verify that source and rsource throw exceptions for missing files
+
+        # TODO: Make an exception test helper
+
+        try:
+            Kconfig("tests/Kmissingsource")
+        except KconfigError as e:
+            if "not found" not in str(e):
+                fail("'source' with missing file raised wrong KconfigError")
+        except:
+            fail("'source' with missing file raised wrong exception")
+        else:
+            fail("'source' with missing file did not raise exception")
+
+        try:
+            Kconfig("tests/Kmissingrsource")
+        except KconfigError as e:
+            if "not found" not in str(e):
+                fail("'rsource' with missing file raised wrong KconfigError")
+        except:
+            fail("'rsource' with missing file raised wrong exception")
+        else:
+            fail("'rsource' with missing file did not raise exception")
+
+    # Test a tricky case involving symlinks. $srctree is tests/symlink, which
+    # points to tests/sub/sub, meaning tests/symlink/.. != tests/. Previously,
+    # using 'rsource' from a file sourced with an absolute path triggered an
+    # unsafe relpath() with tests/symlink/.. in it, crashing.
+
+    os.environ["srctree"] = "Kconfiglib/tests/symlink"
+    os.environ["KCONFIG_SYMLINK_2"] = os.path.abspath(
+        "Kconfiglib/tests/sub/Kconfig_symlink_2")
+    if not os.path.isabs(
+        Kconfig("Kconfig_symlink_1").syms["FOUNDME"].nodes[0].filename):
+
+        fail("Symlink + rsource issues")
+
+
+    print("Testing Kconfig.node_iter()")
+
+    # Reuse tests/Klocation. The node_iter(unique_syms=True) case already gets
+    # plenty of testing from write_config() as well.
+
+    os.environ["srctree"] = "Kconfiglib"
+    c = Kconfig("tests/Klocation", warn=False)
+
+    verify_equal(
+        [node.item.name for node in c.node_iter()
+         if isinstance(node.item, Symbol)],
+        ["ONE_DEF", "TWO_DEF", "TWO_DEF", "MANY_DEF", "HELP_1", "HELP_2",
+         "HELP_3", "MANY_DEF", "MANY_DEF", "MANY_DEF", "MENU_HOOK",
+         "COMMENT_HOOK"] + 10*["MANY_DEF"])
+
+    verify_equal(
+        [node.item.name for node in c.node_iter(True)
+         if isinstance(node.item, Symbol)],
+        ["ONE_DEF", "TWO_DEF", "MANY_DEF", "HELP_1", "HELP_2", "HELP_3",
+         "MENU_HOOK", "COMMENT_HOOK"])
+
+    verify_equal(
+        [node.prompt[0] for node in c.node_iter()
+         if not isinstance(node.item, Symbol)],
+        ["one-def choice", "two-def choice 1", "two-def choice 2",
+         "menu", "comment"])
+
+    verify_equal(
+        [node.prompt[0] for node in c.node_iter(True)
+         if not isinstance(node.item, Symbol)],
+        ["one-def choice", "two-def choice 1", "two-def choice 2",
+         "menu", "comment"])
+
+
+    print("Testing MenuNode.include_path")
+
+    os.environ["srctree"] = "Kconfiglib/tests"
+
+    c = Kconfig("Kinclude_path")
+
+    def verify_node_path(node, *expected):
+        if node.include_path != expected:
+            fail("Wrong include path for node {!r}. Got {}, expected {}."
+                 .format(node, node.include_path, expected))
+
+    def verify_sym_path(sym_name, node_i, *expected):
+        verify_node_path(c.syms[sym_name].nodes[node_i], *expected)
+
+    verify_sym_path("TOP", 0)
+    verify_sym_path("TOP", 1)
+    verify_sym_path("TOP", 2)
+
+    verify_sym_path("ONE_DOWN", 0, ("Kinclude_path", 4))
+    verify_sym_path("ONE_DOWN", 1, ("Kinclude_path", 4))
+    verify_sym_path("ONE_DOWN", 2, ("Kinclude_path", 4))
+    verify_sym_path("ONE_DOWN", 3, ("Kinclude_path", 9))
+    verify_sym_path("ONE_DOWN", 4, ("Kinclude_path", 9))
+    verify_sym_path("ONE_DOWN", 5, ("Kinclude_path", 9))
+
+    verify_sym_path("TWO_DOWN", 0,
+                    ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
+    verify_sym_path("TWO_DOWN", 1,
+                    ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 9))
+    verify_sym_path("TWO_DOWN", 2,
+                    ("Kinclude_path", 9), ("Kinclude_path_sourced_1", 4))
+    verify_sym_path("TWO_DOWN", 3,
+                    ("Kinclude_path", 9), ("Kinclude_path_sourced_1", 9))
+
+    verify_node_path(c.top_node)
+    verify_node_path(c.menus[0], ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
+    verify_node_path(c.comments[0], ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
+    verify_node_path(c.choices[0].nodes[0], ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
+
+    os.environ.pop("srctree", None)
+
+
+    print("Testing Kconfig.choices/menus/comments")
+
+    c = Kconfig("Kconfiglib/tests/Kitemlists")
+
+    def verify_prompts(items, *expected_prompts):
+        verify(len(items) == len(expected_prompts),
+               "Wrong number of prompts for {}".format(items))
+
+        for item, expected_prompt in zip(items, expected_prompts):
+            if not isinstance(item, MenuNode):
+                item = item.nodes[0]
+
+            verify(item.prompt[0] == expected_prompt,
+                   "Wrong prompt for {}, expected '{}'"
+                   .format(repr(item), expected_prompt))
+
+    verify_prompts(c.choices, "choice 1", "choice 2", "choice 3", "choice 2")
+    verify_prompts(c.menus, "menu 1", "menu 2", "menu 3", "menu 4", "menu 5")
+    verify_prompts(c.comments, "comment 1", "comment 2", "comment 3")
+
+
+    print("Testing Symbol/Choice.direct_dep")
+
+    c = Kconfig("Kconfiglib/tests/Kdirdep")
+
+    verify_equal(expr_str(c.syms["NO_DEP_SYM"].direct_dep), 'y')
+    verify_equal(expr_str(c.syms["DEP_SYM"].direct_dep), "A || (B && C) || !D")
+
+    verify_equal(expr_str(c.named_choices["NO_DEP_CHOICE"].direct_dep), 'y')
+    verify_equal(expr_str(c.named_choices["DEP_CHOICE"].direct_dep),
+                 "A || B || C")
+
+
+    print("Testing expr_items()")
+
+    c = Kconfig("Kconfiglib/tests/Kexpr_items")
+
+    def verify_expr_items(expr, *sym_names):
+        verify_equal(tuple(sorted(item.name for item in expr_items(expr))),
+                     sym_names)
+
+    verify_expr_items(
+        c.syms["TEST"].defaults[0][0],
+        "A", "B", "C", "D", "E", "F", "G", "H"
+    )
+
+    verify_expr_items(
+        c.syms["TEST_CHOICE"].nodes[0].prompt[1],
+        "A", "CHOICE"
+    )
+
+
+    print("Testing MenuNode/Symbol/Choice.referenced")
+
+    c = Kconfig("Kconfiglib/tests/Kreferenced", warn=False)
+
+    def verify_deps(item, *dep_names):
+        verify_equal(tuple(sorted(item.name for item in item.referenced)),
+                     dep_names)
+
+    verify_deps(c.top_node, "y")
+
+    verify_deps(c.syms["NO_REFS"].nodes[0], "y")
+
+    verify_deps(c.syms["JUST_DEPENDS_ON_REFS"].nodes[0], "A", "B")
+
+    verify_deps(c.syms["LOTS_OF_REFS"].nodes[0],
+                *(chr(n) for n in range(ord("A"), ord("Z") + 1)))
+
+    verify_deps(c.syms["INT_REFS"].nodes[0],
+                "A", "B", "C", "D", "E", "F", "G", "H", "y")
+
+    verify_deps(c.syms["CHOICE_REF"].nodes[0], "CHOICE")
+
+    verify_deps(c.menus[0], "A", "B", "C", "D")
+
+    verify_deps(c.comments[0], "A", "B")
+
+    verify_deps(c.syms["MULTI_DEF_SYM"], "A", "B", "C", "y")
+    verify_deps(c.named_choices["MULTI_DEF_CHOICE"], "A", "B", "C")
+
+
+    print("Testing split_expr()")
+
+    c = Kconfig("Kconfiglib/tests/empty")
+    c.warn = False
+
+    def verify_split(to_split, op, operand_strs):
+        # The same hackage as in Kconfig.eval_string()
+        c._tokens = c._tokenize("if " + to_split)[1:]
+        c._tokens_i = 0
+
+        operands = split_expr(c._parse_expr(False), op)
+
+        verify(len(operands) == len(operand_strs),
+               "Wrong number of operands when {} was split by {}"
+               .format(to_split, "OR" if op == OR else "AND"))
+
+        for operand, operand_str in zip(operands, operand_strs):
+            verify_equal(expr_str(operand), operand_str)
+
+    verify_split("A",                    OR, ("A",                ))
+    verify_split("!A",                   OR, ("!A",               ))
+    verify_split("A = B",                OR, ("A = B",            ))
+    verify_split("A && B",               OR, ("A && B",           ))
+    verify_split("A || B",               OR, ("A", "B"            ))
+    verify_split("(A || B) || C",        OR, ("A", "B", "C"       ))
+    verify_split("A || (B || C)",        OR, ("A", "B", "C"       ))
+    verify_split("A || !(B || C)",       OR, ("A", "!(B || C)"    ))
+    verify_split("A || (B && (C || D))", OR, ("A", "B && (C || D)"))
+    verify_split("(A && (B || C)) || D", OR, ("A && (B || C)", "D"))
+
+    verify_split("A",                    AND, ("A",                ))
+    verify_split("!A",                   AND, ("!A",               ))
+    verify_split("A = B",                AND, ("A = B",            ))
+    verify_split("A || B",               AND, ("A || B",           ))
+    verify_split("A && B",               AND, ("A", "B"            ))
+    verify_split("(A && B) && C",        AND, ("A", "B", "C"       ))
+    verify_split("A && (B && C)",        AND, ("A", "B", "C"       ))
+    verify_split("A && !(B && C)",       AND, ("A", "!(B && C)"    ))
+    verify_split("A && (B || (C && D))", AND, ("A", "B || (C && D)"))
+    verify_split("(A || (B && C)) && D", AND, ("A || (B && C)", "D"))
+
+
+    print("Testing visibility")
+
+    c = Kconfig("Kconfiglib/tests/Kvisibility")
+
+    def verify_visibility(item, no_module_vis, module_vis):
+        c.modules.set_value(0)
+        verify(item.visibility == no_module_vis,
+               "expected {} to have visibility {} without modules, had "
+               "visibility {}".
+               format(repr(item), no_module_vis, item.visibility))
+
+        c.modules.set_value(2)
+        verify(item.visibility == module_vis,
+               "expected {} to have visibility {} with modules, had "
+               "visibility {}".
+               format(repr(item), module_vis, item.visibility))
+
+    # Symbol visibility
+
+    verify_visibility(c.syms["NO_PROMPT"],     0, 0)
+    verify_visibility(c.syms["BOOL_N"],        0, 0)
+    verify_visibility(c.syms["BOOL_M"],        0, 2)
+    verify_visibility(c.syms["BOOL_MOD"],      2, 2)
+    verify_visibility(c.syms["BOOL_Y"],        2, 2)
+    verify_visibility(c.syms["TRISTATE_M"],    0, 1)
+    verify_visibility(c.syms["TRISTATE_MOD"],  2, 1)
+    verify_visibility(c.syms["TRISTATE_Y"],    2, 2)
+    verify_visibility(c.syms["BOOL_IF_N"],     0, 0)
+    verify_visibility(c.syms["BOOL_IF_M"],     0, 2)
+    verify_visibility(c.syms["BOOL_IF_Y"],     2, 2)
+    verify_visibility(c.syms["BOOL_MENU_N"],   0, 0)
+    verify_visibility(c.syms["BOOL_MENU_M"],   0, 2)
+    verify_visibility(c.syms["BOOL_MENU_Y"],   2, 2)
+    verify_visibility(c.syms["BOOL_CHOICE_N"], 0, 0)
+
+    # Non-tristate symbols in tristate choices are only visible if the choice
+    # is in y mode
+
+    # The choice can't be brought to y mode because of the 'if m'
+    verify_visibility(c.syms["BOOL_CHOICE_M"], 0, 0)
+    c.syms["BOOL_CHOICE_M"].choice.set_value(2)
+    verify_visibility(c.syms["BOOL_CHOICE_M"], 0, 0)
+
+    # The choice gets y mode only when running without modules, because it
+    # defaults to m mode
+    verify_visibility(c.syms["BOOL_CHOICE_Y"], 2, 0)
+    c.syms["BOOL_CHOICE_Y"].choice.set_value(2)
+    # When set to y mode, the choice symbol becomes visible both with and
+    # without modules
+    verify_visibility(c.syms["BOOL_CHOICE_Y"], 2, 2)
+
+    verify_visibility(c.syms["TRISTATE_IF_N"],     0, 0)
+    verify_visibility(c.syms["TRISTATE_IF_M"],     0, 1)
+    verify_visibility(c.syms["TRISTATE_IF_Y"],     2, 2)
+    verify_visibility(c.syms["TRISTATE_MENU_N"],   0, 0)
+    verify_visibility(c.syms["TRISTATE_MENU_M"],   0, 1)
+    verify_visibility(c.syms["TRISTATE_MENU_Y"],   2, 2)
+    verify_visibility(c.syms["TRISTATE_CHOICE_N"], 0, 0)
+    verify_visibility(c.syms["TRISTATE_CHOICE_M"], 0, 1)
+    verify_visibility(c.syms["TRISTATE_CHOICE_Y"], 2, 2)
+
+    verify_visibility(c.named_choices["BOOL_CHOICE_N"],     0, 0)
+    verify_visibility(c.named_choices["BOOL_CHOICE_M"],     0, 2)
+    verify_visibility(c.named_choices["BOOL_CHOICE_Y"],     2, 2)
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_N"], 0, 0)
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_M"], 0, 1)
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_Y"], 2, 2)
+
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_IF_M_AND_Y"],   0, 1)
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_MENU_N_AND_Y"], 0, 0)
+
+    # Verify that 'visible if' visibility gets propagated to prompts
+
+    verify_visibility(c.syms["VISIBLE_IF_N"], 0, 0)
+    verify_visibility(c.syms["VISIBLE_IF_M"], 0, 1)
+    verify_visibility(c.syms["VISIBLE_IF_Y"], 2, 2)
+    verify_visibility(c.syms["VISIBLE_IF_M_2"], 0, 1)
+
+    # Verify that string/int/hex symbols with m visibility accept a user value
+
+    assign_and_verify("STRING_m", "foo bar")
+    assign_and_verify("INT_m", "123")
+    assign_and_verify("HEX_m", "0x123")
+
+
+    print("Testing .assignable")
+
+    c = Kconfig("Kconfiglib/tests/Kassignable")
+
+    def verify_assignable_imp(item, assignable_no_modules, assignable_modules):
+        # Verifies the assignable values for 'item', with and without modules.
+
+        for modules_val, assignable in (0, assignable_no_modules), \
+                                       (2, assignable_modules):
+
+            c.modules.set_value(modules_val)
+            module_msg = "without modules" if modules_val == 0 else \
+                         "with modules"
+
+            verify(item.assignable == assignable,
+                   "Incorrect assignable values for {} {}. Should be {}, "
+                   "was {}."
+                   .format(item.name, module_msg, assignable, item.assignable))
+
+            # Verify that the values can actually be assigned too
+
+            for val in item.assignable:
+                item.set_value(val)
+                verify(item.tri_value == val,
+                       "Unable to set {} to {} {}, even though it was in "
+                       ".assignable".format(item.name, val, module_msg))
+
+    def verify_assignable(sym_name, assignable_no_modules, assignable_modules):
+        verify_assignable_imp(c.syms[sym_name],
+                              assignable_no_modules,
+                              assignable_modules)
+
+    def verify_const_unassignable(sym_name):
+        verify_assignable_imp(c.const_syms[sym_name], (), ())
+
+    # Things that shouldn't be .assignable
+    verify_const_unassignable("n")
+    verify_const_unassignable("m")
+    verify_const_unassignable("y")
+    verify_const_unassignable("const")
+    verify_assignable("UNDEFINED", (), ())
+    verify_assignable("NO_PROMPT", (), ())
+    verify_assignable("STRING", (), ())
+    verify_assignable("INT", (), ())
+    verify_assignable("HEX", (), ())
+
+    # Non-selected symbols
+    verify_assignable("Y_VIS_BOOL", (0, 2), (0,    2))
+    verify_assignable("M_VIS_BOOL", (    ), (0,    2))  # Vis. promoted
+    verify_assignable("N_VIS_BOOL", (    ), (       ))
+    verify_assignable("Y_VIS_TRI",  (0, 2), (0, 1, 2))
+    verify_assignable("M_VIS_TRI",  (    ), (0, 1   ))
+    verify_assignable("N_VIS_TRI",  (    ), (       ))
+
+    # Symbols selected to y
+    verify_assignable("Y_SEL_Y_VIS_BOOL", (2,), (2,))
+    verify_assignable("Y_SEL_M_VIS_BOOL", (  ), (2,))  # Vis. promoted
+    verify_assignable("Y_SEL_N_VIS_BOOL", (  ), (  ))
+    verify_assignable("Y_SEL_Y_VIS_TRI",  (2,), (2,))
+    verify_assignable("Y_SEL_M_VIS_TRI",  (  ), (2,))
+    verify_assignable("Y_SEL_N_VIS_TRI",  (  ), (  ))
+
+    # Symbols selected to m
+    verify_assignable("M_SEL_Y_VIS_BOOL", (2,), (   2,))  # Value promoted
+    verify_assignable("M_SEL_M_VIS_BOOL", (  ), (   2,))  # Vis./value promoted
+    verify_assignable("M_SEL_N_VIS_BOOL", (  ), (     ))
+    verify_assignable("M_SEL_Y_VIS_TRI",  (2,), (1, 2 ))
+    verify_assignable("M_SEL_M_VIS_TRI",  (  ), (1,   ))
+    verify_assignable("M_SEL_N_VIS_TRI",  (  ), (     ))
+
+    # Symbols implied to y
+    verify_assignable("Y_IMP_Y_VIS_BOOL", (0, 2), (0, 2))
+    verify_assignable("Y_IMP_M_VIS_BOOL", (    ), (0, 2))  # Vis. promoted
+    verify_assignable("Y_IMP_N_VIS_BOOL", (    ), (    ))
+    verify_assignable("Y_IMP_Y_VIS_TRI",  (0, 2), (0, 2))  # m removed by imply
+    verify_assignable("Y_IMP_M_VIS_TRI",  (    ), (0, 2))  # m promoted to y by imply
+    verify_assignable("Y_IMP_N_VIS_TRI",  (    ), (    ))
+
+    # Symbols implied to m (never affects assignable values)
+    verify_assignable("M_IMP_Y_VIS_BOOL", (0, 2), (0,    2))
+    verify_assignable("M_IMP_M_VIS_BOOL", (    ), (0,    2))  # Vis. promoted
+    verify_assignable("M_IMP_N_VIS_BOOL", (    ), (       ))
+    verify_assignable("M_IMP_Y_VIS_TRI",  (0, 2), (0, 1, 2))
+    verify_assignable("M_IMP_M_VIS_TRI",  (    ), (0, 1   ))
+    verify_assignable("M_IMP_N_VIS_TRI",  (    ), (       ))
+
+    # Symbols in y-mode choice
+    verify_assignable("Y_CHOICE_BOOL",           (2,), (2,))
+    verify_assignable("Y_CHOICE_TRISTATE",       (2,), (2,))
+    verify_assignable("Y_CHOICE_N_VIS_TRISTATE", (  ), (  ))
+
+    # Symbols in m/y-mode choice, starting out in m mode, or y mode when
+    # running without modules
+    verify_assignable("MY_CHOICE_BOOL",           (2,), (    ))
+    verify_assignable("MY_CHOICE_TRISTATE",       (2,), (0, 1))
+    verify_assignable("MY_CHOICE_N_VIS_TRISTATE", (  ), (    ))
+
+    c.named_choices["MY_CHOICE"].set_value(2)
+
+    # Symbols in m/y-mode choice, now in y mode
+    verify_assignable("MY_CHOICE_BOOL",           (2,), (2,))
+    verify_assignable("MY_CHOICE_TRISTATE",       (2,), (2,))
+    verify_assignable("MY_CHOICE_N_VIS_TRISTATE", (  ), (  ))
+
+    def verify_choice_assignable(choice_name, assignable_no_modules,
+                                 assignable_modules):
+        verify_assignable_imp(c.named_choices[choice_name],
+                              assignable_no_modules,
+                              assignable_modules)
+
+    # Choices with various possible modes
+    verify_choice_assignable("Y_CHOICE",   (2,  ), (      2,))
+    verify_choice_assignable("MY_CHOICE",  (2,  ), (   1, 2 ))
+    verify_choice_assignable("NMY_CHOICE", (0, 2), (0, 1, 2 ))
+    verify_choice_assignable("NY_CHOICE",  (0, 2), (0,    2 ))
+    verify_choice_assignable("NM_CHOICE",  (    ), (0, 1    ))
+    verify_choice_assignable("M_CHOICE",   (    ), (   1,   ))
+    verify_choice_assignable("N_CHOICE",   (    ), (        ))
+
+
+    print("Testing object relations")
+
+    c = Kconfig("Kconfiglib/tests/Krelation")
+
+    verify(c.syms["A"].nodes[0].parent is c.top_node,
+           "A's parent should be the top node")
+
+    verify(c.syms["B"].nodes[0].parent.item is c.named_choices["CHOICE_1"],
+           "B's parent should be the first choice")
+
+    verify(c.syms["C"].nodes[0].parent.item is c.syms["B"],
+           "C's parent should be B (due to auto menus)")
+
+    verify(c.syms["E"].nodes[0].parent.item == MENU,
+           "E's parent should be a menu")
+
+    verify(c.syms["E"].nodes[0].parent.parent is c.top_node,
+           "E's grandparent should be the top node")
+
+    verify(c.syms["G"].nodes[0].parent.item is c.named_choices["CHOICE_2"],
+           "G's parent should be the second choice")
+
+    verify(c.syms["G"].nodes[0].parent.parent.item == MENU,
+           "G's grandparent should be a menu")
+
+
+    print("Testing hex/int ranges")
+
+    c = Kconfig("Kconfiglib/tests/Krange", warn=False)
+
+    for sym_name in "HEX_NO_RANGE", "INT_NO_RANGE", "HEX_40", "INT_40":
+        sym = c.syms[sym_name]
+        verify(not sym.ranges,
+               "{} should not have ranges".format(sym_name))
+
+    for sym_name in "HEX_ALL_RANGES_DISABLED", "INT_ALL_RANGES_DISABLED", \
+                    "HEX_RANGE_10_20_LOW_DEFAULT", \
+                    "INT_RANGE_10_20_LOW_DEFAULT":
+        sym = c.syms[sym_name]
+        verify(sym.ranges, "{} should have ranges".format(sym_name))
+
+    # hex/int symbols without defaults should get no default value
+    verify_value("HEX_NO_RANGE", "")
+    verify_value("INT_NO_RANGE", "")
+    # And neither if all ranges are disabled
+    verify_value("HEX_ALL_RANGES_DISABLED", "")
+    verify_value("INT_ALL_RANGES_DISABLED", "")
+    # Make sure they are assignable though, and test that the form of the user
+    # value is reflected in the value for hex symbols
+    assign_and_verify("HEX_NO_RANGE", "0x123")
+    assign_and_verify("HEX_NO_RANGE", "123")
+    assign_and_verify("INT_NO_RANGE", "123")
+
+    # Defaults outside of the valid range should be clamped
+    verify_value("HEX_RANGE_10_20_LOW_DEFAULT", "0x10")
+    verify_value("HEX_RANGE_10_20_HIGH_DEFAULT", "0x20")
+    verify_value("INT_RANGE_10_20_LOW_DEFAULT", "10")
+    verify_value("INT_RANGE_10_20_HIGH_DEFAULT", "20")
+    # Defaults inside the valid range should be preserved. For hex symbols,
+    # they should additionally use the same form as in the assignment.
+    verify_value("HEX_RANGE_10_20_OK_DEFAULT", "0x15")
+    verify_value("HEX_RANGE_10_20_OK_DEFAULT_ALTERNATE", "15")
+    verify_value("INT_RANGE_10_20_OK_DEFAULT", "15")
+
+    # hex/int symbols with no defaults but valid ranges should default to the
+    # lower end of the range if it's > 0
+    verify_value("HEX_RANGE_10_20", "0x10")
+    verify_value("HEX_RANGE_0_10", "")
+    verify_value("INT_RANGE_10_20", "10")
+    verify_value("INT_RANGE_0_10", "")
+    verify_value("INT_RANGE_NEG_10_10", "")
+
+    # User values and dependent ranges
+
+    # Avoid warnings for assigning values outside the active range
+    c.warn = False
+
+    def verify_range(sym_name, low, high, default):
+        # Verifies that all values in the range 'low'-'high' can be assigned,
+        # and that assigning values outside the range reverts the value back to
+        # 'default' (None if it should revert back to "").
+
+        is_hex = (c.syms[sym_name].type == HEX)
+
+        for i in range(low, high + 1):
+            assign_and_verify_user_value(sym_name, str(i), str(i), True)
+            if is_hex:
+                # The form of the user value should be preserved for hex
+                # symbols
+                assign_and_verify_user_value(sym_name, hex(i), hex(i), True)
+
+        # Verify that assigning a user value just outside the range causes
+        # defaults to be used
+
+        if default is None:
+            default_str = ""
+        else:
+            default_str = hex(default) if is_hex else str(default)
+
+        if is_hex:
+            too_low_str = hex(low - 1)
+            too_high_str = hex(high + 1)
+        else:
+            too_low_str = str(low - 1)
+            too_high_str = str(high + 1)
+
+        assign_and_verify_value(sym_name, too_low_str, default_str)
+        assign_and_verify_value(sym_name, too_high_str, default_str)
+
+    verify_range("HEX_RANGE_10_20_LOW_DEFAULT",  0x10, 0x20,  0x10)
+    verify_range("HEX_RANGE_10_20_HIGH_DEFAULT", 0x10, 0x20,  0x20)
+    verify_range("HEX_RANGE_10_20_OK_DEFAULT",   0x10, 0x20,  0x15)
+
+    verify_range("INT_RANGE_10_20_LOW_DEFAULT",  10,   20,    10)
+    verify_range("INT_RANGE_10_20_HIGH_DEFAULT", 10,   20,    20)
+    verify_range("INT_RANGE_10_20_OK_DEFAULT",   10,   20,    15)
+
+    verify_range("HEX_RANGE_10_20",              0x10, 0x20,  0x10)
+
+    verify_range("INT_RANGE_10_20",              10,  20,     10)
+    verify_range("INT_RANGE_0_10",               0,   10,     None)
+    verify_range("INT_RANGE_NEG_10_10",          -10, 10,     None)
+
+    # Dependent ranges
+
+    verify_value("HEX_40", "40")
+    verify_value("INT_40", "40")
+
+    c.syms["HEX_RANGE_10_20"].unset_value()
+    c.syms["INT_RANGE_10_20"].unset_value()
+    verify_value("HEX_RANGE_10_40_DEPENDENT", "0x10")
+    verify_value("INT_RANGE_10_40_DEPENDENT", "10")
+    c.syms["HEX_RANGE_10_20"].set_value("15")
+    c.syms["INT_RANGE_10_20"].set_value("15")
+    verify_value("HEX_RANGE_10_40_DEPENDENT", "0x15")
+    verify_value("INT_RANGE_10_40_DEPENDENT", "15")
+    c.unset_values()
+    verify_range("HEX_RANGE_10_40_DEPENDENT", 0x10, 0x40,  0x10)
+    verify_range("INT_RANGE_10_40_DEPENDENT", 10,   40,    10)
+
+    # Ranges and symbols defined in multiple locations
+
+    verify_value("INACTIVE_RANGE", "2")
+    verify_value("ACTIVE_RANGE", "1")
+
+
+    print("Testing defconfig_filename")
+
+    c = Kconfig("Kconfiglib/tests/empty")
+    verify(c.defconfig_filename is None,
+           "defconfig_filename should be None with no defconfig_list symbol")
+
+    c = Kconfig("Kconfiglib/tests/Kdefconfig_nonexistent")
+    verify(c.defconfig_filename is None,
+           "defconfig_filename should be None when none of the files in the "
+           "defconfig_list symbol exist")
+
+    # Referenced in Kdefconfig_existent(_but_n)
+    os.environ["FOO"] = "defconfig_2"
+
+    c = Kconfig("Kconfiglib/tests/Kdefconfig_existent_but_n")
+    verify(c.defconfig_filename is None,
+           "defconfig_filename should be None when the condition is n for all "
+           "the defaults")
+
+    c = Kconfig("Kconfiglib/tests/Kdefconfig_existent")
+    verify(c.defconfig_filename == "Kconfiglib/tests/defconfig_2",
+           "defconfig_filename should return the existing file "
+           "Kconfiglib/tests/defconfig_2")
+
+    # Should also look relative to $srctree if the specified defconfig is a
+    # relative path and can't be opened
+
+    c = Kconfig("Kconfiglib/tests/Kdefconfig_srctree")
+    verify(c.defconfig_filename == "Kconfiglib/tests/defconfig_2",
+           "defconfig_filename gave wrong file with $srctree unset")
+
+    os.environ["srctree"] = "Kconfiglib/tests"
+    c = Kconfig("Kdefconfig_srctree")
+    verify(c.defconfig_filename == "Kconfiglib/tests/sub/defconfig_in_sub",
+           "defconfig_filename gave wrong file with $srctree set")
+
+    os.environ.pop("srctree", None)
+
+
+    print("Testing mainmenu_text")
+
+    c = Kconfig("Kconfiglib/tests/empty")
+    verify(c.mainmenu_text == "Main menu",
+           "An empty Kconfig should get a default main menu prompt")
+
+    # Expanded in the mainmenu text
+    os.environ["FOO"] = "bar baz"
+    c = Kconfig("Kconfiglib/tests/Kmainmenu")
+    verify(c.mainmenu_text == "---bar baz---",
+           "Wrong mainmenu text")
+
+
+    print("Testing user_value")
+
+    # References undefined env. var. Disable warnings.
+    c = Kconfig("Kconfiglib/tests/Kmisc", warn=False)
+
+    # Avoid warnings from assigning invalid user values and assigning user
+    # values to symbols without prompts
+    c.warn = False
+
+    syms = [c.syms[name] for name in
+            ("BOOL", "TRISTATE", "STRING", "INT", "HEX")]
+
+    for sym in syms:
+        verify(sym.user_value is None,
+               "{} should not have a user value to begin with")
+
+    # Assign valid values for the types
+
+    assign_and_verify_user_value("BOOL", 0, 0, True)
+    assign_and_verify_user_value("BOOL", 2, 2, True)
+    assign_and_verify_user_value("TRISTATE", 0, 0, True)
+    assign_and_verify_user_value("TRISTATE", 1, 1, True)
+    assign_and_verify_user_value("TRISTATE", 2, 2, True)
+    assign_and_verify_user_value("STRING", "foo bar", "foo bar", True)
+    assign_and_verify_user_value("INT", "123", "123", True)
+    assign_and_verify_user_value("HEX", "0x123", "0x123", True)
+
+    # Assign invalid values for the types. They should retain their old user
+    # value.
+
+    assign_and_verify_user_value("BOOL", 1, 2, False)
+    assign_and_verify_user_value("BOOL", "foo", 2, False)
+    assign_and_verify_user_value("BOOL", "1", 2, False)
+    assign_and_verify_user_value("TRISTATE", "foo", 2, False)
+    assign_and_verify_user_value("TRISTATE", "1", 2, False)
+    assign_and_verify_user_value("STRING", 0, "foo bar", False)
+    assign_and_verify_user_value("INT", "foo", "123", False)
+    assign_and_verify_user_value("INT", 0, "123", False)
+    assign_and_verify_user_value("HEX", "foo", "0x123", False)
+    assign_and_verify_user_value("HEX", 0, "0x123", False)
+    assign_and_verify_user_value("HEX", "-0x1", "0x123", False)
+
+    for s in syms:
+        s.unset_value()
+        verify(s.user_value is None,
+               "{} should not have a user value after being reset".
+               format(s.name))
+
+
+    print("Testing is_menuconfig")
+
+    c = Kconfig("Kconfiglib/tests/Kmenuconfig")
+
+    for not_menuconfig in c.syms["NOT_MENUCONFIG_1"].nodes[0], \
+                          c.syms["NOT_MENUCONFIG_2"].nodes[0], \
+                          c.syms["MENUCONFIG_MULTI_DEF"].nodes[0], \
+                          c.syms["COMMENT_HOOK"].nodes[0].next:
+
+        verify(not not_menuconfig.is_menuconfig,
+               "'{}' should have is_menuconfig False".format(not_menuconfig))
+
+    for menuconfig in c.top_node, \
+                      c.syms["MENUCONFIG_1"].nodes[0], \
+                      c.syms["MENUCONFIG_MULTI_DEF"].nodes[1], \
+                      c.syms["MENU_HOOK"].nodes[0].next, \
+                      c.syms["CHOICE_HOOK"].nodes[0].next:
+
+        verify(menuconfig.is_menuconfig,
+               "'{}' should have is_menuconfig True".format(menuconfig))
+
+
+    print("Testing 'option env' semantics")
+
+    os.environ["ENV_VAR"] = "ENV_VAR value"
+
+    # References undefined env. var., so disable warnings
+    c = Kconfig("Kconfiglib/tests/Kmisc", warn=False)
+
+    # Verify that 'option env' is treated like a default
+    verify_value("FROM_ENV", "ENV_VAR value")
+    verify_value("FROM_ENV_MISSING", "missing")
+
+    verify_value("FROM_ENV_WEIRD", "weird")
+
+
+    print("Testing defined vs undefined symbols")
+
+    for name in "A", "B", "C", "D", "BOOL", "TRISTATE", "STRING", "INT", "HEX":
+        verify(c.syms[name].nodes,
+               "{} should be defined".format(name))
+
+    for name in "NOT_DEFINED_1", "NOT_DEFINED_2", "NOT_DEFINED_3", \
+                "NOT_DEFINED_4":
+        sym = c.syms[name]
+        verify(not c.syms[name].nodes,
+               "{} should not be defined".format(name))
+
+
+    print("Testing Symbol.choice")
+
+    for name in "A", "B", "C", "D":
+        verify(c.syms[name].choice is not None,
+               "{} should be a choice symbol".format(name))
+
+    for name in "Q1", "Q2", "Q3", "BOOL", "TRISTATE", "STRING", "INT", "HEX", \
+                "FROM_ENV", "FROM_ENV_MISSING", "NOT_DEFINED_1", \
+                "NOT_DEFINED_2", "NOT_DEFINED_3", "NOT_DEFINED_4":
+        verify(c.syms[name].choice is None,
+               "{} should not be a choice symbol".format(name))
+
+
+    print("Testing is_allnoconfig_y")
+
+    verify(not c.syms["NOT_ALLNOCONFIG_Y"].is_allnoconfig_y,
+           "NOT_ALLNOCONFIG_Y should not be allnoconfig_y")
+    verify(c.syms["ALLNOCONFIG_Y"].is_allnoconfig_y,
+           "ALLNOCONFIG_Y should be allnoconfig_y")
+
+
+    print("Testing .config reading and writing")
+
+    config_test_file = "Kconfiglib/tests/config_test"
+
+    def verify_file_contents(fname, contents):
+        with open(fname, "r") as f:
+            file_contents = f.read()
+            verify(file_contents == contents,
+                   "{} contains '{}'. Expected '{}'."
+                   .format(fname, file_contents, contents))
+
+    # Writing/reading strings with characters that need to be escaped
+
+    c = Kconfig("Kconfiglib/tests/Kescape")
+
+    # Test the default value
+    c.write_config(config_test_file + "_from_def")
+    verify_file_contents(config_test_file + "_from_def",
+                         r'''CONFIG_STRING="\"\\"''' "\n")
+    # Write our own value
+    c.syms["STRING"].set_value(r'''\"a'\\''')
+    c.write_config(config_test_file + "_from_user")
+    verify_file_contents(config_test_file + "_from_user",
+                         r'''CONFIG_STRING="\\\"a'\\\\"''' "\n")
+
+    # Read back the two configs and verify the respective values
+    c.load_config(config_test_file + "_from_def")
+    verify_value("STRING", '"\\')
+    c.load_config(config_test_file + "_from_user")
+    verify_value("STRING", r'''\"a'\\''')
+
+    # Appending values from a .config
+
+    c = Kconfig("Kconfiglib/tests/Kappend")
+
+    # Values before assigning
+    verify_value("BOOL", "n")
+    verify_value("STRING", "")
+
+    # Assign BOOL
+    c.load_config("Kconfiglib/tests/config_set_bool", replace=False)
+    verify_value("BOOL", "y")
+    verify_value("STRING", "")
+
+    # Assign STRING
+    c.load_config("Kconfiglib/tests/config_set_string", replace=False)
+    verify_value("BOOL", "y")
+    verify_value("STRING", "foo bar")
+
+    # Reset BOOL
+    c.load_config("Kconfiglib/tests/config_set_string")
+    verify_value("BOOL", "n")
+    verify_value("STRING", "foo bar")
+
+    # Loading a completely empty .config should reset values
+    c.load_config("Kconfiglib/tests/empty")
+    verify_value("STRING", "")
+
+    # An indented assignment in a .config should be ignored
+    c.load_config("Kconfiglib/tests/config_indented")
+    verify_value("IGNOREME", "y")
+
+    # Symbol order in headers and minimal configuration files should match
+    # definition order, like in .config files
+
+    c = Kconfig("Kconfiglib/tests/Korder")
+
+    c.write_autoconf(config_test_file)
+    verify_file_contents(config_test_file, """
+#define CONFIG_O 0
+#define CONFIG_R 1
+#define CONFIG_D 2
+#define CONFIG_E 3
+#define CONFIG_R2 4
+#define CONFIG_I 5
+#define CONFIG_N 6
+#define CONFIG_G 7
+"""[1:])
+
+    # Differs from defaults
+    c.syms["O"].set_value("-1")
+    c.syms["R"].set_value("-1")
+    c.syms["E"].set_value("-1")
+    c.syms["R2"].set_value("-1")
+    c.syms["N"].set_value("-1")
+    c.syms["G"].set_value("-1")
+    c.write_min_config(config_test_file)
+    verify_file_contents(config_test_file, """
+CONFIG_O=-1
+CONFIG_R=-1
+CONFIG_E=-1
+CONFIG_R2=-1
+CONFIG_N=-1
+CONFIG_G=-1
+"""[1:])
+
+    # Test header strings in configuration files and headers
+
+    os.environ["KCONFIG_CONFIG_HEADER"] = "config header from env.\n"
+    os.environ["KCONFIG_AUTOHEADER_HEADER"] = "header header from env.\n"
+
+    c = Kconfig("Kconfiglib/tests/Kheader")
+    c.write_config(config_test_file, header="config header from param\n")
+    verify_file_contents(config_test_file, """\
+config header from param
+CONFIG_FOO=y
+""")
+    c.write_min_config(config_test_file, header="min. config header from param\n")
+    verify_file_contents(config_test_file, """\
+min. config header from param
+""")
+    c.write_config(config_test_file)
+    verify_file_contents(config_test_file, """\
+config header from env.
+CONFIG_FOO=y
+""")
+    c.write_min_config(config_test_file)
+    verify_file_contents(config_test_file, """\
+config header from env.
+""")
+    c.write_autoconf(config_test_file, header="header header from param\n")
+    verify_file_contents(config_test_file, """\
+header header from param
+#define CONFIG_FOO 1
+""")
+    c.write_autoconf(config_test_file)
+    verify_file_contents(config_test_file, """\
+header header from env.
+#define CONFIG_FOO 1
+""")
+
+    del os.environ["KCONFIG_CONFIG_HEADER"]
+    del os.environ["KCONFIG_AUTOHEADER_HEADER"]
+
+
+    print("Testing Kconfig fetching and separation")
+
+    for c in Kconfig("Kconfiglib/tests/Kmisc", warn=False), \
+             Kconfig("Kconfiglib/tests/Kmisc", warn=False):
+        for item in c.syms["BOOL"], \
+                    c.syms["BOOL"].nodes[0], \
+                    c.named_choices["OPTIONAL"], \
+                    c.named_choices["OPTIONAL"].nodes[0], \
+                    c.syms["MENU_HOOK"].nodes[0].next, \
+                    c.syms["COMMENT_HOOK"].nodes[0].next:
+            verify(item.kconfig is c,
+                   ".kconfig not properly set for " + repr(item))
+
+
+    print("Testing imply semantics")
+
+    c = Kconfig("Kconfiglib/tests/Kimply")
+
+    verify_value("IMPLY_DIRECT_DEPS", "y")
+    verify_value("UNMET_DIRECT_1", "n")
+    verify_value("UNMET_DIRECT_2", "n")
+    verify_value("UNMET_DIRECT_3", "n")
+    verify_value("MET_DIRECT_1", "y")
+    verify_value("MET_DIRECT_2", "y")
+    verify_value("MET_DIRECT_3", "y")
+    verify_value("MET_DIRECT_4", "y")
+
+    verify_value("IMPLY_COND", "y")
+    verify_value("IMPLIED_N_COND", "n")
+    verify_value("IMPLIED_M_COND", "m")
+    verify_value("IMPLIED_Y_COND", "y")
+
+    verify_value("IMPLY_N_1", "n")
+    verify_value("IMPLY_N_2", "n")
+    verify_value("IMPLIED_FROM_N_1", "n")
+    verify_value("IMPLIED_FROM_N_2", "n")
+
+    verify_value("IMPLY_M", "m")
+    verify_value("IMPLIED_M", "m")
+    verify_value("IMPLIED_M_BOOL", "y")
+
+    verify_value("IMPLY_M_TO_Y", "y")
+    verify_value("IMPLIED_M_TO_Y", "y")
+
+    # Test user value semantics
+
+    # Verify that IMPLIED_TRISTATE is invalidated if the direct
+    # dependencies change
+
+    assign_and_verify("IMPLY", 2)
+    assign_and_verify("DIRECT_DEP", 2)
+    verify_value("IMPLIED_TRISTATE", 2)
+    assign_and_verify("DIRECT_DEP", 0)
+    verify_value("IMPLIED_TRISTATE", 0)
+    # Set back for later tests
+    assign_and_verify("DIRECT_DEP", 2)
+
+    # Verify that IMPLIED_TRISTATE can be set to anything when IMPLY has value
+    # n, and that it gets the value n by default (for non-imply-related
+    # reasons)
+
+    assign_and_verify("IMPLY", 0)
+    assign_and_verify("IMPLIED_TRISTATE", 0)
+    assign_and_verify("IMPLIED_TRISTATE", 1)
+    assign_and_verify("IMPLIED_TRISTATE", 2)
+    c.syms["IMPLIED_TRISTATE"].unset_value()
+    verify_value("IMPLIED_TRISTATE", "n")
+
+    # Same as above for m. Anything still goes, but m by default now.
+
+    assign_and_verify("IMPLY", 1)
+    assign_and_verify("IMPLIED_TRISTATE", 0)
+    assign_and_verify("IMPLIED_TRISTATE", 1)
+    assign_and_verify("IMPLIED_TRISTATE", 2)
+    c.syms["IMPLIED_TRISTATE"].unset_value()
+    verify_value("IMPLIED_TRISTATE", 1)
+
+    # Same as above for y. Only n and y should be accepted. m gets promoted to
+    # y. Default should be y.
+
+    assign_and_verify("IMPLY", 2)
+    assign_and_verify("IMPLIED_TRISTATE", 0)
+    assign_and_verify_value("IMPLIED_TRISTATE", 1, 2)
+    assign_and_verify("IMPLIED_TRISTATE", 2)
+    c.syms["IMPLIED_TRISTATE"].unset_value()
+    verify_value("IMPLIED_TRISTATE", 2)
+
+    # Being implied to either m or y should give a bool the value y
+
+    c.syms["IMPLY"].unset_value()
+    verify_value("IMPLIED_BOOL", 0)
+    assign_and_verify("IMPLY", 0)
+    verify_value("IMPLIED_BOOL", 0)
+    assign_and_verify("IMPLY", 1)
+    verify_value("IMPLIED_BOOL", 2)
+    assign_and_verify("IMPLY", 2)
+    verify_value("IMPLIED_BOOL", 2)
+
+    # A bool implied to m or y can take the values n and y
+
+    c.syms["IMPLY"].set_value(1)
+    assign_and_verify("IMPLIED_BOOL", 0)
+    assign_and_verify("IMPLIED_BOOL", 2)
+
+    c.syms["IMPLY"].set_value(2)
+    assign_and_verify("IMPLIED_BOOL", 0)
+    assign_and_verify("IMPLIED_BOOL", 2)
+
+
+    print("Testing choice semantics")
+
+    # Would warn for choice value symbols defined without a type, even
+    # though the type is automatically derived. This is probably more
+    # helpful than ignoring those cases, as this feature isn't used
+    # deliberately anywhere from what I've seen.
+    c = Kconfig("Kconfiglib/tests/Kchoice", warn=False)
+
+    for name in "BOOL", "BOOL_OPT", "BOOL_M", "DEFAULTS":
+        verify(c.named_choices[name].orig_type == BOOL,
+               "choice {} should have type bool".format(name))
+
+    for name in "TRISTATE", "TRISTATE_OPT", "TRISTATE_M":
+        verify(c.named_choices[name].orig_type == TRISTATE,
+               "choice {} should have type tristate".format(name))
+
+    def select_and_verify(sym):
+        choice = sym.nodes[0].parent.item
+        choice.set_value(2)
+
+        sym.set_value(2)
+
+        verify(sym.choice.selection is sym,
+               sym.name + " should be the selected symbol")
+
+        verify(choice.user_selection is sym,
+               sym.name + " should be the user selection of the choice")
+
+        verify(sym.tri_value == 2,
+               sym.name + " should have value y when selected")
+
+        verify(sym.user_value == 2,
+               sym.name + " should have user value y when selected")
+
+        for sibling in choice.syms:
+            if sibling is not sym:
+                verify(sibling.tri_value == 0,
+                       sibling.name + " should be n when not selected")
+
+    def select_and_verify_all(choice_name):
+        choice = c.named_choices[choice_name]
+
+        # Select in forward order
+        for sym in choice.syms:
+            select_and_verify(sym)
+
+        # Select in reverse order
+        for sym in reversed(choice.syms):
+            select_and_verify(sym)
+
+    def verify_mode(choice_name, no_modules_mode, modules_mode):
+        choice = c.named_choices[choice_name]
+
+        c.modules.set_value(0)
+        verify(choice.tri_value == no_modules_mode,
+               'Wrong mode for choice {} with no modules. Expected {}, got {}.'
+               .format(choice.name, no_modules_mode, choice.tri_value))
+
+        c.modules.set_value(2)
+        verify(choice.tri_value == modules_mode,
+               'Wrong mode for choice {} with modules. Expected {}, got {}.'
+               .format(choice.name, modules_mode, choice.tri_value))
+
+    verify_mode("BOOL",         2, 2)
+    verify_mode("BOOL_OPT",     0, 0)
+    verify_mode("TRISTATE",     2, 1)
+    verify_mode("TRISTATE_OPT", 0, 0)
+    verify_mode("BOOL_M",       0, 2)
+    verify_mode("TRISTATE_M",   0, 1)
+
+    # Test defaults
+
+    choice = c.named_choices["DEFAULTS"]
+
+    c.syms["TRISTATE_SYM"].set_value(0)
+    verify(choice.selection is c.syms["OPT_4"],
+           "Wrong choice default with TRISTATE_SYM = n")
+
+    c.syms["TRISTATE_SYM"].set_value(2)
+    verify(choice.selection is c.syms["OPT_2"],
+           "Wrong choice default with TRISTATE_SYM = y")
+
+    c.syms["OPT_1"].set_value(2)
+    verify(choice.selection is c.syms["OPT_1"],
+           "User selection should override defaults")
+
+    verify(c.named_choices["DEFAULTS_NOT_VISIBLE"].selection
+           is c.syms["OPT_8"],
+           "Non-visible choice symbols should cause the next default to be "
+           "considered")
+
+    # Test y mode selection
+
+    c.modules.set_value(2)
+
+    select_and_verify_all("BOOL")
+    select_and_verify_all("BOOL_OPT")
+    select_and_verify_all("TRISTATE")
+    select_and_verify_all("TRISTATE_OPT")
+    # For BOOL_M, the mode should have been promoted
+    select_and_verify_all("BOOL_M")
+
+    # Test m mode selection
+
+    c.named_choices["TRISTATE"].set_value(1)
+
+    verify(c.named_choices["TRISTATE"].tri_value == 1,
+           "TRISTATE choice should have mode m after explicit mode assignment")
+
+    assign_and_verify_value("T_1", 0, 0)
+    assign_and_verify_value("T_2", 0, 0)
+    assign_and_verify_value("T_1", 1, 1)
+    assign_and_verify_value("T_2", 1, 1)
+    assign_and_verify_value("T_1", 2, 1)
+    assign_and_verify_value("T_2", 2, 1)
+
+    # Switching to y mode should cause T_2 to become selected
+    c.named_choices["TRISTATE"].set_value(2)
+    verify_value("T_1", 0)
+    verify_value("T_2", 2)
+
+    # Verify that choices with no explicitly specified type get the type of the
+    # first contained symbol with a type
+
+    verify(c.named_choices["NO_TYPE_BOOL"].orig_type == BOOL,
+           "Expected first choice without explicit type to have type bool")
+
+    verify(c.named_choices["NO_TYPE_TRISTATE"].orig_type == TRISTATE,
+           "Expected second choice without explicit type to have type "
+           "tristate")
+
+    # Verify that symbols without a type in the choice get the type of the
+    # choice
+
+    for name in "MMT_1", "MMT_2", "MMT_4", "MMT_5":
+        verify(c.syms[name].orig_type == BOOL,
+               "Expected {} to get type bool".format(name))
+
+    verify(c.syms["MMT_3"].orig_type == TRISTATE,
+           "Expected MMT_3 to have type tristate")
+
+    # Verify that the default selection can change depending on the
+    # visibility of the choice symbols
+
+    default_with_dep_choice = c.named_choices["DEFAULT_WITH_DEP"]
+
+    verify(default_with_dep_choice.selection is c.syms["B"],
+           "Wrong choice default with unsatisfied deps on default")
+
+    c.syms["DEP"].set_value("y")
+
+    verify(default_with_dep_choice.selection is c.syms["A"],
+           "Wrong choice default with satisfied deps on default")
+
+    c.syms["DEP"].set_value("n")
+
+    verify(default_with_dep_choice.selection is c.syms["B"],
+           "Wrong choice default with unsatisfied deps on default (round two)")
+
+    # Verify that symbols in choices that depend on the preceding symbol aren't
+    # considered choice symbols
+
+    weird_choice = c.named_choices["WEIRD_SYMS"]
+
+    def verify_is_normal_choice_symbol(name):
+        sym = c.syms[name]
+        verify(sym.choice is not None and
+               sym in weird_choice.syms and
+               sym.nodes[0].parent.item is weird_choice,
+               "{} should be a normal choice symbol".format(sym.name))
+
+    def verify_is_weird_choice_symbol(name):
+        sym = c.syms[name]
+        verify(sym.choice is None and
+               sym not in weird_choice.syms,
+               "{} should be a weird (non-)choice symbol"
+               .format(sym.name))
+
+    verify_is_normal_choice_symbol("WS1")
+    verify_is_weird_choice_symbol("WS2")
+    verify_is_weird_choice_symbol("WS3")
+    verify_is_weird_choice_symbol("WS4")
+    verify_is_weird_choice_symbol("WS5")
+    verify_is_normal_choice_symbol("WS6")
+    verify_is_weird_choice_symbol("WS7")
+    verify_is_weird_choice_symbol("WS8")
+    verify_is_normal_choice_symbol("WS9")
+
+
+    print("Testing 'if' node removal")
+
+    c = Kconfig("Kconfiglib/tests/Kifremoval", warn=False)
+
+    nodes = tuple(c.node_iter())
+    verify_equal(nodes[0].item.name, "A")
+    verify_equal(nodes[1].item.name, "B")
+    verify_equal(nodes[2].item.name, "C")
+    verify_equal(nodes[3].item.name, "D")
+    verify_equal(nodes[4].prompt[0], "E")
+    verify_equal(nodes[5].prompt[0], "F")
+    verify_equal(nodes[6].prompt[0], "G")
+    verify_equal(nodes[7].item.name, "H")
+    verify_equal(nodes[8].item.name, "I")
+    verify_equal(nodes[9].item.name, "J")
+    verify(len(nodes) == 10,
+           "Wrong number of nodes after 'if' removal")
+
+
+    print("Testing multi.def. property copying")
+
+    c = Kconfig("Kconfiglib/tests/Kdepcopy", warn=False)
+
+    def verify_props(desc, props, prop_names):
+        actual = [prop[0].name for prop in props]
+        expected = prop_names.split()
+
+        verify(actual == expected,
+               "Wrong {} properties, expected '{}', got '{}'"
+               .format(desc, expected, actual))
+
+    verify_props("default", c.syms["MULTIDEF"].defaults,
+                 "A B C D E F G H I J K L M N O P Q R")
+
+    verify_props("select", c.syms["MULTIDEF"].selects,
+                 "AA BB CC DD EE FF GG HH II JJ")
+
+    verify_props("imply", c.syms["MULTIDEF"].selects,
+                 "AA BB CC DD EE FF GG HH II JJ")
+
+    verify_props("select", c.syms["MULTIDEF_CHOICE"].selects,
+                 "A B C")
+
+    verify_props("range", c.syms["MULTIDEF_RANGE"].ranges,
+                 "A B C D E F")
+
+    verify_props("default", c.choices[1].defaults,
+                 "A B C D E")
+
+
+    print("Testing dependency loop detection")
+
+    # These are all expected to raise dependency loop errors
+    for i in range(11):
+        filename = "Kconfiglib/tests/Kdeploop" + str(i)
+        try:
+            Kconfig(filename)
+        except KconfigError as e:
+            if "Dependency loop" not in str(e):
+                fail("dependency loop in {} raised wrong KconfigError"
+                     .format(filename))
+        except:
+            fail("dependency loop in {} raised wrong exception"
+                 .format(filename))
+        else:
+            fail("dependency loop in {} not detected".format(filename))
+
+    # Check the most complicated message completely
+    try:
+        Kconfig("Kconfiglib/tests/Kdeploop10")
+    except KconfigError as e:
+        verify_equal(str(e), """
+Dependency loop
+===============
+
+A (defined at Kconfiglib/tests/Kdeploop10:1), with definition...
+
+config A
+	bool
+	depends on B
+
+...depends on B (defined at Kconfiglib/tests/Kdeploop10:5), with definition...
+
+config B
+	bool
+	depends on C = 7
+
+...depends on C (defined at Kconfiglib/tests/Kdeploop10:9), with definition...
+
+config C
+	int
+	range D 8
+
+...depends on D (defined at Kconfiglib/tests/Kdeploop10:13), with definition...
+
+config D
+	int
+	default 3 if E
+	default 8
+
+...depends on E (defined at Kconfiglib/tests/Kdeploop10:18), with definition...
+
+config E
+	bool
+
+(select-related dependencies: F && G)
+
+...depends on G (defined at Kconfiglib/tests/Kdeploop10:25), with definition...
+
+config G
+	bool
+	depends on H
+
+...depends on the choice symbol H (defined at Kconfiglib/tests/Kdeploop10:32), with definition...
+
+config H
+	bool "H"
+	depends on I && <choice>
+
+...depends on the choice symbol I (defined at Kconfiglib/tests/Kdeploop10:41), with definition...
+
+config I
+	bool "I"
+	depends on <choice>
+
+...depends on <choice> (defined at Kconfiglib/tests/Kdeploop10:38), with definition...
+
+choice
+	bool "choice" if J
+
+...depends on J (defined at Kconfiglib/tests/Kdeploop10:46), with definition...
+
+config J
+	bool
+	depends on A
+
+...depends again on A (defined at Kconfiglib/tests/Kdeploop10:1)
+"""[:-1])
+    except:
+        fail("Loop detection message check raised wrong exception")
+    else:
+        fail("Loop detection message check did not raise exception")
+
+
+    print("Testing preprocessor")
+
+    os.environ["ENV_1"] = "env_1"
+    os.environ["ENV_2"] = "env_2"
+    os.environ["ENV_3"] = "env_3"
+    os.environ["ENV_4"] = "env_4"
+    os.environ["ENV_5"] = "n"
+    os.environ["ENV_6"] = "Kconfiglib/tests/empty"
+    os.environ["ENV_7"] = "env_7"
+    # We verify warnings manually
+    c = Kconfig("Kconfiglib/tests/Kpreprocess", warn_to_stderr=False)
+
+    def verify_variable(name, unexp_value, exp_value, recursive, *args):
+        var = c.variables[name]
+
+        verify(var.value == unexp_value,
+               "expected variable '{}' to have the unexpanded value '{}', had "
+               "the value '{}'".format(name, unexp_value, var.value))
+
+        if not args:
+            verify(var.expanded_value == exp_value,
+                   "expected expanded_value for {} to be '{}', was '{}'"
+                   .format(name, exp_value, var.expanded_value))
+
+        verify(var.expanded_value_w_args(*args) == exp_value,
+               "expected expanded_value_w_args() for '{}' to be '{}', was '{}'"
+               .format(name, exp_value, var.expanded_value_w_args(*args)))
+
+        verify(var.is_recursive == recursive,
+               "{} was {}, shouldn't be"
+               .format(name, "recursive" if var.is_recursive else "simple"))
+
+    verify_variable("simple-recursive", "foo", "foo", True)
+    verify_variable("simple-immediate", "bar", "bar", False)
+    verify_variable("simple-recursive-2", "baz", "baz", True)
+
+    verify_variable("whitespaced", "foo", "foo", True)
+
+    verify_variable("preserve-recursive", "foo bar", "foo bar", True)
+    verify_variable("preserve-immediate", "foo bar", "foo bar", False)
+
+    verify_variable("recursive",
+                    "$(foo) $(bar) $($(b-char)a$(z-char)) $(indir)",
+                    "abc def ghi jkl mno",
+                    True)
+
+    verify_variable("immediate", "foofoo", "foofoo", False)
+
+    verify_variable("messy-fn-res",
+                    "$($(fn-indir)-unused-arg, a  b (,) , c  d )",
+                    'surround-rev-quote " c  d " " a  b (,) " surround-rev-quote ',
+                    True)
+
+    verify_variable("special-chars-fn-res",
+                    "$(fn,$(comma)$(dollar)$(left-paren)foo$(right-paren))",
+                    '",$(foo)"',
+                    True)
+
+    verify_variable("quote", '"$(1)" "$(2)"', '"" ""', True)
+    verify_variable("quote", '"$(1)" "$(2)"', '"one" ""', True,
+                    "one")
+    verify_variable("quote", '"$(1)" "$(2)"', '"one" "two"', True,
+                    "one", "two")
+    verify_variable("quote", '"$(1)" "$(2)"', '"one" "two"', True,
+                    "one", "two", "three")
+
+    verify_str(c.syms["PRINT_ME"], r"""
+config PRINT_ME
+	string "env_1" if (FOO && BAR) || !BAZ || !QAZ
+	default "\"foo\"" if "foo \"bar\" baz" = ""
+""")
+
+    verify_str(c.syms["PRINT_ME_TOO"], r"""
+config PRINT_ME_TOO
+	bool "foo"
+	default FOOBARBAZQAZ if QAZ && QAZFOO && xxx
+""")
+
+    def verify_repr(name, s):
+        verify_equal(repr(c.variables[name]), s)
+
+    verify_repr(
+        "simple-immediate",
+        "<variable simple-immediate, immediate, value 'bar'>")
+
+    verify_repr(
+        "messy-fn-res",
+        "<variable messy-fn-res, recursive, value '$($(fn-indir)-unused-arg, a  b (,) , c  d )'>")
+
+    def verify_recursive(name):
+        try:
+            c.variables[name].expanded_value
+        except KconfigError:
+            pass
+        else:
+            fail("Expected '{}' expansion to flag recursive expansion, didn't"
+                 .format(name))
+
+    verify_recursive("rec-1")
+    # Indirectly verifies that it's not recursive
+    verify_variable("safe-fn-rec-res",
+                    "$(safe-fn-rec,safe-fn-rec-2)",
+                    "foo",
+                    True)
+    verify_recursive("unsafe-fn-rec")
+
+    verify_variable("foo-bar-baz", "$(rhs)", "value", True)
+
+    verify_variable("space-var-res", "$(foo bar)", "value", True)
+
+    verify_variable("shell-res",
+                    "$(shell,false && echo foo bar || echo baz qaz)",
+                    "baz qaz",
+                    True)
+
+    verify_variable("shell-stderr-res", "", "", False)
+
+    verify_variable("parens-res",
+                    "pre-$(shell,echo '(a,$(b-char),(c,d),e)')-post",
+                    "pre-(a,b,(c,d),e)-post",
+                    True)
+
+    verify_variable("location-res",
+                    "Kconfiglib/tests/Kpreprocess:129",
+                    "Kconfiglib/tests/Kpreprocess:129",
+                    False)
+
+    verify_variable("warning-res", "", "", False)
+    verify_variable("error-n-res", "", "", False)
+
+    try:
+        c.variables["error-y-res"].expanded_value
+    except KconfigError:
+        pass
+    else:
+        fail("expanding error-y-res didn't raise an exception")
+
+    # Check Kconfig.env_vars
+    verify_equal(c.env_vars,
+                 set(("ENV_1", "ENV_2", "ENV_3", "ENV_4", "ENV_5", "ENV_6")))
+
+    # Check that the expected warnings were generated
+    verify_equal(c.warnings, [
+        "Kconfiglib/tests/Kpreprocess:122: warning: 'echo message on stderr >&2' wrote to stderr: message on stderr",
+        "Kconfiglib/tests/Kpreprocess:134: warning: a warning"
+    ])
+
+
+    print("Testing user-defined preprocessor functions")
+
+    # Make Kconfiglib/tests/kconfigfunctions.py importable
+    sys.path.insert(0, "Kconfiglib/tests")
+
+    c = Kconfig("Kconfiglib/tests/Kuserfunctions")
+
+    verify_variable("add-zero",  "$(add)",          "0", True)
+    verify_variable("add-one",   "$(add,1)",        "1", True)
+    verify_variable("add-three", "$(add,1,-1,2,1)", "3", True)
+
+    verify_variable("one-one", "$(one,foo bar)", "onefoo barfoo bar", True)
+
+    verify_variable("one-or-more-one", "$(one-or-more,foo)", "foo + ", True)
+    verify_variable("one-or-more-three", "$(one-or-more,foo,bar,baz)",
+                    "foo + bar,baz", True)
+
+    verify_variable("location-1", "Kconfiglib/tests/Kuserfunctions:13",
+                    "Kconfiglib/tests/Kuserfunctions:13", False)
+    verify_variable("location-2", "Kconfiglib/tests/Kuserfunctions:14",
+                    "Kconfiglib/tests/Kuserfunctions:14", False)
+
+    def verify_bad_argno(name):
+        try:
+            c.variables[name].expanded_value
+        except KconfigError:
+            pass
+        else:
+            fail("Expected '{}' expansion to flag wrong number of arguments, "
+                 "didn't".format(name))
+
+    verify_bad_argno("one-zero")
+    verify_bad_argno("one-two")
+    verify_bad_argno("one-or-more-zero")
+
+    sys.path.pop(0)
+
+    # This test can fail on older Python 3.x versions, because they don't
+    # preserve dict insertion order during iteration. The output is still
+    # correct, just different.
+    if not (3, 0) <= sys.version_info <= (3, 5):
+        print("Testing KCONFIG_WARN_UNDEF")
+
+        os.environ["KCONFIG_WARN_UNDEF"] = "y"
+        c = Kconfig("Kconfiglib/tests/Kundef", warn_to_stderr=False)
+
+        verify_equal("\n".join(c.warnings), """
+warning: the int symbol INT (defined at Kconfiglib/tests/Kundef:8) has a non-int range [UNDEF_2 (undefined), 8 (undefined)]
+warning: undefined symbol UNDEF_1:
+
+- Referenced at Kconfiglib/tests/Kundef:4:
+
+config BOOL
+	bool "foo" if DEF || !UNDEF_1
+	default UNDEF_2
+
+- Referenced at Kconfiglib/tests/Kundef:19:
+
+menu "menu"
+	depends on UNDEF_1
+	visible if UNDEF_3
+warning: undefined symbol UNDEF_2:
+
+- Referenced at Kconfiglib/tests/Kundef:4:
+
+config BOOL
+	bool "foo" if DEF || !UNDEF_1
+	default UNDEF_2
+
+- Referenced at Kconfiglib/tests/Kundef:8:
+
+config INT
+	int
+	range UNDEF_2 8
+	range 5 15
+	default 10
+warning: undefined symbol UNDEF_3:
+
+- Referenced at Kconfiglib/tests/Kundef:19:
+
+menu "menu"
+	depends on UNDEF_1
+	visible if UNDEF_3
+"""[1:-1])
+
+        os.environ.pop("KCONFIG_WARN_UNDEF")
+
+
+    print("\nAll selftests passed\n" if all_passed else
+          "\nSome selftests failed\n")
+
+
+def run_compatibility_tests():
+    # Runs tests on configurations from the kernel. Tests compability with the
+    # C implementation by comparing outputs.
+
+    # Referenced inside the kernel Kconfig files.
+    #
+    # The str() makes the type of the value 'str' on both Python 2 and Python 3,
+    # which is nice for some later dictionary key sanity checks.
+
+    os.environ["KERNELVERSION"] = str(
+        subprocess.check_output("make kernelversion", shell=True)
+            .decode("utf-8").rstrip()
+    )
+
+    os.environ["CC_VERSION_TEXT"] = str(
+        subprocess.check_output("gcc --version | head -n1", shell=True)
+            .decode("utf-8").rstrip()
+    )
+
+    os.environ["srctree"] = "."
+    os.environ["CC"] = "gcc"
+    os.environ["LD"] = "ld"
+
+
+    if not os.path.exists("scripts/kconfig/conf"):
+        print("\nscripts/kconfig/conf does not exist -- running "
+              "'make allnoconfig' to build it...")
+        shell("make allnoconfig")
+
+
+    print("Running compatibility tests...\n")
+
+    test_fns = (test_defconfig,
+                # Fails for a few defconfigs due to a bug in the C tools. Will
+                # be enabled once patches get in.
+                #test_min_config,
+                test_alldefconfig,
+                test_allnoconfig,
+                test_allnoconfig_walk,
+                test_allmodconfig,
+                test_allyesconfig,
+                test_sanity)
+
+    for test_fn in test_fns:
+        # The test description is taken from the docstring of the corresponding
+        # function
+        print(textwrap.dedent(test_fn.__doc__))
+
+        for arch, srcarch in all_arch_srcarch():
+            # Referenced inside the Kconfig files
+            os.environ["ARCH"] = arch
+            os.environ["SRCARCH"] = srcarch
+
+            rm_configs()
+
+            test_fn(arch, srcarch)
+
+    if all_passed:
+        print("All selftests and compatibility tests passed")
+    else:
+        sys.exit("Some tests failed")
+
+
+def all_arch_srcarch():
+    for srcarch in os.listdir("arch"):
+        # arc and h8300 are currently broken with the C tools on linux-next as
+        # well. Perhaps they require cross-compilers to be installed.
+        #
+        # User-mode Linux has an unorthodox Kconfig setup that would require a
+        # different testing setup. Skip it too.
+        if srcarch in ("arc", "h8300", "um"):
+            continue
+
+        if os.path.exists(os.path.join("arch", srcarch, "Kconfig")):
+            yield (srcarch, srcarch)
+
+    # Some arches define additional ARCH settings with ARCH != SRCARCH
+    # (search for "Additional ARCH settings for" in the top-level Makefile)
+
+    yield ("i386", "x86")
+    yield ("x86_64", "x86")
+
+    yield ("sparc32", "sparc")
+    yield ("sparc64", "sparc")
+
+    yield ("sh64", "sh")
+
+
+def test_allnoconfig(arch, srcarch):
+    """
+    Verify that allnoconfig.py generates the same .config as
+    'make allnoconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/allnoconfig.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --allnoconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_allnoconfig_walk(arch, srcarch):
+    """
+    Verify that examples/allnoconfig_walk.py generates the same .config as
+    'make allnoconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/examples/allnoconfig_walk.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --allnoconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_allmodconfig(arch, srcarch):
+    """
+    Verify that allmodconfig.py generates the same .config as
+    'make allmodconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/allmodconfig.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --allmodconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_allyesconfig(arch, srcarch):
+    """
+    Verify that allyesconfig.py generates the same .config as
+    'make allyesconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/allyesconfig.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --allyesconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_sanity(arch, srcarch):
+    """
+    Do sanity checks on each configuration and call all public methods on all
+    symbols, choices, and menu nodes for all architectures to make sure we
+    never crash or hang.
+    """
+    print("For {}...".format(arch))
+
+    kconf = Kconfig()
+
+    for sym in kconf.defined_syms:
+        verify(sym._visited == 2,
+               "{} has broken dependency loop detection (_visited = {})"
+               .format(sym.name, sym._visited))
+
+    kconf.modules
+    kconf.defconfig_list
+    kconf.defconfig_filename
+
+    # Legacy warning functions
+    kconf.enable_redun_warnings()
+    kconf.disable_redun_warnings()
+    kconf.enable_undef_warnings()
+    kconf.disable_undef_warnings()
+    kconf.enable_warnings()
+    kconf.disable_warnings()
+    kconf.enable_stderr_warnings()
+    kconf.disable_stderr_warnings()
+
+    kconf.mainmenu_text
+    kconf.unset_values()
+
+    kconf.write_autoconf("/dev/null")
+
+    # No tempfile.TemporaryDirectory in Python 2
+    tmpdir = tempfile.mkdtemp()
+    kconf.sync_deps(os.path.join(tmpdir, "deps"))  # Create
+    kconf.sync_deps(os.path.join(tmpdir, "deps"))  # Update
+    shutil.rmtree(tmpdir)
+
+    # Python 2/3 compatible
+    for key, sym in kconf.syms.items():
+        verify(isinstance(key, str), "weird key '{}' in syms dict".format(key))
+
+        verify(not sym.is_constant, sym.name + " in 'syms' and constant")
+
+        verify(sym not in kconf.const_syms,
+               sym.name + " in both 'syms' and 'const_syms'")
+
+        for dep in sym._dependents:
+            verify(not dep.is_constant,
+                   "the constant symbol {} depends on {}"
+                   .format(dep.name, sym.name))
+
+        sym.__repr__()
+        sym.__str__()
+        sym.assignable
+        kconf.disable_warnings()
+        sym.set_value(2)
+        sym.set_value("foo")
+        sym.unset_value()
+        kconf.enable_warnings()  # Legacy warning function
+        sym.str_value
+        sym.tri_value
+        sym.type
+        sym.user_value
+        sym.visibility
+
+    for sym in kconf.defined_syms:
+        verify(sym.nodes, sym.name + " is defined but lacks menu nodes")
+
+        verify(not (sym.orig_type not in (BOOL, TRISTATE) and sym.choice),
+               sym.name + " is a choice symbol but not bool/tristate")
+
+    for key, sym in kconf.const_syms.items():
+        verify(isinstance(key, str),
+               "weird key '{}' in const_syms dict".format(key))
+
+        verify(sym.is_constant,
+               '"{}" is in const_syms but not marked constant'
+               .format(sym.name))
+
+        verify(not sym.nodes,
+               '"{}" is constant but has menu nodes'.format(sym.name))
+
+        verify(not sym._dependents,
+               '"{}" is constant but is a dependency of some symbol'
+               .format(sym.name))
+
+        verify(not sym.choice,
+               '"{}" is constant and a choice symbol'.format(sym.name))
+
+        sym.__repr__()
+        sym.__str__()
+        sym.assignable
+        kconf.disable_warnings()
+        sym.set_value(2)
+        sym.set_value("foo")
+        sym.unset_value()
+        kconf.enable_warnings()  # Legacy warning function
+        sym.str_value
+        sym.tri_value
+        sym.type
+        sym.visibility
+
+    for choice in kconf.choices:
+        for sym in choice.syms:
+            verify(sym.choice is choice,
+                   "{0} is in choice.syms but 'sym.choice' is not the choice"
+                   .format(sym.name))
+
+            verify(sym.type in (BOOL, TRISTATE),
+                   "{} is a choice symbol but is not a bool/tristate"
+                   .format(sym.name))
+
+        choice.__str__()
+        choice.__repr__()
+        choice.str_value
+        choice.tri_value
+        choice.user_value
+        choice.assignable
+        choice.selection
+        choice.type
+        choice.visibility
+
+    # Menu nodes
+
+    node = kconf.top_node
+
+    while 1:
+        # Everything else should be well exercised elsewhere
+        node.__repr__()
+        node.__str__()
+        verify(isinstance(node.item, (Symbol, Choice)) or \
+               node.item in (MENU, COMMENT),
+               "'{}' appeared as a menu item".format(node.item))
+
+        if node.list is not None:
+            node = node.list
+
+        elif node.next is not None:
+            node = node.next
+
+        else:
+            while node.parent is not None:
+                node = node.parent
+                if node.next is not None:
+                    node = node.next
+                    break
+            else:
+                break
+
+
+def test_alldefconfig(arch, srcarch):
+    """
+    Verify that alldefconfig.py generates the same .config as
+    'make alldefconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/alldefconfig.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --alldefconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_defconfig(arch, srcarch):
+    """
+    Verify that Kconfiglib generates the same .config as scripts/kconfig/conf,
+    for each architecture/defconfig pair. In obsessive mode, this test includes
+    nonsensical groupings of arches with defconfigs from other arches (every
+    arch/defconfig combination) and takes an order of magnitude longer time to
+    run.
+
+    With logging enabled, this test appends any failures to a file
+    test_defconfig_fails in the root.
+    """
+    kconf = Kconfig()
+
+    if obsessive:
+        defconfigs = []
+
+        # Collect all defconfigs. This could be done once instead, but it's
+        # a speedy operation comparatively.
+        for srcarch_ in os.listdir("arch"):
+            defconfigs.extend(defconfig_files(srcarch_))
+    else:
+        defconfigs = defconfig_files(srcarch)
+
+    # Test architecture for each defconfig
+
+    for defconfig in defconfigs:
+        rm_configs()
+
+        kconf.load_config(defconfig)
+        kconf.write_config("._config")
+        shell("scripts/kconfig/conf --defconfig='{}' Kconfig".
+              format(defconfig))
+
+        arch_defconfig_str = "  {:14}with {:60} ".format(arch, defconfig)
+
+        if equal_configs():
+            print(arch_defconfig_str + "OK")
+        else:
+            print(arch_defconfig_str + "FAIL")
+            fail()
+            if log:
+                with open("test_defconfig_fails", "a") as fail_log:
+                    fail_log.write("{} with {} did not match\n"
+                                   .format(arch, defconfig))
+
+
+def test_min_config(arch, srcarch):
+    """
+    Verify that Kconfiglib generates the same .config as 'make savedefconfig'
+    for each architecture/defconfig pair.
+    """
+    kconf = Kconfig()
+
+    if obsessive_min_config:
+        defconfigs = []
+        for srcarch_ in os.listdir("arch"):
+            defconfigs.extend(defconfig_files(srcarch_))
+    else:
+        defconfigs = defconfig_files(srcarch)
+
+    for defconfig in defconfigs:
+        rm_configs()
+
+        kconf.load_config(defconfig)
+        kconf.write_min_config("._config")
+
+        shell("cp {} .config".format(defconfig))
+
+        shell("scripts/kconfig/conf --savedefconfig=.config Kconfig")
+
+        arch_defconfig_str = "  {:14}with {:60} ".format(arch, defconfig)
+
+        if equal_configs():
+            print(arch_defconfig_str + "OK")
+        else:
+            print(arch_defconfig_str + "FAIL")
+
+
+#
+# Helper functions
+#
+
+
+def defconfig_files(srcarch):
+    # Yields a list of defconfig file filenames for a particular srcarch
+    # subdirectory (arch/<srcarch>/)
+
+    srcarch_dir = os.path.join("arch", srcarch)
+
+    # Some arches have a defconfig in the root of their arch/<arch>/ directory
+    root_defconfig = os.path.join(srcarch_dir, "defconfig")
+    if os.path.exists(root_defconfig):
+        yield root_defconfig
+
+    # Assume all files in the arch/<arch>/configs/ directory (if it exists) are
+    # configurations
+    defconfigs_dir = os.path.join(srcarch_dir, "configs")
+
+    if not os.path.isdir(defconfigs_dir):
+        return
+
+    for dirpath, _, filenames in os.walk(defconfigs_dir):
+        for filename in filenames:
+            yield os.path.join(dirpath, filename)
+
+
+def rm_configs():
+    # Delete any old ".config" (generated by the C implementation) and
+    # "._config" (generated by us), if present.
+
+    def rm_if_exists(f):
+        if os.path.exists(f):
+            os.remove(f)
+
+    rm_if_exists(".config")
+    rm_if_exists("._config")
+
+
+def compare_configs(arch):
+    if equal_configs():
+        print("{:14}OK".format(arch))
+    else:
+        print("{:14}FAIL".format(arch))
+        fail()
+
+
+def equal_configs():
+    with open(".config") as f:
+        their = f.readlines()
+
+    # Strip the header generated by 'conf'
+    i = 0
+    for line in their:
+        if not line.startswith("#") or \
+           re.match(r"# CONFIG_(\w+) is not set", line):
+            break
+        i += 1
+    their = their[i:]
+
+    try:
+        f = open("._config")
+    except EnvironmentError as e:
+        if e.errno != errno.ENOENT:
+            raise
+        print("._config not found. Did you forget to apply the Makefile patch?")
+        return False
+    else:
+        with f:
+            our = f.readlines()
+
+    if their == our:
+        return True
+
+    # Print a unified diff to help debugging
+    print("Mismatched .config's! Unified diff:")
+    sys.stdout.writelines(difflib.unified_diff(their, our, fromfile="their",
+                                               tofile="our"))
+
+    return False
+
+
+if __name__ == "__main__":
+    run_tests()
diff --git a/ext/drampower/SConscript b/ext/drampower/SConscript
index 0518197..870d050 100644
--- a/ext/drampower/SConscript
+++ b/ext/drampower/SConscript
@@ -39,9 +39,9 @@
 
 import os
 
-Import('main')
+Import('env')
 
-main.Prepend(CPPPATH=Dir('./src'))
+env.Prepend(CPPPATH=Dir('./src'))
 
 # Add the appropriate files for the library
 drampower_files = []
@@ -63,7 +63,7 @@
 DRAMPowerFile('CmdHandlers.cc')
 DRAMPowerFile('MemBankWiseParams.cc')
 
-main.Library('drampower', [main.SharedObject(f) for f in drampower_files])
+env.Library('drampower', [env.SharedObject(f) for f in drampower_files])
 
-main.Append(LIBS=['drampower'])
-main.Prepend(LIBPATH=[Dir('.')])
+env.Append(LIBS=['drampower'])
+env.Prepend(LIBPATH=[Dir('.')])
diff --git a/ext/dramsim2/SConscript b/ext/dramsim2/SConscript
index b4355fa..95b999d 100644
--- a/ext/dramsim2/SConscript
+++ b/ext/dramsim2/SConscript
@@ -38,16 +38,16 @@
 
 import os
 
-Import('main')
+Import('env')
 
 # See if we got a cloned DRAMSim2 repo as a subdirectory and set the
 # HAVE_DRAMSIM flag accordingly
 if not os.path.exists(Dir('.').srcnode().abspath + '/DRAMSim2'):
-    main['HAVE_DRAMSIM'] = False
+    env['HAVE_DRAMSIM'] = False
     Return()
 
 # We have got the folder, so add the library and build the wrappers
-main['HAVE_DRAMSIM'] = True
+env['HAVE_DRAMSIM'] = True
 
 # Add the appropriate files. We leave out the trace driven simulator
 dram_files = []
@@ -59,7 +59,7 @@
 DRAMFile('Bank.cpp')
 DRAMFile('BankState.cpp')
 DRAMFile('BusPacket.cpp')
-DRAMFile('ClockDomain.cpp')
+DRAMFile('ClockDoenv.cpp')
 DRAMFile('CommandQueue.cpp')
 DRAMFile('IniReader.cpp')
 DRAMFile('MemoryController.cpp')
@@ -71,11 +71,11 @@
 
 # DRAMSim2 violates some of the warning flags used by gem5, so
 # we explicitly disable them here
-dramenv = main.Clone()
+dramenv = env.Clone()
 dramenv.Append(CCFLAGS=['-Wno-unused-value', '-Wno-error=nonnull-compare'])
 
 # If we are using clang, there are more flags to disable
-if main['CLANG']:
+if env['CLANG']:
     dramenv.Append(CCFLAGS=['-Wno-unused-private-field',
                             '-Wno-tautological-undefined-compare'])
 
@@ -85,6 +85,6 @@
 
 dramenv.Library('dramsim2', [dramenv.SharedObject(f) for f in dram_files])
 
-main.Prepend(CPPPATH=Dir('.'))
-main.Append(LIBS=['dramsim2'])
-main.Prepend(LIBPATH=[Dir('.')])
+env.Prepend(CPPPATH=Dir('.'))
+env.Append(LIBS=['dramsim2'])
+env.Prepend(LIBPATH=[Dir('.')])
diff --git a/ext/dramsim3/SConscript b/ext/dramsim3/SConscript
index 9e5a3a1..b717816 100644
--- a/ext/dramsim3/SConscript
+++ b/ext/dramsim3/SConscript
@@ -38,32 +38,32 @@
 
 import os
 
-Import('main')
+Import('env')
 
 thermal = False
 
 # See if we got a cloned DRAMSim3 repo as a subdirectory and set the
 # HAVE_DRAMSIM flag accordingly
 if not os.path.exists(Dir('.').srcnode().abspath + '/DRAMsim3'):
-    main['HAVE_DRAMSIM3'] = False
+    env['HAVE_DRAMSIM3'] = False
     Return()
 
 # We have got the folder, so add the library and build the wrappers
-main['HAVE_DRAMSIM3'] = True
+env['HAVE_DRAMSIM3'] = True
 
 
 dramsim_path = os.path.join(Dir('#').abspath, 'ext/dramsim3/DRAMsim3/')
 
 if thermal:
     superlu_path = os.path.join(dramsim_path, 'ext/SuperLU_MT_3.1/lib')
-    main.Prepend(CPPPATH=Dir('.'))
-    main.Append(LIBS=['dramsim3', 'superlu_mt_OPENMP', 'm', 'f77blas',
+    env.Prepend(CPPPATH=Dir('.'))
+    env.Append(LIBS=['dramsim3', 'superlu_mt_OPENMP', 'm', 'f77blas',
                       'atlas', 'gomp'],
                 LIBPATH=[dramsim_path, superlu_path])
 else:
-    main.Prepend(CPPPATH=Dir('.'))
+    env.Prepend(CPPPATH=Dir('.'))
     # a littel hacky but can get a shared library working
-    main.Append(LIBS=['dramsim3', 'gomp'],
+    env.Append(LIBS=['dramsim3', 'gomp'],
                 LIBPATH=[dramsim_path],  # compile-time lookup
                 RPATH=[dramsim_path],  # runtime lookup
                 CPPPATH=[dramsim_path+'/src/'])
diff --git a/ext/fputils/SConscript b/ext/fputils/SConscript
index 6d3f1ff..6a8e44f 100644
--- a/ext/fputils/SConscript
+++ b/ext/fputils/SConscript
@@ -28,11 +28,11 @@
 #
 # Authors: Andreas Sandberg
 
-Import('main')
+Import('env')
 
-main.Prepend(CPPPATH=Dir('./include'))
+env.Prepend(CPPPATH=Dir('./include'))
 
-fpenv = main.Clone()
+fpenv = env.Clone()
 
 # By default gcc uses c89 and clang uses c99. For fputils to compile
 # we need to use c99.
@@ -44,6 +44,6 @@
         fpenv.SharedObject('fp80.c'),
     ])
 
-main.Append(LIBS=['fputils'])
-main.Prepend(LIBPATH=[Dir('.')])
+env.Append(LIBS=['fputils'])
+env.Prepend(LIBPATH=[Dir('.')])
 
diff --git a/ext/googletest/BUILD.bazel b/ext/googletest/BUILD.bazel
index 9b48aee..3c9a228 100644
--- a/ext/googletest/BUILD.bazel
+++ b/ext/googletest/BUILD.bazel
@@ -36,9 +36,24 @@
 
 licenses(["notice"])
 
+exports_files(["LICENSE"])
+
+config_setting(
+    name = "qnx",
+    constraint_values = ["@platforms//os:qnx"],
+)
+
 config_setting(
     name = "windows",
-    constraint_values = ["@bazel_tools//platforms:windows"],
+    constraint_values = ["@platforms//os:windows"],
+)
+
+config_setting(
+    name = "msvc_compiler",
+    flag_values = {
+        "@bazel_tools//tools/cpp:compiler": "msvc-cl",
+    },
+    visibility = [":__subpackages__"],
 )
 
 config_setting(
@@ -76,6 +91,7 @@
         "googlemock/include/gmock/*.h",
     ]),
     copts = select({
+        ":qnx": [],
         ":windows": [],
         "//conditions:default": ["-pthread"],
     }),
@@ -94,6 +110,7 @@
         "googletest/include",
     ],
     linkopts = select({
+        ":qnx": [],
         ":windows": [],
         "//conditions:default": ["-pthread"],
     }),
@@ -103,6 +120,7 @@
             "@com_google_absl//absl/debugging:stacktrace",
             "@com_google_absl//absl/debugging:symbolize",
             "@com_google_absl//absl/strings",
+            "@com_google_absl//absl/types:any",
             "@com_google_absl//absl/types:optional",
             "@com_google_absl//absl/types:variant",
         ],
diff --git a/ext/googletest/CMakeLists.txt b/ext/googletest/CMakeLists.txt
index f11bbb5..ea81ab1 100644
--- a/ext/googletest/CMakeLists.txt
+++ b/ext/googletest/CMakeLists.txt
@@ -1,21 +1,17 @@
 # Note: CMake support is community-based. The maintainers do not use CMake
 # internally.
 
-cmake_minimum_required(VERSION 2.8.8)
+cmake_minimum_required(VERSION 2.8.12)
 
 if (POLICY CMP0048)
   cmake_policy(SET CMP0048 NEW)
 endif (POLICY CMP0048)
 
 project(googletest-distribution)
-set(GOOGLETEST_VERSION 1.10.0)
+set(GOOGLETEST_VERSION 1.11.0)
 
-if (CMAKE_VERSION VERSION_LESS "3.1")
-  add_definitions(-std=c++11)
-else()
-  set(CMAKE_CXX_STANDARD 11)
-  set(CMAKE_CXX_STANDARD_REQUIRED ON)
-  if(NOT CYGWIN)
+if (CMAKE_VERSION VERSION_GREATER "3.0.2")
+  if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX)
     set(CMAKE_CXX_EXTENSIONS OFF)
   endif()
 endif()
diff --git a/ext/googletest/CONTRIBUTING.md b/ext/googletest/CONTRIBUTING.md
index 30c8d89..da45e44 100644
--- a/ext/googletest/CONTRIBUTING.md
+++ b/ext/googletest/CONTRIBUTING.md
@@ -28,7 +28,7 @@
 ## Contributing A Patch
 
 1.  Submit an issue describing your proposed change to the
-    [issue tracker](https://github.com/google/googletest).
+    [issue tracker](https://github.com/google/googletest/issues).
 2.  Please don't mix more than one logical change per submittal, because it
     makes the history hard to follow. If you want to make a change that doesn't
     have a corresponding issue in the issue tracker, please create one.
@@ -80,7 +80,7 @@
 will be expected to conform to the style outlined
 [here](https://google.github.io/styleguide/cppguide.html). Use
 [.clang-format](https://github.com/google/googletest/blob/master/.clang-format)
-to check your formatting
+to check your formatting.
 
 ## Requirements for Contributors
 
@@ -89,7 +89,7 @@
 
 *   [Python](https://www.python.org/) v2.3 or newer (for running some of the
     tests and re-generating certain source files from templates)
-*   [CMake](https://cmake.org/) v2.6.4 or newer
+*   [CMake](https://cmake.org/) v2.8.12 or newer
 
 ## Developing Google Test and Google Mock
 
@@ -128,15 +128,3 @@
     make test
 
 All tests should pass.
-
-### Regenerating Source Files
-
-Some of Google Test's source files are generated from templates (not in the C++
-sense) using a script. For example, the file
-include/gtest/internal/gtest-type-util.h.pump is used to generate
-gtest-type-util.h in the same directory.
-
-You don't need to worry about regenerating the source files unless you need to
-modify them. You would then modify the corresponding `.pump` files and run the
-'[pump.py](googletest/scripts/pump.py)' generator script. See the
-[Pump Manual](googletest/docs/pump_manual.md).
diff --git a/ext/googletest/googletest/CONTRIBUTORS b/ext/googletest/CONTRIBUTORS
similarity index 61%
rename from ext/googletest/googletest/CONTRIBUTORS
rename to ext/googletest/CONTRIBUTORS
index feae2fc..d9bc587 100644
--- a/ext/googletest/googletest/CONTRIBUTORS
+++ b/ext/googletest/CONTRIBUTORS
@@ -5,33 +5,60 @@
 
 Ajay Joshi <jaj@google.com>
 Balázs Dán <balazs.dan@gmail.com>
+Benoit Sigoure <tsuna@google.com>
 Bharat Mediratta <bharat@menalto.com>
+Bogdan Piloca <boo@google.com>
 Chandler Carruth <chandlerc@google.com>
 Chris Prince <cprince@google.com>
 Chris Taylor <taylorc@google.com>
 Dan Egnor <egnor@google.com>
+Dave MacLachlan <dmaclach@gmail.com>
+David Anderson <danderson@google.com>
+Dean Sturtevant
 Eric Roman <eroman@chromium.org>
+Gene Volovich <gv@cite.com>
 Hady Zalek <hady.zalek@gmail.com>
+Hal Burch <gmock@hburch.com>
 Jeffrey Yasskin <jyasskin@google.com>
+Jim Keller <jimkeller@google.com>
+Joe Walnes <joe@truemesh.com>
+Jon Wray <jwray@google.com>
 Jói Sigurðsson <joi@google.com>
 Keir Mierle <mierle@gmail.com>
 Keith Ray <keith.ray@gmail.com>
 Kenton Varda <kenton@google.com>
+Kostya Serebryany <kcc@google.com>
+Krystian Kuzniarek <krystian.kuzniarek@gmail.com>
+Lev Makhlis
 Manuel Klimek <klimek@google.com>
+Mario Tanev <radix@google.com>
+Mark Paskin
 Markus Heule <markus.heule@gmail.com>
+Martijn Vels <mvels@google.com>
+Matthew Simmons <simmonmt@acm.org>
 Mika Raento <mikie@iki.fi>
+Mike Bland <mbland@google.com>
 Miklós Fazekas <mfazekas@szemafor.com>
+Neal Norwitz <nnorwitz@gmail.com>
+Nermin Ozkiranartli <nermin@google.com>
+Owen Carlsen <ocarlsen@google.com>
+Paneendra Ba <paneendra@google.com>
 Pasi Valminen <pasi.valminen@gmail.com>
 Patrick Hanna <phanna@google.com>
 Patrick Riley <pfr@google.com>
+Paul Menage <menage@google.com>
 Peter Kaminski <piotrk@google.com>
+Piotr Kaminski <piotrk@google.com>
 Preston Jackson <preston.a.jackson@gmail.com>
 Rainer Klaffenboeck <rainer.klaffenboeck@dynatrace.com>
 Russ Cox <rsc@google.com>
 Russ Rufer <russ@pentad.com>
 Sean Mcafee <eefacm@gmail.com>
 Sigurður Ásgeirsson <siggi@google.com>
+Sverre Sundsdal <sundsdal@gmail.com>
+Takeshi Yoshino <tyoshino@google.com>
 Tracy Bialik <tracy@pentad.com>
 Vadim Berman <vadimb@google.com>
 Vlad Losev <vladl@google.com>
+Wolfgang Klier <wklier@google.com>
 Zhanyong Wan <wan@google.com>
diff --git a/ext/googletest/README.md b/ext/googletest/README.md
index 5b417fa..e207d38 100644
--- a/ext/googletest/README.md
+++ b/ext/googletest/README.md
@@ -1,48 +1,44 @@
-# Google Test
+# GoogleTest
 
-#### OSS Builds Status:
+### Announcements
 
-[![Build Status](https://api.travis-ci.org/google/googletest.svg?branch=master)](https://travis-ci.org/google/googletest)
-[![Build status](https://ci.appveyor.com/api/projects/status/4o38plt0xbo1ubc8/branch/master?svg=true)](https://ci.appveyor.com/project/GoogleTestAppVeyor/googletest/branch/master)
+#### Live at Head
 
-### Future Plans
+GoogleTest now follows the
+[Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support).
+We recommend using the latest commit in the `master` branch in your projects.
 
-#### 1.8.x Release:
+#### Documentation Updates
 
-[the 1.8.x](https://github.com/google/googletest/releases/tag/release-1.8.1) is
-the last release that works with pre-C++11 compilers. The 1.8.x will not accept
-any requests for any new features and any bugfix requests will only be accepted
-if proven "critical"
+Our documentation is now live on GitHub Pages at
+https://google.github.io/googletest/. We recommend browsing the documentation on
+GitHub Pages rather than directly in the repository.
 
-#### Post 1.8.x:
+#### Release 1.11.0
 
-On-going work to improve/cleanup/pay technical debt. When this work is completed
-there will be a 1.9.x tagged release
+[Release 1.11.0](https://github.com/google/googletest/releases/tag/release-1.11.0)
+is now available.
 
-#### Post 1.9.x
+#### Coming Soon
 
-Post 1.9.x googletest will follow
-[Abseil Live at Head philosophy](https://abseil.io/about/philosophy)
+*   We are planning to take a dependency on
+    [Abseil](https://github.com/abseil/abseil-cpp).
+*   More documentation improvements are planned.
 
-## Welcome to **Google Test**, Google's C++ test framework!
+## Welcome to **GoogleTest**, Google's C++ test framework!
 
 This repository is a merger of the formerly separate GoogleTest and GoogleMock
 projects. These were so closely related that it makes sense to maintain and
 release them together.
 
-Please subscribe to the mailing list at googletestframework@googlegroups.com for
-questions, discussions, and development.
+### Getting Started
 
-### Getting started:
+See the [GoogleTest User's Guide](https://google.github.io/googletest/) for
+documentation. We recommend starting with the
+[GoogleTest Primer](https://google.github.io/googletest/primer.html).
 
-The information for **Google Test** is available in the
-[Google Test Primer](googletest/docs/primer.md) documentation.
-
-**Google Mock** is an extension to Google Test for writing and using C++ mock
-classes. See the separate [Google Mock documentation](googlemock/README.md).
-
-More detailed documentation for googletest is in its interior
-[googletest/README.md](googletest/README.md) file.
+More information about building GoogleTest can be found at
+[googletest/README.md](googletest/README.md).
 
 ## Features
 
@@ -57,22 +53,45 @@
 *   Various options for running the tests.
 *   XML test report generation.
 
-## Platforms
+## Supported Platforms
 
-Google test has been used on a variety of platforms:
+GoogleTest requires a codebase and compiler compliant with the C++11 standard or
+newer.
+
+The GoogleTest code is officially supported on the following platforms.
+Operating systems or tools not listed below are community-supported. For
+community-supported platforms, patches that do not complicate the code may be
+considered.
+
+If you notice any problems on your platform, please file an issue on the
+[GoogleTest GitHub Issue Tracker](https://github.com/google/googletest/issues).
+Pull requests containing fixes are welcome!
+
+### Operating Systems
 
 *   Linux
-*   Mac OS X
+*   macOS
 *   Windows
-*   Cygwin
-*   MinGW
-*   Windows Mobile
-*   Symbian
-*   PlatformIO
 
-## Who Is Using Google Test?
+### Compilers
 
-In addition to many internal projects at Google, Google Test is also used by the
+*   gcc 5.0+
+*   clang 5.0+
+*   MSVC 2015+
+
+**macOS users:** Xcode 9.3+ provides clang 5.0+.
+
+### Build Systems
+
+*   [Bazel](https://bazel.build/)
+*   [CMake](https://cmake.org/)
+
+**Note:** Bazel is the build system used by the team internally and in tests.
+CMake is supported on a best-effort basis and by the community.
+
+## Who Is Using GoogleTest?
+
+In addition to many internal projects at Google, GoogleTest is also used by the
 following notable projects:
 
 *   The [Chromium projects](http://www.chromium.org/) (behind the Chrome browser
@@ -81,8 +100,6 @@
 *   [Protocol Buffers](https://github.com/google/protobuf), Google's data
     interchange format.
 *   The [OpenCV](http://opencv.org/) computer vision library.
-*   [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn): header only,
-    dependency-free deep learning framework in C++11.
 
 ## Related Open Source Projects
 
@@ -90,13 +107,13 @@
 automated test-runner and Graphical User Interface with powerful features for
 Windows and Linux platforms.
 
-[Google Test UI](https://github.com/ospector/gtest-gbar) is test runner that
+[GoogleTest UI](https://github.com/ospector/gtest-gbar) is a test runner that
 runs your test binary, allows you to track its progress via a progress bar, and
-displays a list of test failures. Clicking on one shows failure text. Google
-Test UI is written in C#.
+displays a list of test failures. Clicking on one shows failure text. GoogleTest
+UI is written in C#.
 
 [GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event
-listener for Google Test that implements the
+listener for GoogleTest that implements the
 [TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test
 result output. If your test runner understands TAP, you may find it useful.
 
@@ -104,31 +121,20 @@
 runs tests from your binary in parallel to provide significant speed-up.
 
 [GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter)
-is a VS Code extension allowing to view Google Tests in a tree view, and
-run/debug your tests.
+is a VS Code extension allowing to view GoogleTest in a tree view, and run/debug
+your tests.
 
-## Requirements
+[C++ TestMate](https://github.com/matepek/vscode-catch2-test-adapter) is a VS
+Code extension allowing to view GoogleTest in a tree view, and run/debug your
+tests.
 
-Google Test is designed to have fairly minimal requirements to build and use
-with your projects, but there are some. If you notice any problems on your
-platform, please notify
-[googletestframework@googlegroups.com](https://groups.google.com/forum/#!forum/googletestframework).
-Patches for fixing them are welcome!
+[Cornichon](https://pypi.org/project/cornichon/) is a small Gherkin DSL parser
+that generates stub code for GoogleTest.
 
-### Build Requirements
+## Contributing Changes
 
-These are the base requirements to build and use Google Test from a source
-package:
-
-*   [Bazel](https://bazel.build/) or [CMake](https://cmake.org/). NOTE: Bazel is
-    the build system that googletest is using internally and tests against.
-    CMake is community-supported.
-
-*   a C++11-standard-compliant compiler
-
-## Contributing change
-
-Please read the [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on how to
-contribute to this project.
+Please read
+[`CONTRIBUTING.md`](https://github.com/google/googletest/blob/master/CONTRIBUTING.md)
+for details on how to contribute to this project.
 
 Happy testing!
diff --git a/ext/googletest/SConscript b/ext/googletest/SConscript
index 5a4b691..21830f0 100644
--- a/ext/googletest/SConscript
+++ b/ext/googletest/SConscript
@@ -26,7 +26,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('main')
+Import('env')
 
 # The root of the build directory.
 build = Dir('.')
@@ -40,18 +40,18 @@
 gtest_src = Dir('googletest/src')
 gmock_src = Dir('googlemock/src')
 
-main.Append(CPPPATH=[gtest_include, gmock_include])
-main.Append(LIBPATH=[build])
+env.Append(CPPPATH=[gtest_include, gmock_include])
+env.Append(LIBPATH=[build])
 
-env = main.Clone(CCFLAGS=['-g', '-pthread', '-Wno-undef', '-isystem',
+genv = env.Clone(CCFLAGS=['-g', '-pthread', '-Wno-undef', '-isystem',
                           str(gtest_include), '-isystem', str(gmock_include)])
-env.Append(CPPPATH=[gtest_base, gmock_base])
+genv.Append(CPPPATH=[gtest_base, gmock_base])
 
-gtest_all = env.Object(gtest_src.File('gtest-all.cc'))
-gmock_all = env.Object(gmock_src.File('gmock-all.cc'))
-gtest_main = env.StaticLibrary(target='libgtest', source=[
+gtest_all = genv.Object(gtest_src.File('gtest-all.cc'))
+gmock_all = genv.Object(gmock_src.File('gmock-all.cc'))
+gtest_main = genv.StaticLibrary(target='libgtest', source=[
         gtest_all, gmock_all, gtest_src.File('gtest_main.cc')])
 
-main['GTEST_LIBS'] = ['libgtest', 'pthread']
-main['GTEST_CPPFLAGS'] = [
+env['GTEST_LIBS'] = ['libgtest', 'pthread']
+env['GTEST_CPPFLAGS'] = [
     '-pthread', '-DUSE_GMOCK', '-Wno-undef', '-isystem', gtest_include.abspath]
diff --git a/ext/googletest/WORKSPACE b/ext/googletest/WORKSPACE
index 2289bdb..614f557 100644
--- a/ext/googletest/WORKSPACE
+++ b/ext/googletest/WORKSPACE
@@ -2,22 +2,23 @@
 
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 
-# Abseil
 http_archive(
-     name = "com_google_absl",
-     urls = ["https://github.com/abseil/abseil-cpp/archive/master.zip"],
-     strip_prefix = "abseil-cpp-master",
+    name = "com_google_absl",
+    urls = ["https://github.com/abseil/abseil-cpp/archive/7971fb358ae376e016d2d4fc9327aad95659b25e.zip"],  # 2021-05-20T02:59:16Z
+    strip_prefix = "abseil-cpp-7971fb358ae376e016d2d4fc9327aad95659b25e",
+    sha256 = "aeba534f7307e36fe084b452299e49b97420667a8d28102cf9a0daeed340b859",
 )
 
 http_archive(
-    name = "rules_cc",
-    strip_prefix = "rules_cc-master",
-    urls = ["https://github.com/bazelbuild/rules_cc/archive/master.zip"],
+  name = "rules_cc",
+  urls = ["https://github.com/bazelbuild/rules_cc/archive/68cb652a71e7e7e2858c50593e5a9e3b94e5b9a9.zip"],  # 2021-05-14T14:51:14Z
+  strip_prefix = "rules_cc-68cb652a71e7e7e2858c50593e5a9e3b94e5b9a9",
+  sha256 = "1e19e9a3bc3d4ee91d7fcad00653485ee6c798efbbf9588d40b34cbfbded143d",
 )
 
 http_archive(
-    name = "rules_python",
-    strip_prefix = "rules_python-master",
-    urls = ["https://github.com/bazelbuild/rules_python/archive/master.zip"],
+  name = "rules_python",
+  urls = ["https://github.com/bazelbuild/rules_python/archive/ed6cc8f2c3692a6a7f013ff8bc185ba77eb9b4d2.zip"],  # 2021-05-17T00:24:16Z
+  strip_prefix = "rules_python-ed6cc8f2c3692a6a7f013ff8bc185ba77eb9b4d2",
+  sha256 = "98b3c592faea9636ac8444bfd9de7f3fb4c60590932d6e6ac5946e3f8dbd5ff6",
 )
-
diff --git a/ext/googletest/appveyor.yml b/ext/googletest/appveyor.yml
deleted file mode 100644
index a58b768..0000000
--- a/ext/googletest/appveyor.yml
+++ /dev/null
@@ -1,154 +0,0 @@
-version: '{build}'
-
-os: Visual Studio 2015
-
-environment:
-  matrix:
-    - compiler: msvc-15-seh
-      generator: "Visual Studio 15 2017"
-      build_system: cmake
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-
-    - compiler: msvc-15-seh
-      generator: "Visual Studio 15 2017 Win64"
-      build_system: cmake
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-      enabled_on_pr: yes
-
-    - compiler: msvc-15-seh
-      build_system: bazel
-      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-      enabled_on_pr: yes
-
-    - compiler: msvc-14-seh
-      build_system: cmake
-      generator: "Visual Studio 14 2015"
-      enabled_on_pr: yes
-
-    - compiler: msvc-14-seh
-      build_system: cmake
-      generator: "Visual Studio 14 2015 Win64"
-
-    - compiler: gcc-6.3.0-posix
-      build_system: cmake
-      generator: "MinGW Makefiles"
-      cxx_path: 'C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin'
-      enabled_on_pr: yes
-
-configuration:
-  - Debug
-
-build:
-  verbosity: minimal
-
-install:
-- ps: |
-    Write-Output "Compiler: $env:compiler"
-    Write-Output "Generator: $env:generator"
-    Write-Output "Env:Configuation: $env:configuration"
-    Write-Output "Env: $env"
-    if (-not (Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER)) {
-      Write-Output "This is *NOT* a pull request build"
-    } else {
-      Write-Output "This is a pull request build"
-      if (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes") {
-        Write-Output "PR builds are *NOT* explicitly enabled"
-      }
-    }
-
-    # install Bazel
-    if ($env:build_system -eq "bazel") {
-        appveyor DownloadFile https://github.com/bazelbuild/bazel/releases/download/0.28.1/bazel-0.28.1-windows-x86_64.exe -FileName bazel.exe
-    }
-
-    if ($env:build_system -eq "cmake") {
-        # git bash conflicts with MinGW makefiles
-        if ($env:generator -eq "MinGW Makefiles") {
-            $env:path = $env:path.replace("C:\Program Files\Git\usr\bin;", "")
-            if ($env:cxx_path -ne "") {
-                $env:path += ";$env:cxx_path"
-            }
-        }
-    }
-
-before_build:
-- ps: |
-     $env:root=$env:APPVEYOR_BUILD_FOLDER
-     Write-Output "env:root: $env:root"
-
-build_script:
-- ps: |
-    # Only enable some builds for pull requests, the AppVeyor queue is too long.
-    if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) {
-      return
-    } else {
-        # special case - build with Bazel
-        if ($env:build_system -eq "bazel") {
-            & $env:root\bazel.exe build -c opt //:gtest_samples
-            if ($LastExitCode -eq 0) { # bazel writes to StdErr and PowerShell interprets it as an error
-                $host.SetShouldExit(0)
-            } else { # a real error
-                throw "Exec: $ErrorMessage"
-            }
-            return
-        }
-    }
-    # by default build with CMake
-    md _build -Force | Out-Null
-    cd _build
-
-    $conf = if ($env:generator -eq "MinGW Makefiles") {"-DCMAKE_BUILD_TYPE=$env:configuration"} else {"-DCMAKE_CONFIGURATION_TYPES=Debug;Release"}
-    # Disable test for MinGW (gtest tests fail, gmock tests can not build)
-    $gtest_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgtest_build_tests=OFF"} else {"-Dgtest_build_tests=ON"}
-    $gmock_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgmock_build_tests=OFF"} else {"-Dgmock_build_tests=ON"}
-    & cmake -G "$env:generator" $conf -Dgtest_build_samples=ON $gtest_build_tests $gmock_build_tests ..
-    if ($LastExitCode -ne 0) {
-        throw "Exec: $ErrorMessage"
-    }
-    $cmake_parallel = if ($env:generator -eq "MinGW Makefiles") {"-j2"} else  {"/m"}
-    & cmake --build . --config $env:configuration -- $cmake_parallel
-    if ($LastExitCode -ne 0) {
-        throw "Exec: $ErrorMessage"
-    }
-
-
-skip_commits:
-  files:
-    - '**/*.md'
-
-test_script:
-- ps: |
-    # Only enable some builds for pull requests, the AppVeyor queue is too long.
-    if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) {
-      return
-    }
-    if ($env:build_system -eq "bazel") {
-        # special case - testing with Bazel
-        & $env:root\bazel.exe test //:gtest_samples
-        if ($LastExitCode -eq 0) { # bazel writes to StdErr and PowerShell interprets it as an error
-            $host.SetShouldExit(0)
-        } else { # a real error
-            throw "Exec: $ErrorMessage"
-        }
-    }
-    if ($env:build_system -eq "cmake") {
-        # built with CMake - test with CTest
-        if ($env:generator -eq "MinGW Makefiles") {
-            return # No test available for MinGW
-        }
-
-        & ctest -C $env:configuration --timeout 600 --output-on-failure
-        if ($LastExitCode -ne 0) {
-            throw "Exec: $ErrorMessage"
-        }
-    }
-
-artifacts:
-  - path: '_build/CMakeFiles/*.log'
-    name: logs
-  - path: '_build/Testing/**/*.xml'
-    name: test_results
-  - path: 'bazel-testlogs/**/test.log'
-    name: test_logs
-  - path: 'bazel-testlogs/**/test.xml'
-    name: test_results
diff --git a/ext/googletest/ci/build-linux-bazel.sh b/ext/googletest/ci/build-linux-bazel.sh
deleted file mode 100755
index ae8fb75..0000000
--- a/ext/googletest/ci/build-linux-bazel.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2017 Google Inc.
-# All Rights Reserved.
-#
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -e
-
-bazel version
-bazel build --curses=no //...:all
-bazel test --curses=no //...:all
-bazel test --curses=no //...:all --define absl=1
diff --git a/ext/googletest/ci/build-platformio.sh b/ext/googletest/ci/build-platformio.sh
deleted file mode 100644
index 1d7658d..0000000
--- a/ext/googletest/ci/build-platformio.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-# run PlatformIO builds
-platformio run
diff --git a/ext/googletest/ci/env-linux.sh b/ext/googletest/ci/env-linux.sh
deleted file mode 100755
index 37800d6..0000000
--- a/ext/googletest/ci/env-linux.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2017 Google Inc.
-# All Rights Reserved.
-#
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#
-# This file should be sourced, and not executed as a standalone script.
-#
-
-# TODO() - we can check if this is being sourced using $BASH_VERSION and $BASH_SOURCE[0] != ${0}.
-
-if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
-    if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
-    if [ "$CXX" = "clang++" ]; then export CXX="clang++-3.9" CC="clang-3.9"; fi
-fi
diff --git a/ext/googletest/ci/env-osx.sh b/ext/googletest/ci/env-osx.sh
deleted file mode 100755
index 9c421e1..0000000
--- a/ext/googletest/ci/env-osx.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2017 Google Inc.
-# All Rights Reserved.
-#
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#
-# This file should be sourced, and not executed as a standalone script.
-#
-
-# TODO() - we can check if this is being sourced using $BASH_VERSION and $BASH_SOURCE[0] != ${0}.
-#
-
-if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
-    if [ "$CXX" = "clang++" ]; then
-        # $PATH needs to be adjusted because the llvm tap doesn't install the
-        # package to /usr/local/bin, etc, like the gcc tap does.
-        # See: https://github.com/Homebrew/legacy-homebrew/issues/29733
-        clang_version=3.9
-        export PATH="/usr/local/opt/llvm@${clang_version}/bin:$PATH";
-    fi
-fi
diff --git a/ext/googletest/ci/install-linux.sh b/ext/googletest/ci/install-linux.sh
deleted file mode 100755
index 05e2cb2..0000000
--- a/ext/googletest/ci/install-linux.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2017 Google Inc.
-# All Rights Reserved.
-#
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -eu
-
-if [ "${TRAVIS_OS_NAME}" != linux ]; then
-    echo "Not a Linux build; skipping installation"
-    exit 0
-fi
-
-
-if [ "${TRAVIS_SUDO}" = "true" ]; then
-    echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | \
-        sudo tee /etc/apt/sources.list.d/bazel.list
-    curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
-    sudo apt-get update && sudo apt-get install -y bazel gcc-4.9 g++-4.9 clang-3.9
-elif [ "${CXX}" = "clang++" ]; then
-    # Use ccache, assuming $HOME/bin is in the path, which is true in the Travis build environment.
-    ln -sf /usr/bin/ccache $HOME/bin/${CXX};
-    ln -sf /usr/bin/ccache $HOME/bin/${CC};
-fi
diff --git a/ext/googletest/ci/install-osx.sh b/ext/googletest/ci/install-osx.sh
deleted file mode 100755
index cc47508..0000000
--- a/ext/googletest/ci/install-osx.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2017 Google Inc.
-# All Rights Reserved.
-#
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -eu
-
-if [ "${TRAVIS_OS_NAME}" != "osx" ]; then
-    echo "Not a macOS build; skipping installation"
-    exit 0
-fi
-
-brew update
-brew install ccache gcc@4.9
diff --git a/ext/googletest/ci/install-platformio.sh b/ext/googletest/ci/install-platformio.sh
deleted file mode 100644
index 4d7860a..0000000
--- a/ext/googletest/ci/install-platformio.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-# install PlatformIO
-sudo pip install -U platformio
-
-# update PlatformIO
-platformio update
diff --git a/ext/googletest/ci/linux-presubmit.sh b/ext/googletest/ci/linux-presubmit.sh
new file mode 100644
index 0000000..6bea1cd
--- /dev/null
+++ b/ext/googletest/ci/linux-presubmit.sh
@@ -0,0 +1,126 @@
+#!/bin/bash
+#
+# Copyright 2020, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -euox pipefail
+
+readonly LINUX_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210525"
+readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20201015"
+
+if [[ -z ${GTEST_ROOT:-} ]]; then
+  GTEST_ROOT="$(realpath $(dirname ${0})/..)"
+fi
+
+if [[ -z ${STD:-} ]]; then
+  STD="c++11 c++14 c++17 c++20"
+fi
+
+# Test the CMake build
+for cc in /usr/local/bin/gcc /opt/llvm/clang/bin/clang; do
+  for cmake_off_on in OFF ON; do
+    time docker run \
+      --volume="${GTEST_ROOT}:/src:ro" \
+      --tmpfs="/build:exec" \
+      --workdir="/build" \
+      --rm \
+      --env="CC=${cc}" \
+      --env="CXX_FLAGS=\"-Werror -Wdeprecated\"" \
+      ${LINUX_LATEST_CONTAINER} \
+      /bin/bash -c "
+        cmake /src \
+          -DCMAKE_CXX_STANDARD=11 \
+          -Dgtest_build_samples=ON \
+          -Dgtest_build_tests=ON \
+          -Dgmock_build_tests=ON \
+          -Dcxx_no_exception=${cmake_off_on} \
+          -Dcxx_no_rtti=${cmake_off_on} && \
+        make -j$(nproc) && \
+        ctest -j$(nproc) --output-on-failure"
+  done
+done
+
+# Do one test with an older version of GCC
+time docker run \
+  --volume="${GTEST_ROOT}:/src:ro" \
+  --workdir="/src" \
+  --rm \
+  --env="CC=/usr/local/bin/gcc" \
+  ${LINUX_GCC_FLOOR_CONTAINER} \
+    /usr/local/bin/bazel test ... \
+      --copt="-Wall" \
+      --copt="-Werror" \
+      --copt="-Wno-error=pragmas" \
+      --keep_going \
+      --show_timestamps \
+      --test_output=errors
+
+# Test GCC
+for std in ${STD}; do
+  for absl in 0 1; do
+    time docker run \
+      --volume="${GTEST_ROOT}:/src:ro" \
+      --workdir="/src" \
+      --rm \
+      --env="CC=/usr/local/bin/gcc" \
+      --env="BAZEL_CXXOPTS=-std=${std}" \
+      ${LINUX_LATEST_CONTAINER} \
+      /usr/local/bin/bazel test ... \
+        --copt="-Wall" \
+        --copt="-Werror" \
+        --define="absl=${absl}" \
+        --distdir="/bazel-distdir" \
+        --keep_going \
+        --show_timestamps \
+        --test_output=errors
+  done
+done
+
+# Test Clang
+for std in ${STD}; do
+  for absl in 0 1; do
+    time docker run \
+      --volume="${GTEST_ROOT}:/src:ro" \
+      --workdir="/src" \
+      --rm \
+      --env="CC=/opt/llvm/clang/bin/clang" \
+      --env="BAZEL_CXXOPTS=-std=${std}" \
+      ${LINUX_LATEST_CONTAINER} \
+      /usr/local/bin/bazel test ... \
+        --copt="--gcc-toolchain=/usr/local" \
+        --copt="-Wall" \
+        --copt="-Werror" \
+        --define="absl=${absl}" \
+        --distdir="/bazel-distdir" \
+        --keep_going \
+        --linkopt="--gcc-toolchain=/usr/local" \
+        --show_timestamps \
+        --test_output=errors
+  done
+done
diff --git a/ext/googletest/ci/log-config.sh b/ext/googletest/ci/log-config.sh
deleted file mode 100755
index 5fef119..0000000
--- a/ext/googletest/ci/log-config.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2017 Google Inc.
-# All Rights Reserved.
-#
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-set -e
-
-# ccache on OS X needs installation first
-# reset ccache statistics
-ccache --zero-stats
-
-echo PATH=${PATH}
-
-echo "Compiler configuration:"
-echo CXX=${CXX}
-echo CC=${CC}
-echo CXXFLAGS=${CXXFLAGS}
-
-echo "C++ compiler version:"
-${CXX} --version || echo "${CXX} does not seem to support the --version flag"
-${CXX} -v || echo "${CXX} does not seem to support the -v flag"
-
-echo "C compiler version:"
-${CC} --version || echo "${CXX} does not seem to support the --version flag"
-${CC} -v || echo "${CXX} does not seem to support the -v flag"
diff --git a/ext/googletest/ci/macos-presubmit.sh b/ext/googletest/ci/macos-presubmit.sh
new file mode 100644
index 0000000..d6423fa
--- /dev/null
+++ b/ext/googletest/ci/macos-presubmit.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+#
+# Copyright 2020, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -euox pipefail
+
+if [[ -z ${GTEST_ROOT:-} ]]; then
+  GTEST_ROOT="$(realpath $(dirname ${0})/..)"
+fi
+
+# Test the CMake build
+for cmake_off_on in OFF ON; do
+  BUILD_DIR=$(mktemp -d build_dir.XXXXXXXX)
+  cd ${BUILD_DIR}
+  time cmake ${GTEST_ROOT} \
+    -DCMAKE_CXX_STANDARD=11 \
+    -Dgtest_build_samples=ON \
+    -Dgtest_build_tests=ON \
+    -Dgmock_build_tests=ON \
+    -Dcxx_no_exception=${cmake_off_on} \
+    -Dcxx_no_rtti=${cmake_off_on}
+  time make
+  time ctest -j$(nproc) --output-on-failure
+done
+
+# Test the Bazel build
+
+# If we are running on Kokoro, check for a versioned Bazel binary.
+KOKORO_GFILE_BAZEL_BIN="bazel-3.7.0-darwin-x86_64"
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then
+  BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}"
+  chmod +x ${BAZEL_BIN}
+else
+  BAZEL_BIN="bazel"
+fi
+
+cd ${GTEST_ROOT}
+for absl in 0 1; do
+  ${BAZEL_BIN} test ... \
+    --copt="-Wall" \
+    --copt="-Werror" \
+    --define="absl=${absl}" \
+    --keep_going \
+    --show_timestamps \
+    --test_output=errors
+done
diff --git a/ext/googletest/ci/travis.sh b/ext/googletest/ci/travis.sh
deleted file mode 100755
index 9ff3bad..0000000
--- a/ext/googletest/ci/travis.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env sh
-set -evx
-
-. ci/get-nprocessors.sh
-
-# if possible, ask for the precise number of processors,
-# otherwise take 2 processors as reasonable default; see
-# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
-if [ -x /usr/bin/getconf ]; then
-    NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
-else
-    NPROCESSORS=2
-fi
-# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
-# crashes if parallelized too much (maybe memory consumption problem),
-# so limit to 4 processors for the time being.
-if [ $NPROCESSORS -gt 4 ] ; then
-	echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
-	NPROCESSORS=4
-fi
-# Tell make to use the processors. No preceding '-' required.
-MAKEFLAGS="j${NPROCESSORS}"
-export MAKEFLAGS
-
-env | sort
-
-# Set default values to OFF for these variables if not specified.
-: "${NO_EXCEPTION:=OFF}"
-: "${NO_RTTI:=OFF}"
-: "${COMPILER_IS_GNUCXX:=OFF}"
-
-mkdir build || true
-cd build
-cmake -Dgtest_build_samples=ON \
-      -Dgtest_build_tests=ON \
-      -Dgmock_build_tests=ON \
-      -Dcxx_no_exception=$NO_EXCEPTION \
-      -Dcxx_no_rtti=$NO_RTTI \
-      -DCMAKE_COMPILER_IS_GNUCXX=$COMPILER_IS_GNUCXX \
-      -DCMAKE_CXX_FLAGS=$CXX_FLAGS \
-      -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-      ..
-make
-CTEST_OUTPUT_ON_FAILURE=1 make test
diff --git a/ext/googletest/docs/_config.yml b/ext/googletest/docs/_config.yml
new file mode 100644
index 0000000..d12867e
--- /dev/null
+++ b/ext/googletest/docs/_config.yml
@@ -0,0 +1 @@
+title: GoogleTest
diff --git a/ext/googletest/docs/_data/navigation.yml b/ext/googletest/docs/_data/navigation.yml
new file mode 100644
index 0000000..9f33327
--- /dev/null
+++ b/ext/googletest/docs/_data/navigation.yml
@@ -0,0 +1,43 @@
+nav:
+- section: "Get Started"
+  items:
+  - title: "Supported Platforms"
+    url: "/platforms.html"
+  - title: "Quickstart: Bazel"
+    url: "/quickstart-bazel.html"
+  - title: "Quickstart: CMake"
+    url: "/quickstart-cmake.html"
+- section: "Guides"
+  items:
+  - title: "GoogleTest Primer"
+    url: "/primer.html"
+  - title: "Advanced Topics"
+    url: "/advanced.html"
+  - title: "Mocking for Dummies"
+    url: "/gmock_for_dummies.html"
+  - title: "Mocking Cookbook"
+    url: "/gmock_cook_book.html"
+  - title: "Mocking Cheat Sheet"
+    url: "/gmock_cheat_sheet.html"
+- section: "References"
+  items:
+  - title: "Testing Reference"
+    url: "/reference/testing.html"
+  - title: "Mocking Reference"
+    url: "/reference/mocking.html"
+  - title: "Assertions"
+    url: "/reference/assertions.html"
+  - title: "Matchers"
+    url: "/reference/matchers.html"
+  - title: "Actions"
+    url: "/reference/actions.html"
+  - title: "Testing FAQ"
+    url: "/faq.html"
+  - title: "Mocking FAQ"
+    url: "/gmock_faq.html"
+  - title: "Code Samples"
+    url: "/samples.html"
+  - title: "Using pkg-config"
+    url: "/pkgconfig.html"
+  - title: "Community Documentation"
+    url: "/community_created_documentation.html"
diff --git a/ext/googletest/docs/_layouts/default.html b/ext/googletest/docs/_layouts/default.html
new file mode 100644
index 0000000..dcb42d9
--- /dev/null
+++ b/ext/googletest/docs/_layouts/default.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html lang="{{ site.lang | default: "en-US" }}">
+  <head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+{% seo %}
+    <link rel="stylesheet" href="{{ "/assets/css/style.css?v=" | append: site.github.build_revision | relative_url }}">
+    <script>
+      window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+      ga('create', 'UA-197576187-1', { 'storage': 'none' });
+      ga('set', 'referrer', document.referrer.split('?')[0]);
+      ga('set', 'location', window.location.href.split('?')[0]);
+      ga('set', 'anonymizeIp', true);
+      ga('send', 'pageview');
+    </script>
+    <script async src='https://www.google-analytics.com/analytics.js'></script>
+  </head>
+  <body>
+    <div class="sidebar">
+      <div class="header">
+        <h1><a href="{{ "/" | relative_url }}">{{ site.title | default: "Documentation" }}</a></h1>
+      </div>
+      <input type="checkbox" id="nav-toggle" class="nav-toggle">
+      <label for="nav-toggle" class="expander">
+        <span class="arrow"></span>
+      </label>
+      <nav>
+        {% for item in site.data.navigation.nav %}
+        <h2>{{ item.section }}</h2>
+        <ul>
+          {% for subitem in item.items %}
+          <a href="{{subitem.url | relative_url }}">
+            <li class="{% if subitem.url == page.url %}active{% endif %}">
+              {{ subitem.title }}
+            </li>
+          </a>
+          {% endfor %}
+        </ul>
+        {% endfor %}
+      </nav>
+    </div>
+    <div class="main markdown-body">
+      <div class="main-inner">
+        {{ content }}
+      </div>
+      <div class="footer">
+        GoogleTest &middot;
+        <a href="https://github.com/google/googletest">GitHub Repository</a> &middot;
+        <a href="https://github.com/google/googletest/blob/master/LICENSE">License</a> &middot;
+        <a href="https://policies.google.com/privacy">Privacy Policy</a>
+      </div>
+    </div>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/anchor-js/4.1.0/anchor.min.js" integrity="sha256-lZaRhKri35AyJSypXXs4o6OPFTbTmUoltBbDCbdzegg=" crossorigin="anonymous"></script>
+    <script>anchors.add('.main h2, .main h3, .main h4, .main h5, .main h6');</script>
+  </body>
+</html>
diff --git a/ext/googletest/docs/_sass/main.scss b/ext/googletest/docs/_sass/main.scss
new file mode 100644
index 0000000..92edc87
--- /dev/null
+++ b/ext/googletest/docs/_sass/main.scss
@@ -0,0 +1,200 @@
+// Styles for GoogleTest docs website on GitHub Pages.
+// Color variables are defined in
+// https://github.com/pages-themes/primer/tree/master/_sass/primer-support/lib/variables
+
+$sidebar-width: 260px;
+
+body {
+  display: flex;
+  margin: 0;
+}
+
+.sidebar {
+  background: $black;
+  color: $text-white;
+  flex-shrink: 0;
+  height: 100vh;
+  overflow: auto;
+  position: sticky;
+  top: 0;
+  width: $sidebar-width;
+}
+
+.sidebar h1 {
+  font-size: 1.5em;
+}
+
+.sidebar h2 {
+  color: $gray-light;
+  font-size: 0.8em;
+  font-weight: normal;
+  margin-bottom: 0.8em;
+  padding-left: 2.5em;
+  text-transform: uppercase;
+}
+
+.sidebar .header {
+  background: $black;
+  padding: 2em;
+  position: sticky;
+  top: 0;
+  width: 100%;
+}
+
+.sidebar .header a {
+  color: $text-white;
+  text-decoration: none;
+}
+
+.sidebar .nav-toggle {
+  display: none;
+}
+
+.sidebar .expander {
+  cursor: pointer;
+  display: none;
+  height: 3em;
+  position: absolute;
+  right: 1em;
+  top: 1.5em;
+  width: 3em;
+}
+
+.sidebar .expander .arrow {
+  border: solid $white;
+  border-width: 0 3px 3px 0;
+  display: block;
+  height: 0.7em;
+  margin: 1em auto;
+  transform: rotate(45deg);
+  transition: transform 0.5s;
+  width: 0.7em;
+}
+
+.sidebar nav {
+  width: 100%;
+}
+
+.sidebar nav ul {
+  list-style-type: none;
+  margin-bottom: 1em;
+  padding: 0;
+
+  &:last-child {
+    margin-bottom: 2em;
+  }
+
+  a {
+   text-decoration: none;
+  }
+
+  li {
+    color: $text-white;
+    padding-left: 2em;
+    text-decoration: none;
+  }
+
+  li.active {
+    background: $border-gray-darker;
+    font-weight: bold;
+  }
+
+  li:hover {
+    background: $border-gray-darker;
+  }
+}
+
+.main {
+  background-color: $bg-gray;
+  width: calc(100% - #{$sidebar-width});
+}
+
+.main .main-inner {
+  background-color: $white;
+  padding: 2em;
+}
+
+.main .footer {
+  margin: 0;
+  padding: 2em;
+}
+
+.main table th {
+  text-align: left;
+}
+
+.main .callout {
+  border-left: 0.25em solid $white;
+  padding: 1em;
+
+  a {
+    text-decoration: underline;
+  }
+
+  &.important {
+    background-color: $bg-yellow-light;
+    border-color: $bg-yellow;
+    color: $black;
+  }
+
+  &.note {
+    background-color: $bg-blue-light;
+    border-color: $text-blue;
+    color: $text-blue;
+  }
+
+  &.tip {
+    background-color: $green-000;
+    border-color: $green-700;
+    color: $green-700;
+  }
+
+  &.warning {
+    background-color: $red-000;
+    border-color: $text-red;
+    color: $text-red;
+  }
+}
+
+.main .good pre {
+  background-color: $bg-green-light;
+}
+
+.main .bad pre {
+  background-color: $red-000;
+}
+
+@media all and (max-width: 768px) {
+  body {
+    flex-direction: column;
+  }
+
+  .sidebar {
+    height: auto;
+    position: relative;
+    width: 100%;
+  }
+
+  .sidebar .expander {
+    display: block;
+  }
+
+  .sidebar nav {
+    height: 0;
+    overflow: hidden;
+  }
+
+  .sidebar .nav-toggle:checked {
+    & ~ nav {
+      height: auto;
+    }
+
+    & + .expander .arrow {
+      transform: rotate(-135deg);
+    }
+  }
+
+  .main {
+    width: 100%;
+  }
+}
diff --git a/ext/googletest/googletest/docs/advanced.md b/ext/googletest/docs/advanced.md
similarity index 72%
rename from ext/googletest/googletest/docs/advanced.md
rename to ext/googletest/docs/advanced.md
index 3e5f779..8dff5ba 100644
--- a/ext/googletest/googletest/docs/advanced.md
+++ b/ext/googletest/docs/advanced.md
@@ -1,7 +1,5 @@
 # Advanced googletest Topics
 
-<!-- GOOGLETEST_CM0016 DO NOT DELETE -->
-
 ## Introduction
 
 Now that you have read the [googletest Primer](primer.md) and learned how to
@@ -17,69 +15,13 @@
 
 ### Explicit Success and Failure
 
-These three assertions do not actually test a value or expression. Instead, they
-generate a success or failure directly. Like the macros that actually perform a
-test, you may stream a custom failure message into them.
-
-```c++
-SUCCEED();
-```
-
-Generates a success. This does **NOT** make the overall test succeed. A test is
-considered successful only if none of its assertions fail during its execution.
-
-NOTE: `SUCCEED()` is purely documentary and currently doesn't generate any
-user-visible output. However, we may add `SUCCEED()` messages to googletest's
-output in the future.
-
-```c++
-FAIL();
-ADD_FAILURE();
-ADD_FAILURE_AT("file_path", line_number);
-```
-
-`FAIL()` generates a fatal failure, while `ADD_FAILURE()` and `ADD_FAILURE_AT()`
-generate a nonfatal failure. These are useful when control flow, rather than a
-Boolean expression, determines the test's success or failure. For example, you
-might want to write something like:
-
-```c++
-switch(expression) {
-  case 1:
-     ... some checks ...
-  case 2:
-     ... some other checks ...
-  default:
-     FAIL() << "We shouldn't get here.";
-}
-```
-
-NOTE: you can only use `FAIL()` in functions that return `void`. See the
-[Assertion Placement section](#assertion-placement) for more information.
+See [Explicit Success and Failure](reference/assertions.md#success-failure) in
+the Assertions Reference.
 
 ### Exception Assertions
 
-These are for verifying that a piece of code throws (or does not throw) an
-exception of the given type:
-
-Fatal assertion                            | Nonfatal assertion                         | Verifies
------------------------------------------- | ------------------------------------------ | --------
-`ASSERT_THROW(statement, exception_type);` | `EXPECT_THROW(statement, exception_type);` | `statement` throws an exception of the given type
-`ASSERT_ANY_THROW(statement);`             | `EXPECT_ANY_THROW(statement);`             | `statement` throws an exception of any type
-`ASSERT_NO_THROW(statement);`              | `EXPECT_NO_THROW(statement);`              | `statement` doesn't throw any exception
-
-Examples:
-
-```c++
-ASSERT_THROW(Foo(5), bar_exception);
-
-EXPECT_NO_THROW({
-  int n = 5;
-  Bar(&n);
-});
-```
-
-**Availability**: requires exceptions to be enabled in the build environment
+See [Exception Assertions](reference/assertions.md#exceptions) in the Assertions
+Reference.
 
 ### Predicate Assertions for Better Error Messages
 
@@ -99,60 +41,9 @@
 
 If you already have a function or functor that returns `bool` (or a type that
 can be implicitly converted to `bool`), you can use it in a *predicate
-assertion* to get the function arguments printed for free:
-
-<!-- mdformat off(github rendering does not support multiline tables) -->
-
-| Fatal assertion                   | Nonfatal assertion                | Verifies                    |
-| --------------------------------- | --------------------------------- | --------------------------- |
-| `ASSERT_PRED1(pred1, val1)`       | `EXPECT_PRED1(pred1, val1)`       | `pred1(val1)` is true       |
-| `ASSERT_PRED2(pred2, val1, val2)` | `EXPECT_PRED2(pred2, val1, val2)` | `pred1(val1, val2)` is true |
-| `...`                             | `...`                             | `...`                       |
-
-<!-- mdformat on-->
-In the above, `predn` is an `n`-ary predicate function or functor, where `val1`,
-`val2`, ..., and `valn` are its arguments. The assertion succeeds if the
-predicate returns `true` when applied to the given arguments, and fails
-otherwise. When the assertion fails, it prints the value of each argument. In
-either case, the arguments are evaluated exactly once.
-
-Here's an example. Given
-
-```c++
-// Returns true if m and n have no common divisors except 1.
-bool MutuallyPrime(int m, int n) { ... }
-
-const int a = 3;
-const int b = 4;
-const int c = 10;
-```
-
-the assertion
-
-```c++
-  EXPECT_PRED2(MutuallyPrime, a, b);
-```
-
-will succeed, while the assertion
-
-```c++
-  EXPECT_PRED2(MutuallyPrime, b, c);
-```
-
-will fail with the message
-
-```none
-MutuallyPrime(b, c) is false, where
-b is 4
-c is 10
-```
-
-> NOTE:
->
-> 1.  If you see a compiler error "no matching function to call" when using
->     `ASSERT_PRED*` or `EXPECT_PRED*`, please see
->     [this](faq.md#the-compiler-complains-no-matching-function-to-call-when-i-use-assert-pred-how-do-i-fix-it)
->     for how to resolve it.
+assertion* to get the function arguments printed for free. See
+[`EXPECT_PRED*`](reference/assertions.md#EXPECT_PRED) in the Assertions
+Reference for details.
 
 #### Using a Function That Returns an AssertionResult
 
@@ -187,11 +78,11 @@
 example, if you define `IsEven()` as:
 
 ```c++
-::testing::AssertionResult IsEven(int n) {
+testing::AssertionResult IsEven(int n) {
   if ((n % 2) == 0)
-     return ::testing::AssertionSuccess();
+    return testing::AssertionSuccess();
   else
-     return ::testing::AssertionFailure() << n << " is odd";
+    return testing::AssertionFailure() << n << " is odd";
 }
 ```
 
@@ -225,11 +116,11 @@
 success message:
 
 ```c++
-::testing::AssertionResult IsEven(int n) {
+testing::AssertionResult IsEven(int n) {
   if ((n % 2) == 0)
-     return ::testing::AssertionSuccess() << n << " is even";
+    return testing::AssertionSuccess() << n << " is even";
   else
-     return ::testing::AssertionFailure() << n << " is odd";
+    return testing::AssertionFailure() << n << " is odd";
 }
 ```
 
@@ -243,176 +134,50 @@
 
 #### Using a Predicate-Formatter
 
-If you find the default message generated by `(ASSERT|EXPECT)_PRED*` and
-`(ASSERT|EXPECT)_(TRUE|FALSE)` unsatisfactory, or some arguments to your
-predicate do not support streaming to `ostream`, you can instead use the
-following *predicate-formatter assertions* to *fully* customize how the message
-is formatted:
-
-Fatal assertion                                  | Nonfatal assertion                               | Verifies
------------------------------------------------- | ------------------------------------------------ | --------
-`ASSERT_PRED_FORMAT1(pred_format1, val1);`       | `EXPECT_PRED_FORMAT1(pred_format1, val1);`       | `pred_format1(val1)` is successful
-`ASSERT_PRED_FORMAT2(pred_format2, val1, val2);` | `EXPECT_PRED_FORMAT2(pred_format2, val1, val2);` | `pred_format2(val1, val2)` is successful
-`...`                                            | `...`                                            | ...
-
-The difference between this and the previous group of macros is that instead of
-a predicate, `(ASSERT|EXPECT)_PRED_FORMAT*` take a *predicate-formatter*
-(`pred_formatn`), which is a function or functor with the signature:
-
-```c++
-::testing::AssertionResult PredicateFormattern(const char* expr1,
-                                               const char* expr2,
-                                               ...
-                                               const char* exprn,
-                                               T1 val1,
-                                               T2 val2,
-                                               ...
-                                               Tn valn);
-```
-
-where `val1`, `val2`, ..., and `valn` are the values of the predicate arguments,
-and `expr1`, `expr2`, ..., and `exprn` are the corresponding expressions as they
-appear in the source code. The types `T1`, `T2`, ..., and `Tn` can be either
-value types or reference types. For example, if an argument has type `Foo`, you
-can declare it as either `Foo` or `const Foo&`, whichever is appropriate.
-
-As an example, let's improve the failure message in `MutuallyPrime()`, which was
-used with `EXPECT_PRED2()`:
-
-```c++
-// Returns the smallest prime common divisor of m and n,
-// or 1 when m and n are mutually prime.
-int SmallestPrimeCommonDivisor(int m, int n) { ... }
-
-// A predicate-formatter for asserting that two integers are mutually prime.
-::testing::AssertionResult AssertMutuallyPrime(const char* m_expr,
-                                               const char* n_expr,
-                                               int m,
-                                               int n) {
-  if (MutuallyPrime(m, n)) return ::testing::AssertionSuccess();
-
-  return ::testing::AssertionFailure() << m_expr << " and " << n_expr
-      << " (" << m << " and " << n << ") are not mutually prime, "
-      << "as they have a common divisor " << SmallestPrimeCommonDivisor(m, n);
-}
-```
-
-With this predicate-formatter, we can use
-
-```c++
-  EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c);
-```
-
-to generate the message
-
-```none
-b and c (4 and 10) are not mutually prime, as they have a common divisor 2.
-```
-
-As you may have realized, many of the built-in assertions we introduced earlier
-are special cases of `(EXPECT|ASSERT)_PRED_FORMAT*`. In fact, most of them are
-indeed defined using `(EXPECT|ASSERT)_PRED_FORMAT*`.
+If you find the default message generated by
+[`EXPECT_PRED*`](reference/assertions.md#EXPECT_PRED) and
+[`EXPECT_TRUE`](reference/assertions.md#EXPECT_TRUE) unsatisfactory, or some
+arguments to your predicate do not support streaming to `ostream`, you can
+instead use *predicate-formatter assertions* to *fully* customize how the
+message is formatted. See
+[`EXPECT_PRED_FORMAT*`](reference/assertions.md#EXPECT_PRED_FORMAT) in the
+Assertions Reference for details.
 
 ### Floating-Point Comparison
 
-Comparing floating-point numbers is tricky. Due to round-off errors, it is very
-unlikely that two floating-points will match exactly. Therefore, `ASSERT_EQ` 's
-naive comparison usually doesn't work. And since floating-points can have a wide
-value range, no single fixed error bound works. It's better to compare by a
-fixed relative error bound, except for values close to 0 due to the loss of
-precision there.
-
-In general, for floating-point comparison to make sense, the user needs to
-carefully choose the error bound. If they don't want or care to, comparing in
-terms of Units in the Last Place (ULPs) is a good default, and googletest
-provides assertions to do this. Full details about ULPs are quite long; if you
-want to learn more, see
-[here](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
-
-#### Floating-Point Macros
-
-<!-- mdformat off(github rendering does not support multiline tables) -->
-
-| Fatal assertion                 | Nonfatal assertion              | Verifies                                 |
-| ------------------------------- | ------------------------------- | ---------------------------------------- |
-| `ASSERT_FLOAT_EQ(val1, val2);`  | `EXPECT_FLOAT_EQ(val1, val2);`  | the two `float` values are almost equal  |
-| `ASSERT_DOUBLE_EQ(val1, val2);` | `EXPECT_DOUBLE_EQ(val1, val2);` | the two `double` values are almost equal |
-
-<!-- mdformat on-->
-
-By "almost equal" we mean the values are within 4 ULP's from each other.
-
-The following assertions allow you to choose the acceptable error bound:
-
-<!-- mdformat off(github rendering does not support multiline tables) -->
-
-| Fatal assertion                       | Nonfatal assertion                    | Verifies                                                                         |
-| ------------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------- |
-| `ASSERT_NEAR(val1, val2, abs_error);` | `EXPECT_NEAR(val1, val2, abs_error);` | the difference between `val1` and `val2` doesn't exceed the given absolute error |
-
-<!-- mdformat on-->
+See [Floating-Point Comparison](reference/assertions.md#floating-point) in the
+Assertions Reference.
 
 #### Floating-Point Predicate-Format Functions
 
 Some floating-point operations are useful, but not that often used. In order to
 avoid an explosion of new macros, we provide them as predicate-format functions
-that can be used in predicate assertion macros (e.g. `EXPECT_PRED_FORMAT2`,
-etc).
+that can be used in the predicate assertion macro
+[`EXPECT_PRED_FORMAT2`](reference/assertions.md#EXPECT_PRED_FORMAT), for
+example:
 
 ```c++
-EXPECT_PRED_FORMAT2(::testing::FloatLE, val1, val2);
-EXPECT_PRED_FORMAT2(::testing::DoubleLE, val1, val2);
+EXPECT_PRED_FORMAT2(testing::FloatLE, val1, val2);
+EXPECT_PRED_FORMAT2(testing::DoubleLE, val1, val2);
 ```
 
-Verifies that `val1` is less than, or almost equal to, `val2`. You can replace
-`EXPECT_PRED_FORMAT2` in the above table with `ASSERT_PRED_FORMAT2`.
+The above code verifies that `val1` is less than, or approximately equal to,
+`val2`.
 
 ### Asserting Using gMock Matchers
 
-[gMock](../../googlemock) comes with a library of matchers for validating
-arguments passed to mock objects. A gMock *matcher* is basically a predicate
-that knows how to describe itself. It can be used in these assertion macros:
-
-<!-- mdformat off(github rendering does not support multiline tables) -->
-
-| Fatal assertion                | Nonfatal assertion             | Verifies              |
-| ------------------------------ | ------------------------------ | --------------------- |
-| `ASSERT_THAT(value, matcher);` | `EXPECT_THAT(value, matcher);` | value matches matcher |
-
-<!-- mdformat on-->
-
-For example, `StartsWith(prefix)` is a matcher that matches a string starting
-with `prefix`, and you can write:
-
-```c++
-using ::testing::StartsWith;
-...
-    // Verifies that Foo() returns a string starting with "Hello".
-    EXPECT_THAT(Foo(), StartsWith("Hello"));
-```
-
-Read this
-[recipe](../../googlemock/docs/cook_book.md#using-matchers-in-googletest-assertions)
-in the gMock Cookbook for more details.
-
-gMock has a rich set of matchers. You can do many things googletest cannot do
-alone with them. For a list of matchers gMock provides, read
-[this](../../googlemock/docs/cook_book.md##using-matchers). It's easy to write
-your [own matchers](../../googlemock/docs/cook_book.md#NewMatchers) too.
-
-gMock is bundled with googletest, so you don't need to add any build dependency
-in order to take advantage of this. Just include `"testing/base/public/gmock.h"`
-and you're ready to go.
+See [`EXPECT_THAT`](reference/assertions.md#EXPECT_THAT) in the Assertions
+Reference.
 
 ### More String Assertions
 
 (Please read the [previous](#asserting-using-gmock-matchers) section first if
 you haven't.)
 
-You can use the gMock
-[string matchers](../../googlemock/docs/cheat_sheet.md#string-matchers) with
-`EXPECT_THAT()` or `ASSERT_THAT()` to do more string comparison tricks
-(sub-string, prefix, suffix, regular expression, and etc). For example,
+You can use the gMock [string matchers](reference/matchers.md#string-matchers)
+with [`EXPECT_THAT`](reference/assertions.md#EXPECT_THAT) to do more string
+comparison tricks (sub-string, prefix, suffix, regular expression, and etc). For
+example,
 
 ```c++
 using ::testing::HasSubstr;
@@ -422,37 +187,10 @@
   EXPECT_THAT(bar_string, MatchesRegex("\\w*\\d+"));
 ```
 
-If the string contains a well-formed HTML or XML document, you can check whether
-its DOM tree matches an
-[XPath expression](http://www.w3.org/TR/xpath/#contents):
-
-```c++
-// Currently still in //template/prototemplate/testing:xpath_matcher
-#include "template/prototemplate/testing/xpath_matcher.h"
-using prototemplate::testing::MatchesXPath;
-EXPECT_THAT(html_string, MatchesXPath("//a[text()='click here']"));
-```
-
 ### Windows HRESULT assertions
 
-These assertions test for `HRESULT` success or failure.
-
-Fatal assertion                        | Nonfatal assertion                     | Verifies
--------------------------------------- | -------------------------------------- | --------
-`ASSERT_HRESULT_SUCCEEDED(expression)` | `EXPECT_HRESULT_SUCCEEDED(expression)` | `expression` is a success `HRESULT`
-`ASSERT_HRESULT_FAILED(expression)`    | `EXPECT_HRESULT_FAILED(expression)`    | `expression` is a failure `HRESULT`
-
-The generated output contains the human-readable error message associated with
-the `HRESULT` code returned by `expression`.
-
-You might use them like this:
-
-```c++
-CComPtr<IShellDispatch2> shell;
-ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
-CComVariant empty;
-ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));
-```
+See [Windows HRESULT Assertions](reference/assertions.md#HRESULT) in the
+Assertions Reference.
 
 ### Type Assertions
 
@@ -465,7 +203,7 @@
 to assert that types `T1` and `T2` are the same. The function does nothing if
 the assertion is satisfied. If the types are different, the function call will
 fail to compile, the compiler error message will say that
-`type1 and type2 are not the same type` and most likely (depending on the compiler)
+`T1 and T2 are not the same type` and most likely (depending on the compiler)
 show you the actual values of `T1` and `T2`. This is mainly useful inside
 template code.
 
@@ -476,7 +214,7 @@
 ```c++
 template <typename T> class Foo {
  public:
-  void Bar() { ::testing::StaticAssertTypeEq<int, T>(); }
+  void Bar() { testing::StaticAssertTypeEq<int, T>(); }
 };
 ```
 
@@ -516,6 +254,7 @@
 If changing the function's type is not an option, you should just use assertions
 that generate non-fatal failures, such as `ADD_FAILURE*` and `EXPECT_*`.
 
+{: .callout .note}
 NOTE: Constructors and destructors are not considered void-returning functions,
 according to the C++ language specification, and so you may not use fatal
 assertions in them; you'll get a compilation error if you try. Instead, either
@@ -523,13 +262,46 @@
 a `SetUp`/`TearDown` function; see
 [constructor/destructor vs. `SetUp`/`TearDown`](faq.md#CtorVsSetUp)
 
+{: .callout .warning}
 WARNING: A fatal assertion in a helper function (private void-returning method)
-called from a constructor or destructor does not does not terminate the current
-test, as your intuition might suggest: it merely returns from the constructor or
+called from a constructor or destructor does not terminate the current test, as
+your intuition might suggest: it merely returns from the constructor or
 destructor early, possibly leaving your object in a partially-constructed or
 partially-destructed state! You almost certainly want to `abort` or use
 `SetUp`/`TearDown` instead.
 
+## Skipping test execution
+
+Related to the assertions `SUCCEED()` and `FAIL()`, you can prevent further test
+execution at runtime with the `GTEST_SKIP()` macro. This is useful when you need
+to check for preconditions of the system under test during runtime and skip
+tests in a meaningful way.
+
+`GTEST_SKIP()` can be used in individual test cases or in the `SetUp()` methods
+of classes derived from either `::testing::Environment` or `::testing::Test`.
+For example:
+
+```c++
+TEST(SkipTest, DoesSkip) {
+  GTEST_SKIP() << "Skipping single test";
+  EXPECT_EQ(0, 1);  // Won't fail; it won't be executed
+}
+
+class SkipFixture : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    GTEST_SKIP() << "Skipping all tests for this fixture";
+  }
+};
+
+// Tests for SkipFixture won't be executed.
+TEST_F(SkipFixture, SkipsOneTest) {
+  EXPECT_EQ(5, 7);  // Won't fail
+}
+```
+
+As with assertion macros, you can stream a custom message into `GTEST_SKIP()`.
+
 ## Teaching googletest How to Print Your Values
 
 When a test assertion such as `EXPECT_EQ` fails, googletest prints the argument
@@ -605,7 +377,7 @@
 vector<pair<Bar, int> > bar_ints = GetBarIntVector();
 
 EXPECT_TRUE(IsCorrectBarIntVector(bar_ints))
-    << "bar_ints = " << ::testing::PrintToString(bar_ints);
+    << "bar_ints = " << testing::PrintToString(bar_ints);
 ```
 
 ## Death Tests
@@ -628,73 +400,16 @@
 code, see [Exception Assertions](#ExceptionAssertions).
 
 If you want to test `EXPECT_*()/ASSERT_*()` failures in your test code, see
-Catching Failures
+["Catching" Failures](#catching-failures).
 
 ### How to Write a Death Test
 
-googletest has the following macros to support death tests:
+GoogleTest provides assertion macros to support death tests. See
+[Death Assertions](reference/assertions.md#death) in the Assertions Reference
+for details.
 
-Fatal assertion                                  | Nonfatal assertion                               | Verifies
------------------------------------------------- | ------------------------------------------------ | --------
-`ASSERT_DEATH(statement, matcher);`              | `EXPECT_DEATH(statement, matcher);`              | `statement` crashes with the given error
-`ASSERT_DEATH_IF_SUPPORTED(statement, matcher);` | `EXPECT_DEATH_IF_SUPPORTED(statement, matcher);` | if death tests are supported, verifies that `statement` crashes with the given error; otherwise verifies nothing
-`ASSERT_EXIT(statement, predicate, matcher);`    | `EXPECT_EXIT(statement, predicate, matcher);`    | `statement` exits with the given error, and its exit code matches `predicate`
-
-where `statement` is a statement that is expected to cause the process to die,
-`predicate` is a function or function object that evaluates an integer exit
-status, and `matcher` is either a GMock matcher matching a `const std::string&`
-or a (Perl) regular expression - either of which is matched against the stderr
-output of `statement`. For legacy reasons, a bare string (i.e. with no matcher)
-is interpreted as `ContainsRegex(str)`, **not** `Eq(str)`. Note that `statement`
-can be *any valid statement* (including *compound statement*) and doesn't have
-to be an expression.
-
-As usual, the `ASSERT` variants abort the current test function, while the
-`EXPECT` variants do not.
-
-> NOTE: We use the word "crash" here to mean that the process terminates with a
-> *non-zero* exit status code. There are two possibilities: either the process
-> has called `exit()` or `_exit()` with a non-zero value, or it may be killed by
-> a signal.
->
-> This means that if `*statement*` terminates the process with a 0 exit code, it
-> is *not* considered a crash by `EXPECT_DEATH`. Use `EXPECT_EXIT` instead if
-> this is the case, or if you want to restrict the exit code more precisely.
-
-A predicate here must accept an `int` and return a `bool`. The death test
-succeeds only if the predicate returns `true`. googletest defines a few
-predicates that handle the most common cases:
-
-```c++
-::testing::ExitedWithCode(exit_code)
-```
-
-This expression is `true` if the program exited normally with the given exit
-code.
-
-```c++
-::testing::KilledBySignal(signal_number)  // Not available on Windows.
-```
-
-This expression is `true` if the program was killed by the given signal.
-
-The `*_DEATH` macros are convenient wrappers for `*_EXIT` that use a predicate
-that verifies the process' exit code is non-zero.
-
-Note that a death test only cares about three things:
-
-1.  does `statement` abort or exit the process?
-2.  (in the case of `ASSERT_EXIT` and `EXPECT_EXIT`) does the exit status
-    satisfy `predicate`? Or (in the case of `ASSERT_DEATH` and `EXPECT_DEATH`)
-    is the exit status non-zero? And
-3.  does the stderr output match `regex`?
-
-In particular, if `statement` generates an `ASSERT_*` or `EXPECT_*` failure, it
-will **not** cause the death test to fail, as googletest assertions don't abort
-the process.
-
-To write a death test, simply use one of the above macros inside your test
-function. For example,
+To write a death test, simply use one of the macros inside your test function.
+For example,
 
 ```c++
 TEST(MyDeathTest, Foo) {
@@ -706,11 +421,11 @@
 }
 
 TEST(MyDeathTest, NormalExit) {
-  EXPECT_EXIT(NormalExit(), ::testing::ExitedWithCode(0), "Success");
+  EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success");
 }
 
-TEST(MyDeathTest, KillMyself) {
-  EXPECT_EXIT(KillMyself(), ::testing::KilledBySignal(SIGKILL),
+TEST(MyDeathTest, KillProcess) {
+  EXPECT_EXIT(KillProcess(), testing::KilledBySignal(SIGKILL),
               "Sending myself unblockable signal");
 }
 ```
@@ -720,13 +435,26 @@
 *   calling `Foo(5)` causes the process to die with the given error message,
 *   calling `NormalExit()` causes the process to print `"Success"` to stderr and
     exit with exit code 0, and
-*   calling `KillMyself()` kills the process with signal `SIGKILL`.
+*   calling `KillProcess()` kills the process with signal `SIGKILL`.
 
 The test function body may contain other assertions and statements as well, if
 necessary.
 
+Note that a death test only cares about three things:
+
+1.  does `statement` abort or exit the process?
+2.  (in the case of `ASSERT_EXIT` and `EXPECT_EXIT`) does the exit status
+    satisfy `predicate`? Or (in the case of `ASSERT_DEATH` and `EXPECT_DEATH`)
+    is the exit status non-zero? And
+3.  does the stderr output match `matcher`?
+
+In particular, if `statement` generates an `ASSERT_*` or `EXPECT_*` failure, it
+will **not** cause the death test to fail, as googletest assertions don't abort
+the process.
+
 ### Death Test Naming
 
+{: .callout .important}
 IMPORTANT: We strongly recommend you to follow the convention of naming your
 **test suite** (not test) `*DeathTest` when it contains a death test, as
 demonstrated in the above example. The
@@ -737,7 +465,7 @@
 duplicating its code:
 
 ```c++
-class FooTest : public ::testing::Test { ... };
+class FooTest : public testing::Test { ... };
 
 using FooDeathTest = FooTest;
 
@@ -795,31 +523,8 @@
 
 ### How It Works
 
-Under the hood, `ASSERT_EXIT()` spawns a new process and executes the death test
-statement in that process. The details of how precisely that happens depend on
-the platform and the variable ::testing::GTEST_FLAG(death_test_style) (which is
-initialized from the command-line flag `--gtest_death_test_style`).
-
-*   On POSIX systems, `fork()` (or `clone()` on Linux) is used to spawn the
-    child, after which:
-    *   If the variable's value is `"fast"`, the death test statement is
-        immediately executed.
-    *   If the variable's value is `"threadsafe"`, the child process re-executes
-        the unit test binary just as it was originally invoked, but with some
-        extra flags to cause just the single death test under consideration to
-        be run.
-*   On Windows, the child is spawned using the `CreateProcess()` API, and
-    re-executes the binary to cause just the single death test under
-    consideration to be run - much like the `threadsafe` mode on POSIX.
-
-Other values for the variable are illegal and will cause the death test to fail.
-Currently, the flag's default value is **"fast"**
-
-1.  the child's exit status satisfies the predicate, and
-2.  the child's stderr matches the regular expression.
-
-If the death test statement runs to completion without dying, the child process
-will nonetheless terminate, and the assertion fails.
+See [Death Assertions](reference/assertions.md#death) in the Assertions
+Reference.
 
 ### Death Tests And Threads
 
@@ -862,13 +567,13 @@
 
 ```c++
 int main(int argc, char** argv) {
-  InitGoogle(argv[0], &argc, &argv, true);
-  ::testing::FLAGS_gtest_death_test_style = "fast";
+  testing::InitGoogleTest(&argc, argv);
+  testing::FLAGS_gtest_death_test_style = "fast";
   return RUN_ALL_TESTS();
 }
 
 TEST(MyDeathTest, TestOne) {
-  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  testing::FLAGS_gtest_death_test_style = "threadsafe";
   // This test is run in the "threadsafe" style:
   ASSERT_DEATH(ThisShouldDie(), "");
 }
@@ -908,6 +613,13 @@
 
 ## Using Assertions in Sub-routines
 
+{: .callout .note}
+Note: If you want to put a series of test assertions in a subroutine to check
+for a complex condition, consider using
+[a custom GMock matcher](gmock_cook_book.md#NewMatchers)
+instead. This lets you provide a more readable error message in case of failure
+and avoid all of the issues described below.
+
 ### Adding Traces to Assertions
 
 If a test sub-routine is called from several places, when an assertion inside it
@@ -918,6 +630,8 @@
 
 ```c++
 SCOPED_TRACE(message);
+```
+```c++
 ScopedTrace trace("file_path", line_number, message);
 ```
 
@@ -953,7 +667,7 @@
 Value of: Bar(n)
 Expected: 1
   Actual: 2
-   Trace:
+Google Test trace:
 path/to/foo_test.cc:17: A
 
 path/to/foo_test.cc:12: Failure
@@ -1003,7 +717,7 @@
                  // in Subroutine() to abort the entire test.
 
   // The actual behavior: the function goes on after Subroutine() returns.
-  int* p = NULL;
+  int* p = nullptr;
   *p = 3;  // Segfault!
 }
 ```
@@ -1097,7 +811,7 @@
 fixture, you must add the `::testing::Test::` prefix, as in:
 
 ```c++
-if (::testing::Test::HasFatalFailure()) return;
+if (testing::Test::HasFatalFailure()) return;
 ```
 
 Similarly, `HasNonfatalFailure()` returns `true` if the current test has at
@@ -1127,12 +841,13 @@
   ...
 ```
 
+{: .callout .note}
 > NOTE:
 >
 > *   `RecordProperty()` is a static member of the `Test` class. Therefore it
 >     needs to be prefixed with `::testing::Test::` if used outside of the
 >     `TEST` body and the test fixture class.
-> *   `*key*` must be a valid XML attribute name, and cannot conflict with the
+> *   *`key`* must be a valid XML attribute name, and cannot conflict with the
 >     ones already used by googletest (`name`, `status`, `time`, `classname`,
 >     `type_param`, and `value_param`).
 > *   Calling `RecordProperty()` outside of the lifespan of a test is allowed.
@@ -1176,7 +891,7 @@
 Here's an example of per-test-suite set-up and tear-down:
 
 ```c++
-class FooTest : public ::testing::Test {
+class FooTest : public testing::Test {
  protected:
   // Per-test-suite set-up.
   // Called before the first test in this test suite.
@@ -1190,20 +905,20 @@
   // Can be omitted if not needed.
   static void TearDownTestSuite() {
     delete shared_resource_;
-    shared_resource_ = NULL;
+    shared_resource_ = nullptr;
   }
 
   // You can define per-test set-up logic as usual.
-  virtual void SetUp() { ... }
+  void SetUp() override { ... }
 
   // You can define per-test tear-down logic as usual.
-  virtual void TearDown() { ... }
+  void TearDown() override { ... }
 
   // Some expensive resource shared by all tests.
   static T* shared_resource_;
 };
 
-T* FooTest::shared_resource_ = NULL;
+T* FooTest::shared_resource_ = nullptr;
 
 TEST_F(FooTest, Test1) {
   ... you can refer to shared_resource_ here ...
@@ -1214,6 +929,7 @@
 }
 ```
 
+{: .callout .note}
 NOTE: Though the above code declares `SetUpTestSuite()` protected, it may
 sometimes be necessary to declare it public, such as when using it with
 `TEST_P`.
@@ -1229,7 +945,7 @@
 ```c++
 class Environment : public ::testing::Environment {
  public:
-  virtual ~Environment() {}
+  ~Environment() override {}
 
   // Override this to define how to set up the environment.
   void SetUp() override {}
@@ -1265,8 +981,8 @@
 variable like this:
 
 ```c++
-::testing::Environment* const foo_env =
-    ::testing::AddGlobalTestEnvironment(new FooEnvironment);
+testing::Environment* const foo_env =
+    testing::AddGlobalTestEnvironment(new FooEnvironment);
 ```
 
 However, we strongly recommend you to write your own `main()` and call
@@ -1301,6 +1017,7 @@
 raw pointer, you are responsible for managing the lifespan of the pointed
 values.
 
+{: .callout .note}
 NOTE: If your test fixture defines `SetUpTestSuite()` or `TearDownTestSuite()`
 they must be declared **public** rather than **protected** in order to use
 `TEST_P`.
@@ -1340,80 +1057,80 @@
 }
 ```
 
-Finally, you can use `INSTANTIATE_TEST_SUITE_P` to instantiate the test suite
-with any set of parameters you want. googletest defines a number of functions
-for generating test parameters. They return what we call (surprise!) *parameter
-generators*. Here is a summary of them, which are all in the `testing`
-namespace:
+Finally, you can use the `INSTANTIATE_TEST_SUITE_P` macro to instantiate the
+test suite with any set of parameters you want. GoogleTest defines a number of
+functions for generating test parameters—see details at
+[`INSTANTIATE_TEST_SUITE_P`](reference/testing.md#INSTANTIATE_TEST_SUITE_P) in
+the Testing Reference.
 
-<!-- mdformat off(github rendering does not support multiline tables) -->
-
-| Parameter Generator                                                                       | Behavior                                                                                                          |
-| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
-| `Range(begin, end [, step])`                                                              | Yields values `{begin, begin+step, begin+step+step, ...}`. The values do not include `end`. `step` defaults to 1. |
-| `Values(v1, v2, ..., vN)`                                                                 | Yields values `{v1, v2, ..., vN}`.                                                                                |
-| `ValuesIn(container)` and  `ValuesIn(begin,end)`                                          | Yields values from a C-style array, an  STL-style container, or an iterator range `[begin, end)`                  |
-| `Bool()`                                                                                  | Yields sequence `{false, true}`.                                                                                  |
-| `Combine(g1, g2, ..., gN)`                                                                | Yields all combinations (Cartesian product) as std\:\:tuples of the values generated by the `N` generators.       |
-
-<!-- mdformat on-->
-
-For more details, see the comments at the definitions of these functions.
-
-The following statement will instantiate tests from the `FooTest` test suite
-each with parameter values `"meeny"`, `"miny"`, and `"moe"`.
+For example, the following statement will instantiate tests from the `FooTest`
+test suite each with parameter values `"meeny"`, `"miny"`, and `"moe"` using the
+[`Values`](reference/testing.md#param-generators) parameter generator:
 
 ```c++
-INSTANTIATE_TEST_SUITE_P(InstantiationName,
+INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe,
                          FooTest,
                          testing::Values("meeny", "miny", "moe"));
 ```
 
+{: .callout .note}
 NOTE: The code above must be placed at global or namespace scope, not at
 function scope.
 
-NOTE: Don't forget this step! If you do your test will silently pass, but none
-of its suites will ever run!
+The first argument to `INSTANTIATE_TEST_SUITE_P` is a unique name for the
+instantiation of the test suite. The next argument is the name of the test
+pattern, and the last is the
+[parameter generator](reference/testing.md#param-generators).
 
-To distinguish different instances of the pattern (yes, you can instantiate it
-more than once), the first argument to `INSTANTIATE_TEST_SUITE_P` is a prefix
-that will be added to the actual test suite name. Remember to pick unique
-prefixes for different instantiations. The tests from the instantiation above
-will have these names:
+You can instantiate a test pattern more than once, so to distinguish different
+instances of the pattern, the instantiation name is added as a prefix to the
+actual test suite name. Remember to pick unique prefixes for different
+instantiations. The tests from the instantiation above will have these names:
 
-*   `InstantiationName/FooTest.DoesBlah/0` for `"meeny"`
-*   `InstantiationName/FooTest.DoesBlah/1` for `"miny"`
-*   `InstantiationName/FooTest.DoesBlah/2` for `"moe"`
-*   `InstantiationName/FooTest.HasBlahBlah/0` for `"meeny"`
-*   `InstantiationName/FooTest.HasBlahBlah/1` for `"miny"`
-*   `InstantiationName/FooTest.HasBlahBlah/2` for `"moe"`
+*   `MeenyMinyMoe/FooTest.DoesBlah/0` for `"meeny"`
+*   `MeenyMinyMoe/FooTest.DoesBlah/1` for `"miny"`
+*   `MeenyMinyMoe/FooTest.DoesBlah/2` for `"moe"`
+*   `MeenyMinyMoe/FooTest.HasBlahBlah/0` for `"meeny"`
+*   `MeenyMinyMoe/FooTest.HasBlahBlah/1` for `"miny"`
+*   `MeenyMinyMoe/FooTest.HasBlahBlah/2` for `"moe"`
 
 You can use these names in [`--gtest_filter`](#running-a-subset-of-the-tests).
 
-This statement will instantiate all tests from `FooTest` again, each with
-parameter values `"cat"` and `"dog"`:
+The following statement will instantiate all tests from `FooTest` again, each
+with parameter values `"cat"` and `"dog"` using the
+[`ValuesIn`](reference/testing.md#param-generators) parameter generator:
 
 ```c++
 const char* pets[] = {"cat", "dog"};
-INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest,
-                         testing::ValuesIn(pets));
+INSTANTIATE_TEST_SUITE_P(Pets, FooTest, testing::ValuesIn(pets));
 ```
 
 The tests from the instantiation above will have these names:
 
-*   `AnotherInstantiationName/FooTest.DoesBlah/0` for `"cat"`
-*   `AnotherInstantiationName/FooTest.DoesBlah/1` for `"dog"`
-*   `AnotherInstantiationName/FooTest.HasBlahBlah/0` for `"cat"`
-*   `AnotherInstantiationName/FooTest.HasBlahBlah/1` for `"dog"`
+*   `Pets/FooTest.DoesBlah/0` for `"cat"`
+*   `Pets/FooTest.DoesBlah/1` for `"dog"`
+*   `Pets/FooTest.HasBlahBlah/0` for `"cat"`
+*   `Pets/FooTest.HasBlahBlah/1` for `"dog"`
 
 Please note that `INSTANTIATE_TEST_SUITE_P` will instantiate *all* tests in the
 given test suite, whether their definitions come before or *after* the
 `INSTANTIATE_TEST_SUITE_P` statement.
 
+Additionally, by default, every `TEST_P` without a corresponding
+`INSTANTIATE_TEST_SUITE_P` causes a failing test in test suite
+`GoogleTestVerification`. If you have a test suite where that omission is not an
+error, for example it is in a library that may be linked in for other reasons or
+where the list of test cases is dynamic and may be empty, then this check can be
+suppressed by tagging the test suite:
+
+```c++
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FooTest);
+```
+
 You can see [sample7_unittest.cc] and [sample8_unittest.cc] for more examples.
 
-[sample7_unittest.cc]: ../samples/sample7_unittest.cc "Parameterized Test example"
-[sample8_unittest.cc]: ../samples/sample8_unittest.cc "Parameterized Test example with multiple parameters"
+[sample7_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample7_unittest.cc "Parameterized Test example"
+[sample8_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample8_unittest.cc "Parameterized Test example with multiple parameters"
 
 ### Creating Value-Parameterized Abstract Tests
 
@@ -1450,6 +1167,7 @@
 returns the value of `testing::PrintToString(GetParam())`. It does not work for
 `std::string` or C strings.
 
+{: .callout .note}
 NOTE: test names must be non-empty, unique, and may only contain ASCII
 alphanumeric characters. In particular, they
 [should not contain underscores](faq.md#why-should-test-suite-names-and-test-names-not-contain-underscore)
@@ -1476,17 +1194,17 @@
 ```c++
 enum class MyType { MY_FOO = 0, MY_BAR = 1 };
 
-class MyTestSuite : public testing::TestWithParam<std::tuple<MyType, string>> {
+class MyTestSuite : public testing::TestWithParam<std::tuple<MyType, std::string>> {
 };
 
 INSTANTIATE_TEST_SUITE_P(
     MyGroup, MyTestSuite,
     testing::Combine(
-        testing::Values(MyType::VALUE_0, MyType::VALUE_1),
-        testing::ValuesIn("", "")),
+        testing::Values(MyType::MY_FOO, MyType::MY_BAR),
+        testing::Values("A", "B")),
     [](const testing::TestParamInfo<MyTestSuite::ParamType>& info) {
-      string name = absl::StrCat(
-          std::get<0>(info.param) == MY_FOO ? "Foo" : "Bar", "_",
+      std::string name = absl::StrCat(
+          std::get<0>(info.param) == MyType::MY_FOO ? "Foo" : "Bar",
           std::get<1>(info.param));
       absl::c_replace_if(name, [](char c) { return !std::isalnum(c); }, '_');
       return name;
@@ -1515,10 +1233,10 @@
 
 ```c++
 template <typename T>
-class FooTest : public ::testing::Test {
+class FooTest : public testing::Test {
  public:
   ...
-  typedef std::list<T> List;
+  using List = std::list<T>;
   static T shared_;
   T value_;
 };
@@ -1563,7 +1281,7 @@
 
 You can see [sample6_unittest.cc] for a complete example.
 
-[sample6_unittest.cc]: ../samples/sample6_unittest.cc "Typed Test example"
+[sample6_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample6_unittest.cc "Typed Test example"
 
 ## Type-Parameterized Tests
 
@@ -1583,7 +1301,7 @@
 
 ```c++
 template <typename T>
-class FooTest : public ::testing::Test {
+class FooTest : public testing::Test {
   ...
 };
 ```
@@ -1622,7 +1340,7 @@
 source files and instantiate it multiple times.
 
 ```c++
-typedef ::testing::Types<char, int, unsigned int> MyTypes;
+using MyTypes = ::testing::Types<char, int, unsigned int>;
 INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
 ```
 
@@ -1718,10 +1436,11 @@
     }
     ```
 
-    Pay special attention when your class is defined in a namespace, as you
-    should define your test fixtures and tests in the same namespace if you want
-    them to be friends of your class. For example, if the code to be tested
-    looks like:
+    Pay special attention when your class is defined in a namespace. If you want
+    your test fixtures and tests to be friends of your class, then they must be
+    defined in the exact same namespace (no anonymous or inline namespaces).
+
+    For example, if the code to be tested looks like:
 
     ```c++
     namespace my_namespace {
@@ -1741,7 +1460,7 @@
     ```c++
     namespace my_namespace {
 
-    class FooTest : public ::testing::Test {
+    class FooTest : public testing::Test {
      protected:
       ...
     };
@@ -1762,7 +1481,7 @@
 the exception and assert on it. But googletest doesn't use exceptions, so how do
 we test that a piece of code generates an expected failure?
 
-gunit-spi.h contains some constructs to do this. After #including this header,
+`"gtest/gtest-spi.h"` contains some constructs to do this. After #including this header,
 you can use
 
 ```c++
@@ -1788,6 +1507,7 @@
   EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substring);
 ```
 
+{: .callout .note}
 NOTE: Assertions from multiple threads are currently not supported on Windows.
 
 For technical reasons, there are some caveats:
@@ -1803,7 +1523,7 @@
 ## Registering tests programmatically
 
 The `TEST` macros handle the vast majority of all use cases, but there are few
-were runtime registration logic is required. For those cases, the framework
+where runtime registration logic is required. For those cases, the framework
 provides the `::testing::RegisterTest` that allows callers to register arbitrary
 tests dynamically.
 
@@ -1836,7 +1556,7 @@
 Use case example:
 
 ```c++
-class MyFixture : public ::testing::Test {
+class MyFixture : public testing::Test {
  public:
   // All of these optional, just like in regular macro usage.
   static void SetUpTestSuite() { ... }
@@ -1856,7 +1576,7 @@
 
 void RegisterMyTests(const std::vector<int>& values) {
   for (int v : values) {
-    ::testing::RegisterTest(
+    testing::RegisterTest(
         "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr,
         std::to_string(v).c_str(),
         __FILE__, __LINE__,
@@ -1876,35 +1596,18 @@
 
 Sometimes a function may need to know the name of the currently running test.
 For example, you may be using the `SetUp()` method of your test fixture to set
-the golden file name based on which test is running. The `::testing::TestInfo`
-class has this information:
-
-```c++
-namespace testing {
-
-class TestInfo {
- public:
-  // Returns the test suite name and the test name, respectively.
-  //
-  // Do NOT delete or free the return value - it's managed by the
-  // TestInfo class.
-  const char* test_suite_name() const;
-  const char* name() const;
-};
-
-}
-```
+the golden file name based on which test is running. The
+[`TestInfo`](reference/testing.md#TestInfo) class has this information.
 
 To obtain a `TestInfo` object for the currently running test, call
-`current_test_info()` on the `UnitTest` singleton object:
+`current_test_info()` on the [`UnitTest`](reference/testing.md#UnitTest)
+singleton object:
 
 ```c++
   // Gets information about the currently running test.
   // Do NOT delete the returned object - it's managed by the UnitTest class.
-  const ::testing::TestInfo* const test_info =
-    ::testing::UnitTest::GetInstance()->current_test_info();
-
-
+  const testing::TestInfo* const test_info =
+      testing::UnitTest::GetInstance()->current_test_info();
 
   printf("We are in test %s of test suite %s.\n",
          test_info->name(),
@@ -1912,8 +1615,8 @@
 ```
 
 `current_test_info()` returns a null pointer if no test is running. In
-particular, you cannot find the test suite name in `TestSuiteSetUp()`,
-`TestSuiteTearDown()` (where you know the test suite name implicitly), or
+particular, you cannot find the test suite name in `SetUpTestSuite()`,
+`TearDownTestSuite()` (where you know the test suite name implicitly), or
 functions called from them.
 
 ## Extending googletest by Handling Test Events
@@ -1928,12 +1631,14 @@
 
 ### Defining Event Listeners
 
-To define a event listener, you subclass either testing::TestEventListener or
-testing::EmptyTestEventListener The former is an (abstract) interface, where
-*each pure virtual method can be overridden to handle a test event* (For
-example, when a test starts, the `OnTestStart()` method will be called.). The
-latter provides an empty implementation of all methods in the interface, such
-that a subclass only needs to override the methods it cares about.
+To define a event listener, you subclass either
+[`testing::TestEventListener`](reference/testing.md#TestEventListener) or
+[`testing::EmptyTestEventListener`](reference/testing.md#EmptyTestEventListener)
+The former is an (abstract) interface, where *each pure virtual method can be
+overridden to handle a test event* (For example, when a test starts, the
+`OnTestStart()` method will be called.). The latter provides an empty
+implementation of all methods in the interface, such that a subclass only needs
+to override the methods it cares about.
 
 When an event is fired, its context is passed to the handler function as an
 argument. The following argument types are used:
@@ -1950,15 +1655,15 @@
 Here's an example:
 
 ```c++
-  class MinimalistPrinter : public ::testing::EmptyTestEventListener {
+  class MinimalistPrinter : public testing::EmptyTestEventListener {
     // Called before a test starts.
-    virtual void OnTestStart(const ::testing::TestInfo& test_info) {
+    void OnTestStart(const testing::TestInfo& test_info) override {
       printf("*** Test %s.%s starting.\n",
              test_info.test_suite_name(), test_info.name());
     }
 
     // Called after a failed assertion or a SUCCESS().
-    virtual void OnTestPartResult(const ::testing::TestPartResult& test_part_result) {
+    void OnTestPartResult(const testing::TestPartResult& test_part_result) override {
       printf("%s in %s:%d\n%s\n",
              test_part_result.failed() ? "*** Failure" : "Success",
              test_part_result.file_name(),
@@ -1967,7 +1672,7 @@
     }
 
     // Called after a test ends.
-    virtual void OnTestEnd(const ::testing::TestInfo& test_info) {
+    void OnTestEnd(const testing::TestInfo& test_info) override {
       printf("*** Test %s.%s ending.\n",
              test_info.test_suite_name(), test_info.name());
     }
@@ -1977,16 +1682,17 @@
 ### Using Event Listeners
 
 To use the event listener you have defined, add an instance of it to the
-googletest event listener list (represented by class TestEventListeners - note
-the "s" at the end of the name) in your `main()` function, before calling
+googletest event listener list (represented by class
+[`TestEventListeners`](reference/testing.md#TestEventListeners) - note the "s"
+at the end of the name) in your `main()` function, before calling
 `RUN_ALL_TESTS()`:
 
 ```c++
 int main(int argc, char** argv) {
-  ::testing::InitGoogleTest(&argc, argv);
+  testing::InitGoogleTest(&argc, argv);
   // Gets hold of the event listener list.
-  ::testing::TestEventListeners& listeners =
-        ::testing::UnitTest::GetInstance()->listeners();
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
   // Adds a listener to the end.  googletest takes the ownership.
   listeners.Append(new MinimalistPrinter);
   return RUN_ALL_TESTS();
@@ -2008,7 +1714,7 @@
 Now, sit back and enjoy a completely different output from your tests. For more
 details, see [sample9_unittest.cc].
 
-[sample9_unittest.cc]: ../samples/sample9_unittest.cc "Event listener example"
+[sample9_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample9_unittest.cc "Event listener example"
 
 You may append more than one listener to the list. When an `On*Start()` or
 `OnTestPartResult()` event is fired, the listeners will receive it in the order
@@ -2035,7 +1741,7 @@
 
 See [sample10_unittest.cc] for an example of a failure-raising listener.
 
-[sample10_unittest.cc]: ../samples/sample10_unittest.cc "Failure-raising listener example"
+[sample10_unittest.cc]: https://github.com/google/googletest/blob/master/googletest/samples/sample10_unittest.cc "Failure-raising listener example"
 
 ## Running Test Programs: Advanced Options
 
@@ -2104,6 +1810,15 @@
     everything in test suite `FooTest` except `FooTest.Bar` and everything in
     test suite `BarTest` except `BarTest.Foo`.
 
+#### Stop test execution upon first failure
+
+By default, a googletest program runs all tests the user has defined. In some
+cases (e.g. iterative test development & execution) it may be desirable stop
+test execution upon first failure (trading improved latency for completeness).
+If `GTEST_FAIL_FAST` environment variable or `--gtest_fail_fast` flag is set,
+the test runner will stop execution as soon as the first test failure is
+found.
+
 #### Temporarily Disabling Tests
 
 If you have a broken test that you cannot fix right away, you can add the
@@ -2122,19 +1837,21 @@
 // Tests that Foo does Abc.
 TEST(FooTest, DISABLED_DoesAbc) { ... }
 
-class DISABLED_BarTest : public ::testing::Test { ... };
+class DISABLED_BarTest : public testing::Test { ... };
 
 // Tests that Bar does Xyz.
 TEST_F(DISABLED_BarTest, DoesXyz) { ... }
 ```
 
+{: .callout .note}
 NOTE: This feature should only be used for temporary pain-relief. You still have
 to fix the disabled tests at a later date. As a reminder, googletest will print
 a banner warning you if a test program contains any disabled tests.
 
-TIP: You can easily count the number of disabled tests you have using `gsearch`
-and/or `grep`. This number can be used as a metric for improving your test
-quality.
+{: .callout .tip}
+TIP: You can easily count the number of disabled tests you have using
+`grep`. This number can be used as a metric for
+improving your test quality.
 
 #### Temporarily Enabling Disabled Tests
 
@@ -2201,38 +1918,25 @@
 googletest can use colors in its terminal output to make it easier to spot the
 important information:
 
-<code>
-...<br/>
-  <font color="green">[----------]</font><font color="black"> 1 test from
-  FooTest</font><br/>
-  <font color="green">[ RUN &nbsp; &nbsp; &nbsp;]</font><font color="black">
-  FooTest.DoesAbc</font><br/>
-  <font color="green">[ &nbsp; &nbsp; &nbsp; OK ]</font><font color="black">
-  FooTest.DoesAbc </font><br/>
-  <font color="green">[----------]</font><font color="black">
-  2 tests from BarTest</font><br/>
-  <font color="green">[ RUN &nbsp; &nbsp; &nbsp;]</font><font color="black">
-  BarTest.HasXyzProperty </font><br/>
-  <font color="green">[ &nbsp; &nbsp; &nbsp; OK ]</font><font color="black">
-  BarTest.HasXyzProperty</font><br/>
-  <font color="green">[ RUN &nbsp; &nbsp; &nbsp;]</font><font color="black">
-  BarTest.ReturnsTrueOnSuccess ... some error messages ...</font><br/>
-  <font color="red">[ &nbsp; FAILED ]</font><font color="black">
-  BarTest.ReturnsTrueOnSuccess ...</font><br/>
-  <font color="green">[==========]</font><font color="black">
-  30 tests from 14 test suites ran.</font><br/>
-  <font color="green">[ &nbsp; PASSED ]</font><font color="black">
-  28 tests.</font><br/>
-  <font color="red">[ &nbsp; FAILED ]</font><font color="black">
-  2 tests, listed below:</font><br/>
-  <font color="red">[ &nbsp; FAILED ]</font><font color="black">
-  BarTest.ReturnsTrueOnSuccess</font><br/>
-  <font color="red">[ &nbsp; FAILED ]</font><font color="black">
-  AnotherTest.DoesXyz<br/>
-<br/>
-  2 FAILED TESTS
-  </font>
-</code>
+<pre>...
+<font color="green">[----------]</font> 1 test from FooTest
+<font color="green">[ RUN      ]</font> FooTest.DoesAbc
+<font color="green">[       OK ]</font> FooTest.DoesAbc
+<font color="green">[----------]</font> 2 tests from BarTest
+<font color="green">[ RUN      ]</font> BarTest.HasXyzProperty
+<font color="green">[       OK ]</font> BarTest.HasXyzProperty
+<font color="green">[ RUN      ]</font> BarTest.ReturnsTrueOnSuccess
+... some error messages ...
+<font color="red">[   FAILED ]</font> BarTest.ReturnsTrueOnSuccess
+...
+<font color="green">[==========]</font> 30 tests from 14 test suites ran.
+<font color="green">[   PASSED ]</font> 28 tests.
+<font color="red">[   FAILED ]</font> 2 tests, listed below:
+<font color="red">[   FAILED ]</font> BarTest.ReturnsTrueOnSuccess
+<font color="red">[   FAILED ]</font> AnotherTest.DoesXyz
+
+ 2 FAILED TESTS
+</pre>
 
 You can set the `GTEST_COLOR` environment variable or the `--gtest_color`
 command line flag to `yes`, `no`, or `auto` (the default) to enable colors,
@@ -2240,6 +1944,12 @@
 will use colors if and only if the output goes to a terminal and (on non-Windows
 platforms) the `TERM` environment variable is set to `xterm` or `xterm-color`.
 
+#### Suppressing test passes
+
+By default, googletest prints 1 line of output for each test, indicating if it
+passed or failed. To show only test failures, run the test program with
+`--gtest_brief=1`, or set the GTEST_BRIEF environment variable to `1`.
+
 #### Suppressing the Elapsed Time
 
 By default, googletest prints the time it takes to run each test. To disable
@@ -2261,8 +1971,7 @@
 
 googletest can emit a detailed XML report to a file in addition to its normal
 textual output. The report contains the duration of each test, and thus can help
-you identify slow tests. The report is also used by the http://unittest
-dashboard to show per-test-method error messages.
+you identify slow tests.
 
 To generate the XML report, set the `GTEST_OUTPUT` environment variable or the
 `--gtest_output` flag to the string `"xml:path_to_output_file"`, which will
@@ -2537,10 +2246,23 @@
 }
 ```
 
+{: .callout .important}
 IMPORTANT: The exact format of the JSON document is subject to change.
 
 ### Controlling How Failures Are Reported
 
+#### Detecting Test Premature Exit
+
+Google Test implements the _premature-exit-file_ protocol for test runners
+to catch any kind of unexpected exits of test programs. Upon start,
+Google Test creates the file which will be automatically deleted after
+all work has been finished. Then, the test runner can check if this file
+exists. In case the file remains undeleted, the inspected test has exited
+prematurely.
+
+This feature is enabled only if the `TEST_PREMATURE_EXIT_FILE` environment
+variable has been set.
+
 #### Turning Assertion Failures into Break-Points
 
 When running test programs under a debugger, it's very convenient if the
@@ -2565,3 +2287,32 @@
 exception is thrown. To achieve that, set the `GTEST_CATCH_EXCEPTIONS`
 environment variable to `0`, or use the `--gtest_catch_exceptions=0` flag when
 running the tests.
+
+### Sanitizer Integration
+
+The
+[Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html),
+[Address Sanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer),
+and
+[Thread Sanitizer](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual)
+all provide weak functions that you can override to trigger explicit failures
+when they detect sanitizer errors, such as creating a reference from `nullptr`.
+To override these functions, place definitions for them in a source file that
+you compile as part of your main binary:
+
+```
+extern "C" {
+void __ubsan_on_report() {
+  FAIL() << "Encountered an undefined behavior sanitizer error";
+}
+void __asan_on_error() {
+  FAIL() << "Encountered an address sanitizer error";
+}
+void __tsan_on_report() {
+  FAIL() << "Encountered a thread sanitizer error";
+}
+}  // extern "C"
+```
+
+After compiling your project with one of the sanitizers enabled, if a particular
+test triggers a sanitizer error, googletest will report that it failed.
diff --git a/ext/googletest/docs/assets/css/style.scss b/ext/googletest/docs/assets/css/style.scss
new file mode 100644
index 0000000..bb30f41
--- /dev/null
+++ b/ext/googletest/docs/assets/css/style.scss
@@ -0,0 +1,5 @@
+---
+---
+
+@import "jekyll-theme-primer";
+@import "main";
diff --git a/ext/googletest/docs/community_created_documentation.md b/ext/googletest/docs/community_created_documentation.md
new file mode 100644
index 0000000..4569075
--- /dev/null
+++ b/ext/googletest/docs/community_created_documentation.md
@@ -0,0 +1,7 @@
+# Community-Created Documentation
+
+The following is a list, in no particular order, of links to documentation
+created by the Googletest community.
+
+*   [Googlemock Insights](https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/googletest/insights.md),
+    by [ElectricRCAircraftGuy](https://github.com/ElectricRCAircraftGuy)
diff --git a/ext/googletest/googletest/docs/faq.md b/ext/googletest/docs/faq.md
similarity index 85%
rename from ext/googletest/googletest/docs/faq.md
rename to ext/googletest/docs/faq.md
index 960a827..9042da1 100644
--- a/ext/googletest/googletest/docs/faq.md
+++ b/ext/googletest/docs/faq.md
@@ -1,9 +1,12 @@
 # Googletest FAQ
 
-<!-- GOOGLETEST_CM0014 DO NOT DELETE -->
-
 ## Why should test suite names and test names not contain underscore?
 
+{: .callout .note}
+Note: Googletest reserves underscore (`_`) for special purpose keywords, such as
+[the `DISABLED_` prefix](advanced.md#temporarily-disabling-tests), in addition
+to the following rationale.
+
 Underscore (`_`) is special, as C++ reserves the following to be used by the
 compiler and the standard library:
 
@@ -57,9 +60,10 @@
 
 ## Why does googletest support `EXPECT_EQ(NULL, ptr)` and `ASSERT_EQ(NULL, ptr)` but not `EXPECT_NE(NULL, ptr)` and `ASSERT_NE(NULL, ptr)`?
 
-First of all you can use `EXPECT_NE(nullptr, ptr)` and `ASSERT_NE(nullptr,
-ptr)`. This is the preferred syntax in the style guide because nullptr does not
-have the type problems that NULL does. Which is why NULL does not work.
+First of all, you can use `nullptr` with each of these macros, e.g.
+`EXPECT_EQ(ptr, nullptr)`, `EXPECT_NE(ptr, nullptr)`, `ASSERT_EQ(ptr, nullptr)`,
+`ASSERT_NE(ptr, nullptr)`. This is the preferred syntax in the style guide
+because `nullptr` does not have the type problems that `NULL` does.
 
 Due to some peculiarity of C++, it requires some non-trivial template meta
 programming tricks to support using `NULL` as an argument of the `EXPECT_XX()`
@@ -67,22 +71,21 @@
 (otherwise we make the implementation of googletest harder to maintain and more
 error-prone than necessary).
 
-The `EXPECT_EQ()` macro takes the *expected* value as its first argument and the
-*actual* value as the second. It's reasonable that someone wants to write
-`EXPECT_EQ(NULL, some_expression)`, and this indeed was requested several times.
-Therefore we implemented it.
+Historically, the `EXPECT_EQ()` macro took the *expected* value as its first
+argument and the *actual* value as the second, though this argument order is now
+discouraged. It was reasonable that someone wanted
+to write `EXPECT_EQ(NULL, some_expression)`, and this indeed was requested
+several times. Therefore we implemented it.
 
-The need for `EXPECT_NE(NULL, ptr)` isn't nearly as strong. When the assertion
+The need for `EXPECT_NE(NULL, ptr)` wasn't nearly as strong. When the assertion
 fails, you already know that `ptr` must be `NULL`, so it doesn't add any
 information to print `ptr` in this case. That means `EXPECT_TRUE(ptr != NULL)`
 works just as well.
 
-If we were to support `EXPECT_NE(NULL, ptr)`, for consistency we'll have to
-support `EXPECT_NE(ptr, NULL)` as well, as unlike `EXPECT_EQ`, we don't have a
-convention on the order of the two arguments for `EXPECT_NE`. This means using
-the template meta programming tricks twice in the implementation, making it even
-harder to understand and maintain. We believe the benefit doesn't justify the
-cost.
+If we were to support `EXPECT_NE(NULL, ptr)`, for consistency we'd have to
+support `EXPECT_NE(ptr, NULL)` as well. This means using the template meta
+programming tricks twice in the implementation, making it even harder to
+understand and maintain. We believe the benefit doesn't justify the cost.
 
 Finally, with the growth of the gMock matcher library, we are encouraging people
 to use the unified `EXPECT_THAT(value, matcher)` syntax more often in tests. One
@@ -127,6 +130,7 @@
 
 ## I got some run-time errors about invalid proto descriptors when using `ProtocolMessageEquals`. Help!
 
+{: .callout .note}
 **Note:** `ProtocolMessageEquals` and `ProtocolMessageEquiv` are *deprecated*
 now. Please use `EqualsProto`, etc instead.
 
@@ -176,18 +180,6 @@
 to `htonl()`. It is difficult to make `EXPECT_EQ` bypass the `htonl()` bug, as
 the solution must work with different compilers on various platforms.
 
-`htonl()` has some other problems as described in `//util/endian/endian.h`,
-which defines `ghtonl()` to replace it. `ghtonl()` does the same thing `htonl()`
-does, only without its problems. We suggest you to use `ghtonl()` instead of
-`htonl()`, both in your tests and production code.
-
-`//util/endian/endian.h` also defines `ghtons()`, which solves similar problems
-in `htons()`.
-
-Don't forget to add `//util/endian` to the list of dependencies in the `BUILD`
-file wherever `ghtonl()` and `ghtons()` are used. The library consists of a
-single header file and will not bloat your binary.
-
 ## The compiler complains about "undefined references" to some static const member variables, but I did define them in the class body. What's wrong?
 
 If your class has a static data member:
@@ -211,6 +203,18 @@
 generate an "undefined reference" linker error. The fact that "it used to work"
 doesn't mean it's valid. It just means that you were lucky. :-)
 
+If the declaration of the static data member is `constexpr` then it is
+implicitly an `inline` definition, and a separate definition in `foo.cc` is not
+needed:
+
+```c++
+// foo.h
+class Foo {
+  ...
+  static constexpr int kBar = 100;  // Defines kBar, no need to do it in foo.cc.
+};
+```
+
 ## Can I derive a test fixture from another?
 
 Yes.
@@ -263,7 +267,7 @@
 googletest has no limit on how deep the hierarchy can be.
 
 For a complete example using derived test fixtures, see
-[sample5_unittest.cc](../samples/sample5_unittest.cc).
+[sample5_unittest.cc](https://github.com/google/googletest/blob/master/googletest/samples/sample5_unittest.cc).
 
 ## My compiler complains "void value not ignored as it ought to be." What does this mean?
 
@@ -275,8 +279,9 @@
 ## My death test hangs (or seg-faults). How do I fix it?
 
 In googletest, death tests are run in a child process and the way they work is
-delicate. To write death tests you really need to understand how they work.
-Please make sure you have read [this](advanced.md#how-it-works).
+delicate. To write death tests you really need to understand how they work—see
+the details at [Death Assertions](reference/assertions.md#death) in the
+Assertions Reference.
 
 In particular, death tests don't like having multiple threads in the parent
 process. So the first thing you can try is to eliminate creating threads outside
@@ -295,7 +300,7 @@
 program can run side-by-side with itself and is deterministic.
 
 In the end, this boils down to good concurrent programming. You have to make
-sure that there is no race conditions or dead locks in your program. No silver
+sure that there are no race conditions or deadlocks in your program. No silver
 bullet - sorry!
 
 ## Should I use the constructor/destructor of the test fixture or SetUp()/TearDown()? {#CtorVsSetUp}
@@ -332,8 +337,8 @@
 *   In the body of a constructor (or destructor), it's not possible to use the
     `ASSERT_xx` macros. Therefore, if the set-up operation could cause a fatal
     test failure that should prevent the test from running, it's necessary to
-    use `abort` <!-- GOOGLETEST_CM0015 DO NOT DELETE --> and abort the whole test executable,
-    or to use `SetUp()` instead of a constructor.
+    use `abort` and abort the whole test
+    executable, or to use `SetUp()` instead of a constructor.
 *   If the tear-down operation could throw an exception, you must use
     `TearDown()` as opposed to the destructor, as throwing in a destructor leads
     to undefined behavior and usually will kill your program right away. Note
@@ -349,72 +354,8 @@
 
 ## The compiler complains "no matching function to call" when I use ASSERT_PRED*. How do I fix it?
 
-If the predicate function you use in `ASSERT_PRED*` or `EXPECT_PRED*` is
-overloaded or a template, the compiler will have trouble figuring out which
-overloaded version it should use. `ASSERT_PRED_FORMAT*` and
-`EXPECT_PRED_FORMAT*` don't have this problem.
-
-If you see this error, you might want to switch to
-`(ASSERT|EXPECT)_PRED_FORMAT*`, which will also give you a better failure
-message. If, however, that is not an option, you can resolve the problem by
-explicitly telling the compiler which version to pick.
-
-For example, suppose you have
-
-```c++
-bool IsPositive(int n) {
-  return n > 0;
-}
-
-bool IsPositive(double x) {
-  return x > 0;
-}
-```
-
-you will get a compiler error if you write
-
-```c++
-EXPECT_PRED1(IsPositive, 5);
-```
-
-However, this will work:
-
-```c++
-EXPECT_PRED1(static_cast<bool (*)(int)>(IsPositive), 5);
-```
-
-(The stuff inside the angled brackets for the `static_cast` operator is the type
-of the function pointer for the `int`-version of `IsPositive()`.)
-
-As another example, when you have a template function
-
-```c++
-template <typename T>
-bool IsNegative(T x) {
-  return x < 0;
-}
-```
-
-you can use it in a predicate assertion like this:
-
-```c++
-ASSERT_PRED1(IsNegative<int>, -5);
-```
-
-Things are more interesting if your template has more than one parameters. The
-following won't compile:
-
-```c++
-ASSERT_PRED2(GreaterThan<int, int>, 5, 0);
-```
-
-as the C++ pre-processor thinks you are giving `ASSERT_PRED2` 4 arguments, which
-is one more than expected. The workaround is to wrap the predicate function in
-parentheses:
-
-```c++
-ASSERT_PRED2((GreaterThan<int, int>), 5, 0);
-```
+See details for [`EXPECT_PRED*`](reference/assertions.md#EXPECT_PRED) in the
+Assertions Reference.
 
 ## My compiler complains about "ignoring return value" when I call RUN_ALL_TESTS(). Why?
 
@@ -531,8 +472,8 @@
 
 ## What can the statement argument in ASSERT_DEATH() be?
 
-`ASSERT_DEATH(*statement*, *regex*)` (or any death assertion macro) can be used
-wherever `*statement*` is valid. So basically `*statement*` can be any C++
+`ASSERT_DEATH(statement, matcher)` (or any death assertion macro) can be used
+wherever *`statement`* is valid. So basically *`statement`* can be any C++
 statement that makes sense in the current context. In particular, it can
 reference global and/or local variables, and can be:
 
@@ -555,7 +496,7 @@
                "(Func1|Method) failed");
 }
 
-// Death assertions can be used any where in a function.  In
+// Death assertions can be used anywhere in a function.  In
 // particular, they can be inside a loop.
 TEST(MyDeathTest, InsideLoop) {
   // Verifies that Foo(0), Foo(1), ..., and Foo(4) all die.
@@ -578,8 +519,6 @@
 }
 ```
 
-gtest-death-test_test.cc contains more examples if you are interested.
-
 ## I have a fixture class `FooTest`, but `TEST_F(FooTest, Bar)` gives me error ``"no matching function for call to `FooTest::FooTest()'"``. Why?
 
 Googletest needs to be able to create objects of your test fixture class, so it
@@ -597,7 +536,7 @@
 ## Why does ASSERT_DEATH complain about previous threads that were already joined?
 
 With the Linux pthread library, there is no turning back once you cross the line
-from single thread to multiple threads. The first time you create a thread, a
+from a single thread to multiple threads. The first time you create a thread, a
 manager thread is created in addition, so you get 3, not 2, threads. Later when
 the thread you create joins the main thread, the thread count decrements by 1,
 but the manager thread will never be killed, so you still have 2 threads, which
@@ -612,7 +551,7 @@
 googletest does not interleave tests from different test suites. That is, it
 runs all tests in one test suite first, and then runs all tests in the next test
 suite, and so on. googletest does this because it needs to set up a test suite
-before the first test in it is run, and tear it down afterwords. Splitting up
+before the first test in it is run, and tear it down afterwards. Splitting up
 the test case would require multiple set-up and tear-down processes, which is
 inefficient and makes the semantics unclean.
 
@@ -661,14 +600,15 @@
 match). Admittedly, this is a hack. We'll consider a more permanent solution
 after the fork-and-exec-style death tests are implemented.
 
-## The compiler complains about "no match for 'operator<<'" when I use an assertion. What gives?
+## The compiler complains about `no match for 'operator<<'` when I use an assertion. What gives?
 
 If you use a user-defined type `FooType` in an assertion, you must make sure
 there is an `std::ostream& operator<<(std::ostream&, const FooType&)` function
 defined such that we can print a value of `FooType`.
 
 In addition, if `FooType` is declared in a name space, the `<<` operator also
-needs to be defined in the *same* name space. See https://abseil.io/tips/49 for details.
+needs to be defined in the *same* name space. See
+[Tip of the Week #49](http://abseil.io/tips/49) for details.
 
 ## How do I suppress the memory leak messages on Windows?
 
@@ -689,10 +629,10 @@
 advise against the practice, and googletest doesn't provide a way to do it.
 
 In general, the recommended way to cause the code to behave differently under
-test is [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection). You can inject
+test is [Dependency Injection](http://en.wikipedia.org/wiki/Dependency_injection). You can inject
 different functionality from the test and from the production code. Since your
 production code doesn't link in the for-test logic at all (the
-[`testonly`](https://docs.bazel.build/versions/master/be/common-definitions.html#common.testonly) attribute for BUILD targets helps to ensure
+[`testonly`](http://docs.bazel.build/versions/master/be/common-definitions.html#common.testonly) attribute for BUILD targets helps to ensure
 that), there is no danger in accidentally running it.
 
 However, if you *really*, *really*, *really* have no choice, and if you follow
@@ -703,12 +643,12 @@
 ## How do I temporarily disable a test?
 
 If you have a broken test that you cannot fix right away, you can add the
-DISABLED_ prefix to its name. This will exclude it from execution. This is
-better than commenting out the code or using #if 0, as disabled tests are still
-compiled (and thus won't rot).
+`DISABLED_` prefix to its name. This will exclude it from execution. This is
+better than commenting out the code or using `#if 0`, as disabled tests are
+still compiled (and thus won't rot).
 
 To include disabled tests in test execution, just invoke the test program with
-the --gtest_also_run_disabled_tests flag.
+the `--gtest_also_run_disabled_tests` flag.
 
 ## Is it OK if I have two separate `TEST(Foo, Bar)` test methods defined in different namespaces?
 
diff --git a/ext/googletest/docs/gmock_cheat_sheet.md b/ext/googletest/docs/gmock_cheat_sheet.md
new file mode 100644
index 0000000..3d164ad
--- /dev/null
+++ b/ext/googletest/docs/gmock_cheat_sheet.md
@@ -0,0 +1,241 @@
+# gMock Cheat Sheet
+
+## Defining a Mock Class
+
+### Mocking a Normal Class {#MockClass}
+
+Given
+
+```cpp
+class Foo {
+  ...
+  virtual ~Foo();
+  virtual int GetSize() const = 0;
+  virtual string Describe(const char* name) = 0;
+  virtual string Describe(int type) = 0;
+  virtual bool Process(Bar elem, int count) = 0;
+};
+```
+
+(note that `~Foo()` **must** be virtual) we can define its mock as
+
+```cpp
+#include "gmock/gmock.h"
+
+class MockFoo : public Foo {
+  ...
+  MOCK_METHOD(int, GetSize, (), (const, override));
+  MOCK_METHOD(string, Describe, (const char* name), (override));
+  MOCK_METHOD(string, Describe, (int type), (override));
+  MOCK_METHOD(bool, Process, (Bar elem, int count), (override));
+};
+```
+
+To create a "nice" mock, which ignores all uninteresting calls, a "naggy" mock,
+which warns on all uninteresting calls, or a "strict" mock, which treats them as
+failures:
+
+```cpp
+using ::testing::NiceMock;
+using ::testing::NaggyMock;
+using ::testing::StrictMock;
+
+NiceMock<MockFoo> nice_foo;      // The type is a subclass of MockFoo.
+NaggyMock<MockFoo> naggy_foo;    // The type is a subclass of MockFoo.
+StrictMock<MockFoo> strict_foo;  // The type is a subclass of MockFoo.
+```
+
+{: .callout .note}
+**Note:** A mock object is currently naggy by default. We may make it nice by
+default in the future.
+
+### Mocking a Class Template {#MockTemplate}
+
+Class templates can be mocked just like any class.
+
+To mock
+
+```cpp
+template <typename Elem>
+class StackInterface {
+  ...
+  virtual ~StackInterface();
+  virtual int GetSize() const = 0;
+  virtual void Push(const Elem& x) = 0;
+};
+```
+
+(note that all member functions that are mocked, including `~StackInterface()`
+**must** be virtual).
+
+```cpp
+template <typename Elem>
+class MockStack : public StackInterface<Elem> {
+  ...
+  MOCK_METHOD(int, GetSize, (), (const, override));
+  MOCK_METHOD(void, Push, (const Elem& x), (override));
+};
+```
+
+### Specifying Calling Conventions for Mock Functions
+
+If your mock function doesn't use the default calling convention, you can
+specify it by adding `Calltype(convention)` to `MOCK_METHOD`'s 4th parameter.
+For example,
+
+```cpp
+  MOCK_METHOD(bool, Foo, (int n), (Calltype(STDMETHODCALLTYPE)));
+  MOCK_METHOD(int, Bar, (double x, double y),
+              (const, Calltype(STDMETHODCALLTYPE)));
+```
+
+where `STDMETHODCALLTYPE` is defined by `<objbase.h>` on Windows.
+
+## Using Mocks in Tests {#UsingMocks}
+
+The typical work flow is:
+
+1.  Import the gMock names you need to use. All gMock symbols are in the
+    `testing` namespace unless they are macros or otherwise noted.
+2.  Create the mock objects.
+3.  Optionally, set the default actions of the mock objects.
+4.  Set your expectations on the mock objects (How will they be called? What
+    will they do?).
+5.  Exercise code that uses the mock objects; if necessary, check the result
+    using googletest assertions.
+6.  When a mock object is destructed, gMock automatically verifies that all
+    expectations on it have been satisfied.
+
+Here's an example:
+
+```cpp
+using ::testing::Return;                          // #1
+
+TEST(BarTest, DoesThis) {
+  MockFoo foo;                                    // #2
+
+  ON_CALL(foo, GetSize())                         // #3
+      .WillByDefault(Return(1));
+  // ... other default actions ...
+
+  EXPECT_CALL(foo, Describe(5))                   // #4
+      .Times(3)
+      .WillRepeatedly(Return("Category 5"));
+  // ... other expectations ...
+
+  EXPECT_EQ(MyProductionFunction(&foo), "good");  // #5
+}                                                 // #6
+```
+
+## Setting Default Actions {#OnCall}
+
+gMock has a **built-in default action** for any function that returns `void`,
+`bool`, a numeric value, or a pointer. In C++11, it will additionally returns
+the default-constructed value, if one exists for the given type.
+
+To customize the default action for functions with return type `T`, use
+[`DefaultValue<T>`](reference/mocking.md#DefaultValue). For example:
+
+```cpp
+  // Sets the default action for return type std::unique_ptr<Buzz> to
+  // creating a new Buzz every time.
+  DefaultValue<std::unique_ptr<Buzz>>::SetFactory(
+      [] { return MakeUnique<Buzz>(AccessLevel::kInternal); });
+
+  // When this fires, the default action of MakeBuzz() will run, which
+  // will return a new Buzz object.
+  EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")).Times(AnyNumber());
+
+  auto buzz1 = mock_buzzer_.MakeBuzz("hello");
+  auto buzz2 = mock_buzzer_.MakeBuzz("hello");
+  EXPECT_NE(buzz1, nullptr);
+  EXPECT_NE(buzz2, nullptr);
+  EXPECT_NE(buzz1, buzz2);
+
+  // Resets the default action for return type std::unique_ptr<Buzz>,
+  // to avoid interfere with other tests.
+  DefaultValue<std::unique_ptr<Buzz>>::Clear();
+```
+
+To customize the default action for a particular method of a specific mock
+object, use [`ON_CALL`](reference/mocking.md#ON_CALL). `ON_CALL` has a similar
+syntax to `EXPECT_CALL`, but it is used for setting default behaviors when you
+do not require that the mock method is called. See
+[Knowing When to Expect](gmock_cook_book.md#UseOnCall) for a more detailed
+discussion.
+
+## Setting Expectations {#ExpectCall}
+
+See [`EXPECT_CALL`](reference/mocking.md#EXPECT_CALL) in the Mocking Reference.
+
+## Matchers {#MatcherList}
+
+See the [Matchers Reference](reference/matchers.md).
+
+## Actions {#ActionList}
+
+See the [Actions Reference](reference/actions.md).
+
+## Cardinalities {#CardinalityList}
+
+See the [`Times` clause](reference/mocking.md#EXPECT_CALL.Times) of
+`EXPECT_CALL` in the Mocking Reference.
+
+## Expectation Order
+
+By default, expectations can be matched in *any* order. If some or all
+expectations must be matched in a given order, you can use the
+[`After` clause](reference/mocking.md#EXPECT_CALL.After) or
+[`InSequence` clause](reference/mocking.md#EXPECT_CALL.InSequence) of
+`EXPECT_CALL`, or use an [`InSequence` object](reference/mocking.md#InSequence).
+
+## Verifying and Resetting a Mock
+
+gMock will verify the expectations on a mock object when it is destructed, or
+you can do it earlier:
+
+```cpp
+using ::testing::Mock;
+...
+// Verifies and removes the expectations on mock_obj;
+// returns true if and only if successful.
+Mock::VerifyAndClearExpectations(&mock_obj);
+...
+// Verifies and removes the expectations on mock_obj;
+// also removes the default actions set by ON_CALL();
+// returns true if and only if successful.
+Mock::VerifyAndClear(&mock_obj);
+```
+
+Do not set new expectations after verifying and clearing a mock after its use.
+Setting expectations after code that exercises the mock has undefined behavior.
+See [Using Mocks in Tests](gmock_for_dummies.md#using-mocks-in-tests) for more
+information.
+
+You can also tell gMock that a mock object can be leaked and doesn't need to be
+verified:
+
+```cpp
+Mock::AllowLeak(&mock_obj);
+```
+
+## Mock Classes
+
+gMock defines a convenient mock class template
+
+```cpp
+class MockFunction<R(A1, ..., An)> {
+ public:
+  MOCK_METHOD(R, Call, (A1, ..., An));
+};
+```
+
+See this [recipe](gmock_cook_book.md#UsingCheckPoints) for one application of
+it.
+
+## Flags
+
+| Flag                           | Description                               |
+| :----------------------------- | :---------------------------------------- |
+| `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. |
+| `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. |
diff --git a/ext/googletest/googlemock/docs/cook_book.md b/ext/googletest/docs/gmock_cook_book.md
similarity index 89%
rename from ext/googletest/googlemock/docs/cook_book.md
rename to ext/googletest/docs/gmock_cook_book.md
index ea55ab3..c08958e 100644
--- a/ext/googletest/googlemock/docs/cook_book.md
+++ b/ext/googletest/docs/gmock_cook_book.md
@@ -1,10 +1,10 @@
 # gMock Cookbook
 
-<!-- GOOGLETEST_CM0012 DO NOT DELETE -->
-
 You can find recipes for using gMock here. If you haven't yet, please read
-[this](for_dummies.md) first to make sure you understand the basics.
+[the dummy guide](gmock_for_dummies.md) first to make sure you understand the
+basics.
 
+{: .callout .note}
 **Note:** gMock lives in the `testing` name space. For readability, it is
 recommended to write `using ::testing::Foo;` once in your file before using the
 name `Foo` defined by gMock. We omit such `using` statements in this section for
@@ -35,13 +35,17 @@
     `noexcept` method.
 *   **`Calltype(...)`** - Sets the call type for the method (e.g. to
     `STDMETHODCALLTYPE`), useful in Windows.
+*   **`ref(...)`** - Marks the method with the reference qualification
+    specified. Required if overriding a method that has reference
+    qualifications. Eg `ref(&)` or `ref(&&)`.
 
 ### Dealing with unprotected commas
 
 Unprotected commas, i.e. commas which are not surrounded by parentheses, prevent
 `MOCK_METHOD` from parsing its arguments correctly:
 
-```cpp {.bad}
+{: .bad}
+```cpp
 class MockFoo {
  public:
   MOCK_METHOD(std::pair<bool, int>, GetPair, ());  // Won't compile!
@@ -51,7 +55,8 @@
 
 Solution 1 - wrap with parentheses:
 
-```cpp {.good}
+{: .good}
+```cpp
 class MockFoo {
  public:
   MOCK_METHOD((std::pair<bool, int>), GetPair, ());
@@ -64,7 +69,8 @@
 
 Solution 2 - define an alias:
 
-```cpp {.good}
+{: .good}
+```cpp
 class MockFoo {
  public:
   using BoolAndInt = std::pair<bool, int>;
@@ -138,6 +144,7 @@
 };
 ```
 
+{: .callout .note}
 **Note:** if you don't mock all versions of the overloaded method, the compiler
 will give you a warning about some methods in the base class being hidden. To
 fix that, use `using` to bring them in scope:
@@ -177,8 +184,7 @@
 
 ### Mocking Non-virtual Methods {#MockingNonVirtualMethods}
 
-gMock can mock non-virtual functions to be used in Hi-perf dependency
-injection.<!-- GOOGLETEST_CM0017 DO NOT DELETE -->
+gMock can mock non-virtual functions to be used in Hi-perf dependency injection.
 
 In this case, instead of sharing a common base class with the real class, your
 mock class will be *unrelated* to the real class, but contain methods with the
@@ -245,9 +251,9 @@
 
 ### Mocking Free Functions
 
-It's possible to use gMock to mock a free function (i.e. a C-style function or a
-static method). You just need to rewrite your code to use an interface (abstract
-class).
+It is not possible to directly mock a free function (i.e. a C-style function or
+a static method). If you need to, you can rewrite your code to use an interface
+(abstract class).
 
 Instead of calling a free function (say, `OpenFile`) directly, introduce an
 interface for it and have a concrete subclass that calls the free function:
@@ -262,7 +268,7 @@
 class File : public FileInterface {
  public:
   ...
-  virtual bool Open(const char* path, const char* mode) {
+  bool Open(const char* path, const char* mode) override {
      return OpenFile(path, mode);
   }
 };
@@ -281,9 +287,11 @@
 
 ### Old-Style `MOCK_METHODn` Macros
 
-Before the generic `MOCK_METHOD` macro was introduced, mocks where created using
-a family of macros collectively called `MOCK_METHODn`. These macros are still
-supported, though migration to the new `MOCK_METHOD` is recommended.
+Before the generic `MOCK_METHOD` macro
+[was introduced in 2018](https://github.com/google/googletest/commit/c5f08bf91944ce1b19bcf414fa1760e69d20afc2),
+mocks where created using a family of macros collectively called `MOCK_METHODn`.
+These macros are still supported, though migration to the new `MOCK_METHOD` is
+recommended.
 
 The macros in the `MOCK_METHODn` family differ from `MOCK_METHOD`:
 
@@ -297,44 +305,86 @@
 
 Old macros and their new equivalents:
 
-<a name="table99"></a>
-<table border="1" cellspacing="0" cellpadding="1">
-<tr> <th colspan=2> Simple </th></tr>
-<tr> <td> Old </td> <td> `MOCK_METHOD1(Foo, bool(int))` </td> </tr>
-<tr> <td> New </td> <td> `MOCK_METHOD(bool, Foo, (int))` </td> </tr>
+<table>
+  <tr><th colspan=2>Simple</th></tr>
+  <tr>
+    <td>Old</td>
+    <td><code>MOCK_METHOD1(Foo, bool(int))</code></td>
+  </tr>
+  <tr>
+    <td>New</td>
+    <td><code>MOCK_METHOD(bool, Foo, (int))</code></td>
+  </tr>
 
-<tr> <th colspan=2> Const Method </th></tr> <tr> <td> Old </td> <td>
-`MOCK_CONST_METHOD1(Foo, bool(int))` </td> </tr> <tr> <td> New </td> <td>
-`MOCK_METHOD(bool, Foo, (int), (const))` </td> </tr>
+  <tr><th colspan=2>Const Method</th></tr>
+  <tr>
+    <td>Old</td>
+    <td><code>MOCK_CONST_METHOD1(Foo, bool(int))</code></td>
+  </tr>
+  <tr>
+    <td>New</td>
+    <td><code>MOCK_METHOD(bool, Foo, (int), (const))</code></td>
+  </tr>
 
-<tr> <th colspan=2> Method in a Class Template </th></tr> <tr> <td> Old </td>
-<td> `MOCK_METHOD1_T(Foo, bool(int))` </td> </tr> <tr> <td> New </td> <td>
-`MOCK_METHOD(bool, Foo, (int))` </td> </tr>
+  <tr><th colspan=2>Method in a Class Template</th></tr>
+  <tr>
+    <td>Old</td>
+    <td><code>MOCK_METHOD1_T(Foo, bool(int))</code></td>
+  </tr>
+  <tr>
+    <td>New</td>
+    <td><code>MOCK_METHOD(bool, Foo, (int))</code></td>
+  </tr>
 
-<tr> <th colspan=2> Const Method in a Class Template </th></tr> <tr> <td> Old
-</td> <td> `MOCK_CONST_METHOD1_T(Foo, bool(int))` </td> </tr> <tr> <td> New
-</td> <td> `MOCK_METHOD(bool, Foo, (int), (const))` </td> </tr>
+  <tr><th colspan=2>Const Method in a Class Template</th></tr>
+  <tr>
+    <td>Old</td>
+    <td><code>MOCK_CONST_METHOD1_T(Foo, bool(int))</code></td>
+  </tr>
+  <tr>
+    <td>New</td>
+    <td><code>MOCK_METHOD(bool, Foo, (int), (const))</code></td>
+  </tr>
 
-<tr> <th colspan=2> Method with Call Type </th></tr> <tr> <td> Old </td> <td>
-`MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))` </td> </tr> <tr>
-<td> New </td> <td> `MOCK_METHOD(bool, Foo, (int),
-(Calltype(STDMETHODCALLTYPE)))` </td> </tr>
+  <tr><th colspan=2>Method with Call Type</th></tr>
+  <tr>
+    <td>Old</td>
+    <td><code>MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))</code></td>
+  </tr>
+  <tr>
+    <td>New</td>
+    <td><code>MOCK_METHOD(bool, Foo, (int), (Calltype(STDMETHODCALLTYPE)))</code></td>
+  </tr>
 
-<tr> <th colspan=2> Const Method with Call Type </th></tr> <tr> <td> Old</td>
-<td> `MOCK_CONST_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))` </td>
-</tr> <tr> <td> New </td> <td> `MOCK_METHOD(bool, Foo, (int), (const,
-Calltype(STDMETHODCALLTYPE)))` </td> </tr>
+  <tr><th colspan=2>Const Method with Call Type</th></tr>
+  <tr>
+    <td>Old</td>
+    <td><code>MOCK_CONST_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))</code></td>
+  </tr>
+  <tr>
+    <td>New</td>
+    <td><code>MOCK_METHOD(bool, Foo, (int), (const, Calltype(STDMETHODCALLTYPE)))</code></td>
+  </tr>
 
-<tr> <th colspan=2> Method with Call Type in a Class Template </th></tr> <tr>
-<td> Old </td> <td> `MOCK_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo,
-bool(int))` </td> </tr> <tr> <td> New </td> <td> `MOCK_METHOD(bool, Foo, (int),
-(Calltype(STDMETHODCALLTYPE)))` </td> </tr>
+  <tr><th colspan=2>Method with Call Type in a Class Template</th></tr>
+  <tr>
+    <td>Old</td>
+    <td><code>MOCK_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))</code></td>
+  </tr>
+  <tr>
+    <td>New</td>
+    <td><code>MOCK_METHOD(bool, Foo, (int), (Calltype(STDMETHODCALLTYPE)))</code></td>
+  </tr>
 
-<tr> <th colspan=2> Const Method with Call Type in a Class Template </th></tr>
-<tr> <td> Old </td> <td> `MOCK_CONST_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE,
-Foo, bool(int))` </td> </tr> <tr> <td> New </td> <td> `MOCK_METHOD(bool, Foo,
-(int), (const, Calltype(STDMETHODCALLTYPE)))` </td> </tr>
-
+  <tr><th colspan=2>Const Method with Call Type in a Class Template</th></tr>
+  <tr>
+    <td>Old</td>
+    <td><code>MOCK_CONST_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))</code></td>
+  </tr>
+  <tr>
+    <td>New</td>
+    <td><code>MOCK_METHOD(bool, Foo, (int), (const, Calltype(STDMETHODCALLTYPE)))</code></td>
+  </tr>
 </table>
 
 ### The Nice, the Strict, and the Naggy {#NiceStrictNaggy}
@@ -405,13 +455,14 @@
 }
 ```
 
+{: .callout .note}
 NOTE: `NiceMock` and `StrictMock` only affects *uninteresting* calls (calls of
 *methods* with no expectations); they do not affect *unexpected* calls (calls of
 methods with expectations, but they don't match). See
 [Understanding Uninteresting vs Unexpected Calls](#uninteresting-vs-unexpected).
 
-There are some caveats though (I dislike them just as much as the next guy, but
-sadly they are side effects of C++'s limitations):
+There are some caveats though (sadly they are side effects of C++'s
+limitations):
 
 1.  `NiceMock<MockFoo>` and `StrictMock<MockFoo>` only work for mock methods
     defined using the `MOCK_METHOD` macro **directly** in the `MockFoo` class.
@@ -421,17 +472,7 @@
     `NiceMock<StrictMock<MockFoo> >`) is **not** supported.
 2.  `NiceMock<MockFoo>` and `StrictMock<MockFoo>` may not work correctly if the
     destructor of `MockFoo` is not virtual. We would like to fix this, but it
-    requires cleaning up existing tests. http://b/28934720 tracks the issue.
-3.  During the constructor or destructor of `MockFoo`, the mock object is *not*
-    nice or strict. This may cause surprises if the constructor or destructor
-    calls a mock method on `this` object. (This behavior, however, is consistent
-    with C++'s general rule: if a constructor or destructor calls a virtual
-    method of `this` object, that method is treated as non-virtual. In other
-    words, to the base class's constructor or destructor, `this` object behaves
-    like an instance of the base class, not the derived class. This rule is
-    required for safety. Otherwise a base constructor may use members of a
-    derived class before they are initialized, or a base destructor may use
-    members of a derived class after they have been destroyed.)
+    requires cleaning up existing tests.
 
 Finally, you should be **very cautious** about when to use naggy or strict
 mocks, as they tend to make tests more brittle and harder to maintain. When you
@@ -471,9 +512,9 @@
 class ScopedMockLog : public LogSink {
  public:
   ...
-  virtual void send(LogSeverity severity, const char* full_filename,
+  void send(LogSeverity severity, const char* full_filename,
                     const char* base_filename, int line, const tm* tm_time,
-                    const char* message, size_t message_len) {
+                    const char* message, size_t message_len) override {
     // We are only interested in the log severity, full file name, and
     // log message.
     Log(severity, full_filename, std::string(message, message_len));
@@ -513,7 +554,7 @@
 
 ```cpp
 ON_CALL(factory, DoMakeTurtle)
-    .WillByDefault(MakeMockTurtle());
+    .WillByDefault(Return(MakeMockTurtle()));
 ```
 
 ### Alternative to Mocking Concrete Classes
@@ -777,28 +818,12 @@
 oh-so painful to have to define a new mock class whenever you don't need to mock
 one of its methods).
 
-The trick is to leave a back door in your mock class for accessing the real
-methods in the base class:
-
-```cpp
-class MockFoo : public Foo {
- public:
-  // Mocking a pure method.
-  MOCK_METHOD(void, Pure, (int n), (override));
-  // Mocking a concrete method.  Foo::Concrete() is shadowed.
-  MOCK_METHOD(int, Concrete, (const char* str), (override));
-
-  // Use this to call Concrete() defined in Foo.
-  int FooConcrete(const char* str) { return Foo::Concrete(str); }
-};
-```
-
-Now, you can call `Foo::Concrete()` inside an action by:
+You can call `Foo::Concrete()` inside an action by:
 
 ```cpp
 ...
   EXPECT_CALL(foo, Concrete).WillOnce([&foo](const char* str) {
-    return foo.FooConcrete(str);
+    return foo.Foo::Concrete(str);
   });
 ```
 
@@ -807,7 +832,7 @@
 ```cpp
 ...
   ON_CALL(foo, Concrete).WillByDefault([&foo](const char* str) {
-    return foo.FooConcrete(str);
+    return foo.Foo::Concrete(str);
   });
 ```
 
@@ -848,7 +873,6 @@
 ```cpp
   EXPECT_CALL(foo, DoThat(_, NotNull()));
 ```
-<!-- GOOGLETEST_CM0022 DO NOT DELETE -->
 
 ### Combining Matchers {#CombiningMatchers}
 
@@ -871,6 +895,22 @@
                           NULL));
 ```
 
+Matchers are function objects, and parametrized matchers can be composed just
+like any other function. However because their types can be long and rarely
+provide meaningful information, it can be easier to express them with C++14
+generic lambdas to avoid specifying types. For example,
+
+```cpp
+using ::testing::Contains;
+using ::testing::Property;
+
+inline constexpr auto HasFoo = [](const auto& f) {
+  return Property(&MyClass::foo, Contains(f));
+};
+...
+  EXPECT_THAT(x, HasFoo("blah"));
+```
+
 ### Casting Matchers {#SafeMatcherCast}
 
 gMock matchers are statically typed, meaning that the compiler can catch your
@@ -1024,9 +1064,8 @@
 says that the first argument of `InRange()` must not be 0, and must be less than
 the second argument.
 
-The expression inside `With()` must be a matcher of type
-`Matcher< ::std::tuple<A1, ..., An> >`, where `A1`, ..., `An` are the types of
-the function arguments.
+The expression inside `With()` must be a matcher of type `Matcher<std::tuple<A1,
+..., An>>`, where `A1`, ..., `An` are the types of the function arguments.
 
 You can also write `AllArgs(m)` instead of `m` inside `.With()`. The two forms
 are equivalent, but `.With(AllArgs(Lt()))` is more readable than `.With(Lt())`.
@@ -1049,13 +1088,14 @@
 matchers.
 
 As a convenience and example, gMock provides some matchers for 2-tuples,
-including the `Lt()` matcher above. See [here](#MultiArgMatchers) for the
+including the `Lt()` matcher above. See
+[Multi-argument Matchers](reference/matchers.md#MultiArgMatchers) for the
 complete list.
 
 Note that if you want to pass the arguments to a predicate of your own (e.g.
 `.With(Args<0, 1>(Truly(&MyPredicate)))`), that predicate MUST be written to
-take a `::std::tuple` as its argument; gMock will pass the `n` selected
-arguments as *one* single tuple to the predicate.
+take a `std::tuple` as its argument; gMock will pass the `n` selected arguments
+as *one* single tuple to the predicate.
 
 ### Using Matchers as Predicates
 
@@ -1097,58 +1137,17 @@
 
 ### Using Matchers in googletest Assertions
 
-Since matchers are basically predicates that also know how to describe
-themselves, there is a way to take advantage of them in googletest assertions.
-It's called `ASSERT_THAT` and `EXPECT_THAT`:
-
-```cpp
-  ASSERT_THAT(value, matcher);  // Asserts that value matches matcher.
-  EXPECT_THAT(value, matcher);  // The non-fatal version.
-```
-
-For example, in a googletest test you can write:
-
-```cpp
-#include "gmock/gmock.h"
-
-using ::testing::AllOf;
-using ::testing::Ge;
-using ::testing::Le;
-using ::testing::MatchesRegex;
-using ::testing::StartsWith;
-
-...
-  EXPECT_THAT(Foo(), StartsWith("Hello"));
-  EXPECT_THAT(Bar(), MatchesRegex("Line \\d+"));
-  ASSERT_THAT(Baz(), AllOf(Ge(5), Le(10)));
-```
-
-which (as you can probably guess) executes `Foo()`, `Bar()`, and `Baz()`, and
-verifies that:
-
-*   `Foo()` returns a string that starts with `"Hello"`.
-*   `Bar()` returns a string that matches regular expression `"Line \\d+"`.
-*   `Baz()` returns a number in the range [5, 10].
-
-The nice thing about these macros is that *they read like English*. They
-generate informative messages too. For example, if the first `EXPECT_THAT()`
-above fails, the message will be something like:
-
-```cpp
-Value of: Foo()
-  Actual: "Hi, world!"
-Expected: starts with "Hello"
-```
-
-**Credit:** The idea of `(ASSERT|EXPECT)_THAT` was borrowed from Joe Walnes'
-Hamcrest project, which adds `assertThat()` to JUnit.
+See [`EXPECT_THAT`](reference/assertions.md#EXPECT_THAT) in the Assertions
+Reference.
 
 ### Using Predicates as Matchers
 
-gMock provides a [built-in set](#MatcherList) of matchers. In case you find them
-lacking, you can use an arbitrary unary predicate function or functor as a
-matcher - as long as the predicate accepts a value of the type you want. You do
-this by wrapping the predicate inside the `Truly()` function, for example:
+gMock provides a set of built-in matchers for matching arguments with expected
+values—see the [Matchers Reference](reference/matchers.md) for more information.
+In case you find the built-in set lacking, you can use an arbitrary unary
+predicate function or functor as a matcher - as long as the predicate accepts a
+value of the type you want. You do this by wrapping the predicate inside the
+`Truly()` function, for example:
 
 ```cpp
 using ::testing::Truly;
@@ -1163,8 +1162,6 @@
 works as long as the return value can be used as the condition in in statement
 `if (condition) ...`.
 
-<!-- GOOGLETEST_CM0023 DO NOT DELETE -->
-
 ### Matching Arguments that Are Not Copyable
 
 When you do an `EXPECT_CALL(mock_obj, Foo(bar))`, gMock saves away a copy of
@@ -1181,15 +1178,14 @@
 copy of it. Here's how:
 
 ```cpp
-using ::testing::ByRef;
 using ::testing::Eq;
 using ::testing::Lt;
 ...
   // Expects that Foo()'s argument == bar.
-  EXPECT_CALL(mock_obj, Foo(Eq(ByRef(bar))));
+  EXPECT_CALL(mock_obj, Foo(Eq(std::ref(bar))));
 
   // Expects that Foo()'s argument < bar.
-  EXPECT_CALL(mock_obj, Foo(Lt(ByRef(bar))));
+  EXPECT_CALL(mock_obj, Foo(Lt(std::ref(bar))));
 ```
 
 Remember: if you do this, don't change `bar` after the `EXPECT_CALL()`, or the
@@ -1219,17 +1215,17 @@
 
 For example:
 
-<!-- mdformat off(github rendering does not support multiline tables) -->
 | Expression                   | Description                              |
 | :--------------------------- | :--------------------------------------- |
 | `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`.       |
 | `Property(&Foo::name,  StartsWith("John "))` | Matches `x` where `x.name()` starts with  `"John "`. |
-<!-- mdformat on -->
 
 Note that in `Property(&Foo::baz, ...)`, method `baz()` must take no argument
-and be declared as `const`.
+and be declared as `const`. Don't use `Property()` against member functions that
+you do not own, because taking addresses of functions is fragile and generally
+not part of the contract of the function.
 
-BTW, `Field()` and `Property()` can also match plain pointers to objects. For
+`Field()` and `Property()` can also match plain pointers to objects. For
 instance,
 
 ```cpp
@@ -1318,32 +1314,30 @@
 
 ```cpp
 using ::testing::Matcher;
-using ::testing::MatcherInterface;
-using ::testing::MatchResultListener;
 
-class BarPlusBazEqMatcher : public MatcherInterface<const Foo&> {
+class BarPlusBazEqMatcher {
  public:
   explicit BarPlusBazEqMatcher(int expected_sum)
       : expected_sum_(expected_sum) {}
 
   bool MatchAndExplain(const Foo& foo,
-                       MatchResultListener* /* listener */) const override {
+                       std::ostream* /* listener */) const {
     return (foo.bar() + foo.baz()) == expected_sum_;
   }
 
-  void DescribeTo(::std::ostream* os) const override {
-    *os << "bar() + baz() equals " << expected_sum_;
+  void DescribeTo(std::ostream& os) const {
+    os << "bar() + baz() equals " << expected_sum_;
   }
 
-  void DescribeNegationTo(::std::ostream* os) const override {
-    *os << "bar() + baz() does not equal " << expected_sum_;
+  void DescribeNegationTo(std::ostream& os) const {
+    os << "bar() + baz() does not equal " << expected_sum_;
   }
  private:
   const int expected_sum_;
 };
 
 Matcher<const Foo&> BarPlusBazEq(int expected_sum) {
-  return MakeMatcher(new BarPlusBazEqMatcher(expected_sum));
+  return BarPlusBazEqMatcher(expected_sum);
 }
 
 ...
@@ -1423,6 +1417,8 @@
 
 Use `Pair` when comparing maps or other associative containers.
 
+{% raw %}
+
 ```cpp
 using testing::ElementsAre;
 using testing::Pair;
@@ -1431,6 +1427,8 @@
   EXPECT_THAT(m, ElementsAre(Pair("a", 1), Pair("b", 2), Pair("c", 3)));
 ```
 
+{% endraw %}
+
 **Tips:**
 
 *   `ElementsAre*()` can be used to match *any* container that implements the
@@ -1469,6 +1467,7 @@
 
 ### Matchers must have no side-effects {#PureMatchers}
 
+{: .callout .warning}
 WARNING: gMock does not guarantee when or how many times a matcher will be
 invoked. Therefore, all matchers must be *purely functional*: they cannot have
 any side effects, and the match result must not depend on anything other than
@@ -1483,8 +1482,6 @@
 
 ### Knowing When to Expect {#UseOnCall}
 
-<!-- GOOGLETEST_CM0018 DO NOT DELETE -->
-
 **`ON_CALL`** is likely the *single most under-utilized construct* in gMock.
 
 There are basically two constructs for defining the behavior of a mock object:
@@ -1676,11 +1673,11 @@
 
 ### Expecting Ordered Calls {#OrderedCalls}
 
-Although an `EXPECT_CALL()` statement defined earlier takes precedence when
-gMock tries to match a function call with an expectation, by default calls don't
-have to happen in the order `EXPECT_CALL()` statements are written. For example,
-if the arguments match the matchers in the third `EXPECT_CALL()`, but not those
-in the first two, then the third expectation will be used.
+Although an `EXPECT_CALL()` statement defined later takes precedence when gMock
+tries to match a function call with an expectation, by default calls don't have
+to happen in the order `EXPECT_CALL()` statements are written. For example, if
+the arguments match the matchers in the second `EXPECT_CALL()`, but not those in
+the first and third, then the second expectation will be used.
 
 If you would rather have all calls occur in the order of the expectations, put
 the `EXPECT_CALL()` statements in a block where you define a variable of type
@@ -1713,8 +1710,8 @@
 the test should reflect our real intent, instead of being overly constraining.
 
 gMock allows you to impose an arbitrary DAG (directed acyclic graph) on the
-calls. One way to express the DAG is to use the [After](#AfterClause) clause of
-`EXPECT_CALL`.
+calls. One way to express the DAG is to use the
+[`After` clause](reference/mocking.md#EXPECT_CALL.After) of `EXPECT_CALL`.
 
 Another way is via the `InSequence()` clause (not the same as the `InSequence`
 class), which we borrowed from jMock 2. It's less flexible than `After()`, but
@@ -1852,10 +1849,9 @@
 whose return type is not a reference, as doing that usually indicates a user
 error. So, what shall you do?
 
-Though you may be tempted, DO NOT use `ByRef()`:
+Though you may be tempted, DO NOT use `std::ref()`:
 
 ```cpp
-using testing::ByRef;
 using testing::Return;
 
 class MockFoo : public Foo {
@@ -1866,7 +1862,7 @@
   int x = 0;
   MockFoo foo;
   EXPECT_CALL(foo, GetValue())
-      .WillRepeatedly(Return(ByRef(x)));  // Wrong!
+      .WillRepeatedly(Return(std::ref(x)));  // Wrong!
   x = 42;
   EXPECT_EQ(42, foo.GetValue());
 ```
@@ -1882,9 +1878,9 @@
 The reason is that `Return(*value*)` converts `value` to the actual return type
 of the mock function at the time when the action is *created*, not when it is
 *executed*. (This behavior was chosen for the action to be safe when `value` is
-a proxy object that references some temporary objects.) As a result, `ByRef(x)`
-is converted to an `int` value (instead of a `const int&`) when the expectation
-is set, and `Return(ByRef(x))` will always return 0.
+a proxy object that references some temporary objects.) As a result,
+`std::ref(x)` is converted to an `int` value (instead of a `const int&`) when
+the expectation is set, and `Return(std::ref(x))` will always return 0.
 
 `ReturnPointee(pointer)` was provided to solve this problem specifically. It
 returns the value pointed to by `pointer` at the time the action is *executed*:
@@ -2129,7 +2125,7 @@
   DefaultValue<Bar>::Clear();
 ```
 
-Please note that changing the default value for a type can make you tests hard
+Please note that changing the default value for a type can make your tests hard
 to understand. We recommend you to use this feature judiciously. For example,
 you may want to make sure the `Set()` and `Clear()` calls are right next to the
 code that uses your mock.
@@ -2175,9 +2171,7 @@
 ### Using Functions/Methods/Functors/Lambdas as Actions {#FunctionsAsActions}
 
 If the built-in actions don't suit you, you can use an existing callable
-(function, `std::function`, method, functor, lambda as an action.
-
-<!-- GOOGLETEST_CM0024 DO NOT DELETE -->
+(function, `std::function`, method, functor, lambda) as an action.
 
 ```cpp
 using ::testing::_; using ::testing::Invoke;
@@ -2203,7 +2197,8 @@
       .WillOnce(&CalculateSum)
       .WillRepeatedly(Invoke(NewPermanentCallback(Sum3, 1)));
   EXPECT_CALL(foo, ComplexJob(_))
-      .WillOnce(Invoke(&helper, &Helper::ComplexJob));
+      .WillOnce(Invoke(&helper, &Helper::ComplexJob))
+      .WillOnce([] { return true; })
       .WillRepeatedly([](int x) { return x > 0; });
 
   foo.Sum(5, 6);         // Invokes CalculateSum(5, 6).
@@ -2213,13 +2208,13 @@
 ```
 
 The only requirement is that the type of the function, etc must be *compatible*
-with the signature of the mock function, meaning that the latter's arguments can
-be implicitly converted to the corresponding arguments of the former, and the
-former's return type can be implicitly converted to that of the latter. So, you
-can invoke something whose type is *not* exactly the same as the mock function,
-as long as it's safe to do so - nice, huh?
+with the signature of the mock function, meaning that the latter's arguments (if
+it takes any) can be implicitly converted to the corresponding arguments of the
+former, and the former's return type can be implicitly converted to that of the
+latter. So, you can invoke something whose type is *not* exactly the same as the
+mock function, as long as it's safe to do so - nice, huh?
 
-**`Note:`{.escaped}**
+Note that:
 
 *   The action takes ownership of the callback and will delete it when the
     action itself is destructed.
@@ -2268,19 +2263,20 @@
 
 ### Invoking a Function/Method/Functor/Lambda/Callback Without Arguments
 
-`Invoke()` is very useful for doing actions that are more complex. It passes the
-mock function's arguments to the function, etc being invoked such that the
-callee has the full context of the call to work with. If the invoked function is
-not interested in some or all of the arguments, it can simply ignore them.
+`Invoke()` passes the mock function's arguments to the function, etc being
+invoked such that the callee has the full context of the call to work with. If
+the invoked function is not interested in some or all of the arguments, it can
+simply ignore them.
 
 Yet, a common pattern is that a test author wants to invoke a function without
-the arguments of the mock function. `Invoke()` allows her to do that using a
-wrapper function that throws away the arguments before invoking an underlining
-nullary function. Needless to say, this can be tedious and obscures the intent
-of the test.
+the arguments of the mock function. She could do that using a wrapper function
+that throws away the arguments before invoking an underlining nullary function.
+Needless to say, this can be tedious and obscures the intent of the test.
 
-`InvokeWithoutArgs()` solves this problem. It's like `Invoke()` except that it
-doesn't pass the mock function's arguments to the callee. Here's an example:
+There are two solutions to this problem. First, you can pass any callable of
+zero args as an action. Alternatively, use `InvokeWithoutArgs()`, which is like
+`Invoke()` except that it doesn't pass the mock function's arguments to the
+callee. Here's an example of each:
 
 ```cpp
 using ::testing::_;
@@ -2297,14 +2293,14 @@
 ...
   MockFoo foo;
   EXPECT_CALL(foo, ComplexJob(_))
-      .WillOnce(InvokeWithoutArgs(Job1))
+      .WillOnce([] { Job1(); });
       .WillOnce(InvokeWithoutArgs(NewPermanentCallback(Job2, 5, 'a')));
 
   foo.ComplexJob(10);  // Invokes Job1().
   foo.ComplexJob(20);  // Invokes Job2(5, 'a').
 ```
 
-**`Note:`{.escaped}**
+Note that:
 
 *   The action takes ownership of the callback and will delete it when the
     action itself is destructed.
@@ -2347,6 +2343,7 @@
       // second argument DoThis() receives.
 ```
 
+{: .callout .note}
 NOTE: The section below is legacy documentation from before C++ had lambdas:
 
 Arghh, you need to refer to a mock function argument but C++ has no lambda
@@ -2375,7 +2372,7 @@
 ```
 
 What if the callable takes an argument by reference? No problem - just wrap it
-inside `ByRef()`:
+inside `std::ref()`:
 
 ```cpp
   ...
@@ -2384,20 +2381,19 @@
               (override));
   ...
   using ::testing::_;
-  using ::testing::ByRef;
   using ::testing::InvokeArgument;
   ...
   MockFoo foo;
   Helper helper;
   ...
   EXPECT_CALL(foo, Bar(_))
-      .WillOnce(InvokeArgument<0>(5, ByRef(helper)));
-      // ByRef(helper) guarantees that a reference to helper, not a copy of it,
-      // will be passed to the callback.
+      .WillOnce(InvokeArgument<0>(5, std::ref(helper)));
+      // std::ref(helper) guarantees that a reference to helper, not a copy of
+      // it, will be passed to the callback.
 ```
 
 What if the callable takes an argument by reference and we do **not** wrap the
-argument in `ByRef()`? Then `InvokeArgument()` will *make a copy* of the
+argument in `std::ref()`? Then `InvokeArgument()` will *make a copy* of the
 argument, and pass a *reference to the copy*, instead of a reference to the
 original value, to the callable. This is especially handy when the argument is a
 temporary value:
@@ -2668,26 +2664,18 @@
 `Notification` objects to force your asynchronous test to behave synchronously.
 
 ```cpp
-using ::testing::DoAll;
-using ::testing::InvokeWithoutArgs;
-using ::testing::Return;
-
 class MockEventDispatcher : public EventDispatcher {
   MOCK_METHOD(bool, DispatchEvent, (int32), (override));
 };
 
-ACTION_P(Notify, notification) {
-  notification->Notify();
-}
-
 TEST(EventQueueTest, EnqueueEventTest) {
   MockEventDispatcher mock_event_dispatcher;
   EventQueue event_queue(&mock_event_dispatcher);
 
   const int32 kEventId = 321;
-  Notification done;
+  absl::Notification done;
   EXPECT_CALL(mock_event_dispatcher, DispatchEvent(kEventId))
-      .WillOnce(Notify(&done));
+      .WillOnce([&done] { done.Notify(); });
 
   event_queue.EnqueueEvent(kEventId);
   done.WaitForNotification();
@@ -2700,6 +2688,7 @@
 asynchronous call to finish. After that, our test suite is complete and we can
 safely exit.
 
+{: .callout .note}
 Note: this example has a downside: namely, if the expectation is not satisfied,
 our test will run forever. It will eventually time-out and fail, but it will
 take longer and be slightly harder to debug. To alleviate this problem, you can
@@ -2850,8 +2839,8 @@
 #### Legacy workarounds for move-only types {#LegacyMoveOnly}
 
 Support for move-only function arguments was only introduced to gMock in April
-2017. In older code, you may encounter the following workaround for the lack of
-this feature (it is no longer necessary - we're including it just for
+of 2017. In older code, you may encounter the following workaround for the lack
+of this feature (it is no longer necessary - we're including it just for
 reference):
 
 ```cpp
@@ -2979,36 +2968,27 @@
 }  // server is destroyed when it goes out of scope here.
 ```
 
+{: .callout .tip}
 **Tip:** The `Mock::VerifyAndClearExpectations()` function returns a `bool` to
 indicate whether the verification was successful (`true` for yes), so you can
 wrap that function call inside a `ASSERT_TRUE()` if there is no point going
 further when the verification has failed.
 
-### Using Check Points {#UsingCheckPoints}
+Do not set new expectations after verifying and clearing a mock after its use.
+Setting expectations after code that exercises the mock has undefined behavior.
+See [Using Mocks in Tests](gmock_for_dummies.md#using-mocks-in-tests) for more
+information.
 
-Sometimes you may want to "reset" a mock object at various check points in your
-test: at each check point, you verify that all existing expectations on the mock
-object have been satisfied, and then you set some new expectations on it as if
-it's newly created. This allows you to work with a mock object in "phases" whose
-sizes are each manageable.
+### Using Checkpoints {#UsingCheckPoints}
 
-One such scenario is that in your test's `SetUp()` function, you may want to put
-the object you are testing into a certain state, with the help from a mock
-object. Once in the desired state, you want to clear all expectations on the
-mock, such that in the `TEST_F` body you can set fresh expectations on it.
+Sometimes you might want to test a mock object's behavior in phases whose sizes
+are each manageable, or you might want to set more detailed expectations about
+which API calls invoke which mock functions.
 
-As you may have figured out, the `Mock::VerifyAndClearExpectations()` function
-we saw in the previous recipe can help you here. Or, if you are using
-`ON_CALL()` to set default actions on the mock object and want to clear the
-default actions as well, use `Mock::VerifyAndClear(&mock_object)` instead. This
-function does what `Mock::VerifyAndClearExpectations(&mock_object)` does and
-returns the same `bool`, **plus** it clears the `ON_CALL()` statements on
-`mock_object` too.
-
-Another trick you can use to achieve the same effect is to put the expectations
-in sequences and insert calls to a dummy "check-point" function at specific
-places. Then you can verify that the mock function calls do happen at the right
-time. For example, if you are exercising code:
+A technique you can use is to put the expectations in a sequence and insert
+calls to a dummy "checkpoint" function at specific places. Then you can verify
+that the mock function calls do happen at the right time. For example, if you
+are exercising the code:
 
 ```cpp
   Foo(1);
@@ -3017,7 +2997,7 @@
 ```
 
 and want to verify that `Foo(1)` and `Foo(3)` both invoke `mock.Bar("a")`, but
-`Foo(2)` doesn't invoke anything. You can write:
+`Foo(2)` doesn't invoke anything, you can write:
 
 ```cpp
 using ::testing::MockFunction;
@@ -3043,10 +3023,10 @@
 }
 ```
 
-The expectation spec says that the first `Bar("a")` must happen before check
-point "1", the second `Bar("a")` must happen after check point "2", and nothing
-should happen between the two check points. The explicit check points make it
-easy to tell which `Bar("a")` is called by which call to `Foo()`.
+The expectation spec says that the first `Bar("a")` call must happen before
+checkpoint "1", the second `Bar("a")` call must happen after checkpoint "2", and
+nothing should happen between the two checkpoints. The explicit checkpoints make
+it clear which `Bar("a")` is called by which call to `Foo()`.
 
 ### Mocking Destructors
 
@@ -3072,7 +3052,7 @@
   ...
   // Add the following two lines to the mock class.
   MOCK_METHOD(void, Die, ());
-  virtual ~MockFoo() { Die(); }
+  ~MockFoo() override { Die(); }
 };
 ```
 
@@ -3271,8 +3251,6 @@
 combine `--gmock_verbose=info` with `--gtest_stack_trace_depth=0` on the test
 command line.
 
-<!-- GOOGLETEST_CM0025 DO NOT DELETE -->
-
 ### Running Tests in Emacs
 
 If you build and run your tests in Emacs using the `M-x google-compile` command
@@ -3297,6 +3275,7 @@
 
 ### Writing New Matchers Quickly {#NewMatchers}
 
+{: .callout .warning}
 WARNING: gMock does not guarantee when or how many times a matcher will be
 invoked. Therefore, all matchers must be functionally pure. See
 [this section](#PureMatchers) for more details.
@@ -3341,7 +3320,7 @@
 ```cpp
   using ::testing::Not;
   ...
-  // Verifies that two values are divisible by 7.
+  // Verifies that a value is divisible by 7 and the other is not.
   EXPECT_THAT(some_expression, IsDivisibleBy7());
   EXPECT_THAT(some_other_expression, Not(IsDivisibleBy7()));
 ```
@@ -3400,6 +3379,7 @@
 the matcher is used inside `Not()`. There is no need to print the argument value
 itself, as gMock already prints it for you.
 
+{: .callout .note}
 NOTE: The type of the value being matched (`arg_type`) is determined by the
 context in which you use the matcher and is supplied to you by the compiler, so
 you don't need to worry about declaring it (nor can you). This allows the
@@ -3538,51 +3518,39 @@
 ```
 
 While it's tempting to always use the `MATCHER*` macros when defining a new
-matcher, you should also consider implementing `MatcherInterface` or using
-`MakePolymorphicMatcher()` instead (see the recipes that follow), especially if
-you need to use the matcher a lot. While these approaches require more work,
-they give you more control on the types of the value being matched and the
-matcher parameters, which in general leads to better compiler error messages
-that pay off in the long run. They also allow overloading matchers based on
-parameter types (as opposed to just based on the number of parameters).
+matcher, you should also consider implementing the matcher interface directly
+instead (see the recipes that follow), especially if you need to use the matcher
+a lot. While these approaches require more work, they give you more control on
+the types of the value being matched and the matcher parameters, which in
+general leads to better compiler error messages that pay off in the long run.
+They also allow overloading matchers based on parameter types (as opposed to
+just based on the number of parameters).
 
 ### Writing New Monomorphic Matchers
 
-A matcher of argument type `T` implements `::testing::MatcherInterface<T>` and
-does two things: it tests whether a value of type `T` matches the matcher, and
-can describe what kind of values it matches. The latter ability is used for
+A matcher of argument type `T` implements the matcher interface for `T` and does
+two things: it tests whether a value of type `T` matches the matcher, and can
+describe what kind of values it matches. The latter ability is used for
 generating readable error messages when expectations are violated.
 
-The interface looks like this:
+A matcher of `T` must declare a typedef like:
 
 ```cpp
-class MatchResultListener {
- public:
-  ...
-  // Streams x to the underlying ostream; does nothing if the ostream
-  // is NULL.
-  template <typename T>
-  MatchResultListener& operator<<(const T& x);
+using is_gtest_matcher = void;
+```
 
-  // Returns the underlying ostream.
-  ::std::ostream* stream();
-};
+and supports the following operations:
 
-template <typename T>
-class MatcherInterface {
- public:
-  virtual ~MatcherInterface();
+```cpp
+// Match a value and optionally explain into an ostream.
+bool matched = matcher.MatchAndExplain(value, maybe_os);
+// where `value` is of type `T` and
+// `maybe_os` is of type `std::ostream*`, where it can be null if the caller
+// is not interested in there textual explanation.
 
-  // Returns true if and only if the matcher matches x; also explains the match
-  // result to 'listener'.
-  virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
-
-  // Describes this matcher to an ostream.
-  virtual void DescribeTo(::std::ostream* os) const = 0;
-
-  // Describes the negation of this matcher to an ostream.
-  virtual void DescribeNegationTo(::std::ostream* os) const;
-};
+matcher.DescribeTo(os);
+matcher.DescribeNegationTo(os);
+// where `os` is of type `std::ostream*`.
 ```
 
 If you need a custom matcher but `Truly()` is not a good option (for example,
@@ -3597,29 +3565,27 @@
 and then use it like this:
 
 ```cpp
-using ::testing::MakeMatcher;
 using ::testing::Matcher;
-using ::testing::MatcherInterface;
-using ::testing::MatchResultListener;
 
-class DivisibleBy7Matcher : public MatcherInterface<int> {
+class DivisibleBy7Matcher {
  public:
-  bool MatchAndExplain(int n,
-                       MatchResultListener* /* listener */) const override {
+  using is_gtest_matcher = void;
+
+  bool MatchAndExplain(int n, std::ostream*) const {
     return (n % 7) == 0;
   }
 
-  void DescribeTo(::std::ostream* os) const override {
+  void DescribeTo(std::ostream* os) const {
     *os << "is divisible by 7";
   }
 
-  void DescribeNegationTo(::std::ostream* os) const override {
+  void DescribeNegationTo(std::ostream* os) const {
     *os << "is not divisible by 7";
   }
 };
 
 Matcher<int> DivisibleBy7() {
-  return MakeMatcher(new DivisibleBy7Matcher);
+  return DivisibleBy7Matcher();
 }
 
 ...
@@ -3627,16 +3593,15 @@
 ```
 
 You may improve the matcher message by streaming additional information to the
-`listener` argument in `MatchAndExplain()`:
+`os` argument in `MatchAndExplain()`:
 
 ```cpp
-class DivisibleBy7Matcher : public MatcherInterface<int> {
+class DivisibleBy7Matcher {
  public:
-  bool MatchAndExplain(int n,
-                       MatchResultListener* listener) const override {
+  bool MatchAndExplain(int n, std::ostream* os) const {
     const int remainder = n % 7;
-    if (remainder != 0) {
-      *listener << "the remainder is " << remainder;
+    if (remainder != 0 && os != nullptr) {
+      *os << "the remainder is " << remainder;
     }
     return remainder == 0;
   }
@@ -3652,15 +3617,87 @@
   Actual: 23 (the remainder is 2)
 ```
 
+{: .callout .tip}
+Tip: for convenience, `MatchAndExplain()` can take a `MatchResultListener*`
+instead of `std::ostream*`.
+
 ### Writing New Polymorphic Matchers
 
-You've learned how to write your own matchers in the previous recipe. Just one
-problem: a matcher created using `MakeMatcher()` only works for one particular
-type of arguments. If you want a *polymorphic* matcher that works with arguments
-of several types (for instance, `Eq(x)` can be used to match a *`value`* as long
-as `value == x` compiles -- *`value`* and `x` don't have to share the same
-type), you can learn the trick from `testing/base/public/gmock-matchers.h` but
-it's a bit involved.
+Expanding what we learned above to *polymorphic* matchers is now just as simple
+as adding templates in the right place.
+
+```cpp
+
+class NotNullMatcher {
+ public:
+  using is_gtest_matcher = void;
+
+  // To implement a polymorphic matcher, we just need to make MatchAndExplain a
+  // template on its first argument.
+
+  // In this example, we want to use NotNull() with any pointer, so
+  // MatchAndExplain() accepts a pointer of any type as its first argument.
+  // In general, you can define MatchAndExplain() as an ordinary method or
+  // a method template, or even overload it.
+  template <typename T>
+  bool MatchAndExplain(T* p, std::ostream*) const {
+    return p != nullptr;
+  }
+
+  // Describes the property of a value matching this matcher.
+  void DescribeTo(std::ostream* os) const { *os << "is not NULL"; }
+
+  // Describes the property of a value NOT matching this matcher.
+  void DescribeNegationTo(std::ostream* os) const { *os << "is NULL"; }
+};
+
+NotNullMatcher NotNull() {
+  return NotNullMatcher();
+}
+
+...
+
+  EXPECT_CALL(foo, Bar(NotNull()));  // The argument must be a non-NULL pointer.
+```
+
+### Legacy Matcher Implementation
+
+Defining matchers used to be somewhat more complicated, in which it required
+several supporting classes and virtual functions. To implement a matcher for
+type `T` using the legacy API you have to derive from `MatcherInterface<T>` and
+call `MakeMatcher` to construct the object.
+
+The interface looks like this:
+
+```cpp
+class MatchResultListener {
+ public:
+  ...
+  // Streams x to the underlying ostream; does nothing if the ostream
+  // is NULL.
+  template <typename T>
+  MatchResultListener& operator<<(const T& x);
+
+  // Returns the underlying ostream.
+  std::ostream* stream();
+};
+
+template <typename T>
+class MatcherInterface {
+ public:
+  virtual ~MatcherInterface();
+
+  // Returns true if and only if the matcher matches x; also explains the match
+  // result to 'listener'.
+  virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
+
+  // Describes this matcher to an ostream.
+  virtual void DescribeTo(std::ostream* os) const = 0;
+
+  // Describes the negation of this matcher to an ostream.
+  virtual void DescribeNegationTo(std::ostream* os) const;
+};
+```
 
 Fortunately, most of the time you can define a polymorphic matcher easily with
 the help of `MakePolymorphicMatcher()`. Here's how you can define `NotNull()` as
@@ -3705,6 +3742,7 @@
   EXPECT_CALL(foo, Bar(NotNull()));  // The argument must be a non-NULL pointer.
 ```
 
+{: .callout .note}
 **Note:** Your polymorphic matcher class does **not** need to inherit from
 `MatcherInterface` or any other class, and its methods do **not** need to be
 virtual.
@@ -3718,8 +3756,8 @@
 call to occur. It doesn't have to be exact. For example, you can say
 `AtLeast(5)` or `Between(2, 4)`.
 
-If the [built-in set](cheat_sheet.md#CardinalityList) of cardinalities doesn't
-suit you, you are free to define your own by implementing the following
+If the [built-in set](gmock_cheat_sheet.md#CardinalityList) of cardinalities
+doesn't suit you, you are free to define your own by implementing the following
 interface (in namespace `testing`):
 
 ```cpp
@@ -3995,7 +4033,7 @@
                 // Note the comma between int and k:
                 HAS_2_TEMPLATE_PARAMS(int, k, typename, T),
                 AND_1_VALUE_PARAMS(output)) {
-  *output = T(::std::get<k>(args));
+  *output = T(std::get<k>(args));
 }
 ```
 
@@ -4042,23 +4080,18 @@
 know its type. The type depends on the macro used to define the action and the
 parameter types. The rule is relatively simple:
 
+
 | Given Definition              | Expression          | Has Type              |
 | ----------------------------- | ------------------- | --------------------- |
 | `ACTION(Foo)`                 | `Foo()`             | `FooAction`           |
-| `ACTION_TEMPLATE(Foo,`        | `Foo<t1, ...,       | `FooAction<t1, ...,   |
-: `HAS_m_TEMPLATE_PARAMS(...),` : t_m>()`             : t_m>`                 :
-: `AND_0_VALUE_PARAMS())`       :                     :                       :
+| `ACTION_TEMPLATE(Foo, HAS_m_TEMPLATE_PARAMS(...), AND_0_VALUE_PARAMS())` | `Foo<t1, ..., t_m>()` | `FooAction<t1, ..., t_m>` |
 | `ACTION_P(Bar, param)`        | `Bar(int_value)`    | `BarActionP<int>`     |
-| `ACTION_TEMPLATE(Bar,`        | `Bar<t1, ..., t_m>` | `FooActionP<t1, ...,  |
-: `HAS_m_TEMPLATE_PARAMS(...),` : `(int_value)`       : t_m, int>`            :
-: `AND_1_VALUE_PARAMS(p1))`     :                     :                       :
-| `ACTION_P2(Baz, p1, p2)`      | `Baz(bool_value,`   | `BazActionP2<bool,    |
-:                               : `int_value)`        : int>`                 :
-| `ACTION_TEMPLATE(Baz,`        | `Baz<t1, ..., t_m>` | `FooActionP2<t1, ..., |
-: `HAS_m_TEMPLATE_PARAMS(...),` : `(bool_value,`      : t_m,` `bool, int>`    :
-: `AND_2_VALUE_PARAMS(p1, p2))` : `int_value)`        :                       :
+| `ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))` | `Bar<t1, ..., t_m>(int_value)` | `BarActionP<t1, ..., t_m, int>` |
+| `ACTION_P2(Baz, p1, p2)`      | `Baz(bool_value, int_value)` | `BazActionP2<bool, int>` |
+| `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))` | `Baz<t1, ..., t_m>(bool_value, int_value)` | `BazActionP2<t1, ..., t_m, bool, int>` |
 | ...                           | ...                 | ...                   |
 
+
 Note that we have to pick different suffixes (`Action`, `ActionP`, `ActionP2`,
 and etc) for actions with different numbers of value parameters, or the action
 definitions cannot be overloaded on the number of them.
@@ -4087,7 +4120,7 @@
   //
 
   // For example, if F is int(bool, const string&), then Result would
-  // be int, and ArgumentTuple would be ::std::tuple<bool, const string&>.
+  // be int, and ArgumentTuple would be std::tuple<bool, const string&>.
   virtual Result Perform(const ArgumentTuple& args) = 0;
 };
 ```
@@ -4102,8 +4135,8 @@
 
 class IncrementArgumentAction : public ActionInterface<IncrementMethod> {
  public:
-  int Perform(const ::std::tuple<int*>& args) override {
-    int* p = ::std::get<0>(args);  // Grabs the first argument.
+  int Perform(const std::tuple<int*>& args) override {
+    int* p = std::get<0>(args);  // Grabs the first argument.
     return *p++;
   }
 };
@@ -4148,8 +4181,8 @@
  public:
   template <typename Result, typename ArgumentTuple>
   Result Perform(const ArgumentTuple& args) const {
-    // To get the i-th (0-based) argument, use ::std::get(args).
-    return ::std::get<1>(args);
+    // To get the i-th (0-based) argument, use std::get(args).
+    return std::get<1>(args);
   }
 };
 ```
@@ -4207,7 +4240,7 @@
 This printer knows how to print built-in C++ types, native arrays, STL
 containers, and any type that supports the `<<` operator. For other types, it
 prints the raw bytes in the value and hopes that you the user can figure it out.
-[googletest's advanced guide](../../googletest/docs/advanced.md#teaching-googletest-how-to-print-your-values)
+[The GoogleTest advanced guide](advanced.md#teaching-googletest-how-to-print-your-values)
 explains how to extend the printer to do a better job at printing your
 particular type than to dump the bytes.
 
@@ -4266,5 +4299,3 @@
 Although `std::function` supports unlimited number of arguments, `MockFunction`
 implementation is limited to ten. If you ever hit that limit... well, your
 callback has bigger problems than being mockable. :-)
-
-<!-- GOOGLETEST_CM0034 DO NOT DELETE -->
diff --git a/ext/googletest/googlemock/docs/gmock_faq.md b/ext/googletest/docs/gmock_faq.md
similarity index 97%
rename from ext/googletest/googlemock/docs/gmock_faq.md
rename to ext/googletest/docs/gmock_faq.md
index 214aabf..2cd9b3f 100644
--- a/ext/googletest/googlemock/docs/gmock_faq.md
+++ b/ext/googletest/docs/gmock_faq.md
@@ -1,11 +1,9 @@
-## Legacy gMock FAQ {#GMockFaq}
-
-<!-- GOOGLETEST_CM0021 DO NOT DELETE -->
+# Legacy gMock FAQ
 
 ### When I call a method on my mock object, the method for the real object is invoked instead. What's the problem?
 
 In order for a method to be mocked, it must be *virtual*, unless you use the
-[high-perf dependency injection technique](#MockingNonVirtualMethods).
+[high-perf dependency injection technique](gmock_cook_book.md#MockingNonVirtualMethods).
 
 ### Can I mock a variadic function?
 
@@ -81,8 +79,6 @@
 void Bar(const int* p);  // p is not const, but *p is.
 ```
 
-<!-- GOOGLETEST_CM0030 DO NOT DELETE -->
-
 ### I can't figure out why gMock thinks my expectations are not satisfied. What should I do?
 
 You might want to run your test with `--gmock_verbose=info`. This flag lets
@@ -91,7 +87,7 @@
 
 If you see the message "The mock function has no default action set, and its
 return type has no default value set.", then try
-[adding a default action](for_dummies.md#DefaultValue). Due to a known issue,
+[adding a default action](gmock_cheat_sheet.md#OnCall). Due to a known issue,
 unexpected calls on mocks without default actions don't print out a detailed
 comparison between the actual arguments and the expected arguments.
 
@@ -126,8 +122,6 @@
       .Times(0);
 ```
 
-<!-- GOOGLETEST_CM0031 DO NOT DELETE -->
-
 ### I have a failed test where gMock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant?
 
 When gMock detects a failure, it prints relevant information (the mock function
@@ -386,8 +380,8 @@
 `SetArgPointee()` with a `Return()` that provides a value appropriate to the API
 being mocked.
 
-See this [recipe](cook_book.md#mocking-side-effects) for more details and an
-example.
+See this [recipe](gmock_cook_book.md#mocking-side-effects) for more details and
+an example.
 
 ### I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do?
 
diff --git a/ext/googletest/googlemock/docs/for_dummies.md b/ext/googletest/docs/gmock_for_dummies.md
similarity index 91%
rename from ext/googletest/googlemock/docs/for_dummies.md
rename to ext/googletest/docs/gmock_for_dummies.md
index e11c18d..0392b5d 100644
--- a/ext/googletest/googlemock/docs/for_dummies.md
+++ b/ext/googletest/docs/gmock_for_dummies.md
@@ -1,8 +1,6 @@
-## gMock for Dummies {#GMockForDummies}
+# gMock for Dummies
 
-<!-- GOOGLETEST_CM0013 DO NOT DELETE -->
-
-### What Is gMock?
+## What Is gMock?
 
 When you write a prototype or test, often it's not feasible or wise to rely on
 real objects entirely. A **mock object** implements the same interface as a real
@@ -10,9 +8,9 @@
 be used and what it should do (which methods will be called? in which order? how
 many times? with what arguments? what will they return? etc).
 
-**Note:** It is easy to confuse the term *fake objects* with mock objects. Fakes
-and mocks actually mean very different things in the Test-Driven Development
-(TDD) community:
+It is easy to confuse the term *fake objects* with mock objects. Fakes and mocks
+actually mean very different things in the Test-Driven Development (TDD)
+community:
 
 *   **Fake** objects have working implementations, but usually take some
     shortcut (perhaps to make the operations less expensive), which makes them
@@ -39,7 +37,7 @@
 3.  then you exercise code that uses the mock objects. gMock will catch any
     violation to the expectations as soon as it arises.
 
-### Why gMock?
+## Why gMock?
 
 While mock objects help you remove unnecessary dependencies in tests and make
 them fast and reliable, using mocks manually in C++ is *hard*:
@@ -53,9 +51,9 @@
     one.
 
 In contrast, Java and Python programmers have some fine mock frameworks (jMock,
-EasyMock, [Mox](http://wtf/mox), etc), which automate the creation of mocks. As
-a result, mocking is a proven effective technique and widely adopted practice in
-those communities. Having the right tool absolutely makes the difference.
+EasyMock, etc), which automate the creation of mocks. As a result, mocking is a
+proven effective technique and widely adopted practice in those communities.
+Having the right tool absolutely makes the difference.
 
 gMock was built to help C++ programmers. It was inspired by jMock and EasyMock,
 but designed with C++'s specifics in mind. It is your friend if any of the
@@ -85,11 +83,11 @@
 *   a *testing* tool to cut your tests' outbound dependencies and probe the
     interaction between your module and its collaborators.
 
-### Getting Started
+## Getting Started
 
 gMock is bundled with googletest.
 
-### A Case for Mock Turtles
+## A Case for Mock Turtles
 
 Let's look at an example. Suppose you are developing a graphics program that
 relies on a [LOGO](http://en.wikipedia.org/wiki/Logo_programming_language)-like
@@ -106,7 +104,7 @@
 ```cpp
 class Turtle {
   ...
-  virtual ~Turtle() {};
+  virtual ~Turtle() {}
   virtual void PenUp() = 0;
   virtual void PenDown() = 0;
   virtual void Forward(int distance) = 0;
@@ -135,20 +133,20 @@
 maintain (the intent of a test is expressed in the code, not in some binary
 images), and run *much, much faster*.
 
-### Writing the Mock Class
+## Writing the Mock Class
 
 If you are lucky, the mocks you need to use have already been implemented by
 some nice people. If, however, you find yourself in the position to write a mock
 class, relax - gMock turns this task into a fun game! (Well, almost.)
 
-#### How to Define It
+### How to Define It
 
 Using the `Turtle` interface as example, here are the simple steps you need to
 follow:
 
 *   Derive a class `MockTurtle` from `Turtle`.
 *   Take a *virtual* function of `Turtle` (while it's possible to
-    [mock non-virtual methods using templates](cook_book.md#MockingNonVirtualMethods),
+    [mock non-virtual methods using templates](gmock_cook_book.md#MockingNonVirtualMethods),
     it's much more involved).
 *   In the `public:` section of the child class, write `MOCK_METHOD();`
 *   Now comes the fun part: you take the function signature, cut-and-paste it
@@ -184,7 +182,7 @@
 You don't need to define these mock methods somewhere else - the `MOCK_METHOD`
 macro will generate the definitions for you. It's that simple!
 
-#### Where to Put It
+### Where to Put It
 
 When you define a mock class, you need to decide where to put its definition.
 Some people put it in a `_test.cc`. This is fine when the interface being mocked
@@ -206,14 +204,12 @@
 readable (a net win in the long run), as you can choose `FooAdaptor` to fit your
 specific domain much better than `Foo` does.
 
-<!-- GOOGLETEST_CM0029 DO NOT DELETE -->
-
-### Using Mocks in Tests
+## Using Mocks in Tests
 
 Once you have a mock class, using it is easy. The typical work flow is:
 
 1.  Import the gMock names from the `testing` namespace such that you can use
-    them unqualified (You only have to do it once per file. Remember that
+    them unqualified (You only have to do it once per file). Remember that
     namespaces are a good idea.
 2.  Create some mock objects.
 3.  Specify your expectations on them (How many times will a method be called?
@@ -257,8 +253,8 @@
 ...
 ```
 
-**Tip 1:** If you run the test from an Emacs buffer, you can hit <Enter> on the
-line number to jump right to the failed expectation.
+**Tip 1:** If you run the test from an Emacs buffer, you can hit `<Enter>` on
+the line number to jump right to the failed expectation.
 
 **Tip 2:** If your mock objects are never deleted, the final verification won't
 happen. Therefore it's a good idea to turn on the heap checker in your tests
@@ -266,8 +262,9 @@
 `gtest_main` library already.
 
 **Important note:** gMock requires expectations to be set **before** the mock
-functions are called, otherwise the behavior is **undefined**. In particular,
-you mustn't interleave `EXPECT_CALL()s` and calls to the mock functions.
+functions are called, otherwise the behavior is **undefined**. Do not alternate
+between calls to `EXPECT_CALL()` and calls to the mock functions, and do not set
+any expectations on a mock after passing the mock to an API.
 
 This means `EXPECT_CALL()` should be read as expecting that a call will occur
 *in the future*, not that a call has occurred. Why does gMock work like that?
@@ -279,7 +276,7 @@
 the same effect without using gMock. However, as we shall reveal soon, gMock
 allows you to do *so much more* with the mocks.
 
-### Setting Expectations
+## Setting Expectations
 
 The key to using a mock object successfully is to set the *right expectations*
 on it. If you set the expectations too strict, your test will fail as the result
@@ -288,7 +285,7 @@
 intend it to catch. gMock provides the necessary means for you to do it "just
 right."
 
-#### General Syntax
+### General Syntax
 
 In gMock we use the `EXPECT_CALL()` macro to set an expectation on a mock
 method. The general syntax is:
@@ -314,8 +311,8 @@
 
 This syntax allows the test writer to specify "called with any arguments"
 without explicitly specifying the number or types of arguments. To avoid
-unintended ambiguity, this syntax may only be used for methods which are not
-overloaded
+unintended ambiguity, this syntax may only be used for methods that are not
+overloaded.
 
 Either form of the macro can be followed by some optional *clauses* that provide
 more information about the expectation. We'll discuss how each clause works in
@@ -338,12 +335,13 @@
 will return 100 the first time, 150 the second time, and then 200 every time.
 Some people like to call this style of syntax a Domain-Specific Language (DSL).
 
+{: .callout .note}
 **Note:** Why do we use a macro to do this? Well it serves two purposes: first
-it makes expectations easily identifiable (either by `gsearch` or by a human
+it makes expectations easily identifiable (either by `grep` or by a human
 reader), and second it allows gMock to include the source file location of a
 failed expectation in messages, making debugging easier.
 
-#### Matchers: What Arguments Do We Expect?
+### Matchers: What Arguments Do We Expect?
 
 When a mock function takes arguments, we may specify what arguments we are
 expecting, for example:
@@ -374,8 +372,8 @@
 In the above examples, `100` and `50` are also matchers; implicitly, they are
 the same as `Eq(100)` and `Eq(50)`, which specify that the argument must be
 equal (using `operator==`) to the matcher argument. There are many
-[built-in matchers](#MatcherList) for common types (as well as
-[custom matchers](cook_book.md#NewMatchers)); for example:
+[built-in matchers](reference/matchers.md) for common types (as well as
+[custom matchers](gmock_cook_book.md#NewMatchers)); for example:
 
 ```cpp
 using ::testing::Ge;
@@ -397,9 +395,9 @@
 This works for all non-overloaded methods; if a method is overloaded, you need
 to help gMock resolve which overload is expected by specifying the number of
 arguments and possibly also the
-[types of the arguments](cook_book.md#SelectOverload).
+[types of the arguments](gmock_cook_book.md#SelectOverload).
 
-#### Cardinalities: How Many Times Will It Be Called?
+### Cardinalities: How Many Times Will It Be Called?
 
 The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We
 call its argument a **cardinality** as it tells *how many times* the call should
@@ -414,7 +412,7 @@
 
 We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the
 list of built-in cardinalities you can use, see
-[here](cheat_sheet.md#CardinalityList).
+[here](gmock_cheat_sheet.md#CardinalityList).
 
 The `Times()` clause can be omitted. **If you omit `Times()`, gMock will infer
 the cardinality for you.** The rules are easy to remember:
@@ -429,7 +427,7 @@
 **Quick quiz:** what do you think will happen if a function is expected to be
 called twice but actually called four times?
 
-#### Actions: What Should It Do?
+### Actions: What Should It Do?
 
 Remember that a mock object doesn't really have a working implementation? We as
 users have to tell it what to do when a method is invoked. This is easy in
@@ -482,8 +480,8 @@
 `WillRepeatedly()`.).
 
 What can we do inside `WillOnce()` besides `Return()`? You can return a
-reference using `ReturnRef(*variable*)`, or invoke a pre-defined function, among
-[others](cook_book.md#using-actions).
+reference using `ReturnRef(`*`variable`*`)`, or invoke a pre-defined function,
+among [others](gmock_cook_book.md#using-actions).
 
 **Important note:** The `EXPECT_CALL()` statement evaluates the action clause
 only once, even though the action may be performed many times. Therefore you
@@ -503,7 +501,7 @@
 will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will
 return the same pointer every time. If you want the side effect to happen every
 time, you need to define a custom action, which we'll teach in the
-[cook book](http://<!-- GOOGLETEST_CM0012 DO NOT DELETE -->).
+[cook book](gmock_cook_book.md).
 
 Time for another quiz! What do you think the following means?
 
@@ -522,7 +520,7 @@
 return 100 the first time, but **return 0 from the second time on**, as
 returning 0 is the default action for `int` functions.
 
-#### Using Multiple Expectations {#MultiExpectations}
+### Using Multiple Expectations {#MultiExpectations}
 
 So far we've only shown examples where you have a single expectation. More
 realistically, you'll specify expectations on multiple mock methods which may be
@@ -547,6 +545,7 @@
 the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK,
 as now #1 will be the matching expectation.
 
+{: .callout .note}
 **Note:** Why does gMock search for a match in the *reverse* order of the
 expectations? The reason is that this allows a user to set up the default
 expectations in a mock object's constructor or the test fixture's set-up phase
@@ -555,15 +554,16 @@
 one with more specific matchers **after** the other, or the more specific rule
 would be shadowed by the more general one that comes after it.
 
+{: .callout .tip}
 **Tip:** It is very common to start with a catch-all expectation for a method
 and `Times(AnyNumber())` (omitting arguments, or with `_` for all arguments, if
 overloaded). This makes any calls to the method expected. This is not necessary
 for methods that are not mentioned at all (these are "uninteresting"), but is
 useful for methods that have some expectations, but for which other calls are
 ok. See
-[Understanding Uninteresting vs Unexpected Calls](cook_book.md#uninteresting-vs-unexpected).
+[Understanding Uninteresting vs Unexpected Calls](gmock_cook_book.md#uninteresting-vs-unexpected).
 
-#### Ordered vs Unordered Calls {#OrderedCalls}
+### Ordered vs Unordered Calls {#OrderedCalls}
 
 By default, an expectation can match a call even though an earlier expectation
 hasn't been satisfied. In other words, the calls don't have to occur in the
@@ -598,9 +598,9 @@
 
 (What if you care about the relative order of some of the calls, but not all of
 them? Can you specify an arbitrary partial order? The answer is ... yes! The
-details can be found [here](cook_book.md#OrderedCalls).)
+details can be found [here](gmock_cook_book.md#OrderedCalls).)
 
-#### All Expectations Are Sticky (Unless Said Otherwise) {#StickyExpectations}
+### All Expectations Are Sticky (Unless Said Otherwise) {#StickyExpectations}
 
 Now let's do a quick quiz to see how well you can use this mock stuff already.
 How would you test that the turtle is asked to go to the origin *exactly twice*
@@ -688,7 +688,7 @@
 sequence has been used, it automatically retires (and will never be used to
 match any call).
 
-#### Uninteresting Calls
+### Uninteresting Calls
 
 A mock object may have many methods, and not all of them are that interesting.
 For example, in some tests we may not care about how many times `GetX()` and
@@ -697,4 +697,4 @@
 In gMock, if you are not interested in a method, just don't say anything about
 it. If a call to this method occurs, you'll see a warning in the test output,
 but it won't be a failure. This is called "naggy" behavior; to change, see
-[The Nice, the Strict, and the Naggy](cook_book.md#NiceStrictNaggy).
+[The Nice, the Strict, and the Naggy](gmock_cook_book.md#NiceStrictNaggy).
diff --git a/ext/googletest/docs/index.md b/ext/googletest/docs/index.md
new file mode 100644
index 0000000..b162c74
--- /dev/null
+++ b/ext/googletest/docs/index.md
@@ -0,0 +1,22 @@
+# GoogleTest User's Guide
+
+## Welcome to GoogleTest!
+
+GoogleTest is Google's C++ testing and mocking framework. This user's guide has
+the following contents:
+
+*   [GoogleTest Primer](primer.md) - Teaches you how to write simple tests using
+    GoogleTest. Read this first if you are new to GoogleTest.
+*   [GoogleTest Advanced](advanced.md) - Read this when you've finished the
+    Primer and want to utilize GoogleTest to its full potential.
+*   [GoogleTest Samples](samples.md) - Describes some GoogleTest samples.
+*   [GoogleTest FAQ](faq.md) - Have a question? Want some tips? Check here
+    first.
+*   [Mocking for Dummies](gmock_for_dummies.md) - Teaches you how to create mock
+    objects and use them in tests.
+*   [Mocking Cookbook](gmock_cook_book.md) - Includes tips and approaches to
+    common mocking use cases.
+*   [Mocking Cheat Sheet](gmock_cheat_sheet.md) - A handy reference for
+    matchers, actions, invariants, and more.
+*   [Mocking FAQ](gmock_faq.md) - Contains answers to some mocking-specific
+    questions.
diff --git a/ext/googletest/docs/pkgconfig.md b/ext/googletest/docs/pkgconfig.md
new file mode 100644
index 0000000..18a2546
--- /dev/null
+++ b/ext/googletest/docs/pkgconfig.md
@@ -0,0 +1,148 @@
+## Using GoogleTest from various build systems
+
+GoogleTest comes with pkg-config files that can be used to determine all
+necessary flags for compiling and linking to GoogleTest (and GoogleMock).
+Pkg-config is a standardised plain-text format containing
+
+*   the includedir (-I) path
+*   necessary macro (-D) definitions
+*   further required flags (-pthread)
+*   the library (-L) path
+*   the library (-l) to link to
+
+All current build systems support pkg-config in one way or another. For all
+examples here we assume you want to compile the sample
+`samples/sample3_unittest.cc`.
+
+### CMake
+
+Using `pkg-config` in CMake is fairly easy:
+
+```cmake
+cmake_minimum_required(VERSION 3.0)
+
+cmake_policy(SET CMP0048 NEW)
+project(my_gtest_pkgconfig VERSION 0.0.1 LANGUAGES CXX)
+
+find_package(PkgConfig)
+pkg_search_module(GTEST REQUIRED gtest_main)
+
+add_executable(testapp samples/sample3_unittest.cc)
+target_link_libraries(testapp ${GTEST_LDFLAGS})
+target_compile_options(testapp PUBLIC ${GTEST_CFLAGS})
+
+include(CTest)
+add_test(first_and_only_test testapp)
+```
+
+It is generally recommended that you use `target_compile_options` + `_CFLAGS`
+over `target_include_directories` + `_INCLUDE_DIRS` as the former includes not
+just -I flags (GoogleTest might require a macro indicating to internal headers
+that all libraries have been compiled with threading enabled. In addition,
+GoogleTest might also require `-pthread` in the compiling step, and as such
+splitting the pkg-config `Cflags` variable into include dirs and macros for
+`target_compile_definitions()` might still miss this). The same recommendation
+goes for using `_LDFLAGS` over the more commonplace `_LIBRARIES`, which happens
+to discard `-L` flags and `-pthread`.
+
+### Help! pkg-config can't find GoogleTest!
+
+Let's say you have a `CMakeLists.txt` along the lines of the one in this
+tutorial and you try to run `cmake`. It is very possible that you get a failure
+along the lines of:
+
+```
+-- Checking for one of the modules 'gtest_main'
+CMake Error at /usr/share/cmake/Modules/FindPkgConfig.cmake:640 (message):
+  None of the required 'gtest_main' found
+```
+
+These failures are common if you installed GoogleTest yourself and have not
+sourced it from a distro or other package manager. If so, you need to tell
+pkg-config where it can find the `.pc` files containing the information. Say you
+installed GoogleTest to `/usr/local`, then it might be that the `.pc` files are
+installed under `/usr/local/lib64/pkgconfig`. If you set
+
+```
+export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig
+```
+
+pkg-config will also try to look in `PKG_CONFIG_PATH` to find `gtest_main.pc`.
+
+### Using pkg-config in a cross-compilation setting
+
+Pkg-config can be used in a cross-compilation setting too. To do this, let's
+assume the final prefix of the cross-compiled installation will be `/usr`, and
+your sysroot is `/home/MYUSER/sysroot`. Configure and install GTest using
+
+```
+mkdir build && cmake -DCMAKE_INSTALL_PREFIX=/usr ..
+```
+
+Install into the sysroot using `DESTDIR`:
+
+```
+make -j install DESTDIR=/home/MYUSER/sysroot
+```
+
+Before we continue, it is recommended to **always** define the following two
+variables for pkg-config in a cross-compilation setting:
+
+```
+export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=yes
+export PKG_CONFIG_ALLOW_SYSTEM_LIBS=yes
+```
+
+otherwise `pkg-config` will filter `-I` and `-L` flags against standard prefixes
+such as `/usr` (see https://bugs.freedesktop.org/show_bug.cgi?id=28264#c3 for
+reasons why this stripping needs to occur usually).
+
+If you look at the generated pkg-config file, it will look something like
+
+```
+libdir=/usr/lib64
+includedir=/usr/include
+
+Name: gtest
+Description: GoogleTest (without main() function)
+Version: 1.11.0
+URL: https://github.com/google/googletest
+Libs: -L${libdir} -lgtest -lpthread
+Cflags: -I${includedir} -DGTEST_HAS_PTHREAD=1 -lpthread
+```
+
+Notice that the sysroot is not included in `libdir` and `includedir`! If you try
+to run `pkg-config` with the correct
+`PKG_CONFIG_LIBDIR=/home/MYUSER/sysroot/usr/lib64/pkgconfig` against this `.pc`
+file, you will get
+
+```
+$ pkg-config --cflags gtest
+-DGTEST_HAS_PTHREAD=1 -lpthread -I/usr/include
+$ pkg-config --libs gtest
+-L/usr/lib64 -lgtest -lpthread
+```
+
+which is obviously wrong and points to the `CBUILD` and not `CHOST` root. In
+order to use this in a cross-compilation setting, we need to tell pkg-config to
+inject the actual sysroot into `-I` and `-L` variables. Let us now tell
+pkg-config about the actual sysroot
+
+```
+export PKG_CONFIG_DIR=
+export PKG_CONFIG_SYSROOT_DIR=/home/MYUSER/sysroot
+export PKG_CONFIG_LIBDIR=${PKG_CONFIG_SYSROOT_DIR}/usr/lib64/pkgconfig
+```
+
+and running `pkg-config` again we get
+
+```
+$ pkg-config --cflags gtest
+-DGTEST_HAS_PTHREAD=1 -lpthread -I/home/MYUSER/sysroot/usr/include
+$ pkg-config --libs gtest
+-L/home/MYUSER/sysroot/usr/lib64 -lgtest -lpthread
+```
+
+which contains the correct sysroot now. For a more comprehensive guide to also
+including `${CHOST}` in build system calls, see the excellent tutorial by Diego
+Elio Pettenò: <https://autotools.io/pkgconfig/cross-compiling.html>
diff --git a/ext/googletest/docs/platforms.md b/ext/googletest/docs/platforms.md
new file mode 100644
index 0000000..eba6ef8
--- /dev/null
+++ b/ext/googletest/docs/platforms.md
@@ -0,0 +1,35 @@
+# Supported Platforms
+
+GoogleTest requires a codebase and compiler compliant with the C++11 standard or
+newer.
+
+The GoogleTest code is officially supported on the following platforms.
+Operating systems or tools not listed below are community-supported. For
+community-supported platforms, patches that do not complicate the code may be
+considered.
+
+If you notice any problems on your platform, please file an issue on the
+[GoogleTest GitHub Issue Tracker](https://github.com/google/googletest/issues).
+Pull requests containing fixes are welcome!
+
+### Operating systems
+
+*   Linux
+*   macOS
+*   Windows
+
+### Compilers
+
+*   gcc 5.0+
+*   clang 5.0+
+*   MSVC 2015+
+
+**macOS users:** Xcode 9.3+ provides clang 5.0+.
+
+### Build systems
+
+*   [Bazel](https://bazel.build/)
+*   [CMake](https://cmake.org/)
+
+Bazel is the build system used by the team internally and in tests. CMake is
+supported on a best-effort basis and by the community.
diff --git a/ext/googletest/googletest/docs/primer.md b/ext/googletest/docs/primer.md
similarity index 73%
rename from ext/googletest/googletest/docs/primer.md
rename to ext/googletest/docs/primer.md
index 0317692..6d8fdf4 100644
--- a/ext/googletest/googletest/docs/primer.md
+++ b/ext/googletest/docs/primer.md
@@ -44,6 +44,7 @@
 
 ## Beware of the nomenclature
 
+{: .callout .note}
 _Note:_ There might be some confusion arising from different definitions of the
 terms _Test_, _Test Case_ and _Test Suite_, so beware of misunderstanding these.
 
@@ -66,13 +67,11 @@
 
 So please be aware of the different definitions of the terms:
 
-<!-- mdformat off(github rendering does not support multiline tables) -->
 
 Meaning                                                                              | googletest Term         | [ISTQB](http://www.istqb.org/) Term
 :----------------------------------------------------------------------------------- | :---------------------- | :----------------------------------
 Exercise a particular program path with specific input values and verify the results | [TEST()](#simple-tests) | [Test Case][istqb test case]
 
-<!-- mdformat on -->
 
 [istqb test case]: http://glossary.istqb.org/en/search/test%20case
 [istqb test suite]: http://glossary.istqb.org/en/search/test%20suite
@@ -119,7 +118,9 @@
 this in mind if you get a heap checker error in addition to assertion errors.
 
 To provide a custom failure message, simply stream it into the macro using the
-`<<` operator or a sequence of such operators. An example:
+`<<` operator or a sequence of such operators. See the following example, using
+the [`ASSERT_EQ` and `EXPECT_EQ`](reference/assertions.md#EXPECT_EQ) macros to
+verify value equality:
 
 ```c++
 ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
@@ -134,112 +135,12 @@
 (`wchar_t*`, `TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is
 streamed to an assertion, it will be translated to UTF-8 when printed.
 
-### Basic Assertions
-
-These assertions do basic true/false condition testing.
-
-Fatal assertion            | Nonfatal assertion         | Verifies
--------------------------- | -------------------------- | --------------------
-`ASSERT_TRUE(condition);`  | `EXPECT_TRUE(condition);`  | `condition` is true
-`ASSERT_FALSE(condition);` | `EXPECT_FALSE(condition);` | `condition` is false
-
-Remember, when they fail, `ASSERT_*` yields a fatal failure and returns from the
-current function, while `EXPECT_*` yields a nonfatal failure, allowing the
-function to continue running. In either case, an assertion failure means its
-containing test fails.
-
-**Availability**: Linux, Windows, Mac.
-
-### Binary Comparison
-
-This section describes assertions that compare two values.
-
-Fatal assertion          | Nonfatal assertion       | Verifies
------------------------- | ------------------------ | --------------
-`ASSERT_EQ(val1, val2);` | `EXPECT_EQ(val1, val2);` | `val1 == val2`
-`ASSERT_NE(val1, val2);` | `EXPECT_NE(val1, val2);` | `val1 != val2`
-`ASSERT_LT(val1, val2);` | `EXPECT_LT(val1, val2);` | `val1 < val2`
-`ASSERT_LE(val1, val2);` | `EXPECT_LE(val1, val2);` | `val1 <= val2`
-`ASSERT_GT(val1, val2);` | `EXPECT_GT(val1, val2);` | `val1 > val2`
-`ASSERT_GE(val1, val2);` | `EXPECT_GE(val1, val2);` | `val1 >= val2`
-
-Value arguments must be comparable by the assertion's comparison operator or
-you'll get a compiler error. We used to require the arguments to support the
-`<<` operator for streaming to an `ostream`, but this is no longer necessary. If
-`<<` is supported, it will be called to print the arguments when the assertion
-fails; otherwise googletest will attempt to print them in the best way it can.
-For more details and how to customize the printing of the arguments, see the
-[documentation](../../googlemock/docs/cook_book.md#teaching-gmock-how-to-print-your-values).
-
-These assertions can work with a user-defined type, but only if you define the
-corresponding comparison operator (e.g., `==` or `<`). Since this is discouraged
-by the Google
-[C++ Style Guide](https://google.github.io/styleguide/cppguide.html#Operator_Overloading),
-you may need to use `ASSERT_TRUE()` or `EXPECT_TRUE()` to assert the equality of
-two objects of a user-defined type.
-
-However, when possible, `ASSERT_EQ(actual, expected)` is preferred to
-`ASSERT_TRUE(actual == expected)`, since it tells you `actual` and `expected`'s
-values on failure.
-
-Arguments are always evaluated exactly once. Therefore, it's OK for the
-arguments to have side effects. However, as with any ordinary C/C++ function,
-the arguments' evaluation order is undefined (i.e., the compiler is free to
-choose any order), and your code should not depend on any particular argument
-evaluation order.
-
-`ASSERT_EQ()` does pointer equality on pointers. If used on two C strings, it
-tests if they are in the same memory location, not if they have the same value.
-Therefore, if you want to compare C strings (e.g. `const char*`) by value, use
-`ASSERT_STREQ()`, which will be described later on. In particular, to assert
-that a C string is `NULL`, use `ASSERT_STREQ(c_string, NULL)`. Consider using
-`ASSERT_EQ(c_string, nullptr)` if c++11 is supported. To compare two `string`
-objects, you should use `ASSERT_EQ`.
-
-When doing pointer comparisons use `*_EQ(ptr, nullptr)` and `*_NE(ptr, nullptr)`
-instead of `*_EQ(ptr, NULL)` and `*_NE(ptr, NULL)`. This is because `nullptr` is
-typed, while `NULL` is not. See the [FAQ](faq.md) for more details.
-
-If you're working with floating point numbers, you may want to use the floating
-point variations of some of these macros in order to avoid problems caused by
-rounding. See [Advanced googletest Topics](advanced.md) for details.
-
-Macros in this section work with both narrow and wide string objects (`string`
-and `wstring`).
-
-**Availability**: Linux, Windows, Mac.
-
-**Historical note**: Before February 2016 `*_EQ` had a convention of calling it
-as `ASSERT_EQ(expected, actual)`, so lots of existing code uses this order. Now
-`*_EQ` treats both parameters in the same way.
-
-### String Comparison
-
-The assertions in this group compare two **C strings**. If you want to compare
-two `string` objects, use `EXPECT_EQ`, `EXPECT_NE`, and etc instead.
-
-<!-- mdformat off(github rendering does not support multiline tables) -->
-
-| Fatal assertion                | Nonfatal assertion             | Verifies                                                 |
-| --------------------------     | ------------------------------ | -------------------------------------------------------- |
-| `ASSERT_STREQ(str1,str2);`     | `EXPECT_STREQ(str1,str2);`     | the two C strings have the same content   		     |
-| `ASSERT_STRNE(str1,str2);`     | `EXPECT_STRNE(str1,str2);`     | the two C strings have different contents 		     |
-| `ASSERT_STRCASEEQ(str1,str2);` | `EXPECT_STRCASEEQ(str1,str2);` | the two C strings have the same content, ignoring case   |
-| `ASSERT_STRCASENE(str1,str2);` | `EXPECT_STRCASENE(str1,str2);` | the two C strings have different contents, ignoring case |
-
-<!-- mdformat on-->
-
-Note that "CASE" in an assertion name means that case is ignored. A `NULL`
-pointer and an empty string are considered *different*.
-
-`*STREQ*` and `*STRNE*` also accept wide C strings (`wchar_t*`). If a comparison
-of two wide strings fails, their values will be printed as UTF-8 narrow strings.
-
-**Availability**: Linux, Windows, Mac.
-
-**See also**: For more string comparison tricks (substring, prefix, suffix, and
-regular expression matching, for example), see [this](advanced.md) in the
-Advanced googletest Guide.
+GoogleTest provides a collection of assertions for verifying the behavior of
+your code in various ways. You can check Boolean conditions, compare values
+based on relational operators, verify string values, floating-point values, and
+much more. There are even assertions that enable you to verify more complex
+states by providing custom predicates. For the complete list of assertions
+provided by GoogleTest, see the [Assertions Reference](reference/assertions.md).
 
 ## Simple Tests
 
@@ -261,7 +162,7 @@
 
 `TEST()` arguments go from general to specific. The *first* argument is the name
 of the test suite, and the *second* argument is the test's name within the test
-case. Both names must be valid C++ identifiers, and they should not contain
+suite. Both names must be valid C++ identifiers, and they should not contain
 any underscores (`_`). A test's *full name* consists of its containing test suite and
 its individual name. Tests from different test suites can have the same
 individual name.
@@ -418,7 +319,7 @@
 to use `EXPECT_*` when you want the test to continue to reveal more errors after
 the assertion failure, and use `ASSERT_*` when continuing after failure doesn't
 make sense. For example, the second assertion in the `Dequeue` test is
-`ASSERT_NE(nullptr, n)`, as we need to dereference the pointer `n` later, which
+`ASSERT_NE(n, nullptr)`, as we need to dereference the pointer `n` later, which
 would lead to a segfault when `n` is `NULL`.
 
 When these tests run, the following happens:
@@ -464,6 +365,7 @@
 
 If a fatal failure happens the subsequent steps will be skipped.
 
+{: .callout .important}
 > IMPORTANT: You must **not** ignore the return value of `RUN_ALL_TESTS()`, or
 > you will get a compiler error. The rationale for this design is that the
 > automated testing service determines whether a test has passed based on its
@@ -478,22 +380,31 @@
 
 ## Writing the main() Function
 
-Write your own main() function, which should return the value of
+Most users should _not_ need to write their own `main` function and instead link
+with `gtest_main` (as opposed to with `gtest`), which defines a suitable entry
+point. See the end of this section for details. The remainder of this section
+should only apply when you need to do something custom before the tests run that
+cannot be expressed within the framework of fixtures and test suites.
+
+If you write your own `main` function, it should return the value of
 `RUN_ALL_TESTS()`.
 
 You can start from this boilerplate:
 
 ```c++
 #include "this/package/foo.h"
+
 #include "gtest/gtest.h"
 
+namespace my {
+namespace project {
 namespace {
 
 // The fixture for testing class Foo.
 class FooTest : public ::testing::Test {
  protected:
-  // You can remove any or all of the following functions if its body
-  // is empty.
+  // You can remove any or all of the following functions if their bodies would
+  // be empty.
 
   FooTest() {
      // You can do set-up work for each test here.
@@ -516,7 +427,8 @@
      // before the destructor).
   }
 
-  // Objects declared here can be used by all tests in the test suite for Foo.
+  // Class members declared here can be used by all tests in the test suite
+  // for Foo.
 };
 
 // Tests that the Foo::Bar() method does Abc.
@@ -533,6 +445,8 @@
 }
 
 }  // namespace
+}  // namespace project
+}  // namespace my
 
 int main(int argc, char **argv) {
   ::testing::InitGoogleTest(&argc, argv);
@@ -549,11 +463,12 @@
 On Windows, `InitGoogleTest()` also works with wide strings, so it can be used
 in programs compiled in `UNICODE` mode as well.
 
-But maybe you think that writing all those main() functions is too much work? We
+But maybe you think that writing all those `main` functions is too much work? We
 agree with you completely, and that's why Google Test provides a basic
 implementation of main(). If it fits your needs, then just link your test with
-gtest\_main library and you are good to go.
+the `gtest_main` library and you are good to go.
 
+{: .callout .note}
 NOTE: `ParseGUnitFlags()` is deprecated in favor of `InitGoogleTest()`.
 
 ## Known Limitations
diff --git a/ext/googletest/docs/quickstart-bazel.md b/ext/googletest/docs/quickstart-bazel.md
new file mode 100644
index 0000000..362ee6d
--- /dev/null
+++ b/ext/googletest/docs/quickstart-bazel.md
@@ -0,0 +1,161 @@
+# Quickstart: Building with Bazel
+
+This tutorial aims to get you up and running with GoogleTest using the Bazel
+build system. If you're using GoogleTest for the first time or need a refresher,
+we recommend this tutorial as a starting point.
+
+## Prerequisites
+
+To complete this tutorial, you'll need:
+
+*   A compatible operating system (e.g. Linux, macOS, Windows).
+*   A compatible C++ compiler that supports at least C++11.
+*   [Bazel](https://bazel.build/), the preferred build system used by the
+    GoogleTest team.
+
+See [Supported Platforms](platforms.md) for more information about platforms
+compatible with GoogleTest.
+
+If you don't already have Bazel installed, see the
+[Bazel installation guide](https://docs.bazel.build/versions/master/install.html).
+
+{: .callout .note}
+Note: The terminal commands in this tutorial show a Unix shell prompt, but the
+commands work on the Windows command line as well.
+
+## Set up a Bazel workspace
+
+A
+[Bazel workspace](https://docs.bazel.build/versions/master/build-ref.html#workspace)
+is a directory on your filesystem that you use to manage source files for the
+software you want to build. Each workspace directory has a text file named
+`WORKSPACE` which may be empty, or may contain references to external
+dependencies required to build the outputs.
+
+First, create a directory for your workspace:
+
+```
+$ mkdir my_workspace && cd my_workspace
+```
+
+Next, you’ll create the `WORKSPACE` file to specify dependencies. A common and
+recommended way to depend on GoogleTest is to use a
+[Bazel external dependency](https://docs.bazel.build/versions/master/external.html)
+via the
+[`http_archive` rule](https://docs.bazel.build/versions/master/repo/http.html#http_archive).
+To do this, in the root directory of your workspace (`my_workspace/`), create a
+file named `WORKSPACE` with the following contents:
+
+```
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+  name = "com_google_googletest",
+  urls = ["https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip"],
+  strip_prefix = "googletest-609281088cfefc76f9d0ce82e1ff6c30cc3591e5",
+)
+```
+
+The above configuration declares a dependency on GoogleTest which is downloaded
+as a ZIP archive from GitHub. In the above example,
+`609281088cfefc76f9d0ce82e1ff6c30cc3591e5` is the Git commit hash of the
+GoogleTest version to use; we recommend updating the hash often to point to the
+latest version.
+
+Bazel also needs a dependency on the
+[`rules_cc` repository](https://github.com/bazelbuild/rules_cc) to build C++
+code, so add the following to the `WORKSPACE` file:
+
+```
+http_archive(
+  name = "rules_cc",
+  urls = ["https://github.com/bazelbuild/rules_cc/archive/40548a2974f1aea06215272d9c2b47a14a24e556.zip"],
+  strip_prefix = "rules_cc-40548a2974f1aea06215272d9c2b47a14a24e556",
+)
+```
+
+Now you're ready to build C++ code that uses GoogleTest.
+
+## Create and run a binary
+
+With your Bazel workspace set up, you can now use GoogleTest code within your
+own project.
+
+As an example, create a file named `hello_test.cc` in your `my_workspace`
+directory with the following contents:
+
+```cpp
+#include <gtest/gtest.h>
+
+// Demonstrate some basic assertions.
+TEST(HelloTest, BasicAssertions) {
+  // Expect two strings not to be equal.
+  EXPECT_STRNE("hello", "world");
+  // Expect equality.
+  EXPECT_EQ(7 * 6, 42);
+}
+```
+
+GoogleTest provides [assertions](primer.md#assertions) that you use to test the
+behavior of your code. The above sample includes the main GoogleTest header file
+and demonstrates some basic assertions.
+
+To build the code, create a file named `BUILD` in the same directory with the
+following contents:
+
+```
+load("@rules_cc//cc:defs.bzl", "cc_test")
+
+cc_test(
+  name = "hello_test",
+  size = "small",
+  srcs = ["hello_test.cc"],
+  deps = ["@com_google_googletest//:gtest_main"],
+)
+```
+
+This `cc_test` rule declares the C++ test binary you want to build, and links to
+GoogleTest (`//:gtest_main`) using the prefix you specified in the `WORKSPACE`
+file (`@com_google_googletest`). For more information about Bazel `BUILD` files,
+see the
+[Bazel C++ Tutorial](https://docs.bazel.build/versions/master/tutorial/cpp.html).
+
+Now you can build and run your test:
+
+<pre>
+<strong>my_workspace$ bazel test --test_output=all //:hello_test</strong>
+INFO: Analyzed target //:hello_test (26 packages loaded, 362 targets configured).
+INFO: Found 1 test target...
+INFO: From Testing //:hello_test:
+==================== Test output for //:hello_test:
+Running main() from gmock_main.cc
+[==========] Running 1 test from 1 test suite.
+[----------] Global test environment set-up.
+[----------] 1 test from HelloTest
+[ RUN      ] HelloTest.BasicAssertions
+[       OK ] HelloTest.BasicAssertions (0 ms)
+[----------] 1 test from HelloTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test suite ran. (0 ms total)
+[  PASSED  ] 1 test.
+================================================================================
+Target //:hello_test up-to-date:
+  bazel-bin/hello_test
+INFO: Elapsed time: 4.190s, Critical Path: 3.05s
+INFO: 27 processes: 8 internal, 19 linux-sandbox.
+INFO: Build completed successfully, 27 total actions
+//:hello_test                                                     PASSED in 0.1s
+
+INFO: Build completed successfully, 27 total actions
+</pre>
+
+Congratulations! You've successfully built and run a test binary using
+GoogleTest.
+
+## Next steps
+
+*   [Check out the Primer](primer.md) to start learning how to write simple
+    tests.
+*   [See the code samples](samples.md) for more examples showing how to use a
+    variety of GoogleTest features.
diff --git a/ext/googletest/docs/quickstart-cmake.md b/ext/googletest/docs/quickstart-cmake.md
new file mode 100644
index 0000000..420f1d3
--- /dev/null
+++ b/ext/googletest/docs/quickstart-cmake.md
@@ -0,0 +1,156 @@
+# Quickstart: Building with CMake
+
+This tutorial aims to get you up and running with GoogleTest using CMake. If
+you're using GoogleTest for the first time or need a refresher, we recommend
+this tutorial as a starting point. If your project uses Bazel, see the
+[Quickstart for Bazel](quickstart-bazel.md) instead.
+
+## Prerequisites
+
+To complete this tutorial, you'll need:
+
+*   A compatible operating system (e.g. Linux, macOS, Windows).
+*   A compatible C++ compiler that supports at least C++11.
+*   [CMake](https://cmake.org/) and a compatible build tool for building the
+    project.
+    *   Compatible build tools include
+        [Make](https://www.gnu.org/software/make/),
+        [Ninja](https://ninja-build.org/), and others - see
+        [CMake Generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
+        for more information.
+
+See [Supported Platforms](platforms.md) for more information about platforms
+compatible with GoogleTest.
+
+If you don't already have CMake installed, see the
+[CMake installation guide](https://cmake.org/install).
+
+{: .callout .note}
+Note: The terminal commands in this tutorial show a Unix shell prompt, but the
+commands work on the Windows command line as well.
+
+## Set up a project
+
+CMake uses a file named `CMakeLists.txt` to configure the build system for a
+project. You'll use this file to set up your project and declare a dependency on
+GoogleTest.
+
+First, create a directory for your project:
+
+```
+$ mkdir my_project && cd my_project
+```
+
+Next, you'll create the `CMakeLists.txt` file and declare a dependency on
+GoogleTest. There are many ways to express dependencies in the CMake ecosystem;
+in this quickstart, you'll use the
+[`FetchContent` CMake module](https://cmake.org/cmake/help/latest/module/FetchContent.html).
+To do this, in your project directory (`my_project`), create a file named
+`CMakeLists.txt` with the following contents:
+
+```cmake
+cmake_minimum_required(VERSION 3.14)
+project(my_project)
+
+# GoogleTest requires at least C++11
+set(CMAKE_CXX_STANDARD 11)
+
+include(FetchContent)
+FetchContent_Declare(
+  googletest
+  URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
+)
+# For Windows: Prevent overriding the parent project's compiler/linker settings
+set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+FetchContent_MakeAvailable(googletest)
+```
+
+The above configuration declares a dependency on GoogleTest which is downloaded
+from GitHub. In the above example, `609281088cfefc76f9d0ce82e1ff6c30cc3591e5` is
+the Git commit hash of the GoogleTest version to use; we recommend updating the
+hash often to point to the latest version.
+
+For more information about how to create `CMakeLists.txt` files, see the
+[CMake Tutorial](https://cmake.org/cmake/help/latest/guide/tutorial/index.html).
+
+## Create and run a binary
+
+With GoogleTest declared as a dependency, you can use GoogleTest code within
+your own project.
+
+As an example, create a file named `hello_test.cc` in your `my_project`
+directory with the following contents:
+
+```cpp
+#include <gtest/gtest.h>
+
+// Demonstrate some basic assertions.
+TEST(HelloTest, BasicAssertions) {
+  // Expect two strings not to be equal.
+  EXPECT_STRNE("hello", "world");
+  // Expect equality.
+  EXPECT_EQ(7 * 6, 42);
+}
+```
+
+GoogleTest provides [assertions](primer.md#assertions) that you use to test the
+behavior of your code. The above sample includes the main GoogleTest header file
+and demonstrates some basic assertions.
+
+To build the code, add the following to the end of your `CMakeLists.txt` file:
+
+```cmake
+enable_testing()
+
+add_executable(
+  hello_test
+  hello_test.cc
+)
+target_link_libraries(
+  hello_test
+  gtest_main
+)
+
+include(GoogleTest)
+gtest_discover_tests(hello_test)
+```
+
+The above configuration enables testing in CMake, declares the C++ test binary
+you want to build (`hello_test`), and links it to GoogleTest (`gtest_main`). The
+last two lines enable CMake's test runner to discover the tests included in the
+binary, using the
+[`GoogleTest` CMake module](https://cmake.org/cmake/help/git-stage/module/GoogleTest.html).
+
+Now you can build and run your test:
+
+<pre>
+<strong>my_project$ cmake -S . -B build</strong>
+-- The C compiler identification is GNU 10.2.1
+-- The CXX compiler identification is GNU 10.2.1
+...
+-- Build files have been written to: .../my_project/build
+
+<strong>my_project$ cmake --build build</strong>
+Scanning dependencies of target gtest
+...
+[100%] Built target gmock_main
+
+<strong>my_project$ cd build && ctest</strong>
+Test project .../my_project/build
+    Start 1: HelloTest.BasicAssertions
+1/1 Test #1: HelloTest.BasicAssertions ........   Passed    0.00 sec
+
+100% tests passed, 0 tests failed out of 1
+
+Total Test time (real) =   0.01 sec
+</pre>
+
+Congratulations! You've successfully built and run a test binary using
+GoogleTest.
+
+## Next steps
+
+*   [Check out the Primer](primer.md) to start learning how to write simple
+    tests.
+*   [See the code samples](samples.md) for more examples showing how to use a
+    variety of GoogleTest features.
diff --git a/ext/googletest/docs/reference/actions.md b/ext/googletest/docs/reference/actions.md
new file mode 100644
index 0000000..ab81a12
--- /dev/null
+++ b/ext/googletest/docs/reference/actions.md
@@ -0,0 +1,115 @@
+# Actions Reference
+
+[**Actions**](../gmock_for_dummies.md#actions-what-should-it-do) specify what a
+mock function should do when invoked. This page lists the built-in actions
+provided by GoogleTest. All actions are defined in the `::testing` namespace.
+
+## Returning a Value
+
+| Action                            | Description                                   |
+| :-------------------------------- | :-------------------------------------------- |
+| `Return()`                        | Return from a `void` mock function.           |
+| `Return(value)`                   | Return `value`. If the type of `value` is     different to the mock function's return type, `value` is converted to the latter type <i>at the time the expectation is set</i>, not when the action is executed. |
+| `ReturnArg<N>()`                  | Return the `N`-th (0-based) argument.         |
+| `ReturnNew<T>(a1, ..., ak)`       | Return `new T(a1, ..., ak)`; a different      object is created each time. |
+| `ReturnNull()`                    | Return a null pointer.                        |
+| `ReturnPointee(ptr)`              | Return the value pointed to by `ptr`.         |
+| `ReturnRef(variable)`             | Return a reference to `variable`.             |
+| `ReturnRefOfCopy(value)`          | Return a reference to a copy of `value`; the  copy lives as long as the action. |
+| `ReturnRoundRobin({a1, ..., ak})` | Each call will return the next `ai` in the list, starting at the beginning when the end of the list is reached. |
+
+## Side Effects
+
+| Action                             | Description                             |
+| :--------------------------------- | :-------------------------------------- |
+| `Assign(&variable, value)` | Assign `value` to variable. |
+| `DeleteArg<N>()` | Delete the `N`-th (0-based) argument, which must be a pointer. |
+| `SaveArg<N>(pointer)` | Save the `N`-th (0-based) argument to `*pointer`. |
+| `SaveArgPointee<N>(pointer)` | Save the value pointed to by the `N`-th (0-based) argument to `*pointer`. |
+| `SetArgReferee<N>(value)` | Assign `value` to the variable referenced by the `N`-th (0-based) argument. |
+| `SetArgPointee<N>(value)` | Assign `value` to the variable pointed by the `N`-th (0-based) argument. |
+| `SetArgumentPointee<N>(value)` | Same as `SetArgPointee<N>(value)`. Deprecated. Will be removed in v1.7.0. |
+| `SetArrayArgument<N>(first, last)` | Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range. |
+| `SetErrnoAndReturn(error, value)` | Set `errno` to `error` and return `value`. |
+| `Throw(exception)` | Throws the given exception, which can be any copyable value. Available since v1.1.0. |
+
+## Using a Function, Functor, or Lambda as an Action
+
+In the following, by "callable" we mean a free function, `std::function`,
+functor, or lambda.
+
+| Action                              | Description                            |
+| :---------------------------------- | :------------------------------------- |
+| `f` | Invoke `f` with the arguments passed to the mock function, where `f` is a callable. |
+| `Invoke(f)` | Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor. |
+| `Invoke(object_pointer, &class::method)` | Invoke the method on the object with the arguments passed to the mock function. |
+| `InvokeWithoutArgs(f)` | Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. |
+| `InvokeWithoutArgs(object_pointer, &class::method)` | Invoke the method on the object, which takes no arguments. |
+| `InvokeArgument<N>(arg1, arg2, ..., argk)` | Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments. |
+
+The return value of the invoked function is used as the return value of the
+action.
+
+When defining a callable to be used with `Invoke*()`, you can declare any unused
+parameters as `Unused`:
+
+```cpp
+using ::testing::Invoke;
+double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); }
+...
+EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));
+```
+
+`Invoke(callback)` and `InvokeWithoutArgs(callback)` take ownership of
+`callback`, which must be permanent. The type of `callback` must be a base
+callback type instead of a derived one, e.g.
+
+```cpp
+  BlockingClosure* done = new BlockingClosure;
+  ... Invoke(done) ...;  // This won't compile!
+
+  Closure* done2 = new BlockingClosure;
+  ... Invoke(done2) ...;  // This works.
+```
+
+In `InvokeArgument<N>(...)`, if an argument needs to be passed by reference,
+wrap it inside `std::ref()`. For example,
+
+```cpp
+using ::testing::InvokeArgument;
+...
+InvokeArgument<2>(5, string("Hi"), std::ref(foo))
+```
+
+calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by
+value, and `foo` by reference.
+
+## Default Action
+
+| Action        | Description                                            |
+| :------------ | :----------------------------------------------------- |
+| `DoDefault()` | Do the default action (specified by `ON_CALL()` or the built-in one). |
+
+{: .callout .note}
+**Note:** due to technical reasons, `DoDefault()` cannot be used inside a
+composite action - trying to do so will result in a run-time error.
+
+## Composite Actions
+
+| Action                         | Description                                 |
+| :----------------------------- | :------------------------------------------ |
+| `DoAll(a1, a2, ..., an)`       | Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void and will receive a  readonly view of the arguments. |
+| `IgnoreResult(a)`              | Perform action `a` and ignore its result. `a` must not return void. |
+| `WithArg<N>(a)`                | Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. |
+| `WithArgs<N1, N2, ..., Nk>(a)` | Pass the selected (0-based) arguments of the mock function to action `a` and perform it. |
+| `WithoutArgs(a)`               | Perform action `a` without any arguments. |
+
+## Defining Actions
+
+| Macro                              | Description                             |
+| :--------------------------------- | :-------------------------------------- |
+| `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. |
+| `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. |
+| `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. |
+
+The `ACTION*` macros cannot be used inside a function or class.
diff --git a/ext/googletest/docs/reference/assertions.md b/ext/googletest/docs/reference/assertions.md
new file mode 100644
index 0000000..7bf03a3
--- /dev/null
+++ b/ext/googletest/docs/reference/assertions.md
@@ -0,0 +1,633 @@
+# Assertions Reference
+
+This page lists the assertion macros provided by GoogleTest for verifying code
+behavior. To use them, include the header `gtest/gtest.h`.
+
+The majority of the macros listed below come as a pair with an `EXPECT_` variant
+and an `ASSERT_` variant. Upon failure, `EXPECT_` macros generate nonfatal
+failures and allow the current function to continue running, while `ASSERT_`
+macros generate fatal failures and abort the current function.
+
+All assertion macros support streaming a custom failure message into them with
+the `<<` operator, for example:
+
+```cpp
+EXPECT_TRUE(my_condition) << "My condition is not true";
+```
+
+Anything that can be streamed to an `ostream` can be streamed to an assertion
+macro—in particular, C strings and string objects. If a wide string (`wchar_t*`,
+`TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is streamed to an
+assertion, it will be translated to UTF-8 when printed.
+
+## Explicit Success and Failure {#success-failure}
+
+The assertions in this section generate a success or failure directly instead of
+testing a value or expression. These are useful when control flow, rather than a
+Boolean expression, determines the test's success or failure, as shown by the
+following example:
+
+```c++
+switch(expression) {
+  case 1:
+    ... some checks ...
+  case 2:
+    ... some other checks ...
+  default:
+    FAIL() << "We shouldn't get here.";
+}
+```
+
+### SUCCEED {#SUCCEED}
+
+`SUCCEED()`
+
+Generates a success. This *does not* make the overall test succeed. A test is
+considered successful only if none of its assertions fail during its execution.
+
+The `SUCCEED` assertion is purely documentary and currently doesn't generate any
+user-visible output. However, we may add `SUCCEED` messages to GoogleTest output
+in the future.
+
+### FAIL {#FAIL}
+
+`FAIL()`
+
+Generates a fatal failure, which returns from the current function.
+
+Can only be used in functions that return `void`. See
+[Assertion Placement](../advanced.md#assertion-placement) for more information.
+
+### ADD_FAILURE {#ADD_FAILURE}
+
+`ADD_FAILURE()`
+
+Generates a nonfatal failure, which allows the current function to continue
+running.
+
+### ADD_FAILURE_AT {#ADD_FAILURE_AT}
+
+`ADD_FAILURE_AT(`*`file_path`*`,`*`line_number`*`)`
+
+Generates a nonfatal failure at the file and line number specified.
+
+## Generalized Assertion {#generalized}
+
+The following assertion allows [matchers](matchers.md) to be used to verify
+values.
+
+### EXPECT_THAT {#EXPECT_THAT}
+
+`EXPECT_THAT(`*`value`*`,`*`matcher`*`)` \
+`ASSERT_THAT(`*`value`*`,`*`matcher`*`)`
+
+Verifies that *`value`* matches the [matcher](matchers.md) *`matcher`*.
+
+For example, the following code verifies that the string `value1` starts with
+`"Hello"`, `value2` matches a regular expression, and `value3` is between 5 and
+10:
+
+```cpp
+#include "gmock/gmock.h"
+
+using ::testing::AllOf;
+using ::testing::Gt;
+using ::testing::Lt;
+using ::testing::MatchesRegex;
+using ::testing::StartsWith;
+
+...
+EXPECT_THAT(value1, StartsWith("Hello"));
+EXPECT_THAT(value2, MatchesRegex("Line \\d+"));
+ASSERT_THAT(value3, AllOf(Gt(5), Lt(10)));
+```
+
+Matchers enable assertions of this form to read like English and generate
+informative failure messages. For example, if the above assertion on `value1`
+fails, the resulting message will be similar to the following:
+
+```
+Value of: value1
+  Actual: "Hi, world!"
+Expected: starts with "Hello"
+```
+
+GoogleTest provides a built-in library of matchers—see the
+[Matchers Reference](matchers.md). It is also possible to write your own
+matchers—see [Writing New Matchers Quickly](../gmock_cook_book.md#NewMatchers).
+The use of matchers makes `EXPECT_THAT` a powerful, extensible assertion.
+
+*The idea for this assertion was borrowed from Joe Walnes' Hamcrest project,
+which adds `assertThat()` to JUnit.*
+
+## Boolean Conditions {#boolean}
+
+The following assertions test Boolean conditions.
+
+### EXPECT_TRUE {#EXPECT_TRUE}
+
+`EXPECT_TRUE(`*`condition`*`)` \
+`ASSERT_TRUE(`*`condition`*`)`
+
+Verifies that *`condition`* is true.
+
+### EXPECT_FALSE {#EXPECT_FALSE}
+
+`EXPECT_FALSE(`*`condition`*`)` \
+`ASSERT_FALSE(`*`condition`*`)`
+
+Verifies that *`condition`* is false.
+
+## Binary Comparison {#binary-comparison}
+
+The following assertions compare two values. The value arguments must be
+comparable by the assertion's comparison operator, otherwise a compiler error
+will result.
+
+If an argument supports the `<<` operator, it will be called to print the
+argument when the assertion fails. Otherwise, GoogleTest will attempt to print
+them in the best way it can—see
+[Teaching GoogleTest How to Print Your Values](../advanced.md#teaching-googletest-how-to-print-your-values).
+
+Arguments are always evaluated exactly once, so it's OK for the arguments to
+have side effects. However, the argument evaluation order is undefined and
+programs should not depend on any particular argument evaluation order.
+
+These assertions work with both narrow and wide string objects (`string` and
+`wstring`).
+
+See also the [Floating-Point Comparison](#floating-point) assertions to compare
+floating-point numbers and avoid problems caused by rounding.
+
+### EXPECT_EQ {#EXPECT_EQ}
+
+`EXPECT_EQ(`*`val1`*`,`*`val2`*`)` \
+`ASSERT_EQ(`*`val1`*`,`*`val2`*`)`
+
+Verifies that *`val1`*`==`*`val2`*.
+
+Does pointer equality on pointers. If used on two C strings, it tests if they
+are in the same memory location, not if they have the same value. Use
+[`EXPECT_STREQ`](#EXPECT_STREQ) to compare C strings (e.g. `const char*`) by
+value.
+
+When comparing a pointer to `NULL`, use `EXPECT_EQ(`*`ptr`*`, nullptr)` instead
+of `EXPECT_EQ(`*`ptr`*`, NULL)`.
+
+### EXPECT_NE {#EXPECT_NE}
+
+`EXPECT_NE(`*`val1`*`,`*`val2`*`)` \
+`ASSERT_NE(`*`val1`*`,`*`val2`*`)`
+
+Verifies that *`val1`*`!=`*`val2`*.
+
+Does pointer equality on pointers. If used on two C strings, it tests if they
+are in different memory locations, not if they have different values. Use
+[`EXPECT_STRNE`](#EXPECT_STRNE) to compare C strings (e.g. `const char*`) by
+value.
+
+When comparing a pointer to `NULL`, use `EXPECT_NE(`*`ptr`*`, nullptr)` instead
+of `EXPECT_NE(`*`ptr`*`, NULL)`.
+
+### EXPECT_LT {#EXPECT_LT}
+
+`EXPECT_LT(`*`val1`*`,`*`val2`*`)` \
+`ASSERT_LT(`*`val1`*`,`*`val2`*`)`
+
+Verifies that *`val1`*`<`*`val2`*.
+
+### EXPECT_LE {#EXPECT_LE}
+
+`EXPECT_LE(`*`val1`*`,`*`val2`*`)` \
+`ASSERT_LE(`*`val1`*`,`*`val2`*`)`
+
+Verifies that *`val1`*`<=`*`val2`*.
+
+### EXPECT_GT {#EXPECT_GT}
+
+`EXPECT_GT(`*`val1`*`,`*`val2`*`)` \
+`ASSERT_GT(`*`val1`*`,`*`val2`*`)`
+
+Verifies that *`val1`*`>`*`val2`*.
+
+### EXPECT_GE {#EXPECT_GE}
+
+`EXPECT_GE(`*`val1`*`,`*`val2`*`)` \
+`ASSERT_GE(`*`val1`*`,`*`val2`*`)`
+
+Verifies that *`val1`*`>=`*`val2`*.
+
+## String Comparison {#c-strings}
+
+The following assertions compare two **C strings**. To compare two `string`
+objects, use [`EXPECT_EQ`](#EXPECT_EQ) or [`EXPECT_NE`](#EXPECT_NE) instead.
+
+These assertions also accept wide C strings (`wchar_t*`). If a comparison of two
+wide strings fails, their values will be printed as UTF-8 narrow strings.
+
+To compare a C string with `NULL`, use `EXPECT_EQ(`*`c_string`*`, nullptr)` or
+`EXPECT_NE(`*`c_string`*`, nullptr)`.
+
+### EXPECT_STREQ {#EXPECT_STREQ}
+
+`EXPECT_STREQ(`*`str1`*`,`*`str2`*`)` \
+`ASSERT_STREQ(`*`str1`*`,`*`str2`*`)`
+
+Verifies that the two C strings *`str1`* and *`str2`* have the same contents.
+
+### EXPECT_STRNE {#EXPECT_STRNE}
+
+`EXPECT_STRNE(`*`str1`*`,`*`str2`*`)` \
+`ASSERT_STRNE(`*`str1`*`,`*`str2`*`)`
+
+Verifies that the two C strings *`str1`* and *`str2`* have different contents.
+
+### EXPECT_STRCASEEQ {#EXPECT_STRCASEEQ}
+
+`EXPECT_STRCASEEQ(`*`str1`*`,`*`str2`*`)` \
+`ASSERT_STRCASEEQ(`*`str1`*`,`*`str2`*`)`
+
+Verifies that the two C strings *`str1`* and *`str2`* have the same contents,
+ignoring case.
+
+### EXPECT_STRCASENE {#EXPECT_STRCASENE}
+
+`EXPECT_STRCASENE(`*`str1`*`,`*`str2`*`)` \
+`ASSERT_STRCASENE(`*`str1`*`,`*`str2`*`)`
+
+Verifies that the two C strings *`str1`* and *`str2`* have different contents,
+ignoring case.
+
+## Floating-Point Comparison {#floating-point}
+
+The following assertions compare two floating-point values.
+
+Due to rounding errors, it is very unlikely that two floating-point values will
+match exactly, so `EXPECT_EQ` is not suitable. In general, for floating-point
+comparison to make sense, the user needs to carefully choose the error bound.
+
+GoogleTest also provides assertions that use a default error bound based on
+Units in the Last Place (ULPs). To learn more about ULPs, see the article
+[Comparing Floating Point Numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
+
+### EXPECT_FLOAT_EQ {#EXPECT_FLOAT_EQ}
+
+`EXPECT_FLOAT_EQ(`*`val1`*`,`*`val2`*`)` \
+`ASSERT_FLOAT_EQ(`*`val1`*`,`*`val2`*`)`
+
+Verifies that the two `float` values *`val1`* and *`val2`* are approximately
+equal, to within 4 ULPs from each other.
+
+### EXPECT_DOUBLE_EQ {#EXPECT_DOUBLE_EQ}
+
+`EXPECT_DOUBLE_EQ(`*`val1`*`,`*`val2`*`)` \
+`ASSERT_DOUBLE_EQ(`*`val1`*`,`*`val2`*`)`
+
+Verifies that the two `double` values *`val1`* and *`val2`* are approximately
+equal, to within 4 ULPs from each other.
+
+### EXPECT_NEAR {#EXPECT_NEAR}
+
+`EXPECT_NEAR(`*`val1`*`,`*`val2`*`,`*`abs_error`*`)` \
+`ASSERT_NEAR(`*`val1`*`,`*`val2`*`,`*`abs_error`*`)`
+
+Verifies that the difference between *`val1`* and *`val2`* does not exceed the
+absolute error bound *`abs_error`*.
+
+## Exception Assertions {#exceptions}
+
+The following assertions verify that a piece of code throws, or does not throw,
+an exception. Usage requires exceptions to be enabled in the build environment.
+
+Note that the piece of code under test can be a compound statement, for example:
+
+```cpp
+EXPECT_NO_THROW({
+  int n = 5;
+  DoSomething(&n);
+});
+```
+
+### EXPECT_THROW {#EXPECT_THROW}
+
+`EXPECT_THROW(`*`statement`*`,`*`exception_type`*`)` \
+`ASSERT_THROW(`*`statement`*`,`*`exception_type`*`)`
+
+Verifies that *`statement`* throws an exception of type *`exception_type`*.
+
+### EXPECT_ANY_THROW {#EXPECT_ANY_THROW}
+
+`EXPECT_ANY_THROW(`*`statement`*`)` \
+`ASSERT_ANY_THROW(`*`statement`*`)`
+
+Verifies that *`statement`* throws an exception of any type.
+
+### EXPECT_NO_THROW {#EXPECT_NO_THROW}
+
+`EXPECT_NO_THROW(`*`statement`*`)` \
+`ASSERT_NO_THROW(`*`statement`*`)`
+
+Verifies that *`statement`* does not throw any exception.
+
+## Predicate Assertions {#predicates}
+
+The following assertions enable more complex predicates to be verified while
+printing a more clear failure message than if `EXPECT_TRUE` were used alone.
+
+### EXPECT_PRED* {#EXPECT_PRED}
+
+`EXPECT_PRED1(`*`pred`*`,`*`val1`*`)` \
+`EXPECT_PRED2(`*`pred`*`,`*`val1`*`,`*`val2`*`)` \
+`EXPECT_PRED3(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
+`EXPECT_PRED4(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` \
+`EXPECT_PRED5(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
+
+`ASSERT_PRED1(`*`pred`*`,`*`val1`*`)` \
+`ASSERT_PRED2(`*`pred`*`,`*`val1`*`,`*`val2`*`)` \
+`ASSERT_PRED3(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
+`ASSERT_PRED4(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` \
+`ASSERT_PRED5(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
+
+Verifies that the predicate *`pred`* returns `true` when passed the given values
+as arguments.
+
+The parameter *`pred`* is a function or functor that accepts as many arguments
+as the corresponding macro accepts values. If *`pred`* returns `true` for the
+given arguments, the assertion succeeds, otherwise the assertion fails.
+
+When the assertion fails, it prints the value of each argument. Arguments are
+always evaluated exactly once.
+
+As an example, see the following code:
+
+```cpp
+// Returns true if m and n have no common divisors except 1.
+bool MutuallyPrime(int m, int n) { ... }
+...
+const int a = 3;
+const int b = 4;
+const int c = 10;
+...
+EXPECT_PRED2(MutuallyPrime, a, b);  // Succeeds
+EXPECT_PRED2(MutuallyPrime, b, c);  // Fails
+```
+
+In the above example, the first assertion succeeds, and the second fails with
+the following message:
+
+```
+MutuallyPrime(b, c) is false, where
+b is 4
+c is 10
+```
+
+Note that if the given predicate is an overloaded function or a function
+template, the assertion macro might not be able to determine which version to
+use, and it might be necessary to explicitly specify the type of the function.
+For example, for a Boolean function `IsPositive()` overloaded to take either a
+single `int` or `double` argument, it would be necessary to write one of the
+following:
+
+```cpp
+EXPECT_PRED1(static_cast<bool (*)(int)>(IsPositive), 5);
+EXPECT_PRED1(static_cast<bool (*)(double)>(IsPositive), 3.14);
+```
+
+Writing simply `EXPECT_PRED1(IsPositive, 5);` would result in a compiler error.
+Similarly, to use a template function, specify the template arguments:
+
+```cpp
+template <typename T>
+bool IsNegative(T x) {
+  return x < 0;
+}
+...
+EXPECT_PRED1(IsNegative<int>, -5);  // Must specify type for IsNegative
+```
+
+If a template has multiple parameters, wrap the predicate in parentheses so the
+macro arguments are parsed correctly:
+
+```cpp
+ASSERT_PRED2((MyPredicate<int, int>), 5, 0);
+```
+
+### EXPECT_PRED_FORMAT* {#EXPECT_PRED_FORMAT}
+
+`EXPECT_PRED_FORMAT1(`*`pred_formatter`*`,`*`val1`*`)` \
+`EXPECT_PRED_FORMAT2(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`)` \
+`EXPECT_PRED_FORMAT3(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
+`EXPECT_PRED_FORMAT4(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)`
+\
+`EXPECT_PRED_FORMAT5(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
+
+`ASSERT_PRED_FORMAT1(`*`pred_formatter`*`,`*`val1`*`)` \
+`ASSERT_PRED_FORMAT2(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`)` \
+`ASSERT_PRED_FORMAT3(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
+`ASSERT_PRED_FORMAT4(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)`
+\
+`ASSERT_PRED_FORMAT5(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
+
+Verifies that the predicate *`pred_formatter`* succeeds when passed the given
+values as arguments.
+
+The parameter *`pred_formatter`* is a *predicate-formatter*, which is a function
+or functor with the signature:
+
+```cpp
+testing::AssertionResult PredicateFormatter(const char* expr1,
+                                            const char* expr2,
+                                            ...
+                                            const char* exprn,
+                                            T1 val1,
+                                            T2 val2,
+                                            ...
+                                            Tn valn);
+```
+
+where *`val1`*, *`val2`*, ..., *`valn`* are the values of the predicate
+arguments, and *`expr1`*, *`expr2`*, ..., *`exprn`* are the corresponding
+expressions as they appear in the source code. The types `T1`, `T2`, ..., `Tn`
+can be either value types or reference types; if an argument has type `T`, it
+can be declared as either `T` or `const T&`, whichever is appropriate. For more
+about the return type `testing::AssertionResult`, see
+[Using a Function That Returns an AssertionResult](../advanced.md#using-a-function-that-returns-an-assertionresult).
+
+As an example, see the following code:
+
+```cpp
+// Returns the smallest prime common divisor of m and n,
+// or 1 when m and n are mutually prime.
+int SmallestPrimeCommonDivisor(int m, int n) { ... }
+
+// Returns true if m and n have no common divisors except 1.
+bool MutuallyPrime(int m, int n) { ... }
+
+// A predicate-formatter for asserting that two integers are mutually prime.
+testing::AssertionResult AssertMutuallyPrime(const char* m_expr,
+                                             const char* n_expr,
+                                             int m,
+                                             int n) {
+  if (MutuallyPrime(m, n)) return testing::AssertionSuccess();
+
+  return testing::AssertionFailure() << m_expr << " and " << n_expr
+      << " (" << m << " and " << n << ") are not mutually prime, "
+      << "as they have a common divisor " << SmallestPrimeCommonDivisor(m, n);
+}
+
+...
+const int a = 3;
+const int b = 4;
+const int c = 10;
+...
+EXPECT_PRED_FORMAT2(AssertMutuallyPrime, a, b);  // Succeeds
+EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c);  // Fails
+```
+
+In the above example, the final assertion fails and the predicate-formatter
+produces the following failure message:
+
+```
+b and c (4 and 10) are not mutually prime, as they have a common divisor 2
+```
+
+## Windows HRESULT Assertions {#HRESULT}
+
+The following assertions test for `HRESULT` success or failure. For example:
+
+```cpp
+CComPtr<IShellDispatch2> shell;
+ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
+CComVariant empty;
+ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));
+```
+
+The generated output contains the human-readable error message associated with
+the returned `HRESULT` code.
+
+### EXPECT_HRESULT_SUCCEEDED {#EXPECT_HRESULT_SUCCEEDED}
+
+`EXPECT_HRESULT_SUCCEEDED(`*`expression`*`)` \
+`ASSERT_HRESULT_SUCCEEDED(`*`expression`*`)`
+
+Verifies that *`expression`* is a success `HRESULT`.
+
+### EXPECT_HRESULT_FAILED {#EXPECT_HRESULT_FAILED}
+
+`EXPECT_HRESULT_FAILED(`*`expression`*`)` \
+`EXPECT_HRESULT_FAILED(`*`expression`*`)`
+
+Verifies that *`expression`* is a failure `HRESULT`.
+
+## Death Assertions {#death}
+
+The following assertions verify that a piece of code causes the process to
+terminate. For context, see [Death Tests](../advanced.md#death-tests).
+
+These assertions spawn a new process and execute the code under test in that
+process. How that happens depends on the platform and the variable
+`::testing::GTEST_FLAG(death_test_style)`, which is initialized from the
+command-line flag `--gtest_death_test_style`.
+
+*   On POSIX systems, `fork()` (or `clone()` on Linux) is used to spawn the
+    child, after which:
+    *   If the variable's value is `"fast"`, the death test statement is
+        immediately executed.
+    *   If the variable's value is `"threadsafe"`, the child process re-executes
+        the unit test binary just as it was originally invoked, but with some
+        extra flags to cause just the single death test under consideration to
+        be run.
+*   On Windows, the child is spawned using the `CreateProcess()` API, and
+    re-executes the binary to cause just the single death test under
+    consideration to be run - much like the `"threadsafe"` mode on POSIX.
+
+Other values for the variable are illegal and will cause the death test to fail.
+Currently, the flag's default value is
+**`"fast"`**.
+
+If the death test statement runs to completion without dying, the child process
+will nonetheless terminate, and the assertion fails.
+
+Note that the piece of code under test can be a compound statement, for example:
+
+```cpp
+EXPECT_DEATH({
+  int n = 5;
+  DoSomething(&n);
+}, "Error on line .* of DoSomething()");
+```
+
+### EXPECT_DEATH {#EXPECT_DEATH}
+
+`EXPECT_DEATH(`*`statement`*`,`*`matcher`*`)` \
+`ASSERT_DEATH(`*`statement`*`,`*`matcher`*`)`
+
+Verifies that *`statement`* causes the process to terminate with a nonzero exit
+status and produces `stderr` output that matches *`matcher`*.
+
+The parameter *`matcher`* is either a [matcher](matchers.md) for a `const
+std::string&`, or a regular expression (see
+[Regular Expression Syntax](../advanced.md#regular-expression-syntax))—a bare
+string *`s`* (with no matcher) is treated as
+[`ContainsRegex(s)`](matchers.md#string-matchers), **not**
+[`Eq(s)`](matchers.md#generic-comparison).
+
+For example, the following code verifies that calling `DoSomething(42)` causes
+the process to die with an error message that contains the text `My error`:
+
+```cpp
+EXPECT_DEATH(DoSomething(42), "My error");
+```
+
+### EXPECT_DEATH_IF_SUPPORTED {#EXPECT_DEATH_IF_SUPPORTED}
+
+`EXPECT_DEATH_IF_SUPPORTED(`*`statement`*`,`*`matcher`*`)` \
+`ASSERT_DEATH_IF_SUPPORTED(`*`statement`*`,`*`matcher`*`)`
+
+If death tests are supported, behaves the same as
+[`EXPECT_DEATH`](#EXPECT_DEATH). Otherwise, verifies nothing.
+
+### EXPECT_DEBUG_DEATH {#EXPECT_DEBUG_DEATH}
+
+`EXPECT_DEBUG_DEATH(`*`statement`*`,`*`matcher`*`)` \
+`ASSERT_DEBUG_DEATH(`*`statement`*`,`*`matcher`*`)`
+
+In debug mode, behaves the same as [`EXPECT_DEATH`](#EXPECT_DEATH). When not in
+debug mode (i.e. `NDEBUG` is defined), just executes *`statement`*.
+
+### EXPECT_EXIT {#EXPECT_EXIT}
+
+`EXPECT_EXIT(`*`statement`*`,`*`predicate`*`,`*`matcher`*`)` \
+`ASSERT_EXIT(`*`statement`*`,`*`predicate`*`,`*`matcher`*`)`
+
+Verifies that *`statement`* causes the process to terminate with an exit status
+that satisfies *`predicate`*, and produces `stderr` output that matches
+*`matcher`*.
+
+The parameter *`predicate`* is a function or functor that accepts an `int` exit
+status and returns a `bool`. GoogleTest provides two predicates to handle common
+cases:
+
+```cpp
+// Returns true if the program exited normally with the given exit status code.
+::testing::ExitedWithCode(exit_code);
+
+// Returns true if the program was killed by the given signal.
+// Not available on Windows.
+::testing::KilledBySignal(signal_number);
+```
+
+The parameter *`matcher`* is either a [matcher](matchers.md) for a `const
+std::string&`, or a regular expression (see
+[Regular Expression Syntax](../advanced.md#regular-expression-syntax))—a bare
+string *`s`* (with no matcher) is treated as
+[`ContainsRegex(s)`](matchers.md#string-matchers), **not**
+[`Eq(s)`](matchers.md#generic-comparison).
+
+For example, the following code verifies that calling `NormalExit()` causes the
+process to print a message containing the text `Success` to `stderr` and exit
+with exit status code 0:
+
+```cpp
+EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success");
+```
diff --git a/ext/googletest/docs/reference/matchers.md b/ext/googletest/docs/reference/matchers.md
new file mode 100644
index 0000000..1a60b4c
--- /dev/null
+++ b/ext/googletest/docs/reference/matchers.md
@@ -0,0 +1,285 @@
+# Matchers Reference
+
+A **matcher** matches a *single* argument. You can use it inside `ON_CALL()` or
+`EXPECT_CALL()`, or use it to validate a value directly using two macros:
+
+| Macro                                | Description                           |
+| :----------------------------------- | :------------------------------------ |
+| `EXPECT_THAT(actual_value, matcher)` | Asserts that `actual_value` matches `matcher`. |
+| `ASSERT_THAT(actual_value, matcher)` | The same as `EXPECT_THAT(actual_value, matcher)`, except that it generates a **fatal** failure. |
+
+{: .callout .note}
+**Note:** Although equality matching via `EXPECT_THAT(actual_value,
+expected_value)` is supported, prefer to make the comparison explicit via
+`EXPECT_THAT(actual_value, Eq(expected_value))` or `EXPECT_EQ(actual_value,
+expected_value)`.
+
+Built-in matchers (where `argument` is the function argument, e.g.
+`actual_value` in the example above, or when used in the context of
+`EXPECT_CALL(mock_object, method(matchers))`, the arguments of `method`) are
+divided into several categories. All matchers are defined in the `::testing`
+namespace unless otherwise noted.
+
+## Wildcard
+
+Matcher                     | Description
+:-------------------------- | :-----------------------------------------------
+`_`                         | `argument` can be any value of the correct type.
+`A<type>()` or `An<type>()` | `argument` can be any value of type `type`.
+
+## Generic Comparison
+
+| Matcher                | Description                                         |
+| :--------------------- | :-------------------------------------------------- |
+| `Eq(value)` or `value` | `argument == value`                                 |
+| `Ge(value)`            | `argument >= value`                                 |
+| `Gt(value)`            | `argument > value`                                  |
+| `Le(value)`            | `argument <= value`                                 |
+| `Lt(value)`            | `argument < value`                                  |
+| `Ne(value)`            | `argument != value`                                 |
+| `IsFalse()`            | `argument` evaluates to `false` in a Boolean context. |
+| `IsTrue()`             | `argument` evaluates to `true` in a Boolean context. |
+| `IsNull()`             | `argument` is a `NULL` pointer (raw or smart).      |
+| `NotNull()`            | `argument` is a non-null pointer (raw or smart).    |
+| `Optional(m)`          | `argument` is `optional<>` that contains a value matching `m`. (For testing whether an `optional<>` is set, check for equality with `nullopt`. You may need to use `Eq(nullopt)` if the inner type doesn't have `==`.)|
+| `VariantWith<T>(m)`    | `argument` is `variant<>` that holds the alternative of type T with a value matching `m`. |
+| `Ref(variable)`        | `argument` is a reference to `variable`.            |
+| `TypedEq<type>(value)` | `argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded. |
+
+Except `Ref()`, these matchers make a *copy* of `value` in case it's modified or
+destructed later. If the compiler complains that `value` doesn't have a public
+copy constructor, try wrap it in `std::ref()`, e.g.
+`Eq(std::ref(non_copyable_value))`. If you do that, make sure
+`non_copyable_value` is not changed afterwards, or the meaning of your matcher
+will be changed.
+
+`IsTrue` and `IsFalse` are useful when you need to use a matcher, or for types
+that can be explicitly converted to Boolean, but are not implicitly converted to
+Boolean. In other cases, you can use the basic
+[`EXPECT_TRUE` and `EXPECT_FALSE`](assertions.md#boolean) assertions.
+
+## Floating-Point Matchers {#FpMatchers}
+
+| Matcher                          | Description                        |
+| :------------------------------- | :--------------------------------- |
+| `DoubleEq(a_double)`             | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal. |
+| `FloatEq(a_float)`               | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. |
+| `NanSensitiveDoubleEq(a_double)` | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. |
+| `NanSensitiveFloatEq(a_float)`   | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. |
+| `IsNan()`   | `argument` is any floating-point type with a NaN value. |
+
+The above matchers use ULP-based comparison (the same as used in googletest).
+They automatically pick a reasonable error bound based on the absolute value of
+the expected value. `DoubleEq()` and `FloatEq()` conform to the IEEE standard,
+which requires comparing two NaNs for equality to return false. The
+`NanSensitive*` version instead treats two NaNs as equal, which is often what a
+user wants.
+
+| Matcher                                           | Description              |
+| :------------------------------------------------ | :----------------------- |
+| `DoubleNear(a_double, max_abs_error)`             | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as unequal. |
+| `FloatNear(a_float, max_abs_error)`               | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as unequal. |
+| `NanSensitiveDoubleNear(a_double, max_abs_error)` | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as equal. |
+| `NanSensitiveFloatNear(a_float, max_abs_error)`   | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as equal. |
+
+## String Matchers
+
+The `argument` can be either a C string or a C++ string object:
+
+| Matcher                 | Description                                        |
+| :---------------------- | :------------------------------------------------- |
+| `ContainsRegex(string)` | `argument` matches the given regular expression.   |
+| `EndsWith(suffix)`      | `argument` ends with string `suffix`.              |
+| `HasSubstr(string)`     | `argument` contains `string` as a sub-string.      |
+| `IsEmpty()`             | `argument` is an empty string.                     |
+| `MatchesRegex(string)`  | `argument` matches the given regular expression with the match starting at the first character and ending at the last character. |
+| `StartsWith(prefix)`    | `argument` starts with string `prefix`.            |
+| `StrCaseEq(string)`     | `argument` is equal to `string`, ignoring case.    |
+| `StrCaseNe(string)`     | `argument` is not equal to `string`, ignoring case. |
+| `StrEq(string)`         | `argument` is equal to `string`.                   |
+| `StrNe(string)`         | `argument` is not equal to `string`.               |
+
+`ContainsRegex()` and `MatchesRegex()` take ownership of the `RE` object. They
+use the regular expression syntax defined
+[here](../advanced.md#regular-expression-syntax). All of these matchers, except
+`ContainsRegex()` and `MatchesRegex()` work for wide strings as well.
+
+## Container Matchers
+
+Most STL-style containers support `==`, so you can use `Eq(expected_container)`
+or simply `expected_container` to match a container exactly. If you want to
+write the elements in-line, match them more flexibly, or get more informative
+messages, you can use:
+
+| Matcher                                   | Description                      |
+| :---------------------------------------- | :------------------------------- |
+| `BeginEndDistanceIs(m)` | `argument` is a container whose `begin()` and `end()` iterators are separated by a number of increments matching `m`. E.g. `BeginEndDistanceIs(2)` or `BeginEndDistanceIs(Lt(2))`. For containers that define a `size()` method, `SizeIs(m)` may be more efficient. |
+| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. |
+| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. |
+| `Contains(e).Times(n)` | `argument` contains elements that match `e`, which can be either a value or a matcher, and the number of matches is `n`, which can be either a value or a matcher. Unlike the plain `Contains` and `Each` this allows to check for arbitrary occurrences including testing for absence with `Contains(e).Times(0)`. |
+| `Each(e)` | `argument` is a container where *every* element matches `e`, which can be either a value or a matcher. |
+| `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the *i*-th element matches `ei`, which can be a value or a matcher. |
+| `ElementsAreArray({e0, e1, ..., en})`, `ElementsAreArray(a_container)`, `ElementsAreArray(begin, end)`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
+| `IsEmpty()` | `argument` is an empty container (`container.empty()`). |
+| `IsSubsetOf({e0, e1, ..., en})`, `IsSubsetOf(a_container)`, `IsSubsetOf(begin, end)`, `IsSubsetOf(array)`, or `IsSubsetOf(array, count)` | `argument` matches `UnorderedElementsAre(x0, x1, ..., xk)` for some subset `{x0, x1, ..., xk}` of the expected matchers. |
+| `IsSupersetOf({e0, e1, ..., en})`, `IsSupersetOf(a_container)`, `IsSupersetOf(begin, end)`, `IsSupersetOf(array)`, or `IsSupersetOf(array, count)` | Some subset of `argument` matches `UnorderedElementsAre(`expected matchers`)`. |
+| `Pointwise(m, container)`, `Pointwise(m, {e0, e1, ..., en})` | `argument` contains the same number of elements as in `container`, and for all i, (the i-th element in `argument`, the i-th element in `container`) match `m`, which is a matcher on 2-tuples. E.g. `Pointwise(Le(), upper_bounds)` verifies that each element in `argument` doesn't exceed the corresponding element in `upper_bounds`. See more detail below. |
+| `SizeIs(m)` | `argument` is a container whose size matches `m`. E.g. `SizeIs(2)` or `SizeIs(Lt(2))`. |
+| `UnorderedElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, and under *some* permutation of the elements, each element matches an `ei` (for a different `i`), which can be a value or a matcher. |
+| `UnorderedElementsAreArray({e0, e1, ..., en})`, `UnorderedElementsAreArray(a_container)`, `UnorderedElementsAreArray(begin, end)`, `UnorderedElementsAreArray(array)`, or `UnorderedElementsAreArray(array, count)` | The same as `UnorderedElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
+| `UnorderedPointwise(m, container)`, `UnorderedPointwise(m, {e0, e1, ..., en})` | Like `Pointwise(m, container)`, but ignores the order of elements. |
+| `WhenSorted(m)` | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(ElementsAre(1, 2, 3))` verifies that `argument` contains elements 1, 2, and 3, ignoring order. |
+| `WhenSortedBy(comparator, m)` | The same as `WhenSorted(m)`, except that the given comparator instead of `<` is used to sort `argument`. E.g. `WhenSortedBy(std::greater(), ElementsAre(3, 2, 1))`. |
+
+**Notes:**
+
+*   These matchers can also match:
+    1.  a native array passed by reference (e.g. in `Foo(const int (&a)[5])`),
+        and
+    2.  an array passed as a pointer and a count (e.g. in `Bar(const T* buffer,
+        int len)` -- see [Multi-argument Matchers](#MultiArgMatchers)).
+*   The array being matched may be multi-dimensional (i.e. its elements can be
+    arrays).
+*   `m` in `Pointwise(m, ...)` and `UnorderedPointwise(m, ...)` should be a
+    matcher for `::std::tuple<T, U>` where `T` and `U` are the element type of
+    the actual container and the expected container, respectively. For example,
+    to compare two `Foo` containers where `Foo` doesn't support `operator==`,
+    one might write:
+
+    ```cpp
+    using ::std::get;
+    MATCHER(FooEq, "") {
+      return std::get<0>(arg).Equals(std::get<1>(arg));
+    }
+    ...
+    EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos));
+    ```
+
+## Member Matchers
+
+| Matcher                         | Description                                |
+| :------------------------------ | :----------------------------------------- |
+| `Field(&class::field, m)`       | `argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. |
+| `Field(field_name, &class::field, m)` | The same as the two-parameter version, but provides a better error message. |
+| `Key(e)`                        | `argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`. |
+| `Pair(m1, m2)`                  | `argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. |
+| `FieldsAre(m...)`                   | `argument` is a compatible object where each field matches piecewise with the matchers `m...`. A compatible object is any that supports the `std::tuple_size<Obj>`+`get<I>(obj)` protocol. In C++17 and up this also supports types compatible with structured bindings, like aggregates. |
+| `Property(&class::property, m)` | `argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. The method `property()` must take no argument and be declared as `const`. |
+| `Property(property_name, &class::property, m)` | The same as the two-parameter version, but provides a better error message.
+
+**Notes:**
+
+*   You can use `FieldsAre()` to match any type that supports structured
+    bindings, such as `std::tuple`, `std::pair`, `std::array`, and aggregate
+    types. For example:
+
+    ```cpp
+    std::tuple<int, std::string> my_tuple{7, "hello world"};
+    EXPECT_THAT(my_tuple, FieldsAre(Ge(0), HasSubstr("hello")));
+
+    struct MyStruct {
+      int value = 42;
+      std::string greeting = "aloha";
+    };
+    MyStruct s;
+    EXPECT_THAT(s, FieldsAre(42, "aloha"));
+    ```
+
+*   Don't use `Property()` against member functions that you do not own, because
+    taking addresses of functions is fragile and generally not part of the
+    contract of the function.
+
+## Matching the Result of a Function, Functor, or Callback
+
+| Matcher          | Description                                       |
+| :--------------- | :------------------------------------------------ |
+| `ResultOf(f, m)` | `f(argument)` matches matcher `m`, where `f` is a function or functor. |
+
+## Pointer Matchers
+
+| Matcher                   | Description                                     |
+| :------------------------ | :---------------------------------------------- |
+| `Address(m)`              | the result of `std::addressof(argument)` matches `m`. |
+| `Pointee(m)`              | `argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`. |
+| `Pointer(m)`              | `argument` (either a smart pointer or a raw pointer) contains a pointer that matches `m`. `m` will match against the raw pointer regardless of the type of `argument`. |
+| `WhenDynamicCastTo<T>(m)` | when `argument` is passed through `dynamic_cast<T>()`, it matches matcher `m`. |
+
+## Multi-argument Matchers {#MultiArgMatchers}
+
+Technically, all matchers match a *single* value. A "multi-argument" matcher is
+just one that matches a *tuple*. The following matchers can be used to match a
+tuple `(x, y)`:
+
+Matcher | Description
+:------ | :----------
+`Eq()`  | `x == y`
+`Ge()`  | `x >= y`
+`Gt()`  | `x > y`
+`Le()`  | `x <= y`
+`Lt()`  | `x < y`
+`Ne()`  | `x != y`
+
+You can use the following selectors to pick a subset of the arguments (or
+reorder them) to participate in the matching:
+
+| Matcher                    | Description                                     |
+| :------------------------- | :---------------------------------------------- |
+| `AllArgs(m)`               | Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`. |
+| `Args<N1, N2, ..., Nk>(m)` | The tuple of the `k` selected (using 0-based indices) arguments matches `m`, e.g. `Args<1, 2>(Eq())`. |
+
+## Composite Matchers
+
+You can make a matcher from one or more other matchers:
+
+| Matcher                          | Description                             |
+| :------------------------------- | :-------------------------------------- |
+| `AllOf(m1, m2, ..., mn)` | `argument` matches all of the matchers `m1` to `mn`. |
+| `AllOfArray({m0, m1, ..., mn})`, `AllOfArray(a_container)`, `AllOfArray(begin, end)`, `AllOfArray(array)`, or `AllOfArray(array, count)` | The same as `AllOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
+| `AnyOf(m1, m2, ..., mn)` | `argument` matches at least one of the matchers `m1` to `mn`. |
+| `AnyOfArray({m0, m1, ..., mn})`, `AnyOfArray(a_container)`, `AnyOfArray(begin, end)`, `AnyOfArray(array)`, or `AnyOfArray(array, count)` | The same as `AnyOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
+| `Not(m)` | `argument` doesn't match matcher `m`. |
+| `Conditional(cond, m1, m2)` | Matches matcher `m1` if `cond` evalutes to true, else matches `m2`.|
+
+## Adapters for Matchers
+
+| Matcher                 | Description                           |
+| :---------------------- | :------------------------------------ |
+| `MatcherCast<T>(m)`     | casts matcher `m` to type `Matcher<T>`. |
+| `SafeMatcherCast<T>(m)` | [safely casts](../gmock_cook_book.md#SafeMatcherCast) matcher `m` to type `Matcher<T>`. |
+| `Truly(predicate)`      | `predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor. |
+
+`AddressSatisfies(callback)` and `Truly(callback)` take ownership of `callback`,
+which must be a permanent callback.
+
+## Using Matchers as Predicates {#MatchersAsPredicatesCheat}
+
+| Matcher                       | Description                                 |
+| :---------------------------- | :------------------------------------------ |
+| `Matches(m)(value)` | evaluates to `true` if `value` matches `m`. You can use `Matches(m)` alone as a unary functor. |
+| `ExplainMatchResult(m, value, result_listener)` | evaluates to `true` if `value` matches `m`, explaining the result to `result_listener`. |
+| `Value(value, m)` | evaluates to `true` if `value` matches `m`. |
+
+## Defining Matchers
+
+| Macro                                | Description                           |
+| :----------------------------------- | :------------------------------------ |
+| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. |
+| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a matcher `IsDivisibleBy(n)` to match a number divisible by `n`. |
+| `MATCHER_P2(IsBetween, a, b, absl::StrCat(negation ? "isn't" : "is", " between ", PrintToString(a), " and ", PrintToString(b))) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. |
+
+**Notes:**
+
+1.  The `MATCHER*` macros cannot be used inside a function or class.
+2.  The matcher body must be *purely functional* (i.e. it cannot have any side
+    effect, and the result must not depend on anything other than the value
+    being matched and the matcher parameters).
+3.  You can use `PrintToString(x)` to convert a value `x` of any type to a
+    string.
+4.  You can use `ExplainMatchResult()` in a custom matcher to wrap another
+    matcher, for example:
+
+    ```cpp
+    MATCHER_P(NestedPropertyMatches, matcher, "") {
+      return ExplainMatchResult(matcher, arg.nested().property(), result_listener);
+    }
+    ```
diff --git a/ext/googletest/docs/reference/mocking.md b/ext/googletest/docs/reference/mocking.md
new file mode 100644
index 0000000..c29f716
--- /dev/null
+++ b/ext/googletest/docs/reference/mocking.md
@@ -0,0 +1,587 @@
+# Mocking Reference
+
+This page lists the facilities provided by GoogleTest for creating and working
+with mock objects. To use them, include the header
+`gmock/gmock.h`.
+
+## Macros {#macros}
+
+GoogleTest defines the following macros for working with mocks.
+
+### MOCK_METHOD {#MOCK_METHOD}
+
+`MOCK_METHOD(`*`return_type`*`,`*`method_name`*`, (`*`args...`*`));` \
+`MOCK_METHOD(`*`return_type`*`,`*`method_name`*`, (`*`args...`*`),
+(`*`specs...`*`));`
+
+Defines a mock method *`method_name`* with arguments `(`*`args...`*`)` and
+return type *`return_type`* within a mock class.
+
+The parameters of `MOCK_METHOD` mirror the method declaration. The optional
+fourth parameter *`specs...`* is a comma-separated list of qualifiers. The
+following qualifiers are accepted:
+
+| Qualifier                  | Meaning                                      |
+| -------------------------- | -------------------------------------------- |
+| `const`                    | Makes the mocked method a `const` method. Required if overriding a `const` method. |
+| `override`                 | Marks the method with `override`. Recommended if overriding a `virtual` method. |
+| `noexcept`                 | Marks the method with `noexcept`. Required if overriding a `noexcept` method. |
+| `Calltype(`*`calltype`*`)` | Sets the call type for the method, for example `Calltype(STDMETHODCALLTYPE)`. Useful on Windows. |
+| `ref(`*`qualifier`*`)`     | Marks the method with the given reference qualifier, for example `ref(&)` or `ref(&&)`. Required if overriding a method that has a reference qualifier. |
+
+Note that commas in arguments prevent `MOCK_METHOD` from parsing the arguments
+correctly if they are not appropriately surrounded by parentheses. See the
+following example:
+
+```cpp
+class MyMock {
+ public:
+  // The following 2 lines will not compile due to commas in the arguments:
+  MOCK_METHOD(std::pair<bool, int>, GetPair, ());              // Error!
+  MOCK_METHOD(bool, CheckMap, (std::map<int, double>, bool));  // Error!
+
+  // One solution - wrap arguments that contain commas in parentheses:
+  MOCK_METHOD((std::pair<bool, int>), GetPair, ());
+  MOCK_METHOD(bool, CheckMap, ((std::map<int, double>), bool));
+
+  // Another solution - use type aliases:
+  using BoolAndInt = std::pair<bool, int>;
+  MOCK_METHOD(BoolAndInt, GetPair, ());
+  using MapIntDouble = std::map<int, double>;
+  MOCK_METHOD(bool, CheckMap, (MapIntDouble, bool));
+};
+```
+
+`MOCK_METHOD` must be used in the `public:` section of a mock class definition,
+regardless of whether the method being mocked is `public`, `protected`, or
+`private` in the base class.
+
+### EXPECT_CALL {#EXPECT_CALL}
+
+`EXPECT_CALL(`*`mock_object`*`,`*`method_name`*`(`*`matchers...`*`))`
+
+Creates an [expectation](../gmock_for_dummies.md#setting-expectations) that the
+method *`method_name`* of the object *`mock_object`* is called with arguments
+that match the given matchers *`matchers...`*. `EXPECT_CALL` must precede any
+code that exercises the mock object.
+
+The parameter *`matchers...`* is a comma-separated list of
+[matchers](../gmock_for_dummies.md#matchers-what-arguments-do-we-expect) that
+correspond to each argument of the method *`method_name`*. The expectation will
+apply only to calls of *`method_name`* whose arguments match all of the
+matchers. If `(`*`matchers...`*`)` is omitted, the expectation behaves as if
+each argument's matcher were a [wildcard matcher (`_`)](matchers.md#wildcard).
+See the [Matchers Reference](matchers.md) for a list of all built-in matchers.
+
+The following chainable clauses can be used to modify the expectation, and they
+must be used in the following order:
+
+```cpp
+EXPECT_CALL(mock_object, method_name(matchers...))
+    .With(multi_argument_matcher)  // Can be used at most once
+    .Times(cardinality)            // Can be used at most once
+    .InSequence(sequences...)      // Can be used any number of times
+    .After(expectations...)        // Can be used any number of times
+    .WillOnce(action)              // Can be used any number of times
+    .WillRepeatedly(action)        // Can be used at most once
+    .RetiresOnSaturation();        // Can be used at most once
+```
+
+See details for each modifier clause below.
+
+#### With {#EXPECT_CALL.With}
+
+`.With(`*`multi_argument_matcher`*`)`
+
+Restricts the expectation to apply only to mock function calls whose arguments
+as a whole match the multi-argument matcher *`multi_argument_matcher`*.
+
+GoogleTest passes all of the arguments as one tuple into the matcher. The
+parameter *`multi_argument_matcher`* must thus be a matcher of type
+`Matcher<std::tuple<A1, ..., An>>`, where `A1, ..., An` are the types of the
+function arguments.
+
+For example, the following code sets the expectation that
+`my_mock.SetPosition()` is called with any two arguments, the first argument
+being less than the second:
+
+```cpp
+using ::testing::_;
+using ::testing::Lt;
+...
+EXPECT_CALL(my_mock, SetPosition(_, _))
+    .With(Lt());
+```
+
+GoogleTest provides some built-in matchers for 2-tuples, including the `Lt()`
+matcher above. See [Multi-argument Matchers](matchers.md#MultiArgMatchers).
+
+The `With` clause can be used at most once on an expectation and must be the
+first clause.
+
+#### Times {#EXPECT_CALL.Times}
+
+`.Times(`*`cardinality`*`)`
+
+Specifies how many times the mock function call is expected.
+
+The parameter *`cardinality`* represents the number of expected calls and can be
+one of the following, all defined in the `::testing` namespace:
+
+| Cardinality         | Meaning                                             |
+| ------------------- | --------------------------------------------------- |
+| `AnyNumber()`       | The function can be called any number of times.     |
+| `AtLeast(n)`        | The function call is expected at least *n* times.   |
+| `AtMost(n)`         | The function call is expected at most *n* times.    |
+| `Between(m, n)`     | The function call is expected between *m* and *n* times, inclusive. |
+| `Exactly(n)` or `n` | The function call is expected exactly *n* times. If *n* is 0, the call should never happen. |
+
+If the `Times` clause is omitted, GoogleTest infers the cardinality as follows:
+
+*   If neither [`WillOnce`](#EXPECT_CALL.WillOnce) nor
+    [`WillRepeatedly`](#EXPECT_CALL.WillRepeatedly) are specified, the inferred
+    cardinality is `Times(1)`.
+*   If there are *n* `WillOnce` clauses and no `WillRepeatedly` clause, where
+    *n* >= 1, the inferred cardinality is `Times(n)`.
+*   If there are *n* `WillOnce` clauses and one `WillRepeatedly` clause, where
+    *n* >= 0, the inferred cardinality is `Times(AtLeast(n))`.
+
+The `Times` clause can be used at most once on an expectation.
+
+#### InSequence {#EXPECT_CALL.InSequence}
+
+`.InSequence(`*`sequences...`*`)`
+
+Specifies that the mock function call is expected in a certain sequence.
+
+The parameter *`sequences...`* is any number of [`Sequence`](#Sequence) objects.
+Expected calls assigned to the same sequence are expected to occur in the order
+the expectations are declared.
+
+For example, the following code sets the expectation that the `Reset()` method
+of `my_mock` is called before both `GetSize()` and `Describe()`, and `GetSize()`
+and `Describe()` can occur in any order relative to each other:
+
+```cpp
+using ::testing::Sequence;
+Sequence s1, s2;
+...
+EXPECT_CALL(my_mock, Reset())
+    .InSequence(s1, s2);
+EXPECT_CALL(my_mock, GetSize())
+    .InSequence(s1);
+EXPECT_CALL(my_mock, Describe())
+    .InSequence(s2);
+```
+
+The `InSequence` clause can be used any number of times on an expectation.
+
+See also the [`InSequence` class](#InSequence).
+
+#### After {#EXPECT_CALL.After}
+
+`.After(`*`expectations...`*`)`
+
+Specifies that the mock function call is expected to occur after one or more
+other calls.
+
+The parameter *`expectations...`* can be up to five
+[`Expectation`](#Expectation) or [`ExpectationSet`](#ExpectationSet) objects.
+The mock function call is expected to occur after all of the given expectations.
+
+For example, the following code sets the expectation that the `Describe()`
+method of `my_mock` is called only after both `InitX()` and `InitY()` have been
+called.
+
+```cpp
+using ::testing::Expectation;
+...
+Expectation init_x = EXPECT_CALL(my_mock, InitX());
+Expectation init_y = EXPECT_CALL(my_mock, InitY());
+EXPECT_CALL(my_mock, Describe())
+    .After(init_x, init_y);
+```
+
+The `ExpectationSet` object is helpful when the number of prerequisites for an
+expectation is large or variable, for example:
+
+```cpp
+using ::testing::ExpectationSet;
+...
+ExpectationSet all_inits;
+// Collect all expectations of InitElement() calls
+for (int i = 0; i < element_count; i++) {
+  all_inits += EXPECT_CALL(my_mock, InitElement(i));
+}
+EXPECT_CALL(my_mock, Describe())
+    .After(all_inits);  // Expect Describe() call after all InitElement() calls
+```
+
+The `After` clause can be used any number of times on an expectation.
+
+#### WillOnce {#EXPECT_CALL.WillOnce}
+
+`.WillOnce(`*`action`*`)`
+
+Specifies the mock function's actual behavior when invoked, for a single
+matching function call.
+
+The parameter *`action`* represents the
+[action](../gmock_for_dummies.md#actions-what-should-it-do) that the function
+call will perform. See the [Actions Reference](actions.md) for a list of
+built-in actions.
+
+The use of `WillOnce` implicitly sets a cardinality on the expectation when
+`Times` is not specified. See [`Times`](#EXPECT_CALL.Times).
+
+Each matching function call will perform the next action in the order declared.
+For example, the following code specifies that `my_mock.GetNumber()` is expected
+to be called exactly 3 times and will return `1`, `2`, and `3` respectively on
+the first, second, and third calls:
+
+```cpp
+using ::testing::Return;
+...
+EXPECT_CALL(my_mock, GetNumber())
+    .WillOnce(Return(1))
+    .WillOnce(Return(2))
+    .WillOnce(Return(3));
+```
+
+The `WillOnce` clause can be used any number of times on an expectation.
+
+#### WillRepeatedly {#EXPECT_CALL.WillRepeatedly}
+
+`.WillRepeatedly(`*`action`*`)`
+
+Specifies the mock function's actual behavior when invoked, for all subsequent
+matching function calls. Takes effect after the actions specified in the
+[`WillOnce`](#EXPECT_CALL.WillOnce) clauses, if any, have been performed.
+
+The parameter *`action`* represents the
+[action](../gmock_for_dummies.md#actions-what-should-it-do) that the function
+call will perform. See the [Actions Reference](actions.md) for a list of
+built-in actions.
+
+The use of `WillRepeatedly` implicitly sets a cardinality on the expectation
+when `Times` is not specified. See [`Times`](#EXPECT_CALL.Times).
+
+If any `WillOnce` clauses have been specified, matching function calls will
+perform those actions before the action specified by `WillRepeatedly`. See the
+following example:
+
+```cpp
+using ::testing::Return;
+...
+EXPECT_CALL(my_mock, GetName())
+    .WillRepeatedly(Return("John Doe"));  // Return "John Doe" on all calls
+
+EXPECT_CALL(my_mock, GetNumber())
+    .WillOnce(Return(42))        // Return 42 on the first call
+    .WillRepeatedly(Return(7));  // Return 7 on all subsequent calls
+```
+
+The `WillRepeatedly` clause can be used at most once on an expectation.
+
+#### RetiresOnSaturation {#EXPECT_CALL.RetiresOnSaturation}
+
+`.RetiresOnSaturation()`
+
+Indicates that the expectation will no longer be active after the expected
+number of matching function calls has been reached.
+
+The `RetiresOnSaturation` clause is only meaningful for expectations with an
+upper-bounded cardinality. The expectation will *retire* (no longer match any
+function calls) after it has been *saturated* (the upper bound has been
+reached). See the following example:
+
+```cpp
+using ::testing::_;
+using ::testing::AnyNumber;
+...
+EXPECT_CALL(my_mock, SetNumber(_))  // Expectation 1
+    .Times(AnyNumber());
+EXPECT_CALL(my_mock, SetNumber(7))  // Expectation 2
+    .Times(2)
+    .RetiresOnSaturation();
+```
+
+In the above example, the first two calls to `my_mock.SetNumber(7)` match
+expectation 2, which then becomes inactive and no longer matches any calls. A
+third call to `my_mock.SetNumber(7)` would then match expectation 1. Without
+`RetiresOnSaturation()` on expectation 2, a third call to `my_mock.SetNumber(7)`
+would match expectation 2 again, producing a failure since the limit of 2 calls
+was exceeded.
+
+The `RetiresOnSaturation` clause can be used at most once on an expectation and
+must be the last clause.
+
+### ON_CALL {#ON_CALL}
+
+`ON_CALL(`*`mock_object`*`,`*`method_name`*`(`*`matchers...`*`))`
+
+Defines what happens when the method *`method_name`* of the object
+*`mock_object`* is called with arguments that match the given matchers
+*`matchers...`*. Requires a modifier clause to specify the method's behavior.
+*Does not* set any expectations that the method will be called.
+
+The parameter *`matchers...`* is a comma-separated list of
+[matchers](../gmock_for_dummies.md#matchers-what-arguments-do-we-expect) that
+correspond to each argument of the method *`method_name`*. The `ON_CALL`
+specification will apply only to calls of *`method_name`* whose arguments match
+all of the matchers. If `(`*`matchers...`*`)` is omitted, the behavior is as if
+each argument's matcher were a [wildcard matcher (`_`)](matchers.md#wildcard).
+See the [Matchers Reference](matchers.md) for a list of all built-in matchers.
+
+The following chainable clauses can be used to set the method's behavior, and
+they must be used in the following order:
+
+```cpp
+ON_CALL(mock_object, method_name(matchers...))
+    .With(multi_argument_matcher)  // Can be used at most once
+    .WillByDefault(action);        // Required
+```
+
+See details for each modifier clause below.
+
+#### With {#ON_CALL.With}
+
+`.With(`*`multi_argument_matcher`*`)`
+
+Restricts the specification to only mock function calls whose arguments as a
+whole match the multi-argument matcher *`multi_argument_matcher`*.
+
+GoogleTest passes all of the arguments as one tuple into the matcher. The
+parameter *`multi_argument_matcher`* must thus be a matcher of type
+`Matcher<std::tuple<A1, ..., An>>`, where `A1, ..., An` are the types of the
+function arguments.
+
+For example, the following code sets the default behavior when
+`my_mock.SetPosition()` is called with any two arguments, the first argument
+being less than the second:
+
+```cpp
+using ::testing::_;
+using ::testing::Lt;
+using ::testing::Return;
+...
+ON_CALL(my_mock, SetPosition(_, _))
+    .With(Lt())
+    .WillByDefault(Return(true));
+```
+
+GoogleTest provides some built-in matchers for 2-tuples, including the `Lt()`
+matcher above. See [Multi-argument Matchers](matchers.md#MultiArgMatchers).
+
+The `With` clause can be used at most once with each `ON_CALL` statement.
+
+#### WillByDefault {#ON_CALL.WillByDefault}
+
+`.WillByDefault(`*`action`*`)`
+
+Specifies the default behavior of a matching mock function call.
+
+The parameter *`action`* represents the
+[action](../gmock_for_dummies.md#actions-what-should-it-do) that the function
+call will perform. See the [Actions Reference](actions.md) for a list of
+built-in actions.
+
+For example, the following code specifies that by default, a call to
+`my_mock.Greet()` will return `"hello"`:
+
+```cpp
+using ::testing::Return;
+...
+ON_CALL(my_mock, Greet())
+    .WillByDefault(Return("hello"));
+```
+
+The action specified by `WillByDefault` is superseded by the actions specified
+on a matching `EXPECT_CALL` statement, if any. See the
+[`WillOnce`](#EXPECT_CALL.WillOnce) and
+[`WillRepeatedly`](#EXPECT_CALL.WillRepeatedly) clauses of `EXPECT_CALL`.
+
+The `WillByDefault` clause must be used exactly once with each `ON_CALL`
+statement.
+
+## Classes {#classes}
+
+GoogleTest defines the following classes for working with mocks.
+
+### DefaultValue {#DefaultValue}
+
+`::testing::DefaultValue<T>`
+
+Allows a user to specify the default value for a type `T` that is both copyable
+and publicly destructible (i.e. anything that can be used as a function return
+type). For mock functions with a return type of `T`, this default value is
+returned from function calls that do not specify an action.
+
+Provides the static methods `Set()`, `SetFactory()`, and `Clear()` to manage the
+default value:
+
+```cpp
+// Sets the default value to be returned. T must be copy constructible.
+DefaultValue<T>::Set(value);
+
+// Sets a factory. Will be invoked on demand. T must be move constructible.
+T MakeT();
+DefaultValue<T>::SetFactory(&MakeT);
+
+// Unsets the default value.
+DefaultValue<T>::Clear();
+```
+
+### NiceMock {#NiceMock}
+
+`::testing::NiceMock<T>`
+
+Represents a mock object that suppresses warnings on
+[uninteresting calls](../gmock_cook_book.md#uninteresting-vs-unexpected). The
+template parameter `T` is any mock class, except for another `NiceMock`,
+`NaggyMock`, or `StrictMock`.
+
+Usage of `NiceMock<T>` is analogous to usage of `T`. `NiceMock<T>` is a subclass
+of `T`, so it can be used wherever an object of type `T` is accepted. In
+addition, `NiceMock<T>` can be constructed with any arguments that a constructor
+of `T` accepts.
+
+For example, the following code suppresses warnings on the mock `my_mock` of
+type `MockClass` if a method other than `DoSomething()` is called:
+
+```cpp
+using ::testing::NiceMock;
+...
+NiceMock<MockClass> my_mock("some", "args");
+EXPECT_CALL(my_mock, DoSomething());
+... code that uses my_mock ...
+```
+
+`NiceMock<T>` only works for mock methods defined using the `MOCK_METHOD` macro
+directly in the definition of class `T`. If a mock method is defined in a base
+class of `T`, a warning might still be generated.
+
+`NiceMock<T>` might not work correctly if the destructor of `T` is not virtual.
+
+### NaggyMock {#NaggyMock}
+
+`::testing::NaggyMock<T>`
+
+Represents a mock object that generates warnings on
+[uninteresting calls](../gmock_cook_book.md#uninteresting-vs-unexpected). The
+template parameter `T` is any mock class, except for another `NiceMock`,
+`NaggyMock`, or `StrictMock`.
+
+Usage of `NaggyMock<T>` is analogous to usage of `T`. `NaggyMock<T>` is a
+subclass of `T`, so it can be used wherever an object of type `T` is accepted.
+In addition, `NaggyMock<T>` can be constructed with any arguments that a
+constructor of `T` accepts.
+
+For example, the following code generates warnings on the mock `my_mock` of type
+`MockClass` if a method other than `DoSomething()` is called:
+
+```cpp
+using ::testing::NaggyMock;
+...
+NaggyMock<MockClass> my_mock("some", "args");
+EXPECT_CALL(my_mock, DoSomething());
+... code that uses my_mock ...
+```
+
+Mock objects of type `T` by default behave the same way as `NaggyMock<T>`.
+
+### StrictMock {#StrictMock}
+
+`::testing::StrictMock<T>`
+
+Represents a mock object that generates test failures on
+[uninteresting calls](../gmock_cook_book.md#uninteresting-vs-unexpected). The
+template parameter `T` is any mock class, except for another `NiceMock`,
+`NaggyMock`, or `StrictMock`.
+
+Usage of `StrictMock<T>` is analogous to usage of `T`. `StrictMock<T>` is a
+subclass of `T`, so it can be used wherever an object of type `T` is accepted.
+In addition, `StrictMock<T>` can be constructed with any arguments that a
+constructor of `T` accepts.
+
+For example, the following code generates a test failure on the mock `my_mock`
+of type `MockClass` if a method other than `DoSomething()` is called:
+
+```cpp
+using ::testing::StrictMock;
+...
+StrictMock<MockClass> my_mock("some", "args");
+EXPECT_CALL(my_mock, DoSomething());
+... code that uses my_mock ...
+```
+
+`StrictMock<T>` only works for mock methods defined using the `MOCK_METHOD`
+macro directly in the definition of class `T`. If a mock method is defined in a
+base class of `T`, a failure might not be generated.
+
+`StrictMock<T>` might not work correctly if the destructor of `T` is not
+virtual.
+
+### Sequence {#Sequence}
+
+`::testing::Sequence`
+
+Represents a chronological sequence of expectations. See the
+[`InSequence`](#EXPECT_CALL.InSequence) clause of `EXPECT_CALL` for usage.
+
+### InSequence {#InSequence}
+
+`::testing::InSequence`
+
+An object of this type causes all expectations encountered in its scope to be
+put in an anonymous sequence.
+
+This allows more convenient expression of multiple expectations in a single
+sequence:
+
+```cpp
+using ::testing::InSequence;
+{
+  InSequence seq;
+
+  // The following are expected to occur in the order declared.
+  EXPECT_CALL(...);
+  EXPECT_CALL(...);
+  ...
+  EXPECT_CALL(...);
+}
+```
+
+The name of the `InSequence` object does not matter.
+
+### Expectation {#Expectation}
+
+`::testing::Expectation`
+
+Represents a mock function call expectation as created by
+[`EXPECT_CALL`](#EXPECT_CALL):
+
+```cpp
+using ::testing::Expectation;
+Expectation my_expectation = EXPECT_CALL(...);
+```
+
+Useful for specifying sequences of expectations; see the
+[`After`](#EXPECT_CALL.After) clause of `EXPECT_CALL`.
+
+### ExpectationSet {#ExpectationSet}
+
+`::testing::ExpectationSet`
+
+Represents a set of mock function call expectations.
+
+Use the `+=` operator to add [`Expectation`](#Expectation) objects to the set:
+
+```cpp
+using ::testing::ExpectationSet;
+ExpectationSet my_expectations;
+my_expectations += EXPECT_CALL(...);
+```
+
+Useful for specifying sequences of expectations; see the
+[`After`](#EXPECT_CALL.After) clause of `EXPECT_CALL`.
diff --git a/ext/googletest/docs/reference/testing.md b/ext/googletest/docs/reference/testing.md
new file mode 100644
index 0000000..554d6c9
--- /dev/null
+++ b/ext/googletest/docs/reference/testing.md
@@ -0,0 +1,1431 @@
+# Testing Reference
+
+<!--* toc_depth: 3 *-->
+
+This page lists the facilities provided by GoogleTest for writing test programs.
+To use them, include the header `gtest/gtest.h`.
+
+## Macros
+
+GoogleTest defines the following macros for writing tests.
+
+### TEST {#TEST}
+
+<pre>
+TEST(<em>TestSuiteName</em>, <em>TestName</em>) {
+  ... <em>statements</em> ...
+}
+</pre>
+
+Defines an individual test named *`TestName`* in the test suite
+*`TestSuiteName`*, consisting of the given statements.
+
+Both arguments *`TestSuiteName`* and *`TestName`* must be valid C++ identifiers
+and must not contain underscores (`_`). Tests in different test suites can have
+the same individual name.
+
+The statements within the test body can be any code under test.
+[Assertions](assertions.md) used within the test body determine the outcome of
+the test.
+
+### TEST_F {#TEST_F}
+
+<pre>
+TEST_F(<em>TestFixtureName</em>, <em>TestName</em>) {
+  ... <em>statements</em> ...
+}
+</pre>
+
+Defines an individual test named *`TestName`* that uses the test fixture class
+*`TestFixtureName`*. The test suite name is *`TestFixtureName`*.
+
+Both arguments *`TestFixtureName`* and *`TestName`* must be valid C++
+identifiers and must not contain underscores (`_`). *`TestFixtureName`* must be
+the name of a test fixture class—see
+[Test Fixtures](../primer.md#same-data-multiple-tests).
+
+The statements within the test body can be any code under test.
+[Assertions](assertions.md) used within the test body determine the outcome of
+the test.
+
+### TEST_P {#TEST_P}
+
+<pre>
+TEST_P(<em>TestFixtureName</em>, <em>TestName</em>) {
+  ... <em>statements</em> ...
+}
+</pre>
+
+Defines an individual value-parameterized test named *`TestName`* that uses the
+test fixture class *`TestFixtureName`*. The test suite name is
+*`TestFixtureName`*.
+
+Both arguments *`TestFixtureName`* and *`TestName`* must be valid C++
+identifiers and must not contain underscores (`_`). *`TestFixtureName`* must be
+the name of a value-parameterized test fixture class—see
+[Value-Parameterized Tests](../advanced.md#value-parameterized-tests).
+
+The statements within the test body can be any code under test. Within the test
+body, the test parameter can be accessed with the `GetParam()` function (see
+[`WithParamInterface`](#WithParamInterface)). For example:
+
+```cpp
+TEST_P(MyTestSuite, DoesSomething) {
+  ...
+  EXPECT_TRUE(DoSomething(GetParam()));
+  ...
+}
+```
+
+[Assertions](assertions.md) used within the test body determine the outcome of
+the test.
+
+See also [`INSTANTIATE_TEST_SUITE_P`](#INSTANTIATE_TEST_SUITE_P).
+
+### INSTANTIATE_TEST_SUITE_P {#INSTANTIATE_TEST_SUITE_P}
+
+`INSTANTIATE_TEST_SUITE_P(`*`InstantiationName`*`,`*`TestSuiteName`*`,`*`param_generator`*`)`
+\
+`INSTANTIATE_TEST_SUITE_P(`*`InstantiationName`*`,`*`TestSuiteName`*`,`*`param_generator`*`,`*`name_generator`*`)`
+
+Instantiates the value-parameterized test suite *`TestSuiteName`* (defined with
+[`TEST_P`](#TEST_P)).
+
+The argument *`InstantiationName`* is a unique name for the instantiation of the
+test suite, to distinguish between multiple instantiations. In test output, the
+instantiation name is added as a prefix to the test suite name
+*`TestSuiteName`*.
+
+The argument *`param_generator`* is one of the following GoogleTest-provided
+functions that generate the test parameters, all defined in the `::testing`
+namespace:
+
+<span id="param-generators"></span>
+
+| Parameter Generator | Behavior                                             |
+| ------------------- | ---------------------------------------------------- |
+| `Range(begin, end [, step])` | Yields values `{begin, begin+step, begin+step+step, ...}`. The values do not include `end`. `step` defaults to 1. |
+| `Values(v1, v2, ..., vN)`    | Yields values `{v1, v2, ..., vN}`.          |
+| `ValuesIn(container)` or `ValuesIn(begin,end)` | Yields values from a C-style array, an STL-style container, or an iterator range `[begin, end)`. |
+| `Bool()`                     | Yields sequence `{false, true}`.            |
+| `Combine(g1, g2, ..., gN)`   | Yields as `std::tuple` *n*-tuples all combinations (Cartesian product) of the values generated by the given *n* generators `g1`, `g2`, ..., `gN`. |
+
+The optional last argument *`name_generator`* is a function or functor that
+generates custom test name suffixes based on the test parameters. The function
+must accept an argument of type
+[`TestParamInfo<class ParamType>`](#TestParamInfo) and return a `std::string`.
+The test name suffix can only contain alphanumeric characters and underscores.
+GoogleTest provides [`PrintToStringParamName`](#PrintToStringParamName), or a
+custom function can be used for more control:
+
+```cpp
+INSTANTIATE_TEST_SUITE_P(
+    MyInstantiation, MyTestSuite,
+    ::testing::Values(...),
+    [](const ::testing::TestParamInfo<MyTestSuite::ParamType>& info) {
+      // Can use info.param here to generate the test suffix
+      std::string name = ...
+      return name;
+    });
+```
+
+For more information, see
+[Value-Parameterized Tests](../advanced.md#value-parameterized-tests).
+
+See also
+[`GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST`](#GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST).
+
+### TYPED_TEST_SUITE {#TYPED_TEST_SUITE}
+
+`TYPED_TEST_SUITE(`*`TestFixtureName`*`,`*`Types`*`)`
+
+Defines a typed test suite based on the test fixture *`TestFixtureName`*. The
+test suite name is *`TestFixtureName`*.
+
+The argument *`TestFixtureName`* is a fixture class template, parameterized by a
+type, for example:
+
+```cpp
+template <typename T>
+class MyFixture : public ::testing::Test {
+ public:
+  ...
+  using List = std::list<T>;
+  static T shared_;
+  T value_;
+};
+```
+
+The argument *`Types`* is a [`Types`](#Types) object representing the list of
+types to run the tests on, for example:
+
+```cpp
+using MyTypes = ::testing::Types<char, int, unsigned int>;
+TYPED_TEST_SUITE(MyFixture, MyTypes);
+```
+
+The type alias (`using` or `typedef`) is necessary for the `TYPED_TEST_SUITE`
+macro to parse correctly.
+
+See also [`TYPED_TEST`](#TYPED_TEST) and
+[Typed Tests](../advanced.md#typed-tests) for more information.
+
+### TYPED_TEST {#TYPED_TEST}
+
+<pre>
+TYPED_TEST(<em>TestSuiteName</em>, <em>TestName</em>) {
+  ... <em>statements</em> ...
+}
+</pre>
+
+Defines an individual typed test named *`TestName`* in the typed test suite
+*`TestSuiteName`*. The test suite must be defined with
+[`TYPED_TEST_SUITE`](#TYPED_TEST_SUITE).
+
+Within the test body, the special name `TypeParam` refers to the type parameter,
+and `TestFixture` refers to the fixture class. See the following example:
+
+```cpp
+TYPED_TEST(MyFixture, Example) {
+  // Inside a test, refer to the special name TypeParam to get the type
+  // parameter.  Since we are inside a derived class template, C++ requires
+  // us to visit the members of MyFixture via 'this'.
+  TypeParam n = this->value_;
+
+  // To visit static members of the fixture, add the 'TestFixture::'
+  // prefix.
+  n += TestFixture::shared_;
+
+  // To refer to typedefs in the fixture, add the 'typename TestFixture::'
+  // prefix. The 'typename' is required to satisfy the compiler.
+  typename TestFixture::List values;
+
+  values.push_back(n);
+  ...
+}
+```
+
+For more information, see [Typed Tests](../advanced.md#typed-tests).
+
+### TYPED_TEST_SUITE_P {#TYPED_TEST_SUITE_P}
+
+`TYPED_TEST_SUITE_P(`*`TestFixtureName`*`)`
+
+Defines a type-parameterized test suite based on the test fixture
+*`TestFixtureName`*. The test suite name is *`TestFixtureName`*.
+
+The argument *`TestFixtureName`* is a fixture class template, parameterized by a
+type. See [`TYPED_TEST_SUITE`](#TYPED_TEST_SUITE) for an example.
+
+See also [`TYPED_TEST_P`](#TYPED_TEST_P) and
+[Type-Parameterized Tests](../advanced.md#type-parameterized-tests) for more
+information.
+
+### TYPED_TEST_P {#TYPED_TEST_P}
+
+<pre>
+TYPED_TEST_P(<em>TestSuiteName</em>, <em>TestName</em>) {
+  ... <em>statements</em> ...
+}
+</pre>
+
+Defines an individual type-parameterized test named *`TestName`* in the
+type-parameterized test suite *`TestSuiteName`*. The test suite must be defined
+with [`TYPED_TEST_SUITE_P`](#TYPED_TEST_SUITE_P).
+
+Within the test body, the special name `TypeParam` refers to the type parameter,
+and `TestFixture` refers to the fixture class. See [`TYPED_TEST`](#TYPED_TEST)
+for an example.
+
+See also [`REGISTER_TYPED_TEST_SUITE_P`](#REGISTER_TYPED_TEST_SUITE_P) and
+[Type-Parameterized Tests](../advanced.md#type-parameterized-tests) for more
+information.
+
+### REGISTER_TYPED_TEST_SUITE_P {#REGISTER_TYPED_TEST_SUITE_P}
+
+`REGISTER_TYPED_TEST_SUITE_P(`*`TestSuiteName`*`,`*`TestNames...`*`)`
+
+Registers the type-parameterized tests *`TestNames...`* of the test suite
+*`TestSuiteName`*. The test suite and tests must be defined with
+[`TYPED_TEST_SUITE_P`](#TYPED_TEST_SUITE_P) and [`TYPED_TEST_P`](#TYPED_TEST_P).
+
+For example:
+
+```cpp
+// Define the test suite and tests.
+TYPED_TEST_SUITE_P(MyFixture);
+TYPED_TEST_P(MyFixture, HasPropertyA) { ... }
+TYPED_TEST_P(MyFixture, HasPropertyB) { ... }
+
+// Register the tests in the test suite.
+REGISTER_TYPED_TEST_SUITE_P(MyFixture, HasPropertyA, HasPropertyB);
+```
+
+See also [`INSTANTIATE_TYPED_TEST_SUITE_P`](#INSTANTIATE_TYPED_TEST_SUITE_P) and
+[Type-Parameterized Tests](../advanced.md#type-parameterized-tests) for more
+information.
+
+### INSTANTIATE_TYPED_TEST_SUITE_P {#INSTANTIATE_TYPED_TEST_SUITE_P}
+
+`INSTANTIATE_TYPED_TEST_SUITE_P(`*`InstantiationName`*`,`*`TestSuiteName`*`,`*`Types`*`)`
+
+Instantiates the type-parameterized test suite *`TestSuiteName`*. The test suite
+must be registered with
+[`REGISTER_TYPED_TEST_SUITE_P`](#REGISTER_TYPED_TEST_SUITE_P).
+
+The argument *`InstantiationName`* is a unique name for the instantiation of the
+test suite, to distinguish between multiple instantiations. In test output, the
+instantiation name is added as a prefix to the test suite name
+*`TestSuiteName`*.
+
+The argument *`Types`* is a [`Types`](#Types) object representing the list of
+types to run the tests on, for example:
+
+```cpp
+using MyTypes = ::testing::Types<char, int, unsigned int>;
+INSTANTIATE_TYPED_TEST_SUITE_P(MyInstantiation, MyFixture, MyTypes);
+```
+
+The type alias (`using` or `typedef`) is necessary for the
+`INSTANTIATE_TYPED_TEST_SUITE_P` macro to parse correctly.
+
+For more information, see
+[Type-Parameterized Tests](../advanced.md#type-parameterized-tests).
+
+### FRIEND_TEST {#FRIEND_TEST}
+
+`FRIEND_TEST(`*`TestSuiteName`*`,`*`TestName`*`)`
+
+Within a class body, declares an individual test as a friend of the class,
+enabling the test to access private class members.
+
+If the class is defined in a namespace, then in order to be friends of the
+class, test fixtures and tests must be defined in the exact same namespace,
+without inline or anonymous namespaces.
+
+For example, if the class definition looks like the following:
+
+```cpp
+namespace my_namespace {
+
+class MyClass {
+  friend class MyClassTest;
+  FRIEND_TEST(MyClassTest, HasPropertyA);
+  FRIEND_TEST(MyClassTest, HasPropertyB);
+  ... definition of class MyClass ...
+};
+
+}  // namespace my_namespace
+```
+
+Then the test code should look like:
+
+```cpp
+namespace my_namespace {
+
+class MyClassTest : public ::testing::Test {
+  ...
+};
+
+TEST_F(MyClassTest, HasPropertyA) { ... }
+TEST_F(MyClassTest, HasPropertyB) { ... }
+
+}  // namespace my_namespace
+```
+
+See [Testing Private Code](../advanced.md#testing-private-code) for more
+information.
+
+### SCOPED_TRACE {#SCOPED_TRACE}
+
+`SCOPED_TRACE(`*`message`*`)`
+
+Causes the current file name, line number, and the given message *`message`* to
+be added to the failure message for each assertion failure that occurs in the
+scope.
+
+For more information, see
+[Adding Traces to Assertions](../advanced.md#adding-traces-to-assertions).
+
+See also the [`ScopedTrace` class](#ScopedTrace).
+
+### GTEST_SKIP {#GTEST_SKIP}
+
+`GTEST_SKIP()`
+
+Prevents further test execution at runtime.
+
+Can be used in individual test cases or in the `SetUp()` methods of test
+environments or test fixtures (classes derived from the
+[`Environment`](#Environment) or [`Test`](#Test) classes). If used in a global
+test environment `SetUp()` method, it skips all tests in the test program. If
+used in a test fixture `SetUp()` method, it skips all tests in the corresponding
+test suite.
+
+Similar to assertions, `GTEST_SKIP` allows streaming a custom message into it.
+
+See [Skipping Test Execution](../advanced.md#skipping-test-execution) for more
+information.
+
+### GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST {#GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST}
+
+`GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(`*`TestSuiteName`*`)`
+
+Allows the value-parameterized test suite *`TestSuiteName`* to be
+uninstantiated.
+
+By default, every [`TEST_P`](#TEST_P) call without a corresponding
+[`INSTANTIATE_TEST_SUITE_P`](#INSTANTIATE_TEST_SUITE_P) call causes a failing
+test in the test suite `GoogleTestVerification`.
+`GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST` suppresses this failure for the
+given test suite.
+
+## Classes and types
+
+GoogleTest defines the following classes and types to help with writing tests.
+
+### AssertionResult {#AssertionResult}
+
+`::testing::AssertionResult`
+
+A class for indicating whether an assertion was successful.
+
+When the assertion wasn't successful, the `AssertionResult` object stores a
+non-empty failure message that can be retrieved with the object's `message()`
+method.
+
+To create an instance of this class, use one of the factory functions
+[`AssertionSuccess()`](#AssertionSuccess) or
+[`AssertionFailure()`](#AssertionFailure).
+
+### AssertionException {#AssertionException}
+
+`::testing::AssertionException`
+
+Exception which can be thrown from
+[`TestEventListener::OnTestPartResult`](#TestEventListener::OnTestPartResult).
+
+### EmptyTestEventListener {#EmptyTestEventListener}
+
+`::testing::EmptyTestEventListener`
+
+Provides an empty implementation of all methods in the
+[`TestEventListener`](#TestEventListener) interface, such that a subclass only
+needs to override the methods it cares about.
+
+### Environment {#Environment}
+
+`::testing::Environment`
+
+Represents a global test environment. See
+[Global Set-Up and Tear-Down](../advanced.md#global-set-up-and-tear-down).
+
+#### Protected Methods {#Environment-protected}
+
+##### SetUp {#Environment::SetUp}
+
+`virtual void Environment::SetUp()`
+
+Override this to define how to set up the environment.
+
+##### TearDown {#Environment::TearDown}
+
+`virtual void Environment::TearDown()`
+
+Override this to define how to tear down the environment.
+
+### ScopedTrace {#ScopedTrace}
+
+`::testing::ScopedTrace`
+
+An instance of this class causes a trace to be included in every test failure
+message generated by code in the scope of the lifetime of the `ScopedTrace`
+instance. The effect is undone with the destruction of the instance.
+
+The `ScopedTrace` constructor has the following form:
+
+```cpp
+template <typename T>
+ScopedTrace(const char* file, int line, const T& message)
+```
+
+Example usage:
+
+```cpp
+::testing::ScopedTrace trace("file.cc", 123, "message");
+```
+
+The resulting trace includes the given source file path and line number, and the
+given message. The `message` argument can be anything streamable to
+`std::ostream`.
+
+See also [`SCOPED_TRACE`](#SCOPED_TRACE).
+
+### Test {#Test}
+
+`::testing::Test`
+
+The abstract class that all tests inherit from. `Test` is not copyable.
+
+#### Public Methods {#Test-public}
+
+##### SetUpTestSuite {#Test::SetUpTestSuite}
+
+`static void Test::SetUpTestSuite()`
+
+Performs shared setup for all tests in the test suite. GoogleTest calls
+`SetUpTestSuite()` before running the first test in the test suite.
+
+##### TearDownTestSuite {#Test::TearDownTestSuite}
+
+`static void Test::TearDownTestSuite()`
+
+Performs shared teardown for all tests in the test suite. GoogleTest calls
+`TearDownTestSuite()` after running the last test in the test suite.
+
+##### HasFatalFailure {#Test::HasFatalFailure}
+
+`static bool Test::HasFatalFailure()`
+
+Returns true if and only if the current test has a fatal failure.
+
+##### HasNonfatalFailure {#Test::HasNonfatalFailure}
+
+`static bool Test::HasNonfatalFailure()`
+
+Returns true if and only if the current test has a nonfatal failure.
+
+##### HasFailure {#Test::HasFailure}
+
+`static bool Test::HasFailure()`
+
+Returns true if and only if the current test has any failure, either fatal or
+nonfatal.
+
+##### IsSkipped {#Test::IsSkipped}
+
+`static bool Test::IsSkipped()`
+
+Returns true if and only if the current test was skipped.
+
+##### RecordProperty {#Test::RecordProperty}
+
+`static void Test::RecordProperty(const std::string& key, const std::string&
+value)` \
+`static void Test::RecordProperty(const std::string& key, int value)`
+
+Logs a property for the current test, test suite, or entire invocation of the
+test program. Only the last value for a given key is logged.
+
+The key must be a valid XML attribute name, and cannot conflict with the ones
+already used by GoogleTest (`name`, `status`, `time`, `classname`, `type_param`,
+and `value_param`).
+
+`RecordProperty` is `public static` so it can be called from utility functions
+that are not members of the test fixture.
+
+Calls to `RecordProperty` made during the lifespan of the test (from the moment
+its constructor starts to the moment its destructor finishes) are output in XML
+as attributes of the `<testcase>` element. Properties recorded from a fixture's
+`SetUpTestSuite` or `TearDownTestSuite` methods are logged as attributes of the
+corresponding `<testsuite>` element. Calls to `RecordProperty` made in the
+global context (before or after invocation of `RUN_ALL_TESTS` or from the
+`SetUp`/`TearDown` methods of registered `Environment` objects) are output as
+attributes of the `<testsuites>` element.
+
+#### Protected Methods {#Test-protected}
+
+##### SetUp {#Test::SetUp}
+
+`virtual void Test::SetUp()`
+
+Override this to perform test fixture setup. GoogleTest calls `SetUp()` before
+running each individual test.
+
+##### TearDown {#Test::TearDown}
+
+`virtual void Test::TearDown()`
+
+Override this to perform test fixture teardown. GoogleTest calls `TearDown()`
+after running each individual test.
+
+### TestWithParam {#TestWithParam}
+
+`::testing::TestWithParam<T>`
+
+A convenience class which inherits from both [`Test`](#Test) and
+[`WithParamInterface<T>`](#WithParamInterface).
+
+### TestSuite {#TestSuite}
+
+Represents a test suite. `TestSuite` is not copyable.
+
+#### Public Methods {#TestSuite-public}
+
+##### name {#TestSuite::name}
+
+`const char* TestSuite::name() const`
+
+Gets the name of the test suite.
+
+##### type_param {#TestSuite::type_param}
+
+`const char* TestSuite::type_param() const`
+
+Returns the name of the parameter type, or `NULL` if this is not a typed or
+type-parameterized test suite. See [Typed Tests](../advanced.md#typed-tests) and
+[Type-Parameterized Tests](../advanced.md#type-parameterized-tests).
+
+##### should_run {#TestSuite::should_run}
+
+`bool TestSuite::should_run() const`
+
+Returns true if any test in this test suite should run.
+
+##### successful_test_count {#TestSuite::successful_test_count}
+
+`int TestSuite::successful_test_count() const`
+
+Gets the number of successful tests in this test suite.
+
+##### skipped_test_count {#TestSuite::skipped_test_count}
+
+`int TestSuite::skipped_test_count() const`
+
+Gets the number of skipped tests in this test suite.
+
+##### failed_test_count {#TestSuite::failed_test_count}
+
+`int TestSuite::failed_test_count() const`
+
+Gets the number of failed tests in this test suite.
+
+##### reportable_disabled_test_count {#TestSuite::reportable_disabled_test_count}
+
+`int TestSuite::reportable_disabled_test_count() const`
+
+Gets the number of disabled tests that will be reported in the XML report.
+
+##### disabled_test_count {#TestSuite::disabled_test_count}
+
+`int TestSuite::disabled_test_count() const`
+
+Gets the number of disabled tests in this test suite.
+
+##### reportable_test_count {#TestSuite::reportable_test_count}
+
+`int TestSuite::reportable_test_count() const`
+
+Gets the number of tests to be printed in the XML report.
+
+##### test_to_run_count {#TestSuite::test_to_run_count}
+
+`int TestSuite::test_to_run_count() const`
+
+Get the number of tests in this test suite that should run.
+
+##### total_test_count {#TestSuite::total_test_count}
+
+`int TestSuite::total_test_count() const`
+
+Gets the number of all tests in this test suite.
+
+##### Passed {#TestSuite::Passed}
+
+`bool TestSuite::Passed() const`
+
+Returns true if and only if the test suite passed.
+
+##### Failed {#TestSuite::Failed}
+
+`bool TestSuite::Failed() const`
+
+Returns true if and only if the test suite failed.
+
+##### elapsed_time {#TestSuite::elapsed_time}
+
+`TimeInMillis TestSuite::elapsed_time() const`
+
+Returns the elapsed time, in milliseconds.
+
+##### start_timestamp {#TestSuite::start_timestamp}
+
+`TimeInMillis TestSuite::start_timestamp() const`
+
+Gets the time of the test suite start, in ms from the start of the UNIX epoch.
+
+##### GetTestInfo {#TestSuite::GetTestInfo}
+
+`const TestInfo* TestSuite::GetTestInfo(int i) const`
+
+Returns the [`TestInfo`](#TestInfo) for the `i`-th test among all the tests. `i`
+can range from 0 to `total_test_count() - 1`. If `i` is not in that range,
+returns `NULL`.
+
+##### ad_hoc_test_result {#TestSuite::ad_hoc_test_result}
+
+`const TestResult& TestSuite::ad_hoc_test_result() const`
+
+Returns the [`TestResult`](#TestResult) that holds test properties recorded
+during execution of `SetUpTestSuite` and `TearDownTestSuite`.
+
+### TestInfo {#TestInfo}
+
+`::testing::TestInfo`
+
+Stores information about a test.
+
+#### Public Methods {#TestInfo-public}
+
+##### test_suite_name {#TestInfo::test_suite_name}
+
+`const char* TestInfo::test_suite_name() const`
+
+Returns the test suite name.
+
+##### name {#TestInfo::name}
+
+`const char* TestInfo::name() const`
+
+Returns the test name.
+
+##### type_param {#TestInfo::type_param}
+
+`const char* TestInfo::type_param() const`
+
+Returns the name of the parameter type, or `NULL` if this is not a typed or
+type-parameterized test. See [Typed Tests](../advanced.md#typed-tests) and
+[Type-Parameterized Tests](../advanced.md#type-parameterized-tests).
+
+##### value_param {#TestInfo::value_param}
+
+`const char* TestInfo::value_param() const`
+
+Returns the text representation of the value parameter, or `NULL` if this is not
+a value-parameterized test. See
+[Value-Parameterized Tests](../advanced.md#value-parameterized-tests).
+
+##### file {#TestInfo::file}
+
+`const char* TestInfo::file() const`
+
+Returns the file name where this test is defined.
+
+##### line {#TestInfo::line}
+
+`int TestInfo::line() const`
+
+Returns the line where this test is defined.
+
+##### is_in_another_shard {#TestInfo::is_in_another_shard}
+
+`bool TestInfo::is_in_another_shard() const`
+
+Returns true if this test should not be run because it's in another shard.
+
+##### should_run {#TestInfo::should_run}
+
+`bool TestInfo::should_run() const`
+
+Returns true if this test should run, that is if the test is not disabled (or it
+is disabled but the `also_run_disabled_tests` flag has been specified) and its
+full name matches the user-specified filter.
+
+GoogleTest allows the user to filter the tests by their full names. Only the
+tests that match the filter will run. See
+[Running a Subset of the Tests](../advanced.md#running-a-subset-of-the-tests)
+for more information.
+
+##### is_reportable {#TestInfo::is_reportable}
+
+`bool TestInfo::is_reportable() const`
+
+Returns true if and only if this test will appear in the XML report.
+
+##### result {#TestInfo::result}
+
+`const TestResult* TestInfo::result() const`
+
+Returns the result of the test. See [`TestResult`](#TestResult).
+
+### TestParamInfo {#TestParamInfo}
+
+`::testing::TestParamInfo<T>`
+
+Describes a parameter to a value-parameterized test. The type `T` is the type of
+the parameter.
+
+Contains the fields `param` and `index` which hold the value of the parameter
+and its integer index respectively.
+
+### UnitTest {#UnitTest}
+
+`::testing::UnitTest`
+
+This class contains information about the test program.
+
+`UnitTest` is a singleton class. The only instance is created when
+`UnitTest::GetInstance()` is first called. This instance is never deleted.
+
+`UnitTest` is not copyable.
+
+#### Public Methods {#UnitTest-public}
+
+##### GetInstance {#UnitTest::GetInstance}
+
+`static UnitTest* UnitTest::GetInstance()`
+
+Gets the singleton `UnitTest` object. The first time this method is called, a
+`UnitTest` object is constructed and returned. Consecutive calls will return the
+same object.
+
+##### original_working_dir {#UnitTest::original_working_dir}
+
+`const char* UnitTest::original_working_dir() const`
+
+Returns the working directory when the first [`TEST()`](#TEST) or
+[`TEST_F()`](#TEST_F) was executed. The `UnitTest` object owns the string.
+
+##### current_test_suite {#UnitTest::current_test_suite}
+
+`const TestSuite* UnitTest::current_test_suite() const`
+
+Returns the [`TestSuite`](#TestSuite) object for the test that's currently
+running, or `NULL` if no test is running.
+
+##### current_test_info {#UnitTest::current_test_info}
+
+`const TestInfo* UnitTest::current_test_info() const`
+
+Returns the [`TestInfo`](#TestInfo) object for the test that's currently
+running, or `NULL` if no test is running.
+
+##### random_seed {#UnitTest::random_seed}
+
+`int UnitTest::random_seed() const`
+
+Returns the random seed used at the start of the current test run.
+
+##### successful_test_suite_count {#UnitTest::successful_test_suite_count}
+
+`int UnitTest::successful_test_suite_count() const`
+
+Gets the number of successful test suites.
+
+##### failed_test_suite_count {#UnitTest::failed_test_suite_count}
+
+`int UnitTest::failed_test_suite_count() const`
+
+Gets the number of failed test suites.
+
+##### total_test_suite_count {#UnitTest::total_test_suite_count}
+
+`int UnitTest::total_test_suite_count() const`
+
+Gets the number of all test suites.
+
+##### test_suite_to_run_count {#UnitTest::test_suite_to_run_count}
+
+`int UnitTest::test_suite_to_run_count() const`
+
+Gets the number of all test suites that contain at least one test that should
+run.
+
+##### successful_test_count {#UnitTest::successful_test_count}
+
+`int UnitTest::successful_test_count() const`
+
+Gets the number of successful tests.
+
+##### skipped_test_count {#UnitTest::skipped_test_count}
+
+`int UnitTest::skipped_test_count() const`
+
+Gets the number of skipped tests.
+
+##### failed_test_count {#UnitTest::failed_test_count}
+
+`int UnitTest::failed_test_count() const`
+
+Gets the number of failed tests.
+
+##### reportable_disabled_test_count {#UnitTest::reportable_disabled_test_count}
+
+`int UnitTest::reportable_disabled_test_count() const`
+
+Gets the number of disabled tests that will be reported in the XML report.
+
+##### disabled_test_count {#UnitTest::disabled_test_count}
+
+`int UnitTest::disabled_test_count() const`
+
+Gets the number of disabled tests.
+
+##### reportable_test_count {#UnitTest::reportable_test_count}
+
+`int UnitTest::reportable_test_count() const`
+
+Gets the number of tests to be printed in the XML report.
+
+##### total_test_count {#UnitTest::total_test_count}
+
+`int UnitTest::total_test_count() const`
+
+Gets the number of all tests.
+
+##### test_to_run_count {#UnitTest::test_to_run_count}
+
+`int UnitTest::test_to_run_count() const`
+
+Gets the number of tests that should run.
+
+##### start_timestamp {#UnitTest::start_timestamp}
+
+`TimeInMillis UnitTest::start_timestamp() const`
+
+Gets the time of the test program start, in ms from the start of the UNIX epoch.
+
+##### elapsed_time {#UnitTest::elapsed_time}
+
+`TimeInMillis UnitTest::elapsed_time() const`
+
+Gets the elapsed time, in milliseconds.
+
+##### Passed {#UnitTest::Passed}
+
+`bool UnitTest::Passed() const`
+
+Returns true if and only if the unit test passed (i.e. all test suites passed).
+
+##### Failed {#UnitTest::Failed}
+
+`bool UnitTest::Failed() const`
+
+Returns true if and only if the unit test failed (i.e. some test suite failed or
+something outside of all tests failed).
+
+##### GetTestSuite {#UnitTest::GetTestSuite}
+
+`const TestSuite* UnitTest::GetTestSuite(int i) const`
+
+Gets the [`TestSuite`](#TestSuite) object for the `i`-th test suite among all
+the test suites. `i` can range from 0 to `total_test_suite_count() - 1`. If `i`
+is not in that range, returns `NULL`.
+
+##### ad_hoc_test_result {#UnitTest::ad_hoc_test_result}
+
+`const TestResult& UnitTest::ad_hoc_test_result() const`
+
+Returns the [`TestResult`](#TestResult) containing information on test failures
+and properties logged outside of individual test suites.
+
+##### listeners {#UnitTest::listeners}
+
+`TestEventListeners& UnitTest::listeners()`
+
+Returns the list of event listeners that can be used to track events inside
+GoogleTest. See [`TestEventListeners`](#TestEventListeners).
+
+### TestEventListener {#TestEventListener}
+
+`::testing::TestEventListener`
+
+The interface for tracing execution of tests. The methods below are listed in
+the order the corresponding events are fired.
+
+#### Public Methods {#TestEventListener-public}
+
+##### OnTestProgramStart {#TestEventListener::OnTestProgramStart}
+
+`virtual void TestEventListener::OnTestProgramStart(const UnitTest& unit_test)`
+
+Fired before any test activity starts.
+
+##### OnTestIterationStart {#TestEventListener::OnTestIterationStart}
+
+`virtual void TestEventListener::OnTestIterationStart(const UnitTest& unit_test,
+int iteration)`
+
+Fired before each iteration of tests starts. There may be more than one
+iteration if `GTEST_FLAG(repeat)` is set. `iteration` is the iteration index,
+starting from 0.
+
+##### OnEnvironmentsSetUpStart {#TestEventListener::OnEnvironmentsSetUpStart}
+
+`virtual void TestEventListener::OnEnvironmentsSetUpStart(const UnitTest&
+unit_test)`
+
+Fired before environment set-up for each iteration of tests starts.
+
+##### OnEnvironmentsSetUpEnd {#TestEventListener::OnEnvironmentsSetUpEnd}
+
+`virtual void TestEventListener::OnEnvironmentsSetUpEnd(const UnitTest&
+unit_test)`
+
+Fired after environment set-up for each iteration of tests ends.
+
+##### OnTestSuiteStart {#TestEventListener::OnTestSuiteStart}
+
+`virtual void TestEventListener::OnTestSuiteStart(const TestSuite& test_suite)`
+
+Fired before the test suite starts.
+
+##### OnTestStart {#TestEventListener::OnTestStart}
+
+`virtual void TestEventListener::OnTestStart(const TestInfo& test_info)`
+
+Fired before the test starts.
+
+##### OnTestPartResult {#TestEventListener::OnTestPartResult}
+
+`virtual void TestEventListener::OnTestPartResult(const TestPartResult&
+test_part_result)`
+
+Fired after a failed assertion or a `SUCCEED()` invocation. If you want to throw
+an exception from this function to skip to the next test, it must be an
+[`AssertionException`](#AssertionException) or inherited from it.
+
+##### OnTestEnd {#TestEventListener::OnTestEnd}
+
+`virtual void TestEventListener::OnTestEnd(const TestInfo& test_info)`
+
+Fired after the test ends.
+
+##### OnTestSuiteEnd {#TestEventListener::OnTestSuiteEnd}
+
+`virtual void TestEventListener::OnTestSuiteEnd(const TestSuite& test_suite)`
+
+Fired after the test suite ends.
+
+##### OnEnvironmentsTearDownStart {#TestEventListener::OnEnvironmentsTearDownStart}
+
+`virtual void TestEventListener::OnEnvironmentsTearDownStart(const UnitTest&
+unit_test)`
+
+Fired before environment tear-down for each iteration of tests starts.
+
+##### OnEnvironmentsTearDownEnd {#TestEventListener::OnEnvironmentsTearDownEnd}
+
+`virtual void TestEventListener::OnEnvironmentsTearDownEnd(const UnitTest&
+unit_test)`
+
+Fired after environment tear-down for each iteration of tests ends.
+
+##### OnTestIterationEnd {#TestEventListener::OnTestIterationEnd}
+
+`virtual void TestEventListener::OnTestIterationEnd(const UnitTest& unit_test,
+int iteration)`
+
+Fired after each iteration of tests finishes.
+
+##### OnTestProgramEnd {#TestEventListener::OnTestProgramEnd}
+
+`virtual void TestEventListener::OnTestProgramEnd(const UnitTest& unit_test)`
+
+Fired after all test activities have ended.
+
+### TestEventListeners {#TestEventListeners}
+
+`::testing::TestEventListeners`
+
+Lets users add listeners to track events in GoogleTest.
+
+#### Public Methods {#TestEventListeners-public}
+
+##### Append {#TestEventListeners::Append}
+
+`void TestEventListeners::Append(TestEventListener* listener)`
+
+Appends an event listener to the end of the list. GoogleTest assumes ownership
+of the listener (i.e. it will delete the listener when the test program
+finishes).
+
+##### Release {#TestEventListeners::Release}
+
+`TestEventListener* TestEventListeners::Release(TestEventListener* listener)`
+
+Removes the given event listener from the list and returns it. It then becomes
+the caller's responsibility to delete the listener. Returns `NULL` if the
+listener is not found in the list.
+
+##### default_result_printer {#TestEventListeners::default_result_printer}
+
+`TestEventListener* TestEventListeners::default_result_printer() const`
+
+Returns the standard listener responsible for the default console output. Can be
+removed from the listeners list to shut down default console output. Note that
+removing this object from the listener list with
+[`Release()`](#TestEventListeners::Release) transfers its ownership to the
+caller and makes this function return `NULL` the next time.
+
+##### default_xml_generator {#TestEventListeners::default_xml_generator}
+
+`TestEventListener* TestEventListeners::default_xml_generator() const`
+
+Returns the standard listener responsible for the default XML output controlled
+by the `--gtest_output=xml` flag. Can be removed from the listeners list by
+users who want to shut down the default XML output controlled by this flag and
+substitute it with custom one. Note that removing this object from the listener
+list with [`Release()`](#TestEventListeners::Release) transfers its ownership to
+the caller and makes this function return `NULL` the next time.
+
+### TestPartResult {#TestPartResult}
+
+`::testing::TestPartResult`
+
+A copyable object representing the result of a test part (i.e. an assertion or
+an explicit `FAIL()`, `ADD_FAILURE()`, or `SUCCESS()`).
+
+#### Public Methods {#TestPartResult-public}
+
+##### type {#TestPartResult::type}
+
+`Type TestPartResult::type() const`
+
+Gets the outcome of the test part.
+
+The return type `Type` is an enum defined as follows:
+
+```cpp
+enum Type {
+  kSuccess,          // Succeeded.
+  kNonFatalFailure,  // Failed but the test can continue.
+  kFatalFailure,     // Failed and the test should be terminated.
+  kSkip              // Skipped.
+};
+```
+
+##### file_name {#TestPartResult::file_name}
+
+`const char* TestPartResult::file_name() const`
+
+Gets the name of the source file where the test part took place, or `NULL` if
+it's unknown.
+
+##### line_number {#TestPartResult::line_number}
+
+`int TestPartResult::line_number() const`
+
+Gets the line in the source file where the test part took place, or `-1` if it's
+unknown.
+
+##### summary {#TestPartResult::summary}
+
+`const char* TestPartResult::summary() const`
+
+Gets the summary of the failure message.
+
+##### message {#TestPartResult::message}
+
+`const char* TestPartResult::message() const`
+
+Gets the message associated with the test part.
+
+##### skipped {#TestPartResult::skipped}
+
+`bool TestPartResult::skipped() const`
+
+Returns true if and only if the test part was skipped.
+
+##### passed {#TestPartResult::passed}
+
+`bool TestPartResult::passed() const`
+
+Returns true if and only if the test part passed.
+
+##### nonfatally_failed {#TestPartResult::nonfatally_failed}
+
+`bool TestPartResult::nonfatally_failed() const`
+
+Returns true if and only if the test part non-fatally failed.
+
+##### fatally_failed {#TestPartResult::fatally_failed}
+
+`bool TestPartResult::fatally_failed() const`
+
+Returns true if and only if the test part fatally failed.
+
+##### failed {#TestPartResult::failed}
+
+`bool TestPartResult::failed() const`
+
+Returns true if and only if the test part failed.
+
+### TestProperty {#TestProperty}
+
+`::testing::TestProperty`
+
+A copyable object representing a user-specified test property which can be
+output as a key/value string pair.
+
+#### Public Methods {#TestProperty-public}
+
+##### key {#key}
+
+`const char* key() const`
+
+Gets the user-supplied key.
+
+##### value {#value}
+
+`const char* value() const`
+
+Gets the user-supplied value.
+
+##### SetValue {#SetValue}
+
+`void SetValue(const std::string& new_value)`
+
+Sets a new value, overriding the previous one.
+
+### TestResult {#TestResult}
+
+`::testing::TestResult`
+
+Contains information about the result of a single test.
+
+`TestResult` is not copyable.
+
+#### Public Methods {#TestResult-public}
+
+##### total_part_count {#TestResult::total_part_count}
+
+`int TestResult::total_part_count() const`
+
+Gets the number of all test parts. This is the sum of the number of successful
+test parts and the number of failed test parts.
+
+##### test_property_count {#TestResult::test_property_count}
+
+`int TestResult::test_property_count() const`
+
+Returns the number of test properties.
+
+##### Passed {#TestResult::Passed}
+
+`bool TestResult::Passed() const`
+
+Returns true if and only if the test passed (i.e. no test part failed).
+
+##### Skipped {#TestResult::Skipped}
+
+`bool TestResult::Skipped() const`
+
+Returns true if and only if the test was skipped.
+
+##### Failed {#TestResult::Failed}
+
+`bool TestResult::Failed() const`
+
+Returns true if and only if the test failed.
+
+##### HasFatalFailure {#TestResult::HasFatalFailure}
+
+`bool TestResult::HasFatalFailure() const`
+
+Returns true if and only if the test fatally failed.
+
+##### HasNonfatalFailure {#TestResult::HasNonfatalFailure}
+
+`bool TestResult::HasNonfatalFailure() const`
+
+Returns true if and only if the test has a non-fatal failure.
+
+##### elapsed_time {#TestResult::elapsed_time}
+
+`TimeInMillis TestResult::elapsed_time() const`
+
+Returns the elapsed time, in milliseconds.
+
+##### start_timestamp {#TestResult::start_timestamp}
+
+`TimeInMillis TestResult::start_timestamp() const`
+
+Gets the time of the test case start, in ms from the start of the UNIX epoch.
+
+##### GetTestPartResult {#TestResult::GetTestPartResult}
+
+`const TestPartResult& TestResult::GetTestPartResult(int i) const`
+
+Returns the [`TestPartResult`](#TestPartResult) for the `i`-th test part result
+among all the results. `i` can range from 0 to `total_part_count() - 1`. If `i`
+is not in that range, aborts the program.
+
+##### GetTestProperty {#TestResult::GetTestProperty}
+
+`const TestProperty& TestResult::GetTestProperty(int i) const`
+
+Returns the [`TestProperty`](#TestProperty) object for the `i`-th test property.
+`i` can range from 0 to `test_property_count() - 1`. If `i` is not in that
+range, aborts the program.
+
+### TimeInMillis {#TimeInMillis}
+
+`::testing::TimeInMillis`
+
+An integer type representing time in milliseconds.
+
+### Types {#Types}
+
+`::testing::Types<T...>`
+
+Represents a list of types for use in typed tests and type-parameterized tests.
+
+The template argument `T...` can be any number of types, for example:
+
+```
+::testing::Types<char, int, unsigned int>
+```
+
+See [Typed Tests](../advanced.md#typed-tests) and
+[Type-Parameterized Tests](../advanced.md#type-parameterized-tests) for more
+information.
+
+### WithParamInterface {#WithParamInterface}
+
+`::testing::WithParamInterface<T>`
+
+The pure interface class that all value-parameterized tests inherit from.
+
+A value-parameterized test fixture class must inherit from both [`Test`](#Test)
+and `WithParamInterface`. In most cases that just means inheriting from
+[`TestWithParam`](#TestWithParam), but more complicated test hierarchies may
+need to inherit from `Test` and `WithParamInterface` at different levels.
+
+This interface defines the type alias `ParamType` for the parameter type `T` and
+has support for accessing the test parameter value via the `GetParam()` method:
+
+```
+static const ParamType& GetParam()
+```
+
+For more information, see
+[Value-Parameterized Tests](../advanced.md#value-parameterized-tests).
+
+## Functions
+
+GoogleTest defines the following functions to help with writing and running
+tests.
+
+### InitGoogleTest {#InitGoogleTest}
+
+`void ::testing::InitGoogleTest(int* argc, char** argv)` \
+`void ::testing::InitGoogleTest(int* argc, wchar_t** argv)` \
+`void ::testing::InitGoogleTest()`
+
+Initializes GoogleTest. This must be called before calling
+[`RUN_ALL_TESTS()`](#RUN_ALL_TESTS). In particular, it parses the command line
+for the flags that GoogleTest recognizes. Whenever a GoogleTest flag is seen, it
+is removed from `argv`, and `*argc` is decremented.
+
+No value is returned. Instead, the GoogleTest flag variables are updated.
+
+The `InitGoogleTest(int* argc, wchar_t** argv)` overload can be used in Windows
+programs compiled in `UNICODE` mode.
+
+The argument-less `InitGoogleTest()` overload can be used on Arduino/embedded
+platforms where there is no `argc`/`argv`.
+
+### AddGlobalTestEnvironment {#AddGlobalTestEnvironment}
+
+`Environment* ::testing::AddGlobalTestEnvironment(Environment* env)`
+
+Adds a test environment to the test program. Must be called before
+[`RUN_ALL_TESTS()`](#RUN_ALL_TESTS) is called. See
+[Global Set-Up and Tear-Down](../advanced.md#global-set-up-and-tear-down) for
+more information.
+
+See also [`Environment`](#Environment).
+
+### RegisterTest {#RegisterTest}
+
+```cpp
+template <typename Factory>
+TestInfo* ::testing::RegisterTest(const char* test_suite_name, const char* test_name,
+                                  const char* type_param, const char* value_param,
+                                  const char* file, int line, Factory factory)
+```
+
+Dynamically registers a test with the framework.
+
+The `factory` argument is a factory callable (move-constructible) object or
+function pointer that creates a new instance of the `Test` object. It handles
+ownership to the caller. The signature of the callable is `Fixture*()`, where
+`Fixture` is the test fixture class for the test. All tests registered with the
+same `test_suite_name` must return the same fixture type. This is checked at
+runtime.
+
+The framework will infer the fixture class from the factory and will call the
+`SetUpTestSuite` and `TearDownTestSuite` methods for it.
+
+Must be called before [`RUN_ALL_TESTS()`](#RUN_ALL_TESTS) is invoked, otherwise
+behavior is undefined.
+
+See
+[Registering tests programmatically](../advanced.md#registering-tests-programmatically)
+for more information.
+
+### RUN_ALL_TESTS {#RUN_ALL_TESTS}
+
+`int RUN_ALL_TESTS()`
+
+Use this function in `main()` to run all tests. It returns `0` if all tests are
+successful, or `1` otherwise.
+
+`RUN_ALL_TESTS()` should be invoked after the command line has been parsed by
+[`InitGoogleTest()`](#InitGoogleTest).
+
+This function was formerly a macro; thus, it is in the global namespace and has
+an all-caps name.
+
+### AssertionSuccess {#AssertionSuccess}
+
+`AssertionResult ::testing::AssertionSuccess()`
+
+Creates a successful assertion result. See
+[`AssertionResult`](#AssertionResult).
+
+### AssertionFailure {#AssertionFailure}
+
+`AssertionResult ::testing::AssertionFailure()`
+
+Creates a failed assertion result. Use the `<<` operator to store a failure
+message:
+
+```cpp
+::testing::AssertionFailure() << "My failure message";
+```
+
+See [`AssertionResult`](#AssertionResult).
+
+### StaticAssertTypeEq {#StaticAssertTypeEq}
+
+`::testing::StaticAssertTypeEq<T1, T2>()`
+
+Compile-time assertion for type equality. Compiles if and only if `T1` and `T2`
+are the same type. The value it returns is irrelevant.
+
+See [Type Assertions](../advanced.md#type-assertions) for more information.
+
+### PrintToString {#PrintToString}
+
+`std::string ::testing::PrintToString(x)`
+
+Prints any value `x` using GoogleTest's value printer.
+
+See
+[Teaching GoogleTest How to Print Your Values](../advanced.md#teaching-googletest-how-to-print-your-values)
+for more information.
+
+### PrintToStringParamName {#PrintToStringParamName}
+
+`std::string ::testing::PrintToStringParamName(TestParamInfo<T>& info)`
+
+A built-in parameterized test name generator which returns the result of
+[`PrintToString`](#PrintToString) called on `info.param`. Does not work when the
+test parameter is a `std::string` or C string. See
+[Specifying Names for Value-Parameterized Test Parameters](../advanced.md#specifying-names-for-value-parameterized-test-parameters)
+for more information.
+
+See also [`TestParamInfo`](#TestParamInfo) and
+[`INSTANTIATE_TEST_SUITE_P`](#INSTANTIATE_TEST_SUITE_P).
diff --git a/ext/googletest/googletest/docs/samples.md b/ext/googletest/docs/samples.md
similarity index 96%
rename from ext/googletest/googletest/docs/samples.md
rename to ext/googletest/docs/samples.md
index aaa5883..2d97ca5 100644
--- a/ext/googletest/googletest/docs/samples.md
+++ b/ext/googletest/docs/samples.md
@@ -1,4 +1,4 @@
-# Googletest Samples {#samples}
+# Googletest Samples
 
 If you're like us, you'd like to look at
 [googletest samples.](https://github.com/google/googletest/tree/master/googletest/samples)
diff --git a/ext/googletest/googlemock/CMakeLists.txt b/ext/googletest/googlemock/CMakeLists.txt
index d32b70b..e7df8ec 100644
--- a/ext/googletest/googlemock/CMakeLists.txt
+++ b/ext/googletest/googlemock/CMakeLists.txt
@@ -42,7 +42,7 @@
   cmake_policy(SET CMP0048 NEW)
   project(gmock VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C)
 endif()
-cmake_minimum_required(VERSION 2.6.4)
+cmake_minimum_required(VERSION 2.8.12)
 
 if (COMMAND set_up_hermetic_build)
   set_up_hermetic_build()
@@ -100,8 +100,10 @@
 else()
   cxx_library(gmock "${cxx_strict}" src/gmock-all.cc)
   target_link_libraries(gmock PUBLIC gtest)
+  set_target_properties(gmock PROPERTIES VERSION ${GOOGLETEST_VERSION})
   cxx_library(gmock_main "${cxx_strict}" src/gmock_main.cc)
   target_link_libraries(gmock_main PUBLIC gmock)
+  set_target_properties(gmock_main PROPERTIES VERSION ${GOOGLETEST_VERSION})
 endif()
 # If the CMake version supports it, attach header directory information
 # to the targets for when we are part of a parent build (ie being pulled
@@ -136,20 +138,6 @@
   # 'make test' or ctest.
   enable_testing()
 
-  if (WIN32)
-    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/RunTest.ps1"
-         CONTENT
-"$project_bin = \"${CMAKE_BINARY_DIR}/bin/$<CONFIG>\"
-$env:Path = \"$project_bin;$env:Path\"
-& $args")
-  elseif (MINGW OR CYGWIN)
-    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/RunTest.ps1"
-         CONTENT
-"$project_bin = (cygpath --windows ${CMAKE_BINARY_DIR}/bin)
-$env:Path = \"$project_bin;$env:Path\"
-& $args")
-  endif()
-
   if (MINGW OR CYGWIN)
     if (CMAKE_VERSION VERSION_LESS "2.8.12")
       add_compile_options("-Wa,-mbig-obj")
@@ -165,9 +153,6 @@
   cxx_test(gmock-cardinalities_test gmock_main)
   cxx_test(gmock_ex_test gmock_main)
   cxx_test(gmock-function-mocker_test gmock_main)
-  cxx_test(gmock-generated-actions_test gmock_main)
-  cxx_test(gmock-generated-function-mockers_test gmock_main)
-  cxx_test(gmock-generated-matchers_test gmock_main)
   cxx_test(gmock-internal-utils_test gmock_main)
   cxx_test(gmock-matchers_test gmock_main)
   cxx_test(gmock-more-actions_test gmock_main)
diff --git a/ext/googletest/googlemock/CONTRIBUTORS b/ext/googletest/googlemock/CONTRIBUTORS
deleted file mode 100644
index 6e9ae36..0000000
--- a/ext/googletest/googlemock/CONTRIBUTORS
+++ /dev/null
@@ -1,40 +0,0 @@
-# This file contains a list of people who've made non-trivial
-# contribution to the Google C++ Mocking Framework project.  People
-# who commit code to the project are encouraged to add their names
-# here.  Please keep the list sorted by first names.
-
-Benoit Sigoure <tsuna@google.com>
-Bogdan Piloca <boo@google.com>
-Chandler Carruth <chandlerc@google.com>
-Dave MacLachlan <dmaclach@gmail.com>
-David Anderson <danderson@google.com>
-Dean Sturtevant
-Gene Volovich <gv@cite.com>
-Hal Burch <gmock@hburch.com>
-Jeffrey Yasskin <jyasskin@google.com>
-Jim Keller <jimkeller@google.com>
-Joe Walnes <joe@truemesh.com>
-Jon Wray <jwray@google.com>
-Keir Mierle <mierle@gmail.com>
-Keith Ray <keith.ray@gmail.com>
-Kostya Serebryany <kcc@google.com>
-Lev Makhlis
-Manuel Klimek <klimek@google.com>
-Mario Tanev <radix@google.com>
-Mark Paskin
-Markus Heule <markus.heule@gmail.com>
-Matthew Simmons <simmonmt@acm.org>
-Mike Bland <mbland@google.com>
-Neal Norwitz <nnorwitz@gmail.com>
-Nermin Ozkiranartli <nermin@google.com>
-Owen Carlsen <ocarlsen@google.com>
-Paneendra Ba <paneendra@google.com>
-Paul Menage <menage@google.com>
-Piotr Kaminski <piotrk@google.com>
-Russ Rufer <russ@pentad.com>
-Sverre Sundsdal <sundsdal@gmail.com>
-Takeshi Yoshino <tyoshino@google.com>
-Vadim Berman <vadimb@google.com>
-Vlad Losev <vladl@google.com>
-Wolfgang Klier <wklier@google.com>
-Zhanyong Wan <wan@google.com>
diff --git a/ext/googletest/googlemock/LICENSE b/ext/googletest/googlemock/LICENSE
deleted file mode 100644
index 1941a11..0000000
--- a/ext/googletest/googlemock/LICENSE
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright 2008, Google Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-    * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-    * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ext/googletest/googlemock/README.md b/ext/googletest/googlemock/README.md
index 183fdb8..ead6883 100644
--- a/ext/googletest/googlemock/README.md
+++ b/ext/googletest/googlemock/README.md
@@ -7,38 +7,38 @@
 
 It is inspired by:
 
-*   [jMock](http://www.jmock.org/),
-*   [EasyMock](http://www.easymock.org/), and
-*   [Hamcrest](http://code.google.com/p/hamcrest/),
+*   [jMock](http://www.jmock.org/)
+*   [EasyMock](http://www.easymock.org/)
+*   [Hamcrest](http://code.google.com/p/hamcrest/)
 
-and designed with C++'s specifics in mind.
+It is designed with C++'s specifics in mind.
 
 gMock:
 
--   provides a declarative syntax for defining mocks,
--   can define partial (hybrid) mocks, which are a cross of real and mock
-    objects,
--   handles functions of arbitrary types and overloaded functions,
--   comes with a rich set of matchers for validating function arguments,
--   uses an intuitive syntax for controlling the behavior of a mock,
--   does automatic verification of expectations (no record-and-replay needed),
--   allows arbitrary (partial) ordering constraints on function calls to be
-    expressed,
--   lets a user extend it by defining new matchers and actions.
--   does not use exceptions, and
--   is easy to learn and use.
+-   Provides a declarative syntax for defining mocks.
+-   Can define partial (hybrid) mocks, which are a cross of real and mock
+    objects.
+-   Handles functions of arbitrary types and overloaded functions.
+-   Comes with a rich set of matchers for validating function arguments.
+-   Uses an intuitive syntax for controlling the behavior of a mock.
+-   Does automatic verification of expectations (no record-and-replay needed).
+-   Allows arbitrary (partial) ordering constraints on function calls to be
+    expressed.
+-   Lets a user extend it by defining new matchers and actions.
+-   Does not use exceptions.
+-   Is easy to learn and use.
 
 Details and examples can be found here:
 
-*   [gMock for Dummies](docs/for_dummies.md)
-*   [Legacy gMock FAQ](docs/gmock_faq.md)
-*   [gMock Cookbook](docs/cook_book.md)
-*   [gMock Cheat Sheet](docs/cheat_sheet.md)
+*   [gMock for Dummies](https://google.github.io/googletest/gmock_for_dummies.html)
+*   [Legacy gMock FAQ](https://google.github.io/googletest/gmock_faq.html)
+*   [gMock Cookbook](https://google.github.io/googletest/gmock_cook_book.html)
+*   [gMock Cheat Sheet](https://google.github.io/googletest/gmock_cheat_sheet.html)
 
-Please note that code under scripts/generator/ is from the [cppclean
-project](http://code.google.com/p/cppclean/) and under the Apache
-License, which is different from Google Mock's license.
+Please note that code under scripts/generator/ is from the
+[cppclean project](http://code.google.com/p/cppclean/) and under the Apache
+License, which is different from GoogleMock's license.
 
-Google Mock is a part of
-[Google Test C++ testing framework](http://github.com/google/googletest/) and a
+GoogleMock is a part of
+[GoogleTest C++ testing framework](http://github.com/google/googletest/) and a
 subject to the same requirements.
diff --git a/ext/googletest/googlemock/cmake/gmock.pc.in b/ext/googletest/googlemock/cmake/gmock.pc.in
index 08e0454..23c67b5 100644
--- a/ext/googletest/googlemock/cmake/gmock.pc.in
+++ b/ext/googletest/googlemock/cmake/gmock.pc.in
@@ -1,11 +1,10 @@
-prefix=${pcfiledir}/../..
-libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
-includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
 
 Name: gmock
 Description: GoogleMock (without main() function)
 Version: @PROJECT_VERSION@
 URL: https://github.com/google/googletest
-Requires: gtest
+Requires: gtest = @PROJECT_VERSION@
 Libs: -L${libdir} -lgmock @CMAKE_THREAD_LIBS_INIT@
-Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ @CMAKE_THREAD_LIBS_INIT@
+Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@
diff --git a/ext/googletest/googlemock/cmake/gmock_main.pc.in b/ext/googletest/googlemock/cmake/gmock_main.pc.in
index b22fe61..66ffea7 100644
--- a/ext/googletest/googlemock/cmake/gmock_main.pc.in
+++ b/ext/googletest/googlemock/cmake/gmock_main.pc.in
@@ -1,11 +1,10 @@
-prefix=${pcfiledir}/../..
-libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
-includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
 
 Name: gmock_main
 Description: GoogleMock (with main() function)
 Version: @PROJECT_VERSION@
 URL: https://github.com/google/googletest
-Requires: gmock
+Requires: gmock = @PROJECT_VERSION@
 Libs: -L${libdir} -lgmock_main @CMAKE_THREAD_LIBS_INIT@
-Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ @CMAKE_THREAD_LIBS_INIT@
+Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@
diff --git a/ext/googletest/googlemock/docs/README.md b/ext/googletest/googlemock/docs/README.md
new file mode 100644
index 0000000..1bc57b7
--- /dev/null
+++ b/ext/googletest/googlemock/docs/README.md
@@ -0,0 +1,4 @@
+# Content Moved
+
+We are working on updates to the GoogleTest documentation, which has moved to
+the top-level [docs](../../docs) directory.
diff --git a/ext/googletest/googlemock/docs/cheat_sheet.md b/ext/googletest/googlemock/docs/cheat_sheet.md
deleted file mode 100644
index 850963a..0000000
--- a/ext/googletest/googlemock/docs/cheat_sheet.md
+++ /dev/null
@@ -1,781 +0,0 @@
-## gMock Cheat Sheet
-
-<!-- GOOGLETEST_CM0019 DO NOT DELETE -->
-
-<!-- GOOGLETEST_CM0033 DO NOT DELETE -->
-
-### Defining a Mock Class
-
-#### Mocking a Normal Class {#MockClass}
-
-Given
-
-```cpp
-class Foo {
-  ...
-  virtual ~Foo();
-  virtual int GetSize() const = 0;
-  virtual string Describe(const char* name) = 0;
-  virtual string Describe(int type) = 0;
-  virtual bool Process(Bar elem, int count) = 0;
-};
-```
-
-(note that `~Foo()` **must** be virtual) we can define its mock as
-
-```cpp
-#include "gmock/gmock.h"
-
-class MockFoo : public Foo {
-  ...
-  MOCK_METHOD(int, GetSize, (), (const, override));
-  MOCK_METHOD(string, Describe, (const char* name), (override));
-  MOCK_METHOD(string, Describe, (int type), (override));
-  MOCK_METHOD(bool, Process, (Bar elem, int count), (override));
-};
-```
-
-To create a "nice" mock, which ignores all uninteresting calls, a "naggy" mock,
-which warns on all uninteresting calls, or a "strict" mock, which treats them as
-failures:
-
-```cpp
-using ::testing::NiceMock;
-using ::testing::NaggyMock;
-using ::testing::StrictMock;
-
-NiceMock<MockFoo> nice_foo;      // The type is a subclass of MockFoo.
-NaggyMock<MockFoo> naggy_foo;    // The type is a subclass of MockFoo.
-StrictMock<MockFoo> strict_foo;  // The type is a subclass of MockFoo.
-```
-
-**Note:** A mock object is currently naggy by default. We may make it nice by
-default in the future.
-
-#### Mocking a Class Template {#MockTemplate}
-
-Class templates can be mocked just like any class.
-
-To mock
-
-```cpp
-template <typename Elem>
-class StackInterface {
-  ...
-  virtual ~StackInterface();
-  virtual int GetSize() const = 0;
-  virtual void Push(const Elem& x) = 0;
-};
-```
-
-(note that all member functions that are mocked, including `~StackInterface()`
-**must** be virtual).
-
-```cpp
-template <typename Elem>
-class MockStack : public StackInterface<Elem> {
-  ...
-  MOCK_METHOD(int, GetSize, (), (const, override));
-  MOCK_METHOD(void, Push, (const Elem& x), (override));
-};
-```
-
-#### Specifying Calling Conventions for Mock Functions
-
-If your mock function doesn't use the default calling convention, you can
-specify it by adding `Calltype(convention)` to `MOCK_METHOD`'s 4th parameter.
-For example,
-
-```cpp
-  MOCK_METHOD(bool, Foo, (int n), (Calltype(STDMETHODCALLTYPE)));
-  MOCK_METHOD(int, Bar, (double x, double y),
-              (const, Calltype(STDMETHODCALLTYPE)));
-```
-
-where `STDMETHODCALLTYPE` is defined by `<objbase.h>` on Windows.
-
-### Using Mocks in Tests {#UsingMocks}
-
-The typical work flow is:
-
-1.  Import the gMock names you need to use. All gMock symbols are in the
-    `testing` namespace unless they are macros or otherwise noted.
-2.  Create the mock objects.
-3.  Optionally, set the default actions of the mock objects.
-4.  Set your expectations on the mock objects (How will they be called? What
-    will they do?).
-5.  Exercise code that uses the mock objects; if necessary, check the result
-    using googletest assertions.
-6.  When a mock object is destructed, gMock automatically verifies that all
-    expectations on it have been satisfied.
-
-Here's an example:
-
-```cpp
-using ::testing::Return;                          // #1
-
-TEST(BarTest, DoesThis) {
-  MockFoo foo;                                    // #2
-
-  ON_CALL(foo, GetSize())                         // #3
-      .WillByDefault(Return(1));
-  // ... other default actions ...
-
-  EXPECT_CALL(foo, Describe(5))                   // #4
-      .Times(3)
-      .WillRepeatedly(Return("Category 5"));
-  // ... other expectations ...
-
-  EXPECT_EQ("good", MyProductionFunction(&foo));  // #5
-}                                                 // #6
-```
-
-### Setting Default Actions {#OnCall}
-
-gMock has a **built-in default action** for any function that returns `void`,
-`bool`, a numeric value, or a pointer. In C++11, it will additionally returns
-the default-constructed value, if one exists for the given type.
-
-To customize the default action for functions with return type *`T`*:
-
-```cpp
-using ::testing::DefaultValue;
-
-// Sets the default value to be returned. T must be CopyConstructible.
-DefaultValue<T>::Set(value);
-// Sets a factory. Will be invoked on demand. T must be MoveConstructible.
-//  T MakeT();
-DefaultValue<T>::SetFactory(&MakeT);
-// ... use the mocks ...
-// Resets the default value.
-DefaultValue<T>::Clear();
-```
-
-Example usage:
-
-```cpp
-  // Sets the default action for return type std::unique_ptr<Buzz> to
-  // creating a new Buzz every time.
-  DefaultValue<std::unique_ptr<Buzz>>::SetFactory(
-      [] { return MakeUnique<Buzz>(AccessLevel::kInternal); });
-
-  // When this fires, the default action of MakeBuzz() will run, which
-  // will return a new Buzz object.
-  EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")).Times(AnyNumber());
-
-  auto buzz1 = mock_buzzer_.MakeBuzz("hello");
-  auto buzz2 = mock_buzzer_.MakeBuzz("hello");
-  EXPECT_NE(nullptr, buzz1);
-  EXPECT_NE(nullptr, buzz2);
-  EXPECT_NE(buzz1, buzz2);
-
-  // Resets the default action for return type std::unique_ptr<Buzz>,
-  // to avoid interfere with other tests.
-  DefaultValue<std::unique_ptr<Buzz>>::Clear();
-```
-
-To customize the default action for a particular method of a specific mock
-object, use `ON_CALL()`. `ON_CALL()` has a similar syntax to `EXPECT_CALL()`,
-but it is used for setting default behaviors (when you do not require that the
-mock method is called). See [here](cook_book.md#UseOnCall) for a more detailed
-discussion.
-
-```cpp
-ON_CALL(mock-object, method(matchers))
-    .With(multi-argument-matcher)   ?
-    .WillByDefault(action);
-```
-
-### Setting Expectations {#ExpectCall}
-
-`EXPECT_CALL()` sets **expectations** on a mock method (How will it be called?
-What will it do?):
-
-```cpp
-EXPECT_CALL(mock-object, method (matchers)?)
-     .With(multi-argument-matcher)  ?
-     .Times(cardinality)            ?
-     .InSequence(sequences)         *
-     .After(expectations)           *
-     .WillOnce(action)              *
-     .WillRepeatedly(action)        ?
-     .RetiresOnSaturation();        ?
-```
-
-For each item above, `?` means it can be used at most once, while `*` means it
-can be used any number of times.
-
-In order to pass, `EXPECT_CALL` must be used before the calls are actually made.
-
-The `(matchers)` is a comma-separated list of matchers that correspond to each
-of the arguments of `method`, and sets the expectation only for calls of
-`method` that matches all of the matchers.
-
-If `(matchers)` is omitted, the expectation is the same as if the matchers were
-set to anything matchers (for example, `(_, _, _, _)` for a four-arg method).
-
-If `Times()` is omitted, the cardinality is assumed to be:
-
-*   `Times(1)` when there is neither `WillOnce()` nor `WillRepeatedly()`;
-*   `Times(n)` when there are `n` `WillOnce()`s but no `WillRepeatedly()`, where
-    `n` >= 1; or
-*   `Times(AtLeast(n))` when there are `n` `WillOnce()`s and a
-    `WillRepeatedly()`, where `n` >= 0.
-
-A method with no `EXPECT_CALL()` is free to be invoked *any number of times*,
-and the default action will be taken each time.
-
-### Matchers {#MatcherList}
-
-<!-- GOOGLETEST_CM0020 DO NOT DELETE -->
-
-A **matcher** matches a *single* argument. You can use it inside `ON_CALL()` or
-`EXPECT_CALL()`, or use it to validate a value directly using two macros:
-
-<!-- mdformat off(github rendering does not support multiline tables) -->
-| Macro                                | Description                           |
-| :----------------------------------- | :------------------------------------ |
-| `EXPECT_THAT(actual_value, matcher)` | Asserts that `actual_value` matches `matcher`. |
-| `ASSERT_THAT(actual_value, matcher)` | The same as `EXPECT_THAT(actual_value, matcher)`, except that it generates a **fatal** failure. |
-<!-- mdformat on -->
-
-Built-in matchers (where `argument` is the function argument, e.g.
-`actual_value` in the example above, or when used in the context of
-`EXPECT_CALL(mock_object, method(matchers))`, the arguments of `method`) are
-divided into several categories:
-
-#### Wildcard
-
-Matcher                     | Description
-:-------------------------- | :-----------------------------------------------
-`_`                         | `argument` can be any value of the correct type.
-`A<type>()` or `An<type>()` | `argument` can be any value of type `type`.
-
-#### Generic Comparison
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                | Description                                         |
-| :--------------------- | :-------------------------------------------------- |
-| `Eq(value)` or `value` | `argument == value`                                 |
-| `Ge(value)`            | `argument >= value`                                 |
-| `Gt(value)`            | `argument > value`                                  |
-| `Le(value)`            | `argument <= value`                                 |
-| `Lt(value)`            | `argument < value`                                  |
-| `Ne(value)`            | `argument != value`                                 |
-| `IsFalse()`            | `argument` evaluates to `false` in a Boolean context. |
-| `IsTrue()`             | `argument` evaluates to `true` in a Boolean context. |
-| `IsNull()`             | `argument` is a `NULL` pointer (raw or smart).      |
-| `NotNull()`            | `argument` is a non-null pointer (raw or smart).    |
-| `Optional(m)`          | `argument` is `optional<>` that contains a value matching `m`. |
-| `VariantWith<T>(m)`    | `argument` is `variant<>` that holds the alternative of type T with a value matching `m`. |
-| `Ref(variable)`        | `argument` is a reference to `variable`.            |
-| `TypedEq<type>(value)` | `argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded. |
-<!-- mdformat on -->
-
-Except `Ref()`, these matchers make a *copy* of `value` in case it's modified or
-destructed later. If the compiler complains that `value` doesn't have a public
-copy constructor, try wrap it in `ByRef()`, e.g.
-`Eq(ByRef(non_copyable_value))`. If you do that, make sure `non_copyable_value`
-is not changed afterwards, or the meaning of your matcher will be changed.
-
-#### Floating-Point Matchers {#FpMatchers}
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                          | Description                        |
-| :------------------------------- | :--------------------------------- |
-| `DoubleEq(a_double)`             | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal. |
-| `FloatEq(a_float)`               | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. |
-| `NanSensitiveDoubleEq(a_double)` | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. |
-| `NanSensitiveFloatEq(a_float)`   | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. |
-<!-- mdformat on -->
-
-The above matchers use ULP-based comparison (the same as used in googletest).
-They automatically pick a reasonable error bound based on the absolute value of
-the expected value. `DoubleEq()` and `FloatEq()` conform to the IEEE standard,
-which requires comparing two NaNs for equality to return false. The
-`NanSensitive*` version instead treats two NaNs as equal, which is often what a
-user wants.
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                                           | Description              |
-| :------------------------------------------------ | :----------------------- |
-| `DoubleNear(a_double, max_abs_error)`             | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as unequal. |
-| `FloatNear(a_float, max_abs_error)`               | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as unequal. |
-| `NanSensitiveDoubleNear(a_double, max_abs_error)` | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as equal. |
-| `NanSensitiveFloatNear(a_float, max_abs_error)`   | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as equal. |
-<!-- mdformat on -->
-
-#### String Matchers
-
-The `argument` can be either a C string or a C++ string object:
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                 | Description                                        |
-| :---------------------- | :------------------------------------------------- |
-| `ContainsRegex(string)` | `argument` matches the given regular expression.   |
-| `EndsWith(suffix)`      | `argument` ends with string `suffix`.              |
-| `HasSubstr(string)`     | `argument` contains `string` as a sub-string.      |
-| `MatchesRegex(string)`  | `argument` matches the given regular expression with the match starting at the first character and ending at the last character. |
-| `StartsWith(prefix)`    | `argument` starts with string `prefix`.            |
-| `StrCaseEq(string)`     | `argument` is equal to `string`, ignoring case.    |
-| `StrCaseNe(string)`     | `argument` is not equal to `string`, ignoring case. |
-| `StrEq(string)`         | `argument` is equal to `string`.                   |
-| `StrNe(string)`         | `argument` is not equal to `string`.               |
-<!-- mdformat on -->
-
-`ContainsRegex()` and `MatchesRegex()` take ownership of the `RE` object. They
-use the regular expression syntax defined
-[here](../../googletest/docs/advanced.md#regular-expression-syntax).
-`StrCaseEq()`, `StrCaseNe()`, `StrEq()`, and `StrNe()` work for wide strings as
-well.
-
-#### Container Matchers
-
-Most STL-style containers support `==`, so you can use `Eq(expected_container)`
-or simply `expected_container` to match a container exactly. If you want to
-write the elements in-line, match them more flexibly, or get more informative
-messages, you can use:
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                                   | Description                      |
-| :---------------------------------------- | :------------------------------- |
-| `BeginEndDistanceIs(m)` | `argument` is a container whose `begin()` and `end()` iterators are separated by a number of increments matching `m`. E.g. `BeginEndDistanceIs(2)` or `BeginEndDistanceIs(Lt(2))`. For containers that define a `size()` method, `SizeIs(m)` may be more efficient. |
-| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. |
-| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. |
-| `Each(e)` | `argument` is a container where *every* element matches `e`, which can be either a value or a matcher. |
-| `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the *i*-th element matches `ei`, which can be a value or a matcher. |
-| `ElementsAreArray({e0, e1, ..., en})`, `ElementsAreArray(a_container)`, `ElementsAreArray(begin, end)`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
-| `IsEmpty()` | `argument` is an empty container (`container.empty()`). |
-| `IsSubsetOf({e0, e1, ..., en})`, `IsSubsetOf(a_container)`, `IsSubsetOf(begin, end)`, `IsSubsetOf(array)`, or `IsSubsetOf(array, count)` | `argument` matches `UnorderedElementsAre(x0, x1, ..., xk)` for some subset `{x0, x1, ..., xk}` of the expected matchers. |
-| `IsSupersetOf({e0, e1, ..., en})`, `IsSupersetOf(a_container)`, `IsSupersetOf(begin, end)`, `IsSupersetOf(array)`, or `IsSupersetOf(array, count)` | Some subset of `argument` matches `UnorderedElementsAre(`expected matchers`)`. |
-| `Pointwise(m, container)`, `Pointwise(m, {e0, e1, ..., en})` | `argument` contains the same number of elements as in `container`, and for all i, (the i-th element in `argument`, the i-th element in `container`) match `m`, which is a matcher on 2-tuples. E.g. `Pointwise(Le(), upper_bounds)` verifies that each element in `argument` doesn't exceed the corresponding element in `upper_bounds`. See more detail below. |
-| `SizeIs(m)` | `argument` is a container whose size matches `m`. E.g. `SizeIs(2)` or `SizeIs(Lt(2))`. |
-| `UnorderedElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, and under *some* permutation of the elements, each element matches an `ei` (for a different `i`), which can be a value or a matcher. |
-| `UnorderedElementsAreArray({e0, e1, ..., en})`, `UnorderedElementsAreArray(a_container)`, `UnorderedElementsAreArray(begin, end)`, `UnorderedElementsAreArray(array)`, or `UnorderedElementsAreArray(array, count)` | The same as `UnorderedElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
-| `UnorderedPointwise(m, container)`, `UnorderedPointwise(m, {e0, e1, ..., en})` | Like `Pointwise(m, container)`, but ignores the order of elements. |
-| `WhenSorted(m)` | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(ElementsAre(1, 2, 3))` verifies that `argument` contains elements 1, 2, and 3, ignoring order. |
-| `WhenSortedBy(comparator, m)` | The same as `WhenSorted(m)`, except that the given comparator instead of `<` is used to sort `argument`. E.g. `WhenSortedBy(std::greater(), ElementsAre(3, 2, 1))`. |
-<!-- mdformat on -->
-
-**Notes:**
-
-*   These matchers can also match:
-    1.  a native array passed by reference (e.g. in `Foo(const int (&a)[5])`),
-        and
-    2.  an array passed as a pointer and a count (e.g. in `Bar(const T* buffer,
-        int len)` -- see [Multi-argument Matchers](#MultiArgMatchers)).
-*   The array being matched may be multi-dimensional (i.e. its elements can be
-    arrays).
-*   `m` in `Pointwise(m, ...)` should be a matcher for `::std::tuple<T, U>`
-    where `T` and `U` are the element type of the actual container and the
-    expected container, respectively. For example, to compare two `Foo`
-    containers where `Foo` doesn't support `operator==`, one might write:
-
-    ```cpp
-    using ::std::get;
-    MATCHER(FooEq, "") {
-      return std::get<0>(arg).Equals(std::get<1>(arg));
-    }
-    ...
-    EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos));
-    ```
-
-#### Member Matchers
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                         | Description                                |
-| :------------------------------ | :----------------------------------------- |
-| `Field(&class::field, m)`       | `argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. |
-| `Key(e)`                        | `argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`. |
-| `Pair(m1, m2)`                  | `argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. |
-| `Property(&class::property, m)` | `argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. |
-<!-- mdformat on -->
-
-#### Matching the Result of a Function, Functor, or Callback
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher          | Description                                       |
-| :--------------- | :------------------------------------------------ |
-| `ResultOf(f, m)` | `f(argument)` matches matcher `m`, where `f` is a function or functor. |
-<!-- mdformat on -->
-
-#### Pointer Matchers
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                   | Description                                     |
-| :------------------------ | :---------------------------------------------- |
-| `Pointee(m)`              | `argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`. |
-| `WhenDynamicCastTo<T>(m)` | when `argument` is passed through `dynamic_cast<T>()`, it matches matcher `m`. |
-<!-- mdformat on -->
-
-<!-- GOOGLETEST_CM0026 DO NOT DELETE -->
-
-<!-- GOOGLETEST_CM0027 DO NOT DELETE -->
-
-#### Multi-argument Matchers {#MultiArgMatchers}
-
-Technically, all matchers match a *single* value. A "multi-argument" matcher is
-just one that matches a *tuple*. The following matchers can be used to match a
-tuple `(x, y)`:
-
-Matcher | Description
-:------ | :----------
-`Eq()`  | `x == y`
-`Ge()`  | `x >= y`
-`Gt()`  | `x > y`
-`Le()`  | `x <= y`
-`Lt()`  | `x < y`
-`Ne()`  | `x != y`
-
-You can use the following selectors to pick a subset of the arguments (or
-reorder them) to participate in the matching:
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                    | Description                                     |
-| :------------------------- | :---------------------------------------------- |
-| `AllArgs(m)`               | Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`. |
-| `Args<N1, N2, ..., Nk>(m)` | The tuple of the `k` selected (using 0-based indices) arguments matches `m`, e.g. `Args<1, 2>(Eq())`. |
-<!-- mdformat on -->
-
-#### Composite Matchers
-
-You can make a matcher from one or more other matchers:
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                          | Description                             |
-| :------------------------------- | :-------------------------------------- |
-| `AllOf(m1, m2, ..., mn)` | `argument` matches all of the matchers `m1` to `mn`. |
-| `AllOfArray({m0, m1, ..., mn})`, `AllOfArray(a_container)`, `AllOfArray(begin, end)`, `AllOfArray(array)`, or `AllOfArray(array, count)` | The same as `AllOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
-| `AnyOf(m1, m2, ..., mn)` | `argument` matches at least one of the matchers `m1` to `mn`. |
-| `AnyOfArray({m0, m1, ..., mn})`, `AnyOfArray(a_container)`, `AnyOfArray(begin, end)`, `AnyOfArray(array)`, or `AnyOfArray(array, count)` | The same as `AnyOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
-| `Not(m)` | `argument` doesn't match matcher `m`. |
-<!-- mdformat on -->
-
-<!-- GOOGLETEST_CM0028 DO NOT DELETE -->
-
-#### Adapters for Matchers
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                 | Description                           |
-| :---------------------- | :------------------------------------ |
-| `MatcherCast<T>(m)`     | casts matcher `m` to type `Matcher<T>`. |
-| `SafeMatcherCast<T>(m)` | [safely casts](cook_book.md#casting-matchers) matcher `m` to type `Matcher<T>`. |
-| `Truly(predicate)`      | `predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor. |
-<!-- mdformat on -->
-
-`AddressSatisfies(callback)` and `Truly(callback)` take ownership of `callback`,
-which must be a permanent callback.
-
-#### Using Matchers as Predicates {#MatchersAsPredicatesCheat}
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                       | Description                                 |
-| :---------------------------- | :------------------------------------------ |
-| `Matches(m)(value)` | evaluates to `true` if `value` matches `m`. You can use `Matches(m)` alone as a unary functor. |
-| `ExplainMatchResult(m, value, result_listener)` | evaluates to `true` if `value` matches `m`, explaining the result to `result_listener`. |
-| `Value(value, m)` | evaluates to `true` if `value` matches `m`. |
-<!-- mdformat on -->
-
-#### Defining Matchers
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher                              | Description                           |
-| :----------------------------------- | :------------------------------------ |
-| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. |
-| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a macher `IsDivisibleBy(n)` to match a number divisible by `n`. |
-| `MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + PrintToString(a) + " and " + PrintToString(b)) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. |
-<!-- mdformat on -->
-
-**Notes:**
-
-1.  The `MATCHER*` macros cannot be used inside a function or class.
-2.  The matcher body must be *purely functional* (i.e. it cannot have any side
-    effect, and the result must not depend on anything other than the value
-    being matched and the matcher parameters).
-3.  You can use `PrintToString(x)` to convert a value `x` of any type to a
-    string.
-
-### Actions {#ActionList}
-
-**Actions** specify what a mock function should do when invoked.
-
-#### Returning a Value
-
-<!-- mdformat off(no multiline tables) -->
-|                             |                                               |
-| :-------------------------- | :-------------------------------------------- |
-| `Return()`                  | Return from a `void` mock function.           |
-| `Return(value)`             | Return `value`. If the type of `value` is     different to the mock function's return type, `value` is converted to the latter type <i>at the time the expectation is set</i>, not when the action is executed. |
-| `ReturnArg<N>()`            | Return the `N`-th (0-based) argument.         |
-| `ReturnNew<T>(a1, ..., ak)` | Return `new T(a1, ..., ak)`; a different      object is created each time. |
-| `ReturnNull()`              | Return a null pointer.                        |
-| `ReturnPointee(ptr)`        | Return the value pointed to by `ptr`.         |
-| `ReturnRef(variable)`       | Return a reference to `variable`.             |
-| `ReturnRefOfCopy(value)`    | Return a reference to a copy of `value`; the  copy lives as long as the action. |
-<!-- mdformat on -->
-
-#### Side Effects
-
-<!-- mdformat off(no multiline tables) -->
-|                                    |                                         |
-| :--------------------------------- | :-------------------------------------- |
-| `Assign(&variable, value)` | Assign `value` to variable. |
-| `DeleteArg<N>()` | Delete the `N`-th (0-based) argument, which must be a pointer. |
-| `SaveArg<N>(pointer)` | Save the `N`-th (0-based) argument to `*pointer`. |
-| `SaveArgPointee<N>(pointer)` | Save the value pointed to by the `N`-th (0-based) argument to `*pointer`. |
-| `SetArgReferee<N>(value)` | Assign value to the variable referenced by the `N`-th (0-based) argument. |
-| `SetArgPointee<N>(value)` | Assign `value` to the variable pointed by the `N`-th (0-based) argument. |
-| `SetArgumentPointee<N>(value)` | Same as `SetArgPointee<N>(value)`. Deprecated. Will be removed in v1.7.0. |
-| `SetArrayArgument<N>(first, last)` | Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range. |
-| `SetErrnoAndReturn(error, value)` | Set `errno` to `error` and return `value`. |
-| `Throw(exception)` | Throws the given exception, which can be any copyable value. Available since v1.1.0. |
-<!-- mdformat on -->
-
-#### Using a Function, Functor, or Lambda as an Action
-
-In the following, by "callable" we mean a free function, `std::function`,
-functor, or lambda.
-
-<!-- mdformat off(no multiline tables) -->
-|                                     |                                        |
-| :---------------------------------- | :------------------------------------- |
-| `f` | Invoke f with the arguments passed to the mock function, where f is a callable. |
-| `Invoke(f)` | Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor. |
-| `Invoke(object_pointer, &class::method)` | Invoke the method on the object with the arguments passed to the mock function. |
-| `InvokeWithoutArgs(f)` | Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. |
-| `InvokeWithoutArgs(object_pointer, &class::method)` | Invoke the method on the object, which takes no arguments. |
-| `InvokeArgument<N>(arg1, arg2, ..., argk)` | Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments. |
-<!-- mdformat on -->
-
-The return value of the invoked function is used as the return value of the
-action.
-
-When defining a callable to be used with `Invoke*()`, you can declare any unused
-parameters as `Unused`:
-
-```cpp
-using ::testing::Invoke;
-double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); }
-...
-EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));
-```
-
-`Invoke(callback)` and `InvokeWithoutArgs(callback)` take ownership of
-`callback`, which must be permanent. The type of `callback` must be a base
-callback type instead of a derived one, e.g.
-
-```cpp
-  BlockingClosure* done = new BlockingClosure;
-  ... Invoke(done) ...;  // This won't compile!
-
-  Closure* done2 = new BlockingClosure;
-  ... Invoke(done2) ...;  // This works.
-```
-
-In `InvokeArgument<N>(...)`, if an argument needs to be passed by reference,
-wrap it inside `ByRef()`. For example,
-
-```cpp
-using ::testing::ByRef;
-using ::testing::InvokeArgument;
-...
-InvokeArgument<2>(5, string("Hi"), ByRef(foo))
-```
-
-calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by
-value, and `foo` by reference.
-
-#### Default Action
-
-<!-- mdformat off(no multiline tables) -->
-| Matcher       | Description                                            |
-| :------------ | :----------------------------------------------------- |
-| `DoDefault()` | Do the default action (specified by `ON_CALL()` or the built-in one). |
-<!-- mdformat on -->
-
-**Note:** due to technical reasons, `DoDefault()` cannot be used inside a
-composite action - trying to do so will result in a run-time error.
-
-<!-- GOOGLETEST_CM0032 DO NOT DELETE -->
-
-#### Composite Actions
-
-<!-- mdformat off(no multiline tables) -->
-|                                |                                             |
-| :----------------------------- | :------------------------------------------ |
-| `DoAll(a1, a2, ..., an)`       | Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void. |
-| `IgnoreResult(a)`              | Perform action `a` and ignore its result. `a` must not return void. |
-| `WithArg<N>(a)`                | Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. |
-| `WithArgs<N1, N2, ..., Nk>(a)` | Pass the selected (0-based) arguments of the mock function to action `a` and perform it. |
-| `WithoutArgs(a)`               | Perform action `a` without any arguments. |
-<!-- mdformat on -->
-
-#### Defining Actions
-
-<table border="1" cellspacing="0" cellpadding="1">
-  <tr>
-    <td>`struct SumAction {` <br>
-        &emsp;`template <typename T>` <br>
-        &emsp;`T operator()(T x, Ty) { return x + y; }` <br>
-        `};`
-    </td>
-    <td> Defines a generic functor that can be used as an action summing its
-    arguments. </td> </tr>
-  <tr>
-  </tr>
-</table>
-
-<!-- mdformat off(no multiline tables) -->
-|                                    |                                         |
-| :--------------------------------- | :-------------------------------------- |
-| `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. |
-| `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. |
-| `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. |
-<!-- mdformat on -->
-
-The `ACTION*` macros cannot be used inside a function or class.
-
-### Cardinalities {#CardinalityList}
-
-These are used in `Times()` to specify how many times a mock function will be
-called:
-
-<!-- mdformat off(no multiline tables) -->
-|                   |                                                        |
-| :---------------- | :----------------------------------------------------- |
-| `AnyNumber()`     | The function can be called any number of times.        |
-| `AtLeast(n)`      | The call is expected at least `n` times.               |
-| `AtMost(n)`       | The call is expected at most `n` times.                |
-| `Between(m, n)`   | The call is expected between `m` and `n` (inclusive) times. |
-| `Exactly(n) or n` | The call is expected exactly `n` times. In particular, the call should never happen when `n` is 0. |
-<!-- mdformat on -->
-
-### Expectation Order
-
-By default, the expectations can be matched in *any* order. If some or all
-expectations must be matched in a given order, there are two ways to specify it.
-They can be used either independently or together.
-
-#### The After Clause {#AfterClause}
-
-```cpp
-using ::testing::Expectation;
-...
-Expectation init_x = EXPECT_CALL(foo, InitX());
-Expectation init_y = EXPECT_CALL(foo, InitY());
-EXPECT_CALL(foo, Bar())
-     .After(init_x, init_y);
-```
-
-says that `Bar()` can be called only after both `InitX()` and `InitY()` have
-been called.
-
-If you don't know how many pre-requisites an expectation has when you write it,
-you can use an `ExpectationSet` to collect them:
-
-```cpp
-using ::testing::ExpectationSet;
-...
-ExpectationSet all_inits;
-for (int i = 0; i < element_count; i++) {
-  all_inits += EXPECT_CALL(foo, InitElement(i));
-}
-EXPECT_CALL(foo, Bar())
-     .After(all_inits);
-```
-
-says that `Bar()` can be called only after all elements have been initialized
-(but we don't care about which elements get initialized before the others).
-
-Modifying an `ExpectationSet` after using it in an `.After()` doesn't affect the
-meaning of the `.After()`.
-
-#### Sequences {#UsingSequences}
-
-When you have a long chain of sequential expectations, it's easier to specify
-the order using **sequences**, which don't require you to given each expectation
-in the chain a different name. *All expected calls* in the same sequence must
-occur in the order they are specified.
-
-```cpp
-using ::testing::Return;
-using ::testing::Sequence;
-Sequence s1, s2;
-...
-EXPECT_CALL(foo, Reset())
-    .InSequence(s1, s2)
-    .WillOnce(Return(true));
-EXPECT_CALL(foo, GetSize())
-    .InSequence(s1)
-    .WillOnce(Return(1));
-EXPECT_CALL(foo, Describe(A<const char*>()))
-    .InSequence(s2)
-    .WillOnce(Return("dummy"));
-```
-
-says that `Reset()` must be called before *both* `GetSize()` *and* `Describe()`,
-and the latter two can occur in any order.
-
-To put many expectations in a sequence conveniently:
-
-```cpp
-using ::testing::InSequence;
-{
-  InSequence seq;
-
-  EXPECT_CALL(...)...;
-  EXPECT_CALL(...)...;
-  ...
-  EXPECT_CALL(...)...;
-}
-```
-
-says that all expected calls in the scope of `seq` must occur in strict order.
-The name `seq` is irrelevant.
-
-### Verifying and Resetting a Mock
-
-gMock will verify the expectations on a mock object when it is destructed, or
-you can do it earlier:
-
-```cpp
-using ::testing::Mock;
-...
-// Verifies and removes the expectations on mock_obj;
-// returns true if and only if successful.
-Mock::VerifyAndClearExpectations(&mock_obj);
-...
-// Verifies and removes the expectations on mock_obj;
-// also removes the default actions set by ON_CALL();
-// returns true if and only if successful.
-Mock::VerifyAndClear(&mock_obj);
-```
-
-You can also tell gMock that a mock object can be leaked and doesn't need to be
-verified:
-
-```cpp
-Mock::AllowLeak(&mock_obj);
-```
-
-### Mock Classes
-
-gMock defines a convenient mock class template
-
-```cpp
-class MockFunction<R(A1, ..., An)> {
- public:
-  MOCK_METHOD(R, Call, (A1, ..., An));
-};
-```
-
-See this [recipe](cook_book.md#using-check-points) for one application of it.
-
-### Flags
-
-<!-- mdformat off(no multiline tables) -->
-| Flag                           | Description                               |
-| :----------------------------- | :---------------------------------------- |
-| `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. |
-| `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. |
-<!-- mdformat on -->
diff --git a/ext/googletest/googlemock/include/gmock/gmock-actions.h b/ext/googletest/googlemock/include/gmock/gmock-actions.h
index f12d39b..f2393bd 100644
--- a/ext/googletest/googlemock/include/gmock/gmock-actions.h
+++ b/ext/googletest/googlemock/include/gmock/gmock-actions.h
@@ -30,12 +30,105 @@
 
 // Google Mock - a framework for writing C++ mock classes.
 //
-// This file implements some commonly used actions.
+// The ACTION* family of macros can be used in a namespace scope to
+// define custom actions easily.  The syntax:
+//
+//   ACTION(name) { statements; }
+//
+// will define an action with the given name that executes the
+// statements.  The value returned by the statements will be used as
+// the return value of the action.  Inside the statements, you can
+// refer to the K-th (0-based) argument of the mock function by
+// 'argK', and refer to its type by 'argK_type'.  For example:
+//
+//   ACTION(IncrementArg1) {
+//     arg1_type temp = arg1;
+//     return ++(*temp);
+//   }
+//
+// allows you to write
+//
+//   ...WillOnce(IncrementArg1());
+//
+// You can also refer to the entire argument tuple and its type by
+// 'args' and 'args_type', and refer to the mock function type and its
+// return type by 'function_type' and 'return_type'.
+//
+// Note that you don't need to specify the types of the mock function
+// arguments.  However rest assured that your code is still type-safe:
+// you'll get a compiler error if *arg1 doesn't support the ++
+// operator, or if the type of ++(*arg1) isn't compatible with the
+// mock function's return type, for example.
+//
+// Sometimes you'll want to parameterize the action.   For that you can use
+// another macro:
+//
+//   ACTION_P(name, param_name) { statements; }
+//
+// For example:
+//
+//   ACTION_P(Add, n) { return arg0 + n; }
+//
+// will allow you to write:
+//
+//   ...WillOnce(Add(5));
+//
+// Note that you don't need to provide the type of the parameter
+// either.  If you need to reference the type of a parameter named
+// 'foo', you can write 'foo_type'.  For example, in the body of
+// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type
+// of 'n'.
+//
+// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support
+// multi-parameter actions.
+//
+// For the purpose of typing, you can view
+//
+//   ACTION_Pk(Foo, p1, ..., pk) { ... }
+//
+// as shorthand for
+//
+//   template <typename p1_type, ..., typename pk_type>
+//   FooActionPk<p1_type, ..., pk_type> Foo(p1_type p1, ..., pk_type pk) { ... }
+//
+// In particular, you can provide the template type arguments
+// explicitly when invoking Foo(), as in Foo<long, bool>(5, false);
+// although usually you can rely on the compiler to infer the types
+// for you automatically.  You can assign the result of expression
+// Foo(p1, ..., pk) to a variable of type FooActionPk<p1_type, ...,
+// pk_type>.  This can be useful when composing actions.
+//
+// You can also overload actions with different numbers of parameters:
+//
+//   ACTION_P(Plus, a) { ... }
+//   ACTION_P2(Plus, a, b) { ... }
+//
+// While it's tempting to always use the ACTION* macros when defining
+// a new action, you should also consider implementing ActionInterface
+// or using MakePolymorphicAction() instead, especially if you need to
+// use the action a lot.  While these approaches require more work,
+// they give you more control on the types of the mock function
+// arguments and the action parameters, which in general leads to
+// better compiler error messages that pay off in the long run.  They
+// also allow overloading actions based on parameter types (as opposed
+// to just based on the number of parameters).
+//
+// CAVEAT:
+//
+// ACTION*() can only be used in a namespace scope as templates cannot be
+// declared inside of a local class.
+// Users can, however, define any local functors (e.g. a lambda) that
+// can be used as actions.
+//
+// MORE INFORMATION:
+//
+// To learn more about using these macros, please search for 'ACTION' on
+// https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
 
 #ifndef _WIN32_WCE
 # include <errno.h>
@@ -45,11 +138,13 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <tuple>
 #include <type_traits>
 #include <utility>
 
 #include "gmock/internal/gmock-internal-utils.h"
 #include "gmock/internal/gmock-port.h"
+#include "gmock/internal/gmock-pp.h"
 
 #ifdef _MSC_VER
 # pragma warning(push)
@@ -162,13 +257,17 @@
 GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0);
 GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL);  // NOLINT
 GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L);     // NOLINT
-GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(UInt64, 0);
-GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(Int64, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long long, 0);  // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long long, 0);  // NOLINT
 GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0);
 GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0);
 
 #undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_
 
+// Simple two-arg form of std::disjunction.
+template <typename P, typename Q>
+using disjunction = typename ::std::conditional<P::value, P, Q>::type;
+
 }  // namespace internal
 
 // When an unexpected function call is encountered, Google Mock will
@@ -350,6 +449,9 @@
     }
   };
 
+  template <typename G>
+  using IsCompatibleFunctor = std::is_constructible<std::function<F>, G>;
+
  public:
   typedef typename internal::Function<F>::Result Result;
   typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
@@ -361,10 +463,14 @@
   // Construct an Action from a specified callable.
   // This cannot take std::function directly, because then Action would not be
   // directly constructible from lambda (it would require two conversions).
-  template <typename G,
-            typename = typename ::std::enable_if<
-                ::std::is_constructible<::std::function<F>, G>::value>::type>
-  Action(G&& fun) : fun_(::std::forward<G>(fun)) {}  // NOLINT
+  template <
+      typename G,
+      typename = typename std::enable_if<internal::disjunction<
+          IsCompatibleFunctor<G>, std::is_constructible<std::function<Result()>,
+                                                        G>>::value>::type>
+  Action(G&& fun) {  // NOLINT
+    Init(::std::forward<G>(fun), IsCompatibleFunctor<G>());
+  }
 
   // Constructs an Action from its implementation.
   explicit Action(ActionInterface<F>* impl)
@@ -396,6 +502,26 @@
   template <typename G>
   friend class Action;
 
+  template <typename G>
+  void Init(G&& g, ::std::true_type) {
+    fun_ = ::std::forward<G>(g);
+  }
+
+  template <typename G>
+  void Init(G&& g, ::std::false_type) {
+    fun_ = IgnoreArgs<typename ::std::decay<G>::type>{::std::forward<G>(g)};
+  }
+
+  template <typename FunctionImpl>
+  struct IgnoreArgs {
+    template <typename... Args>
+    Result operator()(const Args&...) const {
+      return function_impl();
+    }
+
+    FunctionImpl function_impl;
+  };
+
   // fun_ is an empty function if and only if this is the DoDefault() action.
   ::std::function<F> fun_;
 };
@@ -446,13 +572,9 @@
 
    private:
     Impl impl_;
-
-    GTEST_DISALLOW_ASSIGN_(MonomorphicImpl);
   };
 
   Impl impl_;
-
-  GTEST_DISALLOW_ASSIGN_(PolymorphicAction);
 };
 
 // Creates an Action from its implementation and returns it.  The
@@ -593,13 +715,9 @@
    private:
     bool performed_;
     const std::shared_ptr<R> wrapper_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
   const std::shared_ptr<R> value_;
-
-  GTEST_DISALLOW_ASSIGN_(ReturnAction);
 };
 
 // Implements the ReturnNull() action.
@@ -660,13 +778,9 @@
 
    private:
     T& ref_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
   T& ref_;
-
-  GTEST_DISALLOW_ASSIGN_(ReturnRefAction);
 };
 
 // Implements the polymorphic ReturnRefOfCopy(x) action, which can be
@@ -707,13 +821,39 @@
 
    private:
     T value_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
   const T value_;
+};
 
-  GTEST_DISALLOW_ASSIGN_(ReturnRefOfCopyAction);
+// Implements the polymorphic ReturnRoundRobin(v) action, which can be
+// used in any function that returns the element_type of v.
+template <typename T>
+class ReturnRoundRobinAction {
+ public:
+  explicit ReturnRoundRobinAction(std::vector<T> values) {
+    GTEST_CHECK_(!values.empty())
+        << "ReturnRoundRobin requires at least one element.";
+    state_->values = std::move(values);
+  }
+
+  template <typename... Args>
+  T operator()(Args&&...) const {
+     return state_->Next();
+  }
+
+ private:
+  struct State {
+    T Next() {
+      T ret_val = values[i++];
+      if (i == values.size()) i = 0;
+      return ret_val;
+    }
+
+    std::vector<T> values;
+    size_t i = 0;
+  };
+  std::shared_ptr<State> state_ = std::make_shared<State>();
 };
 
 // Implements the polymorphic DoDefault() action.
@@ -740,8 +880,6 @@
  private:
   T1* const ptr_;
   const T2 value_;
-
-  GTEST_DISALLOW_ASSIGN_(AssignAction);
 };
 
 #if !GTEST_OS_WINDOWS_MOBILE
@@ -763,8 +901,6 @@
  private:
   const int errno_;
   const T result_;
-
-  GTEST_DISALLOW_ASSIGN_(SetErrnoAndReturnAction);
 };
 
 #endif  // !GTEST_OS_WINDOWS_MOBILE
@@ -816,7 +952,8 @@
   Class* const obj_ptr;
   const MethodPtr method_ptr;
 
-  using ReturnType = typename std::result_of<MethodPtr(Class*)>::type;
+  using ReturnType =
+      decltype((std::declval<Class*>()->*std::declval<MethodPtr>())());
 
   template <typename... Args>
   ReturnType operator()(const Args&...) const {
@@ -869,13 +1006,9 @@
         OriginalFunction;
 
     const Action<OriginalFunction> action_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
   const A action_;
-
-  GTEST_DISALLOW_ASSIGN_(IgnoreResultAction);
 };
 
 template <typename InnerAction, size_t... I>
@@ -886,7 +1019,8 @@
   // We use the conversion operator to detect the signature of the inner Action.
   template <typename R, typename... Args>
   operator Action<R(Args...)>() const {  // NOLINT
-    Action<R(typename std::tuple_element<I, std::tuple<Args...>>::type...)>
+    using TupleType = std::tuple<Args...>;
+    Action<R(typename std::tuple_element<I, TupleType>::type...)>
         converted(action);
 
     return [converted](Args... args) -> R {
@@ -899,9 +1033,13 @@
 template <typename... Actions>
 struct DoAllAction {
  private:
-  template <typename... Args, size_t... I>
-  std::vector<Action<void(Args...)>> Convert(IndexSequence<I...>) const {
-    return {std::get<I>(actions)...};
+  template <typename T>
+  using NonFinalType =
+      typename std::conditional<std::is_scalar<T>::value, T, const T&>::type;
+
+  template <typename ActionT, size_t... I>
+  std::vector<ActionT> Convert(IndexSequence<I...>) const {
+    return {ActionT(std::get<I>(actions))...};
   }
 
  public:
@@ -910,21 +1048,121 @@
   template <typename R, typename... Args>
   operator Action<R(Args...)>() const {  // NOLINT
     struct Op {
-      std::vector<Action<void(Args...)>> converted;
+      std::vector<Action<void(NonFinalType<Args>...)>> converted;
       Action<R(Args...)> last;
       R operator()(Args... args) const {
         auto tuple_args = std::forward_as_tuple(std::forward<Args>(args)...);
         for (auto& a : converted) {
           a.Perform(tuple_args);
         }
-        return last.Perform(tuple_args);
+        return last.Perform(std::move(tuple_args));
       }
     };
-    return Op{Convert<Args...>(MakeIndexSequence<sizeof...(Actions) - 1>()),
+    return Op{Convert<Action<void(NonFinalType<Args>...)>>(
+                  MakeIndexSequence<sizeof...(Actions) - 1>()),
               std::get<sizeof...(Actions) - 1>(actions)};
   }
 };
 
+template <typename T, typename... Params>
+struct ReturnNewAction {
+  T* operator()() const {
+    return internal::Apply(
+        [](const Params&... unpacked_params) {
+          return new T(unpacked_params...);
+        },
+        params);
+  }
+  std::tuple<Params...> params;
+};
+
+template <size_t k>
+struct ReturnArgAction {
+  template <typename... Args>
+  auto operator()(const Args&... args) const ->
+      typename std::tuple_element<k, std::tuple<Args...>>::type {
+    return std::get<k>(std::tie(args...));
+  }
+};
+
+template <size_t k, typename Ptr>
+struct SaveArgAction {
+  Ptr pointer;
+
+  template <typename... Args>
+  void operator()(const Args&... args) const {
+    *pointer = std::get<k>(std::tie(args...));
+  }
+};
+
+template <size_t k, typename Ptr>
+struct SaveArgPointeeAction {
+  Ptr pointer;
+
+  template <typename... Args>
+  void operator()(const Args&... args) const {
+    *pointer = *std::get<k>(std::tie(args...));
+  }
+};
+
+template <size_t k, typename T>
+struct SetArgRefereeAction {
+  T value;
+
+  template <typename... Args>
+  void operator()(Args&&... args) const {
+    using argk_type =
+        typename ::std::tuple_element<k, std::tuple<Args...>>::type;
+    static_assert(std::is_lvalue_reference<argk_type>::value,
+                  "Argument must be a reference type.");
+    std::get<k>(std::tie(args...)) = value;
+  }
+};
+
+template <size_t k, typename I1, typename I2>
+struct SetArrayArgumentAction {
+  I1 first;
+  I2 last;
+
+  template <typename... Args>
+  void operator()(const Args&... args) const {
+    auto value = std::get<k>(std::tie(args...));
+    for (auto it = first; it != last; ++it, (void)++value) {
+      *value = *it;
+    }
+  }
+};
+
+template <size_t k>
+struct DeleteArgAction {
+  template <typename... Args>
+  void operator()(const Args&... args) const {
+    delete std::get<k>(std::tie(args...));
+  }
+};
+
+template <typename Ptr>
+struct ReturnPointeeAction {
+  Ptr pointer;
+  template <typename... Args>
+  auto operator()(const Args&...) const -> decltype(*pointer) {
+    return *pointer;
+  }
+};
+
+#if GTEST_HAS_EXCEPTIONS
+template <typename T>
+struct ThrowAction {
+  T exception;
+  // We use a conversion operator to adapt to any return type.
+  template <typename R, typename... Args>
+  operator Action<R(Args...)>() const {  // NOLINT
+    T copy = exception;
+    return [copy](Args...) -> R { throw copy; };
+  }
+};
+#endif  // GTEST_HAS_EXCEPTIONS
+
 }  // namespace internal
 
 // An Unused object can be implicitly constructed from ANY value.
@@ -960,7 +1198,8 @@
 typedef internal::IgnoredValue Unused;
 
 // Creates an action that does actions a1, a2, ..., sequentially in
-// each invocation.
+// each invocation. All but the last action will have a readonly view of the
+// arguments.
 template <typename... Action>
 internal::DoAllAction<typename std::decay<Action>::type...> DoAll(
     Action&&... action) {
@@ -1022,6 +1261,10 @@
   return internal::ReturnRefAction<R>(x);
 }
 
+// Prevent using ReturnRef on reference to temporary.
+template <typename R, R* = nullptr>
+internal::ReturnRefAction<R> ReturnRef(R&&) = delete;
+
 // Creates an action that returns the reference to a copy of the
 // argument.  The copy is created when the action is constructed and
 // lives as long as the action.
@@ -1039,6 +1282,23 @@
   return internal::ByMoveWrapper<R>(std::move(x));
 }
 
+// Creates an action that returns an element of `vals`. Calling this action will
+// repeatedly return the next value from `vals` until it reaches the end and
+// will restart from the beginning.
+template <typename T>
+internal::ReturnRoundRobinAction<T> ReturnRoundRobin(std::vector<T> vals) {
+  return internal::ReturnRoundRobinAction<T>(std::move(vals));
+}
+
+// Creates an action that returns an element of `vals`. Calling this action will
+// repeatedly return the next value from `vals` until it reaches the end and
+// will restart from the beginning.
+template <typename T>
+internal::ReturnRoundRobinAction<T> ReturnRoundRobin(
+    std::initializer_list<T> vals) {
+  return internal::ReturnRoundRobinAction<T>(std::vector<T>(vals));
+}
+
 // Creates an action that does the default action for the give mock function.
 inline internal::DoDefaultAction DoDefault() {
   return internal::DoDefaultAction();
@@ -1047,14 +1307,14 @@
 // Creates an action that sets the variable pointed by the N-th
 // (0-based) function argument to 'value'.
 template <size_t N, typename T>
-internal::SetArgumentPointeeAction<N, T> SetArgPointee(T x) {
-  return {std::move(x)};
+internal::SetArgumentPointeeAction<N, T> SetArgPointee(T value) {
+  return {std::move(value)};
 }
 
 // The following version is DEPRECATED.
 template <size_t N, typename T>
-internal::SetArgumentPointeeAction<N, T> SetArgumentPointee(T x) {
-  return {std::move(x)};
+internal::SetArgumentPointeeAction<N, T> SetArgumentPointee(T value) {
+  return {std::move(value)};
 }
 
 // Creates an action that sets a pointer referent to a given value.
@@ -1132,11 +1392,296 @@
   return ::std::reference_wrapper<T>(l_value);
 }
 
+// The ReturnNew<T>(a1, a2, ..., a_k) action returns a pointer to a new
+// instance of type T, constructed on the heap with constructor arguments
+// a1, a2, ..., and a_k. The caller assumes ownership of the returned value.
+template <typename T, typename... Params>
+internal::ReturnNewAction<T, typename std::decay<Params>::type...> ReturnNew(
+    Params&&... params) {
+  return {std::forward_as_tuple(std::forward<Params>(params)...)};
+}
+
+// Action ReturnArg<k>() returns the k-th argument of the mock function.
+template <size_t k>
+internal::ReturnArgAction<k> ReturnArg() {
+  return {};
+}
+
+// Action SaveArg<k>(pointer) saves the k-th (0-based) argument of the
+// mock function to *pointer.
+template <size_t k, typename Ptr>
+internal::SaveArgAction<k, Ptr> SaveArg(Ptr pointer) {
+  return {pointer};
+}
+
+// Action SaveArgPointee<k>(pointer) saves the value pointed to
+// by the k-th (0-based) argument of the mock function to *pointer.
+template <size_t k, typename Ptr>
+internal::SaveArgPointeeAction<k, Ptr> SaveArgPointee(Ptr pointer) {
+  return {pointer};
+}
+
+// Action SetArgReferee<k>(value) assigns 'value' to the variable
+// referenced by the k-th (0-based) argument of the mock function.
+template <size_t k, typename T>
+internal::SetArgRefereeAction<k, typename std::decay<T>::type> SetArgReferee(
+    T&& value) {
+  return {std::forward<T>(value)};
+}
+
+// Action SetArrayArgument<k>(first, last) copies the elements in
+// source range [first, last) to the array pointed to by the k-th
+// (0-based) argument, which can be either a pointer or an
+// iterator. The action does not take ownership of the elements in the
+// source range.
+template <size_t k, typename I1, typename I2>
+internal::SetArrayArgumentAction<k, I1, I2> SetArrayArgument(I1 first,
+                                                             I2 last) {
+  return {first, last};
+}
+
+// Action DeleteArg<k>() deletes the k-th (0-based) argument of the mock
+// function.
+template <size_t k>
+internal::DeleteArgAction<k> DeleteArg() {
+  return {};
+}
+
+// This action returns the value pointed to by 'pointer'.
+template <typename Ptr>
+internal::ReturnPointeeAction<Ptr> ReturnPointee(Ptr pointer) {
+  return {pointer};
+}
+
+// Action Throw(exception) can be used in a mock function of any type
+// to throw the given exception.  Any copyable value can be thrown.
+#if GTEST_HAS_EXCEPTIONS
+template <typename T>
+internal::ThrowAction<typename std::decay<T>::type> Throw(T&& exception) {
+  return {std::forward<T>(exception)};
+}
+#endif  // GTEST_HAS_EXCEPTIONS
+
+namespace internal {
+
+// A macro from the ACTION* family (defined later in gmock-generated-actions.h)
+// defines an action that can be used in a mock function.  Typically,
+// these actions only care about a subset of the arguments of the mock
+// function.  For example, if such an action only uses the second
+// argument, it can be used in any mock function that takes >= 2
+// arguments where the type of the second argument is compatible.
+//
+// Therefore, the action implementation must be prepared to take more
+// arguments than it needs.  The ExcessiveArg type is used to
+// represent those excessive arguments.  In order to keep the compiler
+// error messages tractable, we define it in the testing namespace
+// instead of testing::internal.  However, this is an INTERNAL TYPE
+// and subject to change without notice, so a user MUST NOT USE THIS
+// TYPE DIRECTLY.
+struct ExcessiveArg {};
+
+// Builds an implementation of an Action<> for some particular signature, using
+// a class defined by an ACTION* macro.
+template <typename F, typename Impl> struct ActionImpl;
+
+template <typename Impl>
+struct ImplBase {
+  struct Holder {
+    // Allows each copy of the Action<> to get to the Impl.
+    explicit operator const Impl&() const { return *ptr; }
+    std::shared_ptr<Impl> ptr;
+  };
+  using type = typename std::conditional<std::is_constructible<Impl>::value,
+                                         Impl, Holder>::type;
+};
+
+template <typename R, typename... Args, typename Impl>
+struct ActionImpl<R(Args...), Impl> : ImplBase<Impl>::type {
+  using Base = typename ImplBase<Impl>::type;
+  using function_type = R(Args...);
+  using args_type = std::tuple<Args...>;
+
+  ActionImpl() = default;  // Only defined if appropriate for Base.
+  explicit ActionImpl(std::shared_ptr<Impl> impl) : Base{std::move(impl)} { }
+
+  R operator()(Args&&... arg) const {
+    static constexpr size_t kMaxArgs =
+        sizeof...(Args) <= 10 ? sizeof...(Args) : 10;
+    return Apply(MakeIndexSequence<kMaxArgs>{},
+                 MakeIndexSequence<10 - kMaxArgs>{},
+                 args_type{std::forward<Args>(arg)...});
+  }
+
+  template <std::size_t... arg_id, std::size_t... excess_id>
+  R Apply(IndexSequence<arg_id...>, IndexSequence<excess_id...>,
+          const args_type& args) const {
+    // Impl need not be specific to the signature of action being implemented;
+    // only the implementing function body needs to have all of the specific
+    // types instantiated.  Up to 10 of the args that are provided by the
+    // args_type get passed, followed by a dummy of unspecified type for the
+    // remainder up to 10 explicit args.
+    static constexpr ExcessiveArg kExcessArg{};
+    return static_cast<const Impl&>(*this).template gmock_PerformImpl<
+        /*function_type=*/function_type, /*return_type=*/R,
+        /*args_type=*/args_type,
+        /*argN_type=*/typename std::tuple_element<arg_id, args_type>::type...>(
+        /*args=*/args, std::get<arg_id>(args)...,
+        ((void)excess_id, kExcessArg)...);
+  }
+};
+
+// Stores a default-constructed Impl as part of the Action<>'s
+// std::function<>. The Impl should be trivial to copy.
+template <typename F, typename Impl>
+::testing::Action<F> MakeAction() {
+  return ::testing::Action<F>(ActionImpl<F, Impl>());
+}
+
+// Stores just the one given instance of Impl.
+template <typename F, typename Impl>
+::testing::Action<F> MakeAction(std::shared_ptr<Impl> impl) {
+  return ::testing::Action<F>(ActionImpl<F, Impl>(std::move(impl)));
+}
+
+#define GMOCK_INTERNAL_ARG_UNUSED(i, data, el) \
+  , const arg##i##_type& arg##i GTEST_ATTRIBUTE_UNUSED_
+#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_           \
+  const args_type& args GTEST_ATTRIBUTE_UNUSED_ GMOCK_PP_REPEAT( \
+      GMOCK_INTERNAL_ARG_UNUSED, , 10)
+
+#define GMOCK_INTERNAL_ARG(i, data, el) , const arg##i##_type& arg##i
+#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_ \
+  const args_type& args GMOCK_PP_REPEAT(GMOCK_INTERNAL_ARG, , 10)
+
+#define GMOCK_INTERNAL_TEMPLATE_ARG(i, data, el) , typename arg##i##_type
+#define GMOCK_ACTION_TEMPLATE_ARGS_NAMES_ \
+  GMOCK_PP_TAIL(GMOCK_PP_REPEAT(GMOCK_INTERNAL_TEMPLATE_ARG, , 10))
+
+#define GMOCK_INTERNAL_TYPENAME_PARAM(i, data, param) , typename param##_type
+#define GMOCK_ACTION_TYPENAME_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPENAME_PARAM, , params))
+
+#define GMOCK_INTERNAL_TYPE_PARAM(i, data, param) , param##_type
+#define GMOCK_ACTION_TYPE_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_PARAM, , params))
+
+#define GMOCK_INTERNAL_TYPE_GVALUE_PARAM(i, data, param) \
+  , param##_type gmock_p##i
+#define GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_GVALUE_PARAM, , params))
+
+#define GMOCK_INTERNAL_GVALUE_PARAM(i, data, param) \
+  , std::forward<param##_type>(gmock_p##i)
+#define GMOCK_ACTION_GVALUE_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GVALUE_PARAM, , params))
+
+#define GMOCK_INTERNAL_INIT_PARAM(i, data, param) \
+  , param(::std::forward<param##_type>(gmock_p##i))
+#define GMOCK_ACTION_INIT_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_INIT_PARAM, , params))
+
+#define GMOCK_INTERNAL_FIELD_PARAM(i, data, param) param##_type param;
+#define GMOCK_ACTION_FIELD_PARAMS_(params) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_FIELD_PARAM, , params)
+
+#define GMOCK_INTERNAL_ACTION(name, full_name, params)                        \
+  template <GMOCK_ACTION_TYPENAME_PARAMS_(params)>                            \
+  class full_name {                                                           \
+   public:                                                                    \
+    explicit full_name(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params))              \
+        : impl_(std::make_shared<gmock_Impl>(                                 \
+                GMOCK_ACTION_GVALUE_PARAMS_(params))) { }                     \
+    full_name(const full_name&) = default;                                    \
+    full_name(full_name&&) noexcept = default;                                \
+    template <typename F>                                                     \
+    operator ::testing::Action<F>() const {                                   \
+      return ::testing::internal::MakeAction<F>(impl_);                       \
+    }                                                                         \
+   private:                                                                   \
+    class gmock_Impl {                                                        \
+     public:                                                                  \
+      explicit gmock_Impl(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params))           \
+          : GMOCK_ACTION_INIT_PARAMS_(params) {}                              \
+      template <typename function_type, typename return_type,                 \
+                typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>        \
+      return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \
+      GMOCK_ACTION_FIELD_PARAMS_(params)                                      \
+    };                                                                        \
+    std::shared_ptr<const gmock_Impl> impl_;                                  \
+  };                                                                          \
+  template <GMOCK_ACTION_TYPENAME_PARAMS_(params)>                            \
+  inline full_name<GMOCK_ACTION_TYPE_PARAMS_(params)> name(                   \
+      GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) {                             \
+    return full_name<GMOCK_ACTION_TYPE_PARAMS_(params)>(                      \
+        GMOCK_ACTION_GVALUE_PARAMS_(params));                                 \
+  }                                                                           \
+  template <GMOCK_ACTION_TYPENAME_PARAMS_(params)>                            \
+  template <typename function_type, typename return_type, typename args_type, \
+            GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>                                \
+  return_type full_name<GMOCK_ACTION_TYPE_PARAMS_(params)>::gmock_Impl::      \
+  gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+}  // namespace internal
+
+// Similar to GMOCK_INTERNAL_ACTION, but no bound parameters are stored.
+#define ACTION(name)                                                          \
+  class name##Action {                                                        \
+   public:                                                                    \
+   explicit name##Action() noexcept {}                                        \
+   name##Action(const name##Action&) noexcept {}                              \
+    template <typename F>                                                     \
+    operator ::testing::Action<F>() const {                                   \
+      return ::testing::internal::MakeAction<F, gmock_Impl>();                \
+    }                                                                         \
+   private:                                                                   \
+    class gmock_Impl {                                                        \
+     public:                                                                  \
+      template <typename function_type, typename return_type,                 \
+                typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>        \
+      return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \
+    };                                                                        \
+  };                                                                          \
+  inline name##Action name() GTEST_MUST_USE_RESULT_;                          \
+  inline name##Action name() { return name##Action(); }                       \
+  template <typename function_type, typename return_type, typename args_type, \
+            GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>                                \
+  return_type name##Action::gmock_Impl::gmock_PerformImpl(                    \
+      GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP, (__VA_ARGS__))
+
+#define ACTION_P2(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP2, (__VA_ARGS__))
+
+#define ACTION_P3(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP3, (__VA_ARGS__))
+
+#define ACTION_P4(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP4, (__VA_ARGS__))
+
+#define ACTION_P5(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP5, (__VA_ARGS__))
+
+#define ACTION_P6(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP6, (__VA_ARGS__))
+
+#define ACTION_P7(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP7, (__VA_ARGS__))
+
+#define ACTION_P8(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP8, (__VA_ARGS__))
+
+#define ACTION_P9(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP9, (__VA_ARGS__))
+
+#define ACTION_P10(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP10, (__VA_ARGS__))
+
 }  // namespace testing
 
 #ifdef _MSC_VER
 # pragma warning(pop)
 #endif
 
-
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-cardinalities.h b/ext/googletest/googlemock/include/gmock/gmock-cardinalities.h
index 46e01e1..fc7f803 100644
--- a/ext/googletest/googlemock/include/gmock/gmock-cardinalities.h
+++ b/ext/googletest/googlemock/include/gmock/gmock-cardinalities.h
@@ -36,8 +36,8 @@
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
 
 #include <limits.h>
 #include <memory>
@@ -154,4 +154,4 @@
 
 GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
 
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-function-mocker.h b/ext/googletest/googlemock/include/gmock/gmock-function-mocker.h
index cc1535c..0fc6f6f 100644
--- a/ext/googletest/googlemock/include/gmock/gmock-function-mocker.h
+++ b/ext/googletest/googlemock/include/gmock/gmock-function-mocker.h
@@ -33,12 +33,47 @@
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_  // NOLINT
-#define THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_  // NOLINT
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_  // NOLINT
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_  // NOLINT
 
-#include "gmock/gmock-generated-function-mockers.h"  // NOLINT
+#include <type_traits>  // IWYU pragma: keep
+#include <utility>      // IWYU pragma: keep
+
+#include "gmock/gmock-spec-builders.h"
+#include "gmock/internal/gmock-internal-utils.h"
 #include "gmock/internal/gmock-pp.h"
 
+namespace testing {
+namespace internal {
+template <typename T>
+using identity_t = T;
+
+template <typename Pattern>
+struct ThisRefAdjuster {
+  template <typename T>
+  using AdjustT = typename std::conditional<
+      std::is_const<typename std::remove_reference<Pattern>::type>::value,
+      typename std::conditional<std::is_lvalue_reference<Pattern>::value,
+                                const T&, const T&&>::type,
+      typename std::conditional<std::is_lvalue_reference<Pattern>::value, T&,
+                                T&&>::type>::type;
+
+  template <typename MockType>
+  static AdjustT<MockType> Adjust(const MockType& mock) {
+    return static_cast<AdjustT<MockType>>(const_cast<MockType&>(mock));
+  }
+};
+
+}  // namespace internal
+
+// The style guide prohibits "using" statements in a namespace scope
+// inside a header file.  However, the FunctionMocker class template
+// is meant to be defined in the ::testing namespace.  The following
+// line is just a trick for working around a bug in MSVC 8.0, which
+// cannot handle it if we define FunctionMocker in ::testing.
+using internal::FunctionMocker;
+}  // namespace testing
+
 #define MOCK_METHOD(...) \
   GMOCK_PP_VARIADIC_CALL(GMOCK_INTERNAL_MOCK_METHOD_ARG_, __VA_ARGS__)
 
@@ -60,7 +95,8 @@
   GMOCK_INTERNAL_MOCK_METHOD_IMPL(                                            \
       GMOCK_PP_NARG0 _Args, _MethodName, GMOCK_INTERNAL_HAS_CONST(_Spec),     \
       GMOCK_INTERNAL_HAS_OVERRIDE(_Spec), GMOCK_INTERNAL_HAS_FINAL(_Spec),    \
-      GMOCK_INTERNAL_HAS_NOEXCEPT(_Spec), GMOCK_INTERNAL_GET_CALLTYPE(_Spec), \
+      GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Spec),                                \
+      GMOCK_INTERNAL_GET_CALLTYPE(_Spec), GMOCK_INTERNAL_GET_REF_SPEC(_Spec), \
       (GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)))
 
 #define GMOCK_INTERNAL_MOCK_METHOD_ARG_5(...) \
@@ -94,21 +130,20 @@
       ::testing::tuple_size<typename ::testing::internal::Function<    \
               __VA_ARGS__>::ArgumentTuple>::value == _N,               \
       "This method does not take " GMOCK_PP_STRINGIZE(                 \
-          _N) " arguments. Parenthesize all types with unproctected commas.")
+          _N) " arguments. Parenthesize all types with unprotected commas.")
 
 #define GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \
   GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT, ~, _Spec)
 
 #define GMOCK_INTERNAL_MOCK_METHOD_IMPL(_N, _MethodName, _Constness,           \
-                                        _Override, _Final, _Noexcept,          \
-                                        _CallType, _Signature)                 \
+                                        _Override, _Final, _NoexceptSpec,      \
+                                        _CallType, _RefSpec, _Signature)       \
   typename ::testing::internal::Function<GMOCK_PP_REMOVE_PARENS(               \
       _Signature)>::Result                                                     \
   GMOCK_INTERNAL_EXPAND(_CallType)                                             \
       _MethodName(GMOCK_PP_REPEAT(GMOCK_INTERNAL_PARAMETER, _Signature, _N))   \
-          GMOCK_PP_IF(_Constness, const, ) GMOCK_PP_IF(_Noexcept, noexcept, )  \
-              GMOCK_PP_IF(_Override, override, )                               \
-                  GMOCK_PP_IF(_Final, final, ) {                               \
+          GMOCK_PP_IF(_Constness, const, ) _RefSpec _NoexceptSpec              \
+          GMOCK_PP_IF(_Override, override, ) GMOCK_PP_IF(_Final, final, ) {    \
     GMOCK_MOCKER_(_N, _Constness, _MethodName)                                 \
         .SetOwnerAndName(this, #_MethodName);                                  \
     return GMOCK_MOCKER_(_N, _Constness, _MethodName)                          \
@@ -116,7 +151,7 @@
   }                                                                            \
   ::testing::MockSpec<GMOCK_PP_REMOVE_PARENS(_Signature)> gmock_##_MethodName( \
       GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_PARAMETER, _Signature, _N))       \
-      GMOCK_PP_IF(_Constness, const, ) {                                       \
+      GMOCK_PP_IF(_Constness, const, ) _RefSpec {                              \
     GMOCK_MOCKER_(_N, _Constness, _MethodName).RegisterOwner(this);            \
     return GMOCK_MOCKER_(_N, _Constness, _MethodName)                          \
         .With(GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_ARGUMENT, , _N));         \
@@ -124,11 +159,10 @@
   ::testing::MockSpec<GMOCK_PP_REMOVE_PARENS(_Signature)> gmock_##_MethodName( \
       const ::testing::internal::WithoutMatchers&,                             \
       GMOCK_PP_IF(_Constness, const, )::testing::internal::Function<           \
-          GMOCK_PP_REMOVE_PARENS(_Signature)>*)                                \
-      const GMOCK_PP_IF(_Noexcept, noexcept, ) {                               \
-    return GMOCK_PP_CAT(::testing::internal::AdjustConstness_,                 \
-                        GMOCK_PP_IF(_Constness, const, ))(this)                \
-        ->gmock_##_MethodName(GMOCK_PP_REPEAT(                                 \
+          GMOCK_PP_REMOVE_PARENS(_Signature)>*) const _RefSpec _NoexceptSpec { \
+    return ::testing::internal::ThisRefAdjuster<GMOCK_PP_IF(                   \
+        _Constness, const, ) int _RefSpec>::Adjust(*this)                      \
+        .gmock_##_MethodName(GMOCK_PP_REPEAT(                                  \
             GMOCK_INTERNAL_A_MATCHER_ARGUMENT, _Signature, _N));               \
   }                                                                            \
   mutable ::testing::FunctionMocker<GMOCK_PP_REMOVE_PARENS(_Signature)>        \
@@ -147,9 +181,20 @@
 #define GMOCK_INTERNAL_HAS_FINAL(_Tuple) \
   GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_FINAL, ~, _Tuple))
 
-#define GMOCK_INTERNAL_HAS_NOEXCEPT(_Tuple) \
-  GMOCK_PP_HAS_COMMA(                       \
-      GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_NOEXCEPT, ~, _Tuple))
+#define GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Tuple) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT, ~, _Tuple)
+
+#define GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT(_i, _, _elem)          \
+  GMOCK_PP_IF(                                                          \
+      GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)), \
+      _elem, )
+
+#define GMOCK_INTERNAL_GET_REF_SPEC(_Tuple) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_REF_SPEC_IF_REF, ~, _Tuple)
+
+#define GMOCK_INTERNAL_REF_SPEC_IF_REF(_i, _, _elem)                       \
+  GMOCK_PP_IF(GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)), \
+              GMOCK_PP_CAT(GMOCK_INTERNAL_UNPACK_, _elem), )
 
 #define GMOCK_INTERNAL_GET_CALLTYPE(_Tuple) \
   GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_CALLTYPE_IMPL, ~, _Tuple)
@@ -160,6 +205,7 @@
        GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem)) + \
        GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem)) +    \
        GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)) + \
+       GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)) +      \
        GMOCK_INTERNAL_IS_CALLTYPE(_elem)) == 1,                           \
       GMOCK_PP_STRINGIZE(                                                 \
           _elem) " cannot be recognized as a valid specification modifier.");
@@ -180,12 +226,18 @@
 
 #define GMOCK_INTERNAL_DETECT_FINAL_I_final ,
 
-// TODO(iserna): Maybe noexcept should accept an argument here as well.
 #define GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem) \
   GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_NOEXCEPT_I_, _elem)
 
 #define GMOCK_INTERNAL_DETECT_NOEXCEPT_I_noexcept ,
 
+#define GMOCK_INTERNAL_DETECT_REF(_i, _, _elem) \
+  GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_REF_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_REF_I_ref ,
+
+#define GMOCK_INTERNAL_UNPACK_ref(x) x
+
 #define GMOCK_INTERNAL_GET_CALLTYPE_IMPL(_i, _, _elem)           \
   GMOCK_PP_IF(GMOCK_INTERNAL_IS_CALLTYPE(_elem),                 \
               GMOCK_INTERNAL_GET_VALUE_CALLTYPE, GMOCK_PP_EMPTY) \
@@ -203,14 +255,28 @@
   GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I(          \
       GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg))
 #define GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I(_arg) \
-  GMOCK_PP_CAT(GMOCK_PP_IDENTITY, _arg)
+  GMOCK_PP_IDENTITY _arg
 
 #define GMOCK_INTERNAL_IS_CALLTYPE_HELPER_Calltype
 
-#define GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)                         \
-  GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_Ret), GMOCK_PP_REMOVE_PARENS, \
-              GMOCK_PP_IDENTITY)                                      \
-  (_Ret)(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_TYPE, _, _Args))
+// Note: The use of `identity_t` here allows _Ret to represent return types that
+// would normally need to be specified in a different way. For example, a method
+// returning a function pointer must be written as
+//
+// fn_ptr_return_t (*method(method_args_t...))(fn_ptr_args_t...)
+//
+// But we only support placing the return type at the beginning. To handle this,
+// we wrap all calls in identity_t, so that a declaration will be expanded to
+//
+// identity_t<fn_ptr_return_t (*)(fn_ptr_args_t...)> method(method_args_t...)
+//
+// This allows us to work around the syntactic oddities of function/method
+// types.
+#define GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)                                 \
+  ::testing::internal::identity_t<GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_Ret), \
+                                              GMOCK_PP_REMOVE_PARENS,         \
+                                              GMOCK_PP_IDENTITY)(_Ret)>(      \
+      GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_TYPE, _, _Args))
 
 #define GMOCK_INTERNAL_GET_TYPE(_i, _, _elem)                          \
   GMOCK_PP_COMMA_IF(_i)                                                \
@@ -218,36 +284,196 @@
               GMOCK_PP_IDENTITY)                                       \
   (_elem)
 
-#define GMOCK_INTERNAL_PARAMETER(_i, _Signature, _)        \
-  GMOCK_PP_COMMA_IF(_i)                                    \
-  GMOCK_INTERNAL_ARG_O(typename, GMOCK_PP_INC(_i),         \
-                       GMOCK_PP_REMOVE_PARENS(_Signature)) \
+#define GMOCK_INTERNAL_PARAMETER(_i, _Signature, _)            \
+  GMOCK_PP_COMMA_IF(_i)                                        \
+  GMOCK_INTERNAL_ARG_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \
   gmock_a##_i
 
-#define GMOCK_INTERNAL_FORWARD_ARG(_i, _Signature, _)                       \
-  GMOCK_PP_COMMA_IF(_i)                                                     \
-  ::std::forward<GMOCK_INTERNAL_ARG_O(typename, GMOCK_PP_INC(_i),           \
-                                      GMOCK_PP_REMOVE_PARENS(_Signature))>( \
-      gmock_a##_i)
+#define GMOCK_INTERNAL_FORWARD_ARG(_i, _Signature, _) \
+  GMOCK_PP_COMMA_IF(_i)                               \
+  ::std::forward<GMOCK_INTERNAL_ARG_O(                \
+      _i, GMOCK_PP_REMOVE_PARENS(_Signature))>(gmock_a##_i)
 
-#define GMOCK_INTERNAL_MATCHER_PARAMETER(_i, _Signature, _)    \
-  GMOCK_PP_COMMA_IF(_i)                                        \
-  GMOCK_INTERNAL_MATCHER_O(typename, GMOCK_PP_INC(_i),         \
-                           GMOCK_PP_REMOVE_PARENS(_Signature)) \
+#define GMOCK_INTERNAL_MATCHER_PARAMETER(_i, _Signature, _)        \
+  GMOCK_PP_COMMA_IF(_i)                                            \
+  GMOCK_INTERNAL_MATCHER_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \
   gmock_a##_i
 
 #define GMOCK_INTERNAL_MATCHER_ARGUMENT(_i, _1, _2) \
   GMOCK_PP_COMMA_IF(_i)                             \
   gmock_a##_i
 
-#define GMOCK_INTERNAL_A_MATCHER_ARGUMENT(_i, _Signature, _)    \
-  GMOCK_PP_COMMA_IF(_i)                                         \
-  ::testing::A<GMOCK_INTERNAL_ARG_O(typename, GMOCK_PP_INC(_i), \
-                                    GMOCK_PP_REMOVE_PARENS(_Signature))>()
+#define GMOCK_INTERNAL_A_MATCHER_ARGUMENT(_i, _Signature, _) \
+  GMOCK_PP_COMMA_IF(_i)                                      \
+  ::testing::A<GMOCK_INTERNAL_ARG_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature))>()
 
-#define GMOCK_INTERNAL_ARG_O(_tn, _i, ...) GMOCK_ARG_(_tn, _i, __VA_ARGS__)
+#define GMOCK_INTERNAL_ARG_O(_i, ...) \
+  typename ::testing::internal::Function<__VA_ARGS__>::template Arg<_i>::type
 
-#define GMOCK_INTERNAL_MATCHER_O(_tn, _i, ...) \
-  GMOCK_MATCHER_(_tn, _i, __VA_ARGS__)
+#define GMOCK_INTERNAL_MATCHER_O(_i, ...)                          \
+  const ::testing::Matcher<typename ::testing::internal::Function< \
+      __VA_ARGS__>::template Arg<_i>::type>&
 
-#endif  // THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_
+#define MOCK_METHOD0(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 0, __VA_ARGS__)
+#define MOCK_METHOD1(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 1, __VA_ARGS__)
+#define MOCK_METHOD2(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 2, __VA_ARGS__)
+#define MOCK_METHOD3(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 3, __VA_ARGS__)
+#define MOCK_METHOD4(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 4, __VA_ARGS__)
+#define MOCK_METHOD5(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 5, __VA_ARGS__)
+#define MOCK_METHOD6(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 6, __VA_ARGS__)
+#define MOCK_METHOD7(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 7, __VA_ARGS__)
+#define MOCK_METHOD8(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 8, __VA_ARGS__)
+#define MOCK_METHOD9(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 9, __VA_ARGS__)
+#define MOCK_METHOD10(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, , m, 10, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 0, __VA_ARGS__)
+#define MOCK_CONST_METHOD1(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 1, __VA_ARGS__)
+#define MOCK_CONST_METHOD2(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 2, __VA_ARGS__)
+#define MOCK_CONST_METHOD3(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 3, __VA_ARGS__)
+#define MOCK_CONST_METHOD4(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 4, __VA_ARGS__)
+#define MOCK_CONST_METHOD5(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 5, __VA_ARGS__)
+#define MOCK_CONST_METHOD6(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 6, __VA_ARGS__)
+#define MOCK_CONST_METHOD7(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 7, __VA_ARGS__)
+#define MOCK_CONST_METHOD8(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 8, __VA_ARGS__)
+#define MOCK_CONST_METHOD9(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 9, __VA_ARGS__)
+#define MOCK_CONST_METHOD10(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 10, __VA_ARGS__)
+
+#define MOCK_METHOD0_T(m, ...) MOCK_METHOD0(m, __VA_ARGS__)
+#define MOCK_METHOD1_T(m, ...) MOCK_METHOD1(m, __VA_ARGS__)
+#define MOCK_METHOD2_T(m, ...) MOCK_METHOD2(m, __VA_ARGS__)
+#define MOCK_METHOD3_T(m, ...) MOCK_METHOD3(m, __VA_ARGS__)
+#define MOCK_METHOD4_T(m, ...) MOCK_METHOD4(m, __VA_ARGS__)
+#define MOCK_METHOD5_T(m, ...) MOCK_METHOD5(m, __VA_ARGS__)
+#define MOCK_METHOD6_T(m, ...) MOCK_METHOD6(m, __VA_ARGS__)
+#define MOCK_METHOD7_T(m, ...) MOCK_METHOD7(m, __VA_ARGS__)
+#define MOCK_METHOD8_T(m, ...) MOCK_METHOD8(m, __VA_ARGS__)
+#define MOCK_METHOD9_T(m, ...) MOCK_METHOD9(m, __VA_ARGS__)
+#define MOCK_METHOD10_T(m, ...) MOCK_METHOD10(m, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0_T(m, ...) MOCK_CONST_METHOD0(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD1_T(m, ...) MOCK_CONST_METHOD1(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2_T(m, ...) MOCK_CONST_METHOD2(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD3_T(m, ...) MOCK_CONST_METHOD3(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD4_T(m, ...) MOCK_CONST_METHOD4(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD5_T(m, ...) MOCK_CONST_METHOD5(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD6_T(m, ...) MOCK_CONST_METHOD6(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD7_T(m, ...) MOCK_CONST_METHOD7(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD8_T(m, ...) MOCK_CONST_METHOD8(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD9_T(m, ...) MOCK_CONST_METHOD9(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD10_T(m, ...) MOCK_CONST_METHOD10(m, __VA_ARGS__)
+
+#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 0, __VA_ARGS__)
+#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 1, __VA_ARGS__)
+#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 2, __VA_ARGS__)
+#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 3, __VA_ARGS__)
+#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 4, __VA_ARGS__)
+#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 5, __VA_ARGS__)
+#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 6, __VA_ARGS__)
+#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 7, __VA_ARGS__)
+#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 8, __VA_ARGS__)
+#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 9, __VA_ARGS__)
+#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 10, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 0, __VA_ARGS__)
+#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 1, __VA_ARGS__)
+#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 2, __VA_ARGS__)
+#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 3, __VA_ARGS__)
+#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 4, __VA_ARGS__)
+#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 5, __VA_ARGS__)
+#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 6, __VA_ARGS__)
+#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 7, __VA_ARGS__)
+#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 8, __VA_ARGS__)
+#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 9, __VA_ARGS__)
+#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 10, __VA_ARGS__)
+
+#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHODN(constness, ct, Method, args_num, ...) \
+  GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE(                                  \
+      args_num, ::testing::internal::identity_t<__VA_ARGS__>);            \
+  GMOCK_INTERNAL_MOCK_METHOD_IMPL(                                        \
+      args_num, Method, GMOCK_PP_NARG0(constness), 0, 0, , ct, ,          \
+      (::testing::internal::identity_t<__VA_ARGS__>))
+
+#define GMOCK_MOCKER_(arity, constness, Method) \
+  GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__)
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-generated-actions.h b/ext/googletest/googlemock/include/gmock/gmock-generated-actions.h
deleted file mode 100644
index 981af78..0000000
--- a/ext/googletest/googlemock/include/gmock/gmock-generated-actions.h
+++ /dev/null
@@ -1,1884 +0,0 @@
-// This file was GENERATED by command:
-//     pump.py gmock-generated-actions.h.pump
-// DO NOT EDIT BY HAND!!!
-
-// Copyright 2007, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file implements some commonly used variadic actions.
-
-// GOOGLETEST_CM0002 DO NOT DELETE
-
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
-
-#include <memory>
-#include <utility>
-
-#include "gmock/gmock-actions.h"
-#include "gmock/internal/gmock-port.h"
-
-namespace testing {
-namespace internal {
-
-// A macro from the ACTION* family (defined later in this file)
-// defines an action that can be used in a mock function.  Typically,
-// these actions only care about a subset of the arguments of the mock
-// function.  For example, if such an action only uses the second
-// argument, it can be used in any mock function that takes >= 2
-// arguments where the type of the second argument is compatible.
-//
-// Therefore, the action implementation must be prepared to take more
-// arguments than it needs.  The ExcessiveArg type is used to
-// represent those excessive arguments.  In order to keep the compiler
-// error messages tractable, we define it in the testing namespace
-// instead of testing::internal.  However, this is an INTERNAL TYPE
-// and subject to change without notice, so a user MUST NOT USE THIS
-// TYPE DIRECTLY.
-struct ExcessiveArg {};
-
-// A helper class needed for implementing the ACTION* macros.
-template <typename Result, class Impl>
-class ActionHelper {
- public:
-  static Result Perform(Impl* impl, const ::std::tuple<>& args) {
-    return impl->template gmock_PerformImpl<>(args, ExcessiveArg(),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
-        ExcessiveArg());
-  }
-
-  template <typename A0>
-  static Result Perform(Impl* impl, const ::std::tuple<A0>& args) {
-    return impl->template gmock_PerformImpl<A0>(args, std::get<0>(args),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
-        ExcessiveArg());
-  }
-
-  template <typename A0, typename A1>
-  static Result Perform(Impl* impl, const ::std::tuple<A0, A1>& args) {
-    return impl->template gmock_PerformImpl<A0, A1>(args, std::get<0>(args),
-        std::get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
-        ExcessiveArg());
-  }
-
-  template <typename A0, typename A1, typename A2>
-  static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2>& args) {
-    return impl->template gmock_PerformImpl<A0, A1, A2>(args,
-        std::get<0>(args), std::get<1>(args), std::get<2>(args),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
-  }
-
-  template <typename A0, typename A1, typename A2, typename A3>
-  static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3>& args) {
-    return impl->template gmock_PerformImpl<A0, A1, A2, A3>(args,
-        std::get<0>(args), std::get<1>(args), std::get<2>(args),
-        std::get<3>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
-  }
-
-  template <typename A0, typename A1, typename A2, typename A3, typename A4>
-  static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3,
-      A4>& args) {
-    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4>(args,
-        std::get<0>(args), std::get<1>(args), std::get<2>(args),
-        std::get<3>(args), std::get<4>(args), ExcessiveArg(), ExcessiveArg(),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
-  }
-
-  template <typename A0, typename A1, typename A2, typename A3, typename A4,
-      typename A5>
-  static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4,
-      A5>& args) {
-    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5>(args,
-        std::get<0>(args), std::get<1>(args), std::get<2>(args),
-        std::get<3>(args), std::get<4>(args), std::get<5>(args),
-        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
-  }
-
-  template <typename A0, typename A1, typename A2, typename A3, typename A4,
-      typename A5, typename A6>
-  static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4, A5,
-      A6>& args) {
-    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6>(args,
-        std::get<0>(args), std::get<1>(args), std::get<2>(args),
-        std::get<3>(args), std::get<4>(args), std::get<5>(args),
-        std::get<6>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg());
-  }
-
-  template <typename A0, typename A1, typename A2, typename A3, typename A4,
-      typename A5, typename A6, typename A7>
-  static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4, A5,
-      A6, A7>& args) {
-    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6,
-        A7>(args, std::get<0>(args), std::get<1>(args), std::get<2>(args),
-        std::get<3>(args), std::get<4>(args), std::get<5>(args),
-        std::get<6>(args), std::get<7>(args), ExcessiveArg(), ExcessiveArg());
-  }
-
-  template <typename A0, typename A1, typename A2, typename A3, typename A4,
-      typename A5, typename A6, typename A7, typename A8>
-  static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4, A5,
-      A6, A7, A8>& args) {
-    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6, A7,
-        A8>(args, std::get<0>(args), std::get<1>(args), std::get<2>(args),
-        std::get<3>(args), std::get<4>(args), std::get<5>(args),
-        std::get<6>(args), std::get<7>(args), std::get<8>(args),
-        ExcessiveArg());
-  }
-
-  template <typename A0, typename A1, typename A2, typename A3, typename A4,
-      typename A5, typename A6, typename A7, typename A8, typename A9>
-  static Result Perform(Impl* impl, const ::std::tuple<A0, A1, A2, A3, A4, A5,
-      A6, A7, A8, A9>& args) {
-    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6, A7, A8,
-        A9>(args, std::get<0>(args), std::get<1>(args), std::get<2>(args),
-        std::get<3>(args), std::get<4>(args), std::get<5>(args),
-        std::get<6>(args), std::get<7>(args), std::get<8>(args),
-        std::get<9>(args));
-  }
-};
-
-}  // namespace internal
-}  // namespace testing
-
-// The ACTION* family of macros can be used in a namespace scope to
-// define custom actions easily.  The syntax:
-//
-//   ACTION(name) { statements; }
-//
-// will define an action with the given name that executes the
-// statements.  The value returned by the statements will be used as
-// the return value of the action.  Inside the statements, you can
-// refer to the K-th (0-based) argument of the mock function by
-// 'argK', and refer to its type by 'argK_type'.  For example:
-//
-//   ACTION(IncrementArg1) {
-//     arg1_type temp = arg1;
-//     return ++(*temp);
-//   }
-//
-// allows you to write
-//
-//   ...WillOnce(IncrementArg1());
-//
-// You can also refer to the entire argument tuple and its type by
-// 'args' and 'args_type', and refer to the mock function type and its
-// return type by 'function_type' and 'return_type'.
-//
-// Note that you don't need to specify the types of the mock function
-// arguments.  However rest assured that your code is still type-safe:
-// you'll get a compiler error if *arg1 doesn't support the ++
-// operator, or if the type of ++(*arg1) isn't compatible with the
-// mock function's return type, for example.
-//
-// Sometimes you'll want to parameterize the action.   For that you can use
-// another macro:
-//
-//   ACTION_P(name, param_name) { statements; }
-//
-// For example:
-//
-//   ACTION_P(Add, n) { return arg0 + n; }
-//
-// will allow you to write:
-//
-//   ...WillOnce(Add(5));
-//
-// Note that you don't need to provide the type of the parameter
-// either.  If you need to reference the type of a parameter named
-// 'foo', you can write 'foo_type'.  For example, in the body of
-// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type
-// of 'n'.
-//
-// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support
-// multi-parameter actions.
-//
-// For the purpose of typing, you can view
-//
-//   ACTION_Pk(Foo, p1, ..., pk) { ... }
-//
-// as shorthand for
-//
-//   template <typename p1_type, ..., typename pk_type>
-//   FooActionPk<p1_type, ..., pk_type> Foo(p1_type p1, ..., pk_type pk) { ... }
-//
-// In particular, you can provide the template type arguments
-// explicitly when invoking Foo(), as in Foo<long, bool>(5, false);
-// although usually you can rely on the compiler to infer the types
-// for you automatically.  You can assign the result of expression
-// Foo(p1, ..., pk) to a variable of type FooActionPk<p1_type, ...,
-// pk_type>.  This can be useful when composing actions.
-//
-// You can also overload actions with different numbers of parameters:
-//
-//   ACTION_P(Plus, a) { ... }
-//   ACTION_P2(Plus, a, b) { ... }
-//
-// While it's tempting to always use the ACTION* macros when defining
-// a new action, you should also consider implementing ActionInterface
-// or using MakePolymorphicAction() instead, especially if you need to
-// use the action a lot.  While these approaches require more work,
-// they give you more control on the types of the mock function
-// arguments and the action parameters, which in general leads to
-// better compiler error messages that pay off in the long run.  They
-// also allow overloading actions based on parameter types (as opposed
-// to just based on the number of parameters).
-//
-// CAVEAT:
-//
-// ACTION*() can only be used in a namespace scope as templates cannot be
-// declared inside of a local class.
-// Users can, however, define any local functors (e.g. a lambda) that
-// can be used as actions.
-//
-// MORE INFORMATION:
-//
-// To learn more about using these macros, please search for 'ACTION' on
-// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md
-
-// An internal macro needed for implementing ACTION*().
-#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\
-    const args_type& args GTEST_ATTRIBUTE_UNUSED_, \
-    const arg0_type& arg0 GTEST_ATTRIBUTE_UNUSED_, \
-    const arg1_type& arg1 GTEST_ATTRIBUTE_UNUSED_, \
-    const arg2_type& arg2 GTEST_ATTRIBUTE_UNUSED_, \
-    const arg3_type& arg3 GTEST_ATTRIBUTE_UNUSED_, \
-    const arg4_type& arg4 GTEST_ATTRIBUTE_UNUSED_, \
-    const arg5_type& arg5 GTEST_ATTRIBUTE_UNUSED_, \
-    const arg6_type& arg6 GTEST_ATTRIBUTE_UNUSED_, \
-    const arg7_type& arg7 GTEST_ATTRIBUTE_UNUSED_, \
-    const arg8_type& arg8 GTEST_ATTRIBUTE_UNUSED_, \
-    const arg9_type& arg9 GTEST_ATTRIBUTE_UNUSED_
-
-// Sometimes you want to give an action explicit template parameters
-// that cannot be inferred from its value parameters.  ACTION() and
-// ACTION_P*() don't support that.  ACTION_TEMPLATE() remedies that
-// and can be viewed as an extension to ACTION() and ACTION_P*().
-//
-// The syntax:
-//
-//   ACTION_TEMPLATE(ActionName,
-//                   HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m),
-//                   AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; }
-//
-// defines an action template that takes m explicit template
-// parameters and n value parameters.  name_i is the name of the i-th
-// template parameter, and kind_i specifies whether it's a typename,
-// an integral constant, or a template.  p_i is the name of the i-th
-// value parameter.
-//
-// Example:
-//
-//   // DuplicateArg<k, T>(output) converts the k-th argument of the mock
-//   // function to type T and copies it to *output.
-//   ACTION_TEMPLATE(DuplicateArg,
-//                   HAS_2_TEMPLATE_PARAMS(int, k, typename, T),
-//                   AND_1_VALUE_PARAMS(output)) {
-//     *output = T(::std::get<k>(args));
-//   }
-//   ...
-//     int n;
-//     EXPECT_CALL(mock, Foo(_, _))
-//         .WillOnce(DuplicateArg<1, unsigned char>(&n));
-//
-// To create an instance of an action template, write:
-//
-//   ActionName<t1, ..., t_m>(v1, ..., v_n)
-//
-// where the ts are the template arguments and the vs are the value
-// arguments.  The value argument types are inferred by the compiler.
-// If you want to explicitly specify the value argument types, you can
-// provide additional template arguments:
-//
-//   ActionName<t1, ..., t_m, u1, ..., u_k>(v1, ..., v_n)
-//
-// where u_i is the desired type of v_i.
-//
-// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the
-// number of value parameters, but not on the number of template
-// parameters.  Without the restriction, the meaning of the following
-// is unclear:
-//
-//   OverloadedAction<int, bool>(x);
-//
-// Are we using a single-template-parameter action where 'bool' refers
-// to the type of x, or are we using a two-template-parameter action
-// where the compiler is asked to infer the type of x?
-//
-// Implementation notes:
-//
-// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and
-// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for
-// implementing ACTION_TEMPLATE.  The main trick we use is to create
-// new macro invocations when expanding a macro.  For example, we have
-//
-//   #define ACTION_TEMPLATE(name, template_params, value_params)
-//       ... GMOCK_INTERNAL_DECL_##template_params ...
-//
-// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...)
-// to expand to
-//
-//       ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ...
-//
-// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the
-// preprocessor will continue to expand it to
-//
-//       ... typename T ...
-//
-// This technique conforms to the C++ standard and is portable.  It
-// allows us to implement action templates using O(N) code, where N is
-// the maximum number of template/value parameters supported.  Without
-// using it, we'd have to devote O(N^2) amount of code to implement all
-// combinations of m and n.
-
-// Declares the template parameters.
-#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0
-#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
-    name1) kind0 name0, kind1 name1
-#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2) kind0 name0, kind1 name1, kind2 name2
-#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \
-    kind3 name3
-#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \
-    kind2 name2, kind3 name3, kind4 name4
-#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \
-    kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5
-#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
-    name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \
-    kind5 name5, kind6 name6
-#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
-    kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \
-    kind4 name4, kind5 name5, kind6 name6, kind7 name7
-#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
-    kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \
-    kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \
-    kind8 name8
-#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
-    name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
-    name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \
-    kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \
-    kind6 name6, kind7 name7, kind8 name8, kind9 name9
-
-// Lists the template parameters.
-#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0
-#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
-    name1) name0, name1
-#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2) name0, name1, name2
-#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3) name0, name1, name2, name3
-#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \
-    name4
-#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \
-    name2, name3, name4, name5
-#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
-    name6) name0, name1, name2, name3, name4, name5, name6
-#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
-    kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7
-#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
-    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
-    kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \
-    name6, name7, name8
-#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
-    name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
-    name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \
-    name3, name4, name5, name6, name7, name8, name9
-
-// Declares the types of value parameters.
-#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS()
-#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type
-#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \
-    typename p0##_type, typename p1##_type
-#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \
-    typename p0##_type, typename p1##_type, typename p2##_type
-#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
-    typename p0##_type, typename p1##_type, typename p2##_type, \
-    typename p3##_type
-#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
-    typename p0##_type, typename p1##_type, typename p2##_type, \
-    typename p3##_type, typename p4##_type
-#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
-    typename p0##_type, typename p1##_type, typename p2##_type, \
-    typename p3##_type, typename p4##_type, typename p5##_type
-#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6) , typename p0##_type, typename p1##_type, typename p2##_type, \
-    typename p3##_type, typename p4##_type, typename p5##_type, \
-    typename p6##_type
-#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \
-    typename p3##_type, typename p4##_type, typename p5##_type, \
-    typename p6##_type, typename p7##_type
-#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \
-    typename p3##_type, typename p4##_type, typename p5##_type, \
-    typename p6##_type, typename p7##_type, typename p8##_type
-#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \
-    typename p2##_type, typename p3##_type, typename p4##_type, \
-    typename p5##_type, typename p6##_type, typename p7##_type, \
-    typename p8##_type, typename p9##_type
-
-// Initializes the value parameters.
-#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\
-    ()
-#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\
-    (p0##_type gmock_p0) : p0(::std::move(gmock_p0))
-#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\
-    (p0##_type gmock_p0, p1##_type gmock_p1) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1))
-#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\
-    (p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2))
-#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\
-    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-        p3##_type gmock_p3) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3))
-#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\
-    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-        p3##_type gmock_p3, p4##_type gmock_p4) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4))
-#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\
-    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-        p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5))
-#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\
-    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-        p6##_type gmock_p6) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6))
-#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\
-    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-        p6##_type gmock_p6, p7##_type gmock_p7) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
-        p7(::std::move(gmock_p7))
-#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8)\
-    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-        p6##_type gmock_p6, p7##_type gmock_p7, \
-        p8##_type gmock_p8) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
-        p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8))
-#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8, p9)\
-    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-        p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
-        p9##_type gmock_p9) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
-        p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)), \
-        p9(::std::move(gmock_p9))
-
-// Declares the fields for storing the value parameters.
-#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS()
-#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0;
-#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \
-    p1##_type p1;
-#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \
-    p1##_type p1; p2##_type p2;
-#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \
-    p1##_type p1; p2##_type p2; p3##_type p3;
-#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
-    p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4;
-#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
-    p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
-    p5##_type p5;
-#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
-    p5##_type p5; p6##_type p6;
-#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
-    p5##_type p5; p6##_type p6; p7##_type p7;
-#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
-    p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8;
-#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
-    p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \
-    p9##_type p9;
-
-// Lists the value parameters.
-#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS()
-#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0
-#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1
-#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2
-#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3
-#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \
-    p2, p3, p4
-#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \
-    p1, p2, p3, p4, p5
-#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6) p0, p1, p2, p3, p4, p5, p6
-#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7) p0, p1, p2, p3, p4, p5, p6, p7
-#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8
-#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9
-
-// Lists the value parameter types.
-#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS()
-#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type
-#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \
-    p1##_type
-#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \
-    p1##_type, p2##_type
-#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
-    p0##_type, p1##_type, p2##_type, p3##_type
-#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
-    p0##_type, p1##_type, p2##_type, p3##_type, p4##_type
-#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
-    p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type
-#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \
-    p6##_type
-#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-    p5##_type, p6##_type, p7##_type
-#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-    p5##_type, p6##_type, p7##_type, p8##_type
-#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-    p5##_type, p6##_type, p7##_type, p8##_type, p9##_type
-
-// Declares the value parameters.
-#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS()
-#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0
-#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \
-    p1##_type p1
-#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \
-    p1##_type p1, p2##_type p2
-#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \
-    p1##_type p1, p2##_type p2, p3##_type p3
-#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
-    p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4
-#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
-    p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
-    p5##_type p5
-#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
-    p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
-    p5##_type p5, p6##_type p6
-#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
-    p5##_type p5, p6##_type p6, p7##_type p7
-#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
-    p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8
-#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
-    p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
-    p9##_type p9
-
-// The suffix of the class template implementing the action template.
-#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS()
-#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P
-#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2
-#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3
-#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4
-#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5
-#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6
-#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7
-#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7) P8
-#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8) P9
-#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
-    p7, p8, p9) P10
-
-// The name of the class template implementing the action template.
-#define GMOCK_ACTION_CLASS_(name, value_params)\
-    GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params)
-
-#define ACTION_TEMPLATE(name, template_params, value_params)\
-  template <GMOCK_INTERNAL_DECL_##template_params\
-            GMOCK_INTERNAL_DECL_TYPE_##value_params>\
-  class GMOCK_ACTION_CLASS_(name, value_params) {\
-   public:\
-    explicit GMOCK_ACTION_CLASS_(name, value_params)\
-        GMOCK_INTERNAL_INIT_##value_params {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      GMOCK_INTERNAL_DEFN_##value_params\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(\
-          new gmock_Impl<F>(GMOCK_INTERNAL_LIST_##value_params));\
-    }\
-    GMOCK_INTERNAL_DEFN_##value_params\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\
-  };\
-  template <GMOCK_INTERNAL_DECL_##template_params\
-            GMOCK_INTERNAL_DECL_TYPE_##value_params>\
-  inline GMOCK_ACTION_CLASS_(name, value_params)<\
-      GMOCK_INTERNAL_LIST_##template_params\
-      GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\
-          GMOCK_INTERNAL_DECL_##value_params) {\
-    return GMOCK_ACTION_CLASS_(name, value_params)<\
-        GMOCK_INTERNAL_LIST_##template_params\
-        GMOCK_INTERNAL_LIST_TYPE_##value_params>(\
-            GMOCK_INTERNAL_LIST_##value_params);\
-  }\
-  template <GMOCK_INTERNAL_DECL_##template_params\
-            GMOCK_INTERNAL_DECL_TYPE_##value_params>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      GMOCK_ACTION_CLASS_(name, value_params)<\
-          GMOCK_INTERNAL_LIST_##template_params\
-          GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl<F>::\
-              gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION(name)\
-  class name##Action {\
-   public:\
-    name##Action() {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl() {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>());\
-    }\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##Action);\
-  };\
-  inline name##Action name() {\
-    return name##Action();\
-  }\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##Action::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P(name, p0)\
-  template <typename p0##_type>\
-  class name##ActionP {\
-   public:\
-    explicit name##ActionP(p0##_type gmock_p0) : \
-        p0(::std::forward<p0##_type>(gmock_p0)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      explicit gmock_Impl(p0##_type gmock_p0) : \
-          p0(::std::forward<p0##_type>(gmock_p0)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0));\
-    }\
-    p0##_type p0;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP);\
-  };\
-  template <typename p0##_type>\
-  inline name##ActionP<p0##_type> name(p0##_type p0) {\
-    return name##ActionP<p0##_type>(p0);\
-  }\
-  template <typename p0##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP<p0##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P2(name, p0, p1)\
-  template <typename p0##_type, typename p1##_type>\
-  class name##ActionP2 {\
-   public:\
-    name##ActionP2(p0##_type gmock_p0, \
-        p1##_type gmock_p1) : p0(::std::forward<p0##_type>(gmock_p0)), \
-        p1(::std::forward<p1##_type>(gmock_p1)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl(p0##_type gmock_p0, \
-          p1##_type gmock_p1) : p0(::std::forward<p0##_type>(gmock_p0)), \
-          p1(::std::forward<p1##_type>(gmock_p1)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-      p1##_type p1;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1));\
-    }\
-    p0##_type p0;\
-    p1##_type p1;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP2);\
-  };\
-  template <typename p0##_type, typename p1##_type>\
-  inline name##ActionP2<p0##_type, p1##_type> name(p0##_type p0, \
-      p1##_type p1) {\
-    return name##ActionP2<p0##_type, p1##_type>(p0, p1);\
-  }\
-  template <typename p0##_type, typename p1##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP2<p0##_type, p1##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P3(name, p0, p1, p2)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type>\
-  class name##ActionP3 {\
-   public:\
-    name##ActionP3(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2) : p0(::std::forward<p0##_type>(gmock_p0)), \
-        p1(::std::forward<p1##_type>(gmock_p1)), \
-        p2(::std::forward<p2##_type>(gmock_p2)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \
-          p2##_type gmock_p2) : p0(::std::forward<p0##_type>(gmock_p0)), \
-          p1(::std::forward<p1##_type>(gmock_p1)), \
-          p2(::std::forward<p2##_type>(gmock_p2)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-      p1##_type p1;\
-      p2##_type p2;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2));\
-    }\
-    p0##_type p0;\
-    p1##_type p1;\
-    p2##_type p2;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP3);\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type>\
-  inline name##ActionP3<p0##_type, p1##_type, p2##_type> name(p0##_type p0, \
-      p1##_type p1, p2##_type p2) {\
-    return name##ActionP3<p0##_type, p1##_type, p2##_type>(p0, p1, p2);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP3<p0##_type, p1##_type, \
-          p2##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P4(name, p0, p1, p2, p3)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type>\
-  class name##ActionP4 {\
-   public:\
-    name##ActionP4(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, \
-        p3##_type gmock_p3) : p0(::std::forward<p0##_type>(gmock_p0)), \
-        p1(::std::forward<p1##_type>(gmock_p1)), \
-        p2(::std::forward<p2##_type>(gmock_p2)), \
-        p3(::std::forward<p3##_type>(gmock_p3)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3) : p0(::std::forward<p0##_type>(gmock_p0)), \
-          p1(::std::forward<p1##_type>(gmock_p1)), \
-          p2(::std::forward<p2##_type>(gmock_p2)), \
-          p3(::std::forward<p3##_type>(gmock_p3)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-      p1##_type p1;\
-      p2##_type p2;\
-      p3##_type p3;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3));\
-    }\
-    p0##_type p0;\
-    p1##_type p1;\
-    p2##_type p2;\
-    p3##_type p3;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP4);\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type>\
-  inline name##ActionP4<p0##_type, p1##_type, p2##_type, \
-      p3##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
-      p3##_type p3) {\
-    return name##ActionP4<p0##_type, p1##_type, p2##_type, p3##_type>(p0, p1, \
-        p2, p3);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP4<p0##_type, p1##_type, p2##_type, \
-          p3##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P5(name, p0, p1, p2, p3, p4)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type>\
-  class name##ActionP5 {\
-   public:\
-    name##ActionP5(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, \
-        p4##_type gmock_p4) : p0(::std::forward<p0##_type>(gmock_p0)), \
-        p1(::std::forward<p1##_type>(gmock_p1)), \
-        p2(::std::forward<p2##_type>(gmock_p2)), \
-        p3(::std::forward<p3##_type>(gmock_p3)), \
-        p4(::std::forward<p4##_type>(gmock_p4)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, \
-          p4##_type gmock_p4) : p0(::std::forward<p0##_type>(gmock_p0)), \
-          p1(::std::forward<p1##_type>(gmock_p1)), \
-          p2(::std::forward<p2##_type>(gmock_p2)), \
-          p3(::std::forward<p3##_type>(gmock_p3)), \
-          p4(::std::forward<p4##_type>(gmock_p4)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-      p1##_type p1;\
-      p2##_type p2;\
-      p3##_type p3;\
-      p4##_type p4;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4));\
-    }\
-    p0##_type p0;\
-    p1##_type p1;\
-    p2##_type p2;\
-    p3##_type p3;\
-    p4##_type p4;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP5);\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type>\
-  inline name##ActionP5<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
-      p4##_type p4) {\
-    return name##ActionP5<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type>(p0, p1, p2, p3, p4);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP5<p0##_type, p1##_type, p2##_type, p3##_type, \
-          p4##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P6(name, p0, p1, p2, p3, p4, p5)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type>\
-  class name##ActionP6 {\
-   public:\
-    name##ActionP6(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5) : p0(::std::forward<p0##_type>(gmock_p0)), \
-        p1(::std::forward<p1##_type>(gmock_p1)), \
-        p2(::std::forward<p2##_type>(gmock_p2)), \
-        p3(::std::forward<p3##_type>(gmock_p3)), \
-        p4(::std::forward<p4##_type>(gmock_p4)), \
-        p5(::std::forward<p5##_type>(gmock_p5)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, \
-          p5##_type gmock_p5) : p0(::std::forward<p0##_type>(gmock_p0)), \
-          p1(::std::forward<p1##_type>(gmock_p1)), \
-          p2(::std::forward<p2##_type>(gmock_p2)), \
-          p3(::std::forward<p3##_type>(gmock_p3)), \
-          p4(::std::forward<p4##_type>(gmock_p4)), \
-          p5(::std::forward<p5##_type>(gmock_p5)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-      p1##_type p1;\
-      p2##_type p2;\
-      p3##_type p3;\
-      p4##_type p4;\
-      p5##_type p5;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5));\
-    }\
-    p0##_type p0;\
-    p1##_type p1;\
-    p2##_type p2;\
-    p3##_type p3;\
-    p4##_type p4;\
-    p5##_type p5;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP6);\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type>\
-  inline name##ActionP6<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
-      p3##_type p3, p4##_type p4, p5##_type p5) {\
-    return name##ActionP6<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP6<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-          p5##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P7(name, p0, p1, p2, p3, p4, p5, p6)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type>\
-  class name##ActionP7 {\
-   public:\
-    name##ActionP7(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5, \
-        p6##_type gmock_p6) : p0(::std::forward<p0##_type>(gmock_p0)), \
-        p1(::std::forward<p1##_type>(gmock_p1)), \
-        p2(::std::forward<p2##_type>(gmock_p2)), \
-        p3(::std::forward<p3##_type>(gmock_p3)), \
-        p4(::std::forward<p4##_type>(gmock_p4)), \
-        p5(::std::forward<p5##_type>(gmock_p5)), \
-        p6(::std::forward<p6##_type>(gmock_p6)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6) : p0(::std::forward<p0##_type>(gmock_p0)), \
-          p1(::std::forward<p1##_type>(gmock_p1)), \
-          p2(::std::forward<p2##_type>(gmock_p2)), \
-          p3(::std::forward<p3##_type>(gmock_p3)), \
-          p4(::std::forward<p4##_type>(gmock_p4)), \
-          p5(::std::forward<p5##_type>(gmock_p5)), \
-          p6(::std::forward<p6##_type>(gmock_p6)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-      p1##_type p1;\
-      p2##_type p2;\
-      p3##_type p3;\
-      p4##_type p4;\
-      p5##_type p5;\
-      p6##_type p6;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
-          p6));\
-    }\
-    p0##_type p0;\
-    p1##_type p1;\
-    p2##_type p2;\
-    p3##_type p3;\
-    p4##_type p4;\
-    p5##_type p5;\
-    p6##_type p6;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP7);\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type>\
-  inline name##ActionP7<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type, p6##_type> name(p0##_type p0, p1##_type p1, \
-      p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
-      p6##_type p6) {\
-    return name##ActionP7<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, p6);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP7<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-          p5##_type, p6##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P8(name, p0, p1, p2, p3, p4, p5, p6, p7)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type>\
-  class name##ActionP8 {\
-   public:\
-    name##ActionP8(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5, p6##_type gmock_p6, \
-        p7##_type gmock_p7) : p0(::std::forward<p0##_type>(gmock_p0)), \
-        p1(::std::forward<p1##_type>(gmock_p1)), \
-        p2(::std::forward<p2##_type>(gmock_p2)), \
-        p3(::std::forward<p3##_type>(gmock_p3)), \
-        p4(::std::forward<p4##_type>(gmock_p4)), \
-        p5(::std::forward<p5##_type>(gmock_p5)), \
-        p6(::std::forward<p6##_type>(gmock_p6)), \
-        p7(::std::forward<p7##_type>(gmock_p7)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6, \
-          p7##_type gmock_p7) : p0(::std::forward<p0##_type>(gmock_p0)), \
-          p1(::std::forward<p1##_type>(gmock_p1)), \
-          p2(::std::forward<p2##_type>(gmock_p2)), \
-          p3(::std::forward<p3##_type>(gmock_p3)), \
-          p4(::std::forward<p4##_type>(gmock_p4)), \
-          p5(::std::forward<p5##_type>(gmock_p5)), \
-          p6(::std::forward<p6##_type>(gmock_p6)), \
-          p7(::std::forward<p7##_type>(gmock_p7)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-      p1##_type p1;\
-      p2##_type p2;\
-      p3##_type p3;\
-      p4##_type p4;\
-      p5##_type p5;\
-      p6##_type p6;\
-      p7##_type p7;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
-          p6, p7));\
-    }\
-    p0##_type p0;\
-    p1##_type p1;\
-    p2##_type p2;\
-    p3##_type p3;\
-    p4##_type p4;\
-    p5##_type p5;\
-    p6##_type p6;\
-    p7##_type p7;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP8);\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type>\
-  inline name##ActionP8<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type, p6##_type, p7##_type> name(p0##_type p0, \
-      p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
-      p6##_type p6, p7##_type p7) {\
-    return name##ActionP8<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, p3, p4, p5, \
-        p6, p7);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP8<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-          p5##_type, p6##_type, \
-          p7##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type>\
-  class name##ActionP9 {\
-   public:\
-    name##ActionP9(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
-        p8##_type gmock_p8) : p0(::std::forward<p0##_type>(gmock_p0)), \
-        p1(::std::forward<p1##_type>(gmock_p1)), \
-        p2(::std::forward<p2##_type>(gmock_p2)), \
-        p3(::std::forward<p3##_type>(gmock_p3)), \
-        p4(::std::forward<p4##_type>(gmock_p4)), \
-        p5(::std::forward<p5##_type>(gmock_p5)), \
-        p6(::std::forward<p6##_type>(gmock_p6)), \
-        p7(::std::forward<p7##_type>(gmock_p7)), \
-        p8(::std::forward<p8##_type>(gmock_p8)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6, p7##_type gmock_p7, \
-          p8##_type gmock_p8) : p0(::std::forward<p0##_type>(gmock_p0)), \
-          p1(::std::forward<p1##_type>(gmock_p1)), \
-          p2(::std::forward<p2##_type>(gmock_p2)), \
-          p3(::std::forward<p3##_type>(gmock_p3)), \
-          p4(::std::forward<p4##_type>(gmock_p4)), \
-          p5(::std::forward<p5##_type>(gmock_p5)), \
-          p6(::std::forward<p6##_type>(gmock_p6)), \
-          p7(::std::forward<p7##_type>(gmock_p7)), \
-          p8(::std::forward<p8##_type>(gmock_p8)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-      p1##_type p1;\
-      p2##_type p2;\
-      p3##_type p3;\
-      p4##_type p4;\
-      p5##_type p5;\
-      p6##_type p6;\
-      p7##_type p7;\
-      p8##_type p8;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
-          p6, p7, p8));\
-    }\
-    p0##_type p0;\
-    p1##_type p1;\
-    p2##_type p2;\
-    p3##_type p3;\
-    p4##_type p4;\
-    p5##_type p5;\
-    p6##_type p6;\
-    p7##_type p7;\
-    p8##_type p8;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP9);\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type>\
-  inline name##ActionP9<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type, p6##_type, p7##_type, \
-      p8##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
-      p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \
-      p8##_type p8) {\
-    return name##ActionP9<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type, p6##_type, p7##_type, p8##_type>(p0, p1, p2, \
-        p3, p4, p5, p6, p7, p8);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP9<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-          p5##_type, p6##_type, p7##_type, \
-          p8##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-#define ACTION_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type, \
-      typename p9##_type>\
-  class name##ActionP10 {\
-   public:\
-    name##ActionP10(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
-        p8##_type gmock_p8, \
-        p9##_type gmock_p9) : p0(::std::forward<p0##_type>(gmock_p0)), \
-        p1(::std::forward<p1##_type>(gmock_p1)), \
-        p2(::std::forward<p2##_type>(gmock_p2)), \
-        p3(::std::forward<p3##_type>(gmock_p3)), \
-        p4(::std::forward<p4##_type>(gmock_p4)), \
-        p5(::std::forward<p5##_type>(gmock_p5)), \
-        p6(::std::forward<p6##_type>(gmock_p6)), \
-        p7(::std::forward<p7##_type>(gmock_p7)), \
-        p8(::std::forward<p8##_type>(gmock_p8)), \
-        p9(::std::forward<p9##_type>(gmock_p9)) {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
-          p9##_type gmock_p9) : p0(::std::forward<p0##_type>(gmock_p0)), \
-          p1(::std::forward<p1##_type>(gmock_p1)), \
-          p2(::std::forward<p2##_type>(gmock_p2)), \
-          p3(::std::forward<p3##_type>(gmock_p3)), \
-          p4(::std::forward<p4##_type>(gmock_p4)), \
-          p5(::std::forward<p5##_type>(gmock_p5)), \
-          p6(::std::forward<p6##_type>(gmock_p6)), \
-          p7(::std::forward<p7##_type>(gmock_p7)), \
-          p8(::std::forward<p8##_type>(gmock_p8)), \
-          p9(::std::forward<p9##_type>(gmock_p9)) {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <typename arg0_type, typename arg1_type, typename arg2_type, \
-          typename arg3_type, typename arg4_type, typename arg5_type, \
-          typename arg6_type, typename arg7_type, typename arg8_type, \
-          typename arg9_type>\
-      return_type gmock_PerformImpl(const args_type& args, \
-          const arg0_type& arg0, const arg1_type& arg1, \
-          const arg2_type& arg2, const arg3_type& arg3, \
-          const arg4_type& arg4, const arg5_type& arg5, \
-          const arg6_type& arg6, const arg7_type& arg7, \
-          const arg8_type& arg8, const arg9_type& arg9) const;\
-      p0##_type p0;\
-      p1##_type p1;\
-      p2##_type p2;\
-      p3##_type p3;\
-      p4##_type p4;\
-      p5##_type p5;\
-      p6##_type p6;\
-      p7##_type p7;\
-      p8##_type p8;\
-      p9##_type p9;\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
-          p6, p7, p8, p9));\
-    }\
-    p0##_type p0;\
-    p1##_type p1;\
-    p2##_type p2;\
-    p3##_type p3;\
-    p4##_type p4;\
-    p5##_type p5;\
-    p6##_type p6;\
-    p7##_type p7;\
-    p8##_type p8;\
-    p9##_type p9;\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(name##ActionP10);\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type, \
-      typename p9##_type>\
-  inline name##ActionP10<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
-      p9##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
-      p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
-      p9##_type p9) {\
-    return name##ActionP10<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, p9##_type>(p0, \
-        p1, p2, p3, p4, p5, p6, p7, p8, p9);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type, \
-      typename p9##_type>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      name##ActionP10<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-          p5##_type, p6##_type, p7##_type, p8##_type, \
-          p9##_type>::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-namespace testing {
-
-
-// The ACTION*() macros trigger warning C4100 (unreferenced formal
-// parameter) in MSVC with -W4.  Unfortunately they cannot be fixed in
-// the macro definition, as the warnings are generated when the macro
-// is expanded and macro expansion cannot contain #pragma.  Therefore
-// we suppress them here.
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable:4100)
-#endif
-
-// Various overloads for InvokeArgument<N>().
-//
-// The InvokeArgument<N>(a1, a2, ..., a_k) action invokes the N-th
-// (0-based) argument, which must be a k-ary callable, of the mock
-// function, with arguments a1, a2, ..., a_k.
-//
-// Notes:
-//
-//   1. The arguments are passed by value by default.  If you need to
-//   pass an argument by reference, wrap it inside ByRef().  For
-//   example,
-//
-//     InvokeArgument<1>(5, string("Hello"), ByRef(foo))
-//
-//   passes 5 and string("Hello") by value, and passes foo by
-//   reference.
-//
-//   2. If the callable takes an argument by reference but ByRef() is
-//   not used, it will receive the reference to a copy of the value,
-//   instead of the original value.  For example, when the 0-th
-//   argument of the mock function takes a const string&, the action
-//
-//     InvokeArgument<0>(string("Hello"))
-//
-//   makes a copy of the temporary string("Hello") object and passes a
-//   reference of the copy, instead of the original temporary object,
-//   to the callable.  This makes it easy for a user to define an
-//   InvokeArgument action from temporary values and have it performed
-//   later.
-
-namespace internal {
-namespace invoke_argument {
-
-// Appears in InvokeArgumentAdl's argument list to help avoid
-// accidental calls to user functions of the same name.
-struct AdlTag {};
-
-// InvokeArgumentAdl - a helper for InvokeArgument.
-// The basic overloads are provided here for generic functors.
-// Overloads for other custom-callables are provided in the
-// internal/custom/callback-actions.h header.
-
-template <typename R, typename F>
-R InvokeArgumentAdl(AdlTag, F f) {
-  return f();
-}
-template <typename R, typename F, typename A1>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1) {
-  return f(a1);
-}
-template <typename R, typename F, typename A1, typename A2>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2) {
-  return f(a1, a2);
-}
-template <typename R, typename F, typename A1, typename A2, typename A3>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3) {
-  return f(a1, a2, a3);
-}
-template <typename R, typename F, typename A1, typename A2, typename A3,
-    typename A4>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4) {
-  return f(a1, a2, a3, a4);
-}
-template <typename R, typename F, typename A1, typename A2, typename A3,
-    typename A4, typename A5>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
-  return f(a1, a2, a3, a4, a5);
-}
-template <typename R, typename F, typename A1, typename A2, typename A3,
-    typename A4, typename A5, typename A6>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
-  return f(a1, a2, a3, a4, a5, a6);
-}
-template <typename R, typename F, typename A1, typename A2, typename A3,
-    typename A4, typename A5, typename A6, typename A7>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
-    A7 a7) {
-  return f(a1, a2, a3, a4, a5, a6, a7);
-}
-template <typename R, typename F, typename A1, typename A2, typename A3,
-    typename A4, typename A5, typename A6, typename A7, typename A8>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
-    A7 a7, A8 a8) {
-  return f(a1, a2, a3, a4, a5, a6, a7, a8);
-}
-template <typename R, typename F, typename A1, typename A2, typename A3,
-    typename A4, typename A5, typename A6, typename A7, typename A8,
-    typename A9>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
-    A7 a7, A8 a8, A9 a9) {
-  return f(a1, a2, a3, a4, a5, a6, a7, a8, a9);
-}
-template <typename R, typename F, typename A1, typename A2, typename A3,
-    typename A4, typename A5, typename A6, typename A7, typename A8,
-    typename A9, typename A10>
-R InvokeArgumentAdl(AdlTag, F f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
-    A7 a7, A8 a8, A9 a9, A10 a10) {
-  return f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
-}
-}  // namespace invoke_argument
-}  // namespace internal
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_0_VALUE_PARAMS()) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args));
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_1_VALUE_PARAMS(p0)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0);
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_2_VALUE_PARAMS(p0, p1)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0, p1);
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_3_VALUE_PARAMS(p0, p1, p2)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0, p1, p2);
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0, p1, p2, p3);
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0, p1, p2, p3, p4);
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0, p1, p2, p3, p4, p5);
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0, p1, p2, p3, p4, p5, p6);
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7);
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8);
-}
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
-}
-
-// Various overloads for ReturnNew<T>().
-//
-// The ReturnNew<T>(a1, a2, ..., a_k) action returns a pointer to a new
-// instance of type T, constructed on the heap with constructor arguments
-// a1, a2, ..., and a_k. The caller assumes ownership of the returned value.
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_0_VALUE_PARAMS()) {
-  return new T();
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_1_VALUE_PARAMS(p0)) {
-  return new T(p0);
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_2_VALUE_PARAMS(p0, p1)) {
-  return new T(p0, p1);
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_3_VALUE_PARAMS(p0, p1, p2)) {
-  return new T(p0, p1, p2);
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
-  return new T(p0, p1, p2, p3);
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
-  return new T(p0, p1, p2, p3, p4);
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
-  return new T(p0, p1, p2, p3, p4, p5);
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
-  return new T(p0, p1, p2, p3, p4, p5, p6);
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) {
-  return new T(p0, p1, p2, p3, p4, p5, p6, p7);
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) {
-  return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8);
-}
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) {
-  return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
-}
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
-
-}  // namespace testing
-
-// Include any custom callback actions added by the local installation.
-// We must include this header at the end to make sure it can use the
-// declarations from this file.
-#include "gmock/internal/custom/gmock-generated-actions.h"
-
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-generated-actions.h.pump b/ext/googletest/googlemock/include/gmock/gmock-generated-actions.h.pump
deleted file mode 100644
index 209603c..0000000
--- a/ext/googletest/googlemock/include/gmock/gmock-generated-actions.h.pump
+++ /dev/null
@@ -1,627 +0,0 @@
-$$ -*- mode: c++; -*-
-$$ This is a Pump source file. Please use Pump to convert it to
-$$ gmock-generated-actions.h.
-$$
-$var n = 10  $$ The maximum arity we support.
-$$}} This meta comment fixes auto-indentation in editors.
-// Copyright 2007, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file implements some commonly used variadic actions.
-
-// GOOGLETEST_CM0002 DO NOT DELETE
-
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
-
-#include <memory>
-#include <utility>
-
-#include "gmock/gmock-actions.h"
-#include "gmock/internal/gmock-port.h"
-
-namespace testing {
-namespace internal {
-
-// A macro from the ACTION* family (defined later in this file)
-// defines an action that can be used in a mock function.  Typically,
-// these actions only care about a subset of the arguments of the mock
-// function.  For example, if such an action only uses the second
-// argument, it can be used in any mock function that takes >= 2
-// arguments where the type of the second argument is compatible.
-//
-// Therefore, the action implementation must be prepared to take more
-// arguments than it needs.  The ExcessiveArg type is used to
-// represent those excessive arguments.  In order to keep the compiler
-// error messages tractable, we define it in the testing namespace
-// instead of testing::internal.  However, this is an INTERNAL TYPE
-// and subject to change without notice, so a user MUST NOT USE THIS
-// TYPE DIRECTLY.
-struct ExcessiveArg {};
-
-// A helper class needed for implementing the ACTION* macros.
-template <typename Result, class Impl>
-class ActionHelper {
- public:
-$range i 0..n
-$for i
-
-[[
-$var template = [[$if i==0 [[]] $else [[
-$range j 0..i-1
-  template <$for j, [[typename A$j]]>
-]]]]
-$range j 0..i-1
-$var As = [[$for j, [[A$j]]]]
-$var as = [[$for j, [[std::get<$j>(args)]]]]
-$range k 1..n-i
-$var eas = [[$for k, [[ExcessiveArg()]]]]
-$var arg_list = [[$if (i==0) | (i==n) [[$as$eas]] $else [[$as, $eas]]]]
-$template
-  static Result Perform(Impl* impl, const ::std::tuple<$As>& args) {
-    return impl->template gmock_PerformImpl<$As>(args, $arg_list);
-  }
-
-]]
-};
-
-}  // namespace internal
-}  // namespace testing
-
-// The ACTION* family of macros can be used in a namespace scope to
-// define custom actions easily.  The syntax:
-//
-//   ACTION(name) { statements; }
-//
-// will define an action with the given name that executes the
-// statements.  The value returned by the statements will be used as
-// the return value of the action.  Inside the statements, you can
-// refer to the K-th (0-based) argument of the mock function by
-// 'argK', and refer to its type by 'argK_type'.  For example:
-//
-//   ACTION(IncrementArg1) {
-//     arg1_type temp = arg1;
-//     return ++(*temp);
-//   }
-//
-// allows you to write
-//
-//   ...WillOnce(IncrementArg1());
-//
-// You can also refer to the entire argument tuple and its type by
-// 'args' and 'args_type', and refer to the mock function type and its
-// return type by 'function_type' and 'return_type'.
-//
-// Note that you don't need to specify the types of the mock function
-// arguments.  However rest assured that your code is still type-safe:
-// you'll get a compiler error if *arg1 doesn't support the ++
-// operator, or if the type of ++(*arg1) isn't compatible with the
-// mock function's return type, for example.
-//
-// Sometimes you'll want to parameterize the action.   For that you can use
-// another macro:
-//
-//   ACTION_P(name, param_name) { statements; }
-//
-// For example:
-//
-//   ACTION_P(Add, n) { return arg0 + n; }
-//
-// will allow you to write:
-//
-//   ...WillOnce(Add(5));
-//
-// Note that you don't need to provide the type of the parameter
-// either.  If you need to reference the type of a parameter named
-// 'foo', you can write 'foo_type'.  For example, in the body of
-// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type
-// of 'n'.
-//
-// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P$n to support
-// multi-parameter actions.
-//
-// For the purpose of typing, you can view
-//
-//   ACTION_Pk(Foo, p1, ..., pk) { ... }
-//
-// as shorthand for
-//
-//   template <typename p1_type, ..., typename pk_type>
-//   FooActionPk<p1_type, ..., pk_type> Foo(p1_type p1, ..., pk_type pk) { ... }
-//
-// In particular, you can provide the template type arguments
-// explicitly when invoking Foo(), as in Foo<long, bool>(5, false);
-// although usually you can rely on the compiler to infer the types
-// for you automatically.  You can assign the result of expression
-// Foo(p1, ..., pk) to a variable of type FooActionPk<p1_type, ...,
-// pk_type>.  This can be useful when composing actions.
-//
-// You can also overload actions with different numbers of parameters:
-//
-//   ACTION_P(Plus, a) { ... }
-//   ACTION_P2(Plus, a, b) { ... }
-//
-// While it's tempting to always use the ACTION* macros when defining
-// a new action, you should also consider implementing ActionInterface
-// or using MakePolymorphicAction() instead, especially if you need to
-// use the action a lot.  While these approaches require more work,
-// they give you more control on the types of the mock function
-// arguments and the action parameters, which in general leads to
-// better compiler error messages that pay off in the long run.  They
-// also allow overloading actions based on parameter types (as opposed
-// to just based on the number of parameters).
-//
-// CAVEAT:
-//
-// ACTION*() can only be used in a namespace scope as templates cannot be
-// declared inside of a local class.
-// Users can, however, define any local functors (e.g. a lambda) that
-// can be used as actions.
-//
-// MORE INFORMATION:
-//
-// To learn more about using these macros, please search for 'ACTION' on
-// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md
-
-$range i 0..n
-$range k 0..n-1
-
-// An internal macro needed for implementing ACTION*().
-#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\
-    const args_type& args GTEST_ATTRIBUTE_UNUSED_
-$for k [[, \
-    const arg$k[[]]_type& arg$k GTEST_ATTRIBUTE_UNUSED_]]
-
-
-// Sometimes you want to give an action explicit template parameters
-// that cannot be inferred from its value parameters.  ACTION() and
-// ACTION_P*() don't support that.  ACTION_TEMPLATE() remedies that
-// and can be viewed as an extension to ACTION() and ACTION_P*().
-//
-// The syntax:
-//
-//   ACTION_TEMPLATE(ActionName,
-//                   HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m),
-//                   AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; }
-//
-// defines an action template that takes m explicit template
-// parameters and n value parameters.  name_i is the name of the i-th
-// template parameter, and kind_i specifies whether it's a typename,
-// an integral constant, or a template.  p_i is the name of the i-th
-// value parameter.
-//
-// Example:
-//
-//   // DuplicateArg<k, T>(output) converts the k-th argument of the mock
-//   // function to type T and copies it to *output.
-//   ACTION_TEMPLATE(DuplicateArg,
-//                   HAS_2_TEMPLATE_PARAMS(int, k, typename, T),
-//                   AND_1_VALUE_PARAMS(output)) {
-//     *output = T(::std::get<k>(args));
-//   }
-//   ...
-//     int n;
-//     EXPECT_CALL(mock, Foo(_, _))
-//         .WillOnce(DuplicateArg<1, unsigned char>(&n));
-//
-// To create an instance of an action template, write:
-//
-//   ActionName<t1, ..., t_m>(v1, ..., v_n)
-//
-// where the ts are the template arguments and the vs are the value
-// arguments.  The value argument types are inferred by the compiler.
-// If you want to explicitly specify the value argument types, you can
-// provide additional template arguments:
-//
-//   ActionName<t1, ..., t_m, u1, ..., u_k>(v1, ..., v_n)
-//
-// where u_i is the desired type of v_i.
-//
-// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the
-// number of value parameters, but not on the number of template
-// parameters.  Without the restriction, the meaning of the following
-// is unclear:
-//
-//   OverloadedAction<int, bool>(x);
-//
-// Are we using a single-template-parameter action where 'bool' refers
-// to the type of x, or are we using a two-template-parameter action
-// where the compiler is asked to infer the type of x?
-//
-// Implementation notes:
-//
-// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and
-// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for
-// implementing ACTION_TEMPLATE.  The main trick we use is to create
-// new macro invocations when expanding a macro.  For example, we have
-//
-//   #define ACTION_TEMPLATE(name, template_params, value_params)
-//       ... GMOCK_INTERNAL_DECL_##template_params ...
-//
-// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...)
-// to expand to
-//
-//       ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ...
-//
-// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the
-// preprocessor will continue to expand it to
-//
-//       ... typename T ...
-//
-// This technique conforms to the C++ standard and is portable.  It
-// allows us to implement action templates using O(N) code, where N is
-// the maximum number of template/value parameters supported.  Without
-// using it, we'd have to devote O(N^2) amount of code to implement all
-// combinations of m and n.
-
-// Declares the template parameters.
-
-$range j 1..n
-$for j [[
-$range m 0..j-1
-#define GMOCK_INTERNAL_DECL_HAS_$j[[]]
-_TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[kind$m name$m]]
-
-
-]]
-
-// Lists the template parameters.
-
-$for j [[
-$range m 0..j-1
-#define GMOCK_INTERNAL_LIST_HAS_$j[[]]
-_TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[name$m]]
-
-
-]]
-
-// Declares the types of value parameters.
-
-$for i [[
-$range j 0..i-1
-#define GMOCK_INTERNAL_DECL_TYPE_AND_$i[[]]
-_VALUE_PARAMS($for j, [[p$j]]) $for j [[, typename p$j##_type]]
-
-
-]]
-
-// Initializes the value parameters.
-
-$for i [[
-$range j 0..i-1
-#define GMOCK_INTERNAL_INIT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])\
-    ($for j, [[p$j##_type gmock_p$j]])$if i>0 [[ : ]]$for j, [[p$j(::std::move(gmock_p$j))]]
-
-
-]]
-
-// Declares the fields for storing the value parameters.
-
-$for i [[
-$range j 0..i-1
-#define GMOCK_INTERNAL_DEFN_AND_$i[[]]
-_VALUE_PARAMS($for j, [[p$j]]) $for j [[p$j##_type p$j; ]]
-
-
-]]
-
-// Lists the value parameters.
-
-$for i [[
-$range j 0..i-1
-#define GMOCK_INTERNAL_LIST_AND_$i[[]]
-_VALUE_PARAMS($for j, [[p$j]]) $for j, [[p$j]]
-
-
-]]
-
-// Lists the value parameter types.
-
-$for i [[
-$range j 0..i-1
-#define GMOCK_INTERNAL_LIST_TYPE_AND_$i[[]]
-_VALUE_PARAMS($for j, [[p$j]]) $for j [[, p$j##_type]]
-
-
-]]
-
-// Declares the value parameters.
-
-$for i [[
-$range j 0..i-1
-#define GMOCK_INTERNAL_DECL_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]]
-$for j, [[p$j##_type p$j]]
-
-
-]]
-
-// The suffix of the class template implementing the action template.
-$for i [[
-
-
-$range j 0..i-1
-#define GMOCK_INTERNAL_COUNT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]]
-$if i==1 [[P]] $elif i>=2 [[P$i]]
-]]
-
-
-// The name of the class template implementing the action template.
-#define GMOCK_ACTION_CLASS_(name, value_params)\
-    GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params)
-
-$range k 0..n-1
-
-#define ACTION_TEMPLATE(name, template_params, value_params)\
-  template <GMOCK_INTERNAL_DECL_##template_params\
-            GMOCK_INTERNAL_DECL_TYPE_##value_params>\
-  class GMOCK_ACTION_CLASS_(name, value_params) {\
-   public:\
-    explicit GMOCK_ACTION_CLASS_(name, value_params)\
-        GMOCK_INTERNAL_INIT_##value_params {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <$for k, [[typename arg$k[[]]_type]]>\
-      return_type gmock_PerformImpl(const args_type& args[[]]
-$for k [[, const arg$k[[]]_type& arg$k]]) const;\
-      GMOCK_INTERNAL_DEFN_##value_params\
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(\
-          new gmock_Impl<F>(GMOCK_INTERNAL_LIST_##value_params));\
-    }\
-    GMOCK_INTERNAL_DEFN_##value_params\
-   private:\
-    GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\
-  };\
-  template <GMOCK_INTERNAL_DECL_##template_params\
-            GMOCK_INTERNAL_DECL_TYPE_##value_params>\
-  inline GMOCK_ACTION_CLASS_(name, value_params)<\
-      GMOCK_INTERNAL_LIST_##template_params\
-      GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\
-          GMOCK_INTERNAL_DECL_##value_params) {\
-    return GMOCK_ACTION_CLASS_(name, value_params)<\
-        GMOCK_INTERNAL_LIST_##template_params\
-        GMOCK_INTERNAL_LIST_TYPE_##value_params>(\
-            GMOCK_INTERNAL_LIST_##value_params);\
-  }\
-  template <GMOCK_INTERNAL_DECL_##template_params\
-            GMOCK_INTERNAL_DECL_TYPE_##value_params>\
-  template <typename F>\
-  template <typename arg0_type, typename arg1_type, typename arg2_type, \
-      typename arg3_type, typename arg4_type, typename arg5_type, \
-      typename arg6_type, typename arg7_type, typename arg8_type, \
-      typename arg9_type>\
-  typename ::testing::internal::Function<F>::Result\
-      GMOCK_ACTION_CLASS_(name, value_params)<\
-          GMOCK_INTERNAL_LIST_##template_params\
-          GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl<F>::\
-              gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-
-$for i
-
-[[
-$var template = [[$if i==0 [[]] $else [[
-$range j 0..i-1
-
-  template <$for j, [[typename p$j##_type]]>\
-]]]]
-$var class_name = [[name##Action[[$if i==0 [[]] $elif i==1 [[P]]
-                                                $else [[P$i]]]]]]
-$range j 0..i-1
-$var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]]
-$var param_types_and_names = [[$for j, [[p$j##_type p$j]]]]
-$var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(::std::forward<p$j##_type>(gmock_p$j))]]]]]]
-$var param_field_decls = [[$for j
-[[
-
-      p$j##_type p$j;\
-]]]]
-$var param_field_decls2 = [[$for j
-[[
-
-    p$j##_type p$j;\
-]]]]
-$var params = [[$for j, [[p$j]]]]
-$var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]]
-$var typename_arg_types = [[$for k, [[typename arg$k[[]]_type]]]]
-$var arg_types_and_names = [[$for k, [[const arg$k[[]]_type& arg$k]]]]
-$var macro_name = [[$if i==0 [[ACTION]] $elif i==1 [[ACTION_P]]
-                                        $else [[ACTION_P$i]]]]
-
-#define $macro_name(name$for j [[, p$j]])\$template
-  class $class_name {\
-   public:\
-    [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {}\
-    template <typename F>\
-    class gmock_Impl : public ::testing::ActionInterface<F> {\
-     public:\
-      typedef F function_type;\
-      typedef typename ::testing::internal::Function<F>::Result return_type;\
-      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
-          args_type;\
-      [[$if i==1 [[explicit ]]]]gmock_Impl($ctor_param_list)$inits {}\
-      virtual return_type Perform(const args_type& args) {\
-        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
-            Perform(this, args);\
-      }\
-      template <$typename_arg_types>\
-      return_type gmock_PerformImpl(const args_type& args, [[]]
-$arg_types_and_names) const;\$param_field_decls
-     private:\
-      GTEST_DISALLOW_ASSIGN_(gmock_Impl);\
-    };\
-    template <typename F> operator ::testing::Action<F>() const {\
-      return ::testing::Action<F>(new gmock_Impl<F>($params));\
-    }\$param_field_decls2
-   private:\
-    GTEST_DISALLOW_ASSIGN_($class_name);\
-  };\$template
-  inline $class_name$param_types name($param_types_and_names) {\
-    return $class_name$param_types($params);\
-  }\$template
-  template <typename F>\
-  template <$typename_arg_types>\
-  typename ::testing::internal::Function<F>::Result\
-      $class_name$param_types::gmock_Impl<F>::gmock_PerformImpl(\
-          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
-]]
-$$ }  // This meta comment fixes auto-indentation in Emacs.  It won't
-$$    // show up in the generated code.
-
-
-namespace testing {
-
-
-// The ACTION*() macros trigger warning C4100 (unreferenced formal
-// parameter) in MSVC with -W4.  Unfortunately they cannot be fixed in
-// the macro definition, as the warnings are generated when the macro
-// is expanded and macro expansion cannot contain #pragma.  Therefore
-// we suppress them here.
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable:4100)
-#endif
-
-// Various overloads for InvokeArgument<N>().
-//
-// The InvokeArgument<N>(a1, a2, ..., a_k) action invokes the N-th
-// (0-based) argument, which must be a k-ary callable, of the mock
-// function, with arguments a1, a2, ..., a_k.
-//
-// Notes:
-//
-//   1. The arguments are passed by value by default.  If you need to
-//   pass an argument by reference, wrap it inside ByRef().  For
-//   example,
-//
-//     InvokeArgument<1>(5, string("Hello"), ByRef(foo))
-//
-//   passes 5 and string("Hello") by value, and passes foo by
-//   reference.
-//
-//   2. If the callable takes an argument by reference but ByRef() is
-//   not used, it will receive the reference to a copy of the value,
-//   instead of the original value.  For example, when the 0-th
-//   argument of the mock function takes a const string&, the action
-//
-//     InvokeArgument<0>(string("Hello"))
-//
-//   makes a copy of the temporary string("Hello") object and passes a
-//   reference of the copy, instead of the original temporary object,
-//   to the callable.  This makes it easy for a user to define an
-//   InvokeArgument action from temporary values and have it performed
-//   later.
-
-namespace internal {
-namespace invoke_argument {
-
-// Appears in InvokeArgumentAdl's argument list to help avoid
-// accidental calls to user functions of the same name.
-struct AdlTag {};
-
-// InvokeArgumentAdl - a helper for InvokeArgument.
-// The basic overloads are provided here for generic functors.
-// Overloads for other custom-callables are provided in the
-// internal/custom/callback-actions.h header.
-
-$range i 0..n
-$for i
-[[
-$range j 1..i
-
-template <typename R, typename F[[$for j [[, typename A$j]]]]>
-R InvokeArgumentAdl(AdlTag, F f[[$for j [[, A$j a$j]]]]) {
-  return f([[$for j, [[a$j]]]]);
-}
-]]
-
-}  // namespace invoke_argument
-}  // namespace internal
-
-$range i 0..n
-$for i [[
-$range j 0..i-1
-
-ACTION_TEMPLATE(InvokeArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])) {
-  using internal::invoke_argument::InvokeArgumentAdl;
-  return InvokeArgumentAdl<return_type>(
-      internal::invoke_argument::AdlTag(),
-      ::std::get<k>(args)$for j [[, p$j]]);
-}
-
-]]
-
-// Various overloads for ReturnNew<T>().
-//
-// The ReturnNew<T>(a1, a2, ..., a_k) action returns a pointer to a new
-// instance of type T, constructed on the heap with constructor arguments
-// a1, a2, ..., and a_k. The caller assumes ownership of the returned value.
-$range i 0..n
-$for i [[
-$range j 0..i-1
-$var ps = [[$for j, [[p$j]]]]
-
-ACTION_TEMPLATE(ReturnNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_$i[[]]_VALUE_PARAMS($ps)) {
-  return new T($ps);
-}
-
-]]
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
-
-}  // namespace testing
-
-// Include any custom callback actions added by the local installation.
-// We must include this header at the end to make sure it can use the
-// declarations from this file.
-#include "gmock/internal/custom/gmock-generated-actions.h"
-
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h b/ext/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h
deleted file mode 100644
index cd95781..0000000
--- a/ext/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h
+++ /dev/null
@@ -1,752 +0,0 @@
-// This file was GENERATED by command:
-//     pump.py gmock-generated-function-mockers.h.pump
-// DO NOT EDIT BY HAND!!!
-
-// Copyright 2007, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file implements function mockers of various arities.
-
-// GOOGLETEST_CM0002 DO NOT DELETE
-
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
-
-#include <functional>
-#include <utility>
-
-#include "gmock/gmock-spec-builders.h"
-#include "gmock/internal/gmock-internal-utils.h"
-
-namespace testing {
-namespace internal {
-// Removes the given pointer; this is a helper for the expectation setter method
-// for parameterless matchers.
-//
-// We want to make sure that the user cannot set a parameterless expectation on
-// overloaded methods, including methods which are overloaded on const. Example:
-//
-//   class MockClass {
-//     MOCK_METHOD0(GetName, string&());
-//     MOCK_CONST_METHOD0(GetName, const string&());
-//   };
-//
-//   TEST() {
-//     // This should be an error, as it's not clear which overload is expected.
-//     EXPECT_CALL(mock, GetName).WillOnce(ReturnRef(value));
-//   }
-//
-// Here are the generated expectation-setter methods:
-//
-//   class MockClass {
-//     // Overload 1
-//     MockSpec<string&()> gmock_GetName() { ... }
-//     // Overload 2. Declared const so that the compiler will generate an
-//     // error when trying to resolve between this and overload 4 in
-//     // 'gmock_GetName(WithoutMatchers(), nullptr)'.
-//     MockSpec<string&()> gmock_GetName(
-//         const WithoutMatchers&, const Function<string&()>*) const {
-//       // Removes const from this, calls overload 1
-//       return AdjustConstness_(this)->gmock_GetName();
-//     }
-//
-//     // Overload 3
-//     const string& gmock_GetName() const { ... }
-//     // Overload 4
-//     MockSpec<const string&()> gmock_GetName(
-//         const WithoutMatchers&, const Function<const string&()>*) const {
-//       // Does not remove const, calls overload 3
-//       return AdjustConstness_const(this)->gmock_GetName();
-//     }
-//   }
-//
-template <typename MockType>
-const MockType* AdjustConstness_const(const MockType* mock) {
-  return mock;
-}
-
-// Removes const from and returns the given pointer; this is a helper for the
-// expectation setter method for parameterless matchers.
-template <typename MockType>
-MockType* AdjustConstness_(const MockType* mock) {
-  return const_cast<MockType*>(mock);
-}
-
-}  // namespace internal
-
-// The style guide prohibits "using" statements in a namespace scope
-// inside a header file.  However, the FunctionMocker class template
-// is meant to be defined in the ::testing namespace.  The following
-// line is just a trick for working around a bug in MSVC 8.0, which
-// cannot handle it if we define FunctionMocker in ::testing.
-using internal::FunctionMocker;
-
-// GMOCK_RESULT_(tn, F) expands to the result type of function type F.
-// We define this as a variadic macro in case F contains unprotected
-// commas (the same reason that we use variadic macros in other places
-// in this file).
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_RESULT_(tn, ...) \
-    tn ::testing::internal::Function<__VA_ARGS__>::Result
-
-// The type of argument N of the given function type.
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_ARG_(tn, N, ...) \
-    tn ::testing::internal::Function<__VA_ARGS__>::template Arg<N-1>::type
-
-// The matcher type for argument N of the given function type.
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_MATCHER_(tn, N, ...) \
-    const ::testing::Matcher<GMOCK_ARG_(tn, N, __VA_ARGS__)>&
-
-// The variable for mocking the given method.
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_MOCKER_(arity, constness, Method) \
-    GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD0_(tn, constness, ct, Method, ...) \
-  static_assert(0 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      ) constness { \
-    GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(0, constness, Method).Invoke(); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method() constness { \
-    GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(0, constness, Method).With(); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(0, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD1_(tn, constness, ct, Method, ...) \
-  static_assert(1 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1) constness { \
-    GMOCK_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(1, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1) constness { \
-    GMOCK_MOCKER_(1, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(1, constness, Method).With(gmock_a1); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(1, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD2_(tn, constness, ct, Method, ...) \
-  static_assert(2 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
-          __VA_ARGS__) gmock_a2) constness { \
-    GMOCK_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(2, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1), \
-  ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
-                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness { \
-    GMOCK_MOCKER_(2, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(2, constness, Method).With(gmock_a1, gmock_a2); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(2, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD3_(tn, constness, ct, Method, ...) \
-  static_assert(3 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
-          __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, \
-          __VA_ARGS__) gmock_a3) constness { \
-    GMOCK_MOCKER_(3, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(3, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1), \
-  ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
-  ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
-                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
-                     GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3) constness { \
-    GMOCK_MOCKER_(3, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(3, constness, Method).With(gmock_a1, gmock_a2, \
-        gmock_a3); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(3, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD4_(tn, constness, ct, Method, ...) \
-  static_assert(4 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
-          __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
-          GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4) constness { \
-    GMOCK_MOCKER_(4, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(4, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1), \
-  ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
-  ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
-  ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
-                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
-                     GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
-                     GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4) constness { \
-    GMOCK_MOCKER_(4, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(4, constness, Method).With(gmock_a1, gmock_a2, \
-        gmock_a3, gmock_a4); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(4, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD5_(tn, constness, ct, Method, ...) \
-  static_assert(5 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
-          __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
-          GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
-          __VA_ARGS__) gmock_a5) constness { \
-    GMOCK_MOCKER_(5, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(5, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1), \
-  ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
-  ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
-  ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
-  ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
-                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
-                     GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
-                     GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
-                     GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5) constness { \
-    GMOCK_MOCKER_(5, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(5, constness, Method).With(gmock_a1, gmock_a2, \
-        gmock_a3, gmock_a4, gmock_a5); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(5, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD6_(tn, constness, ct, Method, ...) \
-  static_assert(6 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
-          __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
-          GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
-          __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, \
-          __VA_ARGS__) gmock_a6) constness { \
-    GMOCK_MOCKER_(6, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(6, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1), \
-  ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
-  ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
-  ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
-  ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
-  ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
-                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
-                     GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
-                     GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
-                     GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
-                     GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6) constness { \
-    GMOCK_MOCKER_(6, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(6, constness, Method).With(gmock_a1, gmock_a2, \
-        gmock_a3, gmock_a4, gmock_a5, gmock_a6); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(6, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD7_(tn, constness, ct, Method, ...) \
-  static_assert(7 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
-          __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
-          GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
-          __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
-          GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7) constness { \
-    GMOCK_MOCKER_(7, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(7, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1), \
-  ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
-  ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
-  ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
-  ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
-  ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6), \
-  ::std::forward<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(gmock_a7)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
-                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
-                     GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
-                     GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
-                     GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
-                     GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \
-                     GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7) constness { \
-    GMOCK_MOCKER_(7, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(7, constness, Method).With(gmock_a1, gmock_a2, \
-        gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 7, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(7, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD8_(tn, constness, ct, Method, ...) \
-  static_assert(8 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
-          __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
-          GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
-          __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
-          GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, GMOCK_ARG_(tn, 8, \
-          __VA_ARGS__) gmock_a8) constness { \
-    GMOCK_MOCKER_(8, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(8, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1), \
-  ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
-  ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
-  ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
-  ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
-  ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6), \
-  ::std::forward<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(gmock_a7), \
-  ::std::forward<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(gmock_a8)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
-                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
-                     GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
-                     GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
-                     GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
-                     GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \
-                     GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \
-                     GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8) constness { \
-    GMOCK_MOCKER_(8, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(8, constness, Method).With(gmock_a1, gmock_a2, \
-        gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 8, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(8, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD9_(tn, constness, ct, Method, ...) \
-  static_assert(9 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
-          __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
-          GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
-          __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
-          GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, GMOCK_ARG_(tn, 8, \
-          __VA_ARGS__) gmock_a8, GMOCK_ARG_(tn, 9, \
-          __VA_ARGS__) gmock_a9) constness { \
-    GMOCK_MOCKER_(9, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(9, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1), \
-  ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
-  ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
-  ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
-  ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
-  ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6), \
-  ::std::forward<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(gmock_a7), \
-  ::std::forward<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(gmock_a8), \
-  ::std::forward<GMOCK_ARG_(tn, 9, __VA_ARGS__)>(gmock_a9)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
-                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
-                     GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
-                     GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
-                     GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
-                     GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \
-                     GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \
-                     GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \
-                     GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9) constness { \
-    GMOCK_MOCKER_(9, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(9, constness, Method).With(gmock_a1, gmock_a2, \
-        gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \
-        gmock_a9); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 9, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(9, constness, \
-      Method)
-
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD10_(tn, constness, ct, Method, ...) \
-  static_assert(10 == \
-      ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, \
-      "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, GMOCK_ARG_(tn, 2, \
-          __VA_ARGS__) gmock_a2, GMOCK_ARG_(tn, 3, __VA_ARGS__) gmock_a3, \
-          GMOCK_ARG_(tn, 4, __VA_ARGS__) gmock_a4, GMOCK_ARG_(tn, 5, \
-          __VA_ARGS__) gmock_a5, GMOCK_ARG_(tn, 6, __VA_ARGS__) gmock_a6, \
-          GMOCK_ARG_(tn, 7, __VA_ARGS__) gmock_a7, GMOCK_ARG_(tn, 8, \
-          __VA_ARGS__) gmock_a8, GMOCK_ARG_(tn, 9, __VA_ARGS__) gmock_a9, \
-          GMOCK_ARG_(tn, 10, __VA_ARGS__) gmock_a10) constness { \
-    GMOCK_MOCKER_(10, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_(10, constness, \
-        Method).Invoke(::std::forward<GMOCK_ARG_(tn, 1, \
-        __VA_ARGS__)>(gmock_a1), \
-  ::std::forward<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(gmock_a2), \
-  ::std::forward<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(gmock_a3), \
-  ::std::forward<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(gmock_a4), \
-  ::std::forward<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(gmock_a5), \
-  ::std::forward<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(gmock_a6), \
-  ::std::forward<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(gmock_a7), \
-  ::std::forward<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(gmock_a8), \
-  ::std::forward<GMOCK_ARG_(tn, 9, __VA_ARGS__)>(gmock_a9), \
-  ::std::forward<GMOCK_ARG_(tn, 10, __VA_ARGS__)>(gmock_a10)); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
-                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2, \
-                     GMOCK_MATCHER_(tn, 3, __VA_ARGS__) gmock_a3, \
-                     GMOCK_MATCHER_(tn, 4, __VA_ARGS__) gmock_a4, \
-                     GMOCK_MATCHER_(tn, 5, __VA_ARGS__) gmock_a5, \
-                     GMOCK_MATCHER_(tn, 6, __VA_ARGS__) gmock_a6, \
-                     GMOCK_MATCHER_(tn, 7, __VA_ARGS__) gmock_a7, \
-                     GMOCK_MATCHER_(tn, 8, __VA_ARGS__) gmock_a8, \
-                     GMOCK_MATCHER_(tn, 9, __VA_ARGS__) gmock_a9, \
-                     GMOCK_MATCHER_(tn, 10, \
-                         __VA_ARGS__) gmock_a10) constness { \
-    GMOCK_MOCKER_(10, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_(10, constness, Method).With(gmock_a1, gmock_a2, \
-        gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \
-        gmock_a10); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method(::testing::A<GMOCK_ARG_(tn, 1, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 2, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 3, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 4, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 5, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 6, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 7, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 8, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 9, __VA_ARGS__)>(), \
-                     ::testing::A<GMOCK_ARG_(tn, 10, __VA_ARGS__)>()); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_(10, constness, \
-      Method)
-
-#define MOCK_METHOD0(m, ...) GMOCK_METHOD0_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD1(m, ...) GMOCK_METHOD1_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD2(m, ...) GMOCK_METHOD2_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD3(m, ...) GMOCK_METHOD3_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD4(m, ...) GMOCK_METHOD4_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD5(m, ...) GMOCK_METHOD5_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD6(m, ...) GMOCK_METHOD6_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD7(m, ...) GMOCK_METHOD7_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD8(m, ...) GMOCK_METHOD8_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD9(m, ...) GMOCK_METHOD9_(, , , m, __VA_ARGS__)
-#define MOCK_METHOD10(m, ...) GMOCK_METHOD10_(, , , m, __VA_ARGS__)
-
-#define MOCK_CONST_METHOD0(m, ...) GMOCK_METHOD0_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD1(m, ...) GMOCK_METHOD1_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD2(m, ...) GMOCK_METHOD2_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD3(m, ...) GMOCK_METHOD3_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD4(m, ...) GMOCK_METHOD4_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD5(m, ...) GMOCK_METHOD5_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD6(m, ...) GMOCK_METHOD6_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD7(m, ...) GMOCK_METHOD7_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD8(m, ...) GMOCK_METHOD8_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD9(m, ...) GMOCK_METHOD9_(, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD10(m, ...) GMOCK_METHOD10_(, const, , m, __VA_ARGS__)
-
-#define MOCK_METHOD0_T(m, ...) GMOCK_METHOD0_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD1_T(m, ...) GMOCK_METHOD1_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD2_T(m, ...) GMOCK_METHOD2_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD3_T(m, ...) GMOCK_METHOD3_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD4_T(m, ...) GMOCK_METHOD4_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD5_T(m, ...) GMOCK_METHOD5_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD6_T(m, ...) GMOCK_METHOD6_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD7_T(m, ...) GMOCK_METHOD7_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD8_T(m, ...) GMOCK_METHOD8_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD9_T(m, ...) GMOCK_METHOD9_(typename, , , m, __VA_ARGS__)
-#define MOCK_METHOD10_T(m, ...) GMOCK_METHOD10_(typename, , , m, __VA_ARGS__)
-
-#define MOCK_CONST_METHOD0_T(m, ...) \
-    GMOCK_METHOD0_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD1_T(m, ...) \
-    GMOCK_METHOD1_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD2_T(m, ...) \
-    GMOCK_METHOD2_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD3_T(m, ...) \
-    GMOCK_METHOD3_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD4_T(m, ...) \
-    GMOCK_METHOD4_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD5_T(m, ...) \
-    GMOCK_METHOD5_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD6_T(m, ...) \
-    GMOCK_METHOD6_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD7_T(m, ...) \
-    GMOCK_METHOD7_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD8_T(m, ...) \
-    GMOCK_METHOD8_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD9_T(m, ...) \
-    GMOCK_METHOD9_(typename, const, , m, __VA_ARGS__)
-#define MOCK_CONST_METHOD10_T(m, ...) \
-    GMOCK_METHOD10_(typename, const, , m, __VA_ARGS__)
-
-#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD0_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD1_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD2_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD3_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD4_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD5_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD6_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD7_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD8_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD9_(, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD10_(, , ct, m, __VA_ARGS__)
-
-#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD0_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD1_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD2_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD3_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD4_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD5_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD6_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD7_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD8_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD9_(, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD10_(, const, ct, m, __VA_ARGS__)
-
-#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD0_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD1_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD2_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD3_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD4_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD5_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD6_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD7_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD8_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD9_(typename, , ct, m, __VA_ARGS__)
-#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD10_(typename, , ct, m, __VA_ARGS__)
-
-#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD0_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD1_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD2_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD3_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD4_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD5_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD6_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD7_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD8_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD9_(typename, const, ct, m, __VA_ARGS__)
-#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD10_(typename, const, ct, m, __VA_ARGS__)
-
-}  // namespace testing
-
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h.pump b/ext/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h.pump
deleted file mode 100644
index a56e132..0000000
--- a/ext/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h.pump
+++ /dev/null
@@ -1,227 +0,0 @@
-$$ -*- mode: c++; -*-
-$$ This is a Pump source file.  Please use Pump to convert
-$$ it to gmock-generated-function-mockers.h.
-$$
-$var n = 10  $$ The maximum arity we support.
-// Copyright 2007, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file implements function mockers of various arities.
-
-// GOOGLETEST_CM0002 DO NOT DELETE
-
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
-
-#include <functional>
-#include <utility>
-
-#include "gmock/gmock-spec-builders.h"
-#include "gmock/internal/gmock-internal-utils.h"
-
-namespace testing {
-namespace internal {
-
-$range i 0..n
-// Removes the given pointer; this is a helper for the expectation setter method
-// for parameterless matchers.
-//
-// We want to make sure that the user cannot set a parameterless expectation on
-// overloaded methods, including methods which are overloaded on const. Example:
-//
-//   class MockClass {
-//     MOCK_METHOD0(GetName, string&());
-//     MOCK_CONST_METHOD0(GetName, const string&());
-//   };
-//
-//   TEST() {
-//     // This should be an error, as it's not clear which overload is expected.
-//     EXPECT_CALL(mock, GetName).WillOnce(ReturnRef(value));
-//   }
-//
-// Here are the generated expectation-setter methods:
-//
-//   class MockClass {
-//     // Overload 1
-//     MockSpec<string&()> gmock_GetName() { ... }
-//     // Overload 2. Declared const so that the compiler will generate an
-//     // error when trying to resolve between this and overload 4 in
-//     // 'gmock_GetName(WithoutMatchers(), nullptr)'.
-//     MockSpec<string&()> gmock_GetName(
-//         const WithoutMatchers&, const Function<string&()>*) const {
-//       // Removes const from this, calls overload 1
-//       return AdjustConstness_(this)->gmock_GetName();
-//     }
-//
-//     // Overload 3
-//     const string& gmock_GetName() const { ... }
-//     // Overload 4
-//     MockSpec<const string&()> gmock_GetName(
-//         const WithoutMatchers&, const Function<const string&()>*) const {
-//       // Does not remove const, calls overload 3
-//       return AdjustConstness_const(this)->gmock_GetName();
-//     }
-//   }
-//
-template <typename MockType>
-const MockType* AdjustConstness_const(const MockType* mock) {
-  return mock;
-}
-
-// Removes const from and returns the given pointer; this is a helper for the
-// expectation setter method for parameterless matchers.
-template <typename MockType>
-MockType* AdjustConstness_(const MockType* mock) {
-  return const_cast<MockType*>(mock);
-}
-
-}  // namespace internal
-
-// The style guide prohibits "using" statements in a namespace scope
-// inside a header file.  However, the FunctionMocker class template
-// is meant to be defined in the ::testing namespace.  The following
-// line is just a trick for working around a bug in MSVC 8.0, which
-// cannot handle it if we define FunctionMocker in ::testing.
-using internal::FunctionMocker;
-
-// GMOCK_RESULT_(tn, F) expands to the result type of function type F.
-// We define this as a variadic macro in case F contains unprotected
-// commas (the same reason that we use variadic macros in other places
-// in this file).
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_RESULT_(tn, ...) \
-    tn ::testing::internal::Function<__VA_ARGS__>::Result
-
-// The type of argument N of the given function type.
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_ARG_(tn, N, ...) \
-    tn ::testing::internal::Function<__VA_ARGS__>::template Arg<N-1>::type
-
-// The matcher type for argument N of the given function type.
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_MATCHER_(tn, N, ...) \
-    const ::testing::Matcher<GMOCK_ARG_(tn, N, __VA_ARGS__)>&
-
-// The variable for mocking the given method.
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_MOCKER_(arity, constness, Method) \
-    GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__)
-
-
-$for i [[
-$range j 1..i
-$var arg_as = [[$for j, [[GMOCK_ARG_(tn, $j, __VA_ARGS__) gmock_a$j]]]]
-$var as = [[$for j, \
-  [[::std::forward<GMOCK_ARG_(tn, $j, __VA_ARGS__)>(gmock_a$j)]]]]
-$var matcher_arg_as = [[$for j, \
-                     [[GMOCK_MATCHER_(tn, $j, __VA_ARGS__) gmock_a$j]]]]
-$var matcher_as = [[$for j, [[gmock_a$j]]]]
-$var anything_matchers = [[$for j, \
-                     [[::testing::A<GMOCK_ARG_(tn, $j, __VA_ARGS__)>()]]]]
-// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
-#define GMOCK_METHOD$i[[]]_(tn, constness, ct, Method, ...) \
-  static_assert($i == ::testing::internal::Function<__VA_ARGS__>::ArgumentCount, "MOCK_METHOD<N> must match argument count.");\
-  GMOCK_RESULT_(tn, __VA_ARGS__) ct Method( \
-      $arg_as) constness { \
-    GMOCK_MOCKER_($i, constness, Method).SetOwnerAndName(this, #Method); \
-    return GMOCK_MOCKER_($i, constness, Method).Invoke($as); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> \
-      gmock_##Method($matcher_arg_as) constness { \
-    GMOCK_MOCKER_($i, constness, Method).RegisterOwner(this); \
-    return GMOCK_MOCKER_($i, constness, Method).With($matcher_as); \
-  } \
-  ::testing::MockSpec<__VA_ARGS__> gmock_##Method( \
-      const ::testing::internal::WithoutMatchers&, \
-      constness ::testing::internal::Function<__VA_ARGS__>* ) const { \
-        return ::testing::internal::AdjustConstness_##constness(this)-> \
-            gmock_##Method($anything_matchers); \
-      } \
-  mutable ::testing::FunctionMocker<__VA_ARGS__> GMOCK_MOCKER_($i, constness, Method)
-
-
-]]
-$for i [[
-#define MOCK_METHOD$i(m, ...) GMOCK_METHOD$i[[]]_(, , , m, __VA_ARGS__)
-
-]]
-
-
-$for i [[
-#define MOCK_CONST_METHOD$i(m, ...) GMOCK_METHOD$i[[]]_(, const, , m, __VA_ARGS__)
-
-]]
-
-
-$for i [[
-#define MOCK_METHOD$i[[]]_T(m, ...) GMOCK_METHOD$i[[]]_(typename, , , m, __VA_ARGS__)
-
-]]
-
-
-$for i [[
-#define MOCK_CONST_METHOD$i[[]]_T(m, ...) \
-    GMOCK_METHOD$i[[]]_(typename, const, , m, __VA_ARGS__)
-
-]]
-
-
-$for i [[
-#define MOCK_METHOD$i[[]]_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD$i[[]]_(, , ct, m, __VA_ARGS__)
-
-]]
-
-
-$for i [[
-#define MOCK_CONST_METHOD$i[[]]_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD$i[[]]_(, const, ct, m, __VA_ARGS__)
-
-]]
-
-
-$for i [[
-#define MOCK_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD$i[[]]_(typename, , ct, m, __VA_ARGS__)
-
-]]
-
-
-$for i [[
-#define MOCK_CONST_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, ...) \
-    GMOCK_METHOD$i[[]]_(typename, const, ct, m, __VA_ARGS__)
-
-]]
-
-}  // namespace testing
-
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-generated-matchers.h b/ext/googletest/googlemock/include/gmock/gmock-generated-matchers.h
deleted file mode 100644
index 690a57f..0000000
--- a/ext/googletest/googlemock/include/gmock/gmock-generated-matchers.h
+++ /dev/null
@@ -1,1097 +0,0 @@
-// This file was GENERATED by command:
-//     pump.py gmock-generated-matchers.h.pump
-// DO NOT EDIT BY HAND!!!
-
-// Copyright 2008, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file implements some commonly used variadic matchers.
-
-// GOOGLETEST_CM0002 DO NOT DELETE
-
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
-
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-#include "gmock/gmock-matchers.h"
-
-// The MATCHER* family of macros can be used in a namespace scope to
-// define custom matchers easily.
-//
-// Basic Usage
-// ===========
-//
-// The syntax
-//
-//   MATCHER(name, description_string) { statements; }
-//
-// defines a matcher with the given name that executes the statements,
-// which must return a bool to indicate if the match succeeds.  Inside
-// the statements, you can refer to the value being matched by 'arg',
-// and refer to its type by 'arg_type'.
-//
-// The description string documents what the matcher does, and is used
-// to generate the failure message when the match fails.  Since a
-// MATCHER() is usually defined in a header file shared by multiple
-// C++ source files, we require the description to be a C-string
-// literal to avoid possible side effects.  It can be empty, in which
-// case we'll use the sequence of words in the matcher name as the
-// description.
-//
-// For example:
-//
-//   MATCHER(IsEven, "") { return (arg % 2) == 0; }
-//
-// allows you to write
-//
-//   // Expects mock_foo.Bar(n) to be called where n is even.
-//   EXPECT_CALL(mock_foo, Bar(IsEven()));
-//
-// or,
-//
-//   // Verifies that the value of some_expression is even.
-//   EXPECT_THAT(some_expression, IsEven());
-//
-// If the above assertion fails, it will print something like:
-//
-//   Value of: some_expression
-//   Expected: is even
-//     Actual: 7
-//
-// where the description "is even" is automatically calculated from the
-// matcher name IsEven.
-//
-// Argument Type
-// =============
-//
-// Note that the type of the value being matched (arg_type) is
-// determined by the context in which you use the matcher and is
-// supplied to you by the compiler, so you don't need to worry about
-// declaring it (nor can you).  This allows the matcher to be
-// polymorphic.  For example, IsEven() can be used to match any type
-// where the value of "(arg % 2) == 0" can be implicitly converted to
-// a bool.  In the "Bar(IsEven())" example above, if method Bar()
-// takes an int, 'arg_type' will be int; if it takes an unsigned long,
-// 'arg_type' will be unsigned long; and so on.
-//
-// Parameterizing Matchers
-// =======================
-//
-// Sometimes you'll want to parameterize the matcher.  For that you
-// can use another macro:
-//
-//   MATCHER_P(name, param_name, description_string) { statements; }
-//
-// For example:
-//
-//   MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; }
-//
-// will allow you to write:
-//
-//   EXPECT_THAT(Blah("a"), HasAbsoluteValue(n));
-//
-// which may lead to this message (assuming n is 10):
-//
-//   Value of: Blah("a")
-//   Expected: has absolute value 10
-//     Actual: -9
-//
-// Note that both the matcher description and its parameter are
-// printed, making the message human-friendly.
-//
-// In the matcher definition body, you can write 'foo_type' to
-// reference the type of a parameter named 'foo'.  For example, in the
-// body of MATCHER_P(HasAbsoluteValue, value) above, you can write
-// 'value_type' to refer to the type of 'value'.
-//
-// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P10 to
-// support multi-parameter matchers.
-//
-// Describing Parameterized Matchers
-// =================================
-//
-// The last argument to MATCHER*() is a string-typed expression.  The
-// expression can reference all of the matcher's parameters and a
-// special bool-typed variable named 'negation'.  When 'negation' is
-// false, the expression should evaluate to the matcher's description;
-// otherwise it should evaluate to the description of the negation of
-// the matcher.  For example,
-//
-//   using testing::PrintToString;
-//
-//   MATCHER_P2(InClosedRange, low, hi,
-//       std::string(negation ? "is not" : "is") + " in range [" +
-//       PrintToString(low) + ", " + PrintToString(hi) + "]") {
-//     return low <= arg && arg <= hi;
-//   }
-//   ...
-//   EXPECT_THAT(3, InClosedRange(4, 6));
-//   EXPECT_THAT(3, Not(InClosedRange(2, 4)));
-//
-// would generate two failures that contain the text:
-//
-//   Expected: is in range [4, 6]
-//   ...
-//   Expected: is not in range [2, 4]
-//
-// If you specify "" as the description, the failure message will
-// contain the sequence of words in the matcher name followed by the
-// parameter values printed as a tuple.  For example,
-//
-//   MATCHER_P2(InClosedRange, low, hi, "") { ... }
-//   ...
-//   EXPECT_THAT(3, InClosedRange(4, 6));
-//   EXPECT_THAT(3, Not(InClosedRange(2, 4)));
-//
-// would generate two failures that contain the text:
-//
-//   Expected: in closed range (4, 6)
-//   ...
-//   Expected: not (in closed range (2, 4))
-//
-// Types of Matcher Parameters
-// ===========================
-//
-// For the purpose of typing, you can view
-//
-//   MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... }
-//
-// as shorthand for
-//
-//   template <typename p1_type, ..., typename pk_type>
-//   FooMatcherPk<p1_type, ..., pk_type>
-//   Foo(p1_type p1, ..., pk_type pk) { ... }
-//
-// When you write Foo(v1, ..., vk), the compiler infers the types of
-// the parameters v1, ..., and vk for you.  If you are not happy with
-// the result of the type inference, you can specify the types by
-// explicitly instantiating the template, as in Foo<long, bool>(5,
-// false).  As said earlier, you don't get to (or need to) specify
-// 'arg_type' as that's determined by the context in which the matcher
-// is used.  You can assign the result of expression Foo(p1, ..., pk)
-// to a variable of type FooMatcherPk<p1_type, ..., pk_type>.  This
-// can be useful when composing matchers.
-//
-// While you can instantiate a matcher template with reference types,
-// passing the parameters by pointer usually makes your code more
-// readable.  If, however, you still want to pass a parameter by
-// reference, be aware that in the failure message generated by the
-// matcher you will see the value of the referenced object but not its
-// address.
-//
-// Explaining Match Results
-// ========================
-//
-// Sometimes the matcher description alone isn't enough to explain why
-// the match has failed or succeeded.  For example, when expecting a
-// long string, it can be very helpful to also print the diff between
-// the expected string and the actual one.  To achieve that, you can
-// optionally stream additional information to a special variable
-// named result_listener, whose type is a pointer to class
-// MatchResultListener:
-//
-//   MATCHER_P(EqualsLongString, str, "") {
-//     if (arg == str) return true;
-//
-//     *result_listener << "the difference: "
-///                     << DiffStrings(str, arg);
-//     return false;
-//   }
-//
-// Overloading Matchers
-// ====================
-//
-// You can overload matchers with different numbers of parameters:
-//
-//   MATCHER_P(Blah, a, description_string1) { ... }
-//   MATCHER_P2(Blah, a, b, description_string2) { ... }
-//
-// Caveats
-// =======
-//
-// When defining a new matcher, you should also consider implementing
-// MatcherInterface or using MakePolymorphicMatcher().  These
-// approaches require more work than the MATCHER* macros, but also
-// give you more control on the types of the value being matched and
-// the matcher parameters, which may leads to better compiler error
-// messages when the matcher is used wrong.  They also allow
-// overloading matchers based on parameter types (as opposed to just
-// based on the number of parameters).
-//
-// MATCHER*() can only be used in a namespace scope as templates cannot be
-// declared inside of a local class.
-//
-// More Information
-// ================
-//
-// To learn more about using these macros, please search for 'MATCHER'
-// on
-// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md
-
-#define MATCHER(name, description)\
-  class name##Matcher {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl()\
-           {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<>()));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>());\
-    }\
-    name##Matcher() {\
-    }\
-   private:\
-  };\
-  inline name##Matcher name() {\
-    return name##Matcher();\
-  }\
-  template <typename arg_type>\
-  bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P(name, p0, description)\
-  template <typename p0##_type>\
-  class name##MatcherP {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      explicit gmock_Impl(p0##_type gmock_p0)\
-           : p0(::std::move(gmock_p0)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type>(p0)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0));\
-    }\
-    explicit name##MatcherP(p0##_type gmock_p0) : p0(::std::move(gmock_p0)) {\
-    }\
-    p0##_type const p0;\
-   private:\
-  };\
-  template <typename p0##_type>\
-  inline name##MatcherP<p0##_type> name(p0##_type p0) {\
-    return name##MatcherP<p0##_type>(p0);\
-  }\
-  template <typename p0##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP<p0##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P2(name, p0, p1, description)\
-  template <typename p0##_type, typename p1##_type>\
-  class name##MatcherP2 {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1)\
-           : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-      p1##_type const p1;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type, p1##_type>(p0, p1)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0, p1));\
-    }\
-    name##MatcherP2(p0##_type gmock_p0, \
-        p1##_type gmock_p1) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)) {\
-    }\
-    p0##_type const p0;\
-    p1##_type const p1;\
-   private:\
-  };\
-  template <typename p0##_type, typename p1##_type>\
-  inline name##MatcherP2<p0##_type, p1##_type> name(p0##_type p0, \
-      p1##_type p1) {\
-    return name##MatcherP2<p0##_type, p1##_type>(p0, p1);\
-  }\
-  template <typename p0##_type, typename p1##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP2<p0##_type, \
-      p1##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P3(name, p0, p1, p2, description)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type>\
-  class name##MatcherP3 {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2)\
-           : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
-               p2(::std::move(gmock_p2)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-      p1##_type const p1;\
-      p2##_type const p2;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type, p1##_type, p2##_type>(p0, p1, p2)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0, p1, p2));\
-    }\
-    name##MatcherP3(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)) {\
-    }\
-    p0##_type const p0;\
-    p1##_type const p1;\
-    p2##_type const p2;\
-   private:\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type>\
-  inline name##MatcherP3<p0##_type, p1##_type, p2##_type> name(p0##_type p0, \
-      p1##_type p1, p2##_type p2) {\
-    return name##MatcherP3<p0##_type, p1##_type, p2##_type>(p0, p1, p2);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP3<p0##_type, p1##_type, \
-      p2##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P4(name, p0, p1, p2, p3, description)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type>\
-  class name##MatcherP4 {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3)\
-           : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
-               p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-      p1##_type const p1;\
-      p2##_type const p2;\
-      p3##_type const p3;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type>(p0, \
-                    p1, p2, p3)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0, p1, p2, p3));\
-    }\
-    name##MatcherP4(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)) {\
-    }\
-    p0##_type const p0;\
-    p1##_type const p1;\
-    p2##_type const p2;\
-    p3##_type const p3;\
-   private:\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type>\
-  inline name##MatcherP4<p0##_type, p1##_type, p2##_type, \
-      p3##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
-      p3##_type p3) {\
-    return name##MatcherP4<p0##_type, p1##_type, p2##_type, p3##_type>(p0, \
-        p1, p2, p3);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP4<p0##_type, p1##_type, p2##_type, \
-      p3##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P5(name, p0, p1, p2, p3, p4, description)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type>\
-  class name##MatcherP5 {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4)\
-           : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
-               p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
-               p4(::std::move(gmock_p4)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-      p1##_type const p1;\
-      p2##_type const p2;\
-      p3##_type const p3;\
-      p4##_type const p4;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
-                    p4##_type>(p0, p1, p2, p3, p4)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4));\
-    }\
-    name##MatcherP5(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, \
-        p4##_type gmock_p4) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)) {\
-    }\
-    p0##_type const p0;\
-    p1##_type const p1;\
-    p2##_type const p2;\
-    p3##_type const p3;\
-    p4##_type const p4;\
-   private:\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type>\
-  inline name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
-      p4##_type p4) {\
-    return name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type>(p0, p1, p2, p3, p4);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type>\
-  class name##MatcherP6 {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5)\
-           : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
-               p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
-               p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-      p1##_type const p1;\
-      p2##_type const p2;\
-      p3##_type const p3;\
-      p4##_type const p4;\
-      p5##_type const p5;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
-                    p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5));\
-    }\
-    name##MatcherP6(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5)) {\
-    }\
-    p0##_type const p0;\
-    p1##_type const p1;\
-    p2##_type const p2;\
-    p3##_type const p3;\
-    p4##_type const p4;\
-    p5##_type const p5;\
-   private:\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type>\
-  inline name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
-      p3##_type p3, p4##_type p4, p5##_type p5) {\
-    return name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-      p5##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type>\
-  class name##MatcherP7 {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6)\
-           : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
-               p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
-               p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \
-               p6(::std::move(gmock_p6)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-      p1##_type const p1;\
-      p2##_type const p2;\
-      p3##_type const p3;\
-      p4##_type const p4;\
-      p5##_type const p5;\
-      p6##_type const p6;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
-                    p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, \
-                    p6)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6));\
-    }\
-    name##MatcherP7(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5, p6##_type gmock_p6) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)) {\
-    }\
-    p0##_type const p0;\
-    p1##_type const p1;\
-    p2##_type const p2;\
-    p3##_type const p3;\
-    p4##_type const p4;\
-    p5##_type const p5;\
-    p6##_type const p6;\
-   private:\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type>\
-  inline name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type, p6##_type> name(p0##_type p0, p1##_type p1, \
-      p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
-      p6##_type p6) {\
-    return name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, p6);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-      p5##_type, p6##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type>\
-  class name##MatcherP8 {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6, p7##_type gmock_p7)\
-           : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
-               p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
-               p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \
-               p6(::std::move(gmock_p6)), p7(::std::move(gmock_p7)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-      p1##_type const p1;\
-      p2##_type const p2;\
-      p3##_type const p3;\
-      p4##_type const p4;\
-      p5##_type const p5;\
-      p6##_type const p6;\
-      p7##_type const p7;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
-                    p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, \
-                    p3, p4, p5, p6, p7)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7));\
-    }\
-    name##MatcherP8(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5, p6##_type gmock_p6, \
-        p7##_type gmock_p7) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
-        p7(::std::move(gmock_p7)) {\
-    }\
-    p0##_type const p0;\
-    p1##_type const p1;\
-    p2##_type const p2;\
-    p3##_type const p3;\
-    p4##_type const p4;\
-    p5##_type const p5;\
-    p6##_type const p6;\
-    p7##_type const p7;\
-   private:\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type>\
-  inline name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type, p6##_type, p7##_type> name(p0##_type p0, \
-      p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
-      p6##_type p6, p7##_type p7) {\
-    return name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, p3, p4, p5, \
-        p6, p7);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-      p5##_type, p6##_type, \
-      p7##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type>\
-  class name##MatcherP9 {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8)\
-           : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
-               p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
-               p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \
-               p6(::std::move(gmock_p6)), p7(::std::move(gmock_p7)), \
-               p8(::std::move(gmock_p8)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-      p1##_type const p1;\
-      p2##_type const p2;\
-      p3##_type const p3;\
-      p4##_type const p4;\
-      p5##_type const p5;\
-      p6##_type const p6;\
-      p7##_type const p7;\
-      p8##_type const p8;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
-                    p4##_type, p5##_type, p6##_type, p7##_type, \
-                    p8##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8));\
-    }\
-    name##MatcherP9(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
-        p8##_type gmock_p8) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
-        p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)) {\
-    }\
-    p0##_type const p0;\
-    p1##_type const p1;\
-    p2##_type const p2;\
-    p3##_type const p3;\
-    p4##_type const p4;\
-    p5##_type const p5;\
-    p6##_type const p6;\
-    p7##_type const p7;\
-    p8##_type const p8;\
-   private:\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type>\
-  inline name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type, p6##_type, p7##_type, \
-      p8##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
-      p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \
-      p8##_type p8) {\
-    return name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type, p6##_type, p7##_type, p8##_type>(p0, p1, p2, \
-        p3, p4, p5, p6, p7, p8);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
-      p5##_type, p6##_type, p7##_type, \
-      p8##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description)\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type, \
-      typename p9##_type>\
-  class name##MatcherP10 {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
-          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
-          p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
-          p9##_type gmock_p9)\
-           : p0(::std::move(gmock_p0)), p1(::std::move(gmock_p1)), \
-               p2(::std::move(gmock_p2)), p3(::std::move(gmock_p3)), \
-               p4(::std::move(gmock_p4)), p5(::std::move(gmock_p5)), \
-               p6(::std::move(gmock_p6)), p7(::std::move(gmock_p7)), \
-               p8(::std::move(gmock_p8)), p9(::std::move(gmock_p9)) {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\
-      p0##_type const p0;\
-      p1##_type const p1;\
-      p2##_type const p2;\
-      p3##_type const p3;\
-      p4##_type const p4;\
-      p5##_type const p5;\
-      p6##_type const p6;\
-      p7##_type const p7;\
-      p8##_type const p8;\
-      p9##_type const p9;\
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
-                    p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
-                    p9##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9));\
-    }\
-    name##MatcherP10(p0##_type gmock_p0, p1##_type gmock_p1, \
-        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
-        p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
-        p8##_type gmock_p8, p9##_type gmock_p9) : p0(::std::move(gmock_p0)), \
-        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
-        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
-        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
-        p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)), \
-        p9(::std::move(gmock_p9)) {\
-    }\
-    p0##_type const p0;\
-    p1##_type const p1;\
-    p2##_type const p2;\
-    p3##_type const p3;\
-    p4##_type const p4;\
-    p5##_type const p5;\
-    p6##_type const p6;\
-    p7##_type const p7;\
-    p8##_type const p8;\
-    p9##_type const p9;\
-   private:\
-  };\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type, \
-      typename p9##_type>\
-  inline name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
-      p9##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
-      p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
-      p9##_type p9) {\
-    return name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
-        p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, p9##_type>(p0, \
-        p1, p2, p3, p4, p5, p6, p7, p8, p9);\
-  }\
-  template <typename p0##_type, typename p1##_type, typename p2##_type, \
-      typename p3##_type, typename p4##_type, typename p5##_type, \
-      typename p6##_type, typename p7##_type, typename p8##_type, \
-      typename p9##_type>\
-  template <typename arg_type>\
-  bool name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
-      p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
-      p9##_type>::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-generated-matchers.h.pump b/ext/googletest/googlemock/include/gmock/gmock-generated-matchers.h.pump
deleted file mode 100644
index ae90917..0000000
--- a/ext/googletest/googlemock/include/gmock/gmock-generated-matchers.h.pump
+++ /dev/null
@@ -1,346 +0,0 @@
-$$ -*- mode: c++; -*-
-$$ This is a Pump source file. Please use Pump to convert
-$$ it to gmock-generated-matchers.h.
-$$
-$var n = 10  $$ The maximum arity we support.
-$$ }} This line fixes auto-indentation of the following code in Emacs.
-// Copyright 2008, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file implements some commonly used variadic matchers.
-
-// GOOGLETEST_CM0002 DO NOT DELETE
-
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
-
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-#include "gmock/gmock-matchers.h"
-
-// The MATCHER* family of macros can be used in a namespace scope to
-// define custom matchers easily.
-//
-// Basic Usage
-// ===========
-//
-// The syntax
-//
-//   MATCHER(name, description_string) { statements; }
-//
-// defines a matcher with the given name that executes the statements,
-// which must return a bool to indicate if the match succeeds.  Inside
-// the statements, you can refer to the value being matched by 'arg',
-// and refer to its type by 'arg_type'.
-//
-// The description string documents what the matcher does, and is used
-// to generate the failure message when the match fails.  Since a
-// MATCHER() is usually defined in a header file shared by multiple
-// C++ source files, we require the description to be a C-string
-// literal to avoid possible side effects.  It can be empty, in which
-// case we'll use the sequence of words in the matcher name as the
-// description.
-//
-// For example:
-//
-//   MATCHER(IsEven, "") { return (arg % 2) == 0; }
-//
-// allows you to write
-//
-//   // Expects mock_foo.Bar(n) to be called where n is even.
-//   EXPECT_CALL(mock_foo, Bar(IsEven()));
-//
-// or,
-//
-//   // Verifies that the value of some_expression is even.
-//   EXPECT_THAT(some_expression, IsEven());
-//
-// If the above assertion fails, it will print something like:
-//
-//   Value of: some_expression
-//   Expected: is even
-//     Actual: 7
-//
-// where the description "is even" is automatically calculated from the
-// matcher name IsEven.
-//
-// Argument Type
-// =============
-//
-// Note that the type of the value being matched (arg_type) is
-// determined by the context in which you use the matcher and is
-// supplied to you by the compiler, so you don't need to worry about
-// declaring it (nor can you).  This allows the matcher to be
-// polymorphic.  For example, IsEven() can be used to match any type
-// where the value of "(arg % 2) == 0" can be implicitly converted to
-// a bool.  In the "Bar(IsEven())" example above, if method Bar()
-// takes an int, 'arg_type' will be int; if it takes an unsigned long,
-// 'arg_type' will be unsigned long; and so on.
-//
-// Parameterizing Matchers
-// =======================
-//
-// Sometimes you'll want to parameterize the matcher.  For that you
-// can use another macro:
-//
-//   MATCHER_P(name, param_name, description_string) { statements; }
-//
-// For example:
-//
-//   MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; }
-//
-// will allow you to write:
-//
-//   EXPECT_THAT(Blah("a"), HasAbsoluteValue(n));
-//
-// which may lead to this message (assuming n is 10):
-//
-//   Value of: Blah("a")
-//   Expected: has absolute value 10
-//     Actual: -9
-//
-// Note that both the matcher description and its parameter are
-// printed, making the message human-friendly.
-//
-// In the matcher definition body, you can write 'foo_type' to
-// reference the type of a parameter named 'foo'.  For example, in the
-// body of MATCHER_P(HasAbsoluteValue, value) above, you can write
-// 'value_type' to refer to the type of 'value'.
-//
-// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to
-// support multi-parameter matchers.
-//
-// Describing Parameterized Matchers
-// =================================
-//
-// The last argument to MATCHER*() is a string-typed expression.  The
-// expression can reference all of the matcher's parameters and a
-// special bool-typed variable named 'negation'.  When 'negation' is
-// false, the expression should evaluate to the matcher's description;
-// otherwise it should evaluate to the description of the negation of
-// the matcher.  For example,
-//
-//   using testing::PrintToString;
-//
-//   MATCHER_P2(InClosedRange, low, hi,
-//       std::string(negation ? "is not" : "is") + " in range [" +
-//       PrintToString(low) + ", " + PrintToString(hi) + "]") {
-//     return low <= arg && arg <= hi;
-//   }
-//   ...
-//   EXPECT_THAT(3, InClosedRange(4, 6));
-//   EXPECT_THAT(3, Not(InClosedRange(2, 4)));
-//
-// would generate two failures that contain the text:
-//
-//   Expected: is in range [4, 6]
-//   ...
-//   Expected: is not in range [2, 4]
-//
-// If you specify "" as the description, the failure message will
-// contain the sequence of words in the matcher name followed by the
-// parameter values printed as a tuple.  For example,
-//
-//   MATCHER_P2(InClosedRange, low, hi, "") { ... }
-//   ...
-//   EXPECT_THAT(3, InClosedRange(4, 6));
-//   EXPECT_THAT(3, Not(InClosedRange(2, 4)));
-//
-// would generate two failures that contain the text:
-//
-//   Expected: in closed range (4, 6)
-//   ...
-//   Expected: not (in closed range (2, 4))
-//
-// Types of Matcher Parameters
-// ===========================
-//
-// For the purpose of typing, you can view
-//
-//   MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... }
-//
-// as shorthand for
-//
-//   template <typename p1_type, ..., typename pk_type>
-//   FooMatcherPk<p1_type, ..., pk_type>
-//   Foo(p1_type p1, ..., pk_type pk) { ... }
-//
-// When you write Foo(v1, ..., vk), the compiler infers the types of
-// the parameters v1, ..., and vk for you.  If you are not happy with
-// the result of the type inference, you can specify the types by
-// explicitly instantiating the template, as in Foo<long, bool>(5,
-// false).  As said earlier, you don't get to (or need to) specify
-// 'arg_type' as that's determined by the context in which the matcher
-// is used.  You can assign the result of expression Foo(p1, ..., pk)
-// to a variable of type FooMatcherPk<p1_type, ..., pk_type>.  This
-// can be useful when composing matchers.
-//
-// While you can instantiate a matcher template with reference types,
-// passing the parameters by pointer usually makes your code more
-// readable.  If, however, you still want to pass a parameter by
-// reference, be aware that in the failure message generated by the
-// matcher you will see the value of the referenced object but not its
-// address.
-//
-// Explaining Match Results
-// ========================
-//
-// Sometimes the matcher description alone isn't enough to explain why
-// the match has failed or succeeded.  For example, when expecting a
-// long string, it can be very helpful to also print the diff between
-// the expected string and the actual one.  To achieve that, you can
-// optionally stream additional information to a special variable
-// named result_listener, whose type is a pointer to class
-// MatchResultListener:
-//
-//   MATCHER_P(EqualsLongString, str, "") {
-//     if (arg == str) return true;
-//
-//     *result_listener << "the difference: "
-///                     << DiffStrings(str, arg);
-//     return false;
-//   }
-//
-// Overloading Matchers
-// ====================
-//
-// You can overload matchers with different numbers of parameters:
-//
-//   MATCHER_P(Blah, a, description_string1) { ... }
-//   MATCHER_P2(Blah, a, b, description_string2) { ... }
-//
-// Caveats
-// =======
-//
-// When defining a new matcher, you should also consider implementing
-// MatcherInterface or using MakePolymorphicMatcher().  These
-// approaches require more work than the MATCHER* macros, but also
-// give you more control on the types of the value being matched and
-// the matcher parameters, which may leads to better compiler error
-// messages when the matcher is used wrong.  They also allow
-// overloading matchers based on parameter types (as opposed to just
-// based on the number of parameters).
-//
-// MATCHER*() can only be used in a namespace scope as templates cannot be
-// declared inside of a local class.
-//
-// More Information
-// ================
-//
-// To learn more about using these macros, please search for 'MATCHER'
-// on
-// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md
-
-$range i 0..n
-$for i
-
-[[
-$var macro_name = [[$if i==0 [[MATCHER]] $elif i==1 [[MATCHER_P]]
-                                         $else [[MATCHER_P$i]]]]
-$var class_name = [[name##Matcher[[$if i==0 [[]] $elif i==1 [[P]]
-                                                 $else [[P$i]]]]]]
-$range j 0..i-1
-$var template = [[$if i==0 [[]] $else [[
-
-  template <$for j, [[typename p$j##_type]]>\
-]]]]
-$var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]]
-$var impl_ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]]
-$var impl_inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(::std::move(gmock_p$j))]]]]]]
-$var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(::std::move(gmock_p$j))]]]]]]
-$var params = [[$for j, [[p$j]]]]
-$var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]]
-$var param_types_and_names = [[$for j, [[p$j##_type p$j]]]]
-$var param_field_decls = [[$for j
-[[
-
-      p$j##_type const p$j;\
-]]]]
-$var param_field_decls2 = [[$for j
-[[
-
-    p$j##_type const p$j;\
-]]]]
-
-#define $macro_name(name$for j [[, p$j]], description)\$template
-  class $class_name {\
-   public:\
-    template <typename arg_type>\
-    class gmock_Impl : public ::testing::MatcherInterface<\
-        GTEST_REFERENCE_TO_CONST_(arg_type)> {\
-     public:\
-      [[$if i==1 [[explicit ]]]]gmock_Impl($impl_ctor_param_list)\
-          $impl_inits {}\
-      virtual bool MatchAndExplain(\
-          GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-          ::testing::MatchResultListener* result_listener) const;\
-      virtual void DescribeTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(false);\
-      }\
-      virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\
-        *gmock_os << FormatDescription(true);\
-      }\$param_field_decls
-     private:\
-      ::std::string FormatDescription(bool negation) const {\
-        ::std::string gmock_description = (description);\
-        if (!gmock_description.empty()) {\
-          return gmock_description;\
-        }\
-        return ::testing::internal::FormatMatcherDescription(\
-            negation, #name, \
-            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
-                ::std::tuple<$for j, [[p$j##_type]]>($for j, [[p$j]])));\
-      }\
-    };\
-    template <typename arg_type>\
-    operator ::testing::Matcher<arg_type>() const {\
-      return ::testing::Matcher<arg_type>(\
-          new gmock_Impl<arg_type>($params));\
-    }\
-    [[$if i==1 [[explicit ]]]]$class_name($ctor_param_list)$inits {\
-    }\$param_field_decls2
-   private:\
-  };\$template
-  inline $class_name$param_types name($param_types_and_names) {\
-    return $class_name$param_types($params);\
-  }\$template
-  template <typename arg_type>\
-  bool $class_name$param_types::gmock_Impl<arg_type>::MatchAndExplain(\
-      GTEST_REFERENCE_TO_CONST_(arg_type) arg,\
-      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\
-          const
-]]
-
-
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-matchers.h b/ext/googletest/googlemock/include/gmock/gmock-matchers.h
index 28e188b..f1bb22c 100644
--- a/ext/googletest/googlemock/include/gmock/gmock-matchers.h
+++ b/ext/googletest/googlemock/include/gmock/gmock-matchers.h
@@ -30,7 +30,220 @@
 
 // Google Mock - a framework for writing C++ mock classes.
 //
-// This file implements some commonly used argument matchers.  More
+// The MATCHER* family of macros can be used in a namespace scope to
+// define custom matchers easily.
+//
+// Basic Usage
+// ===========
+//
+// The syntax
+//
+//   MATCHER(name, description_string) { statements; }
+//
+// defines a matcher with the given name that executes the statements,
+// which must return a bool to indicate if the match succeeds.  Inside
+// the statements, you can refer to the value being matched by 'arg',
+// and refer to its type by 'arg_type'.
+//
+// The description string documents what the matcher does, and is used
+// to generate the failure message when the match fails.  Since a
+// MATCHER() is usually defined in a header file shared by multiple
+// C++ source files, we require the description to be a C-string
+// literal to avoid possible side effects.  It can be empty, in which
+// case we'll use the sequence of words in the matcher name as the
+// description.
+//
+// For example:
+//
+//   MATCHER(IsEven, "") { return (arg % 2) == 0; }
+//
+// allows you to write
+//
+//   // Expects mock_foo.Bar(n) to be called where n is even.
+//   EXPECT_CALL(mock_foo, Bar(IsEven()));
+//
+// or,
+//
+//   // Verifies that the value of some_expression is even.
+//   EXPECT_THAT(some_expression, IsEven());
+//
+// If the above assertion fails, it will print something like:
+//
+//   Value of: some_expression
+//   Expected: is even
+//     Actual: 7
+//
+// where the description "is even" is automatically calculated from the
+// matcher name IsEven.
+//
+// Argument Type
+// =============
+//
+// Note that the type of the value being matched (arg_type) is
+// determined by the context in which you use the matcher and is
+// supplied to you by the compiler, so you don't need to worry about
+// declaring it (nor can you).  This allows the matcher to be
+// polymorphic.  For example, IsEven() can be used to match any type
+// where the value of "(arg % 2) == 0" can be implicitly converted to
+// a bool.  In the "Bar(IsEven())" example above, if method Bar()
+// takes an int, 'arg_type' will be int; if it takes an unsigned long,
+// 'arg_type' will be unsigned long; and so on.
+//
+// Parameterizing Matchers
+// =======================
+//
+// Sometimes you'll want to parameterize the matcher.  For that you
+// can use another macro:
+//
+//   MATCHER_P(name, param_name, description_string) { statements; }
+//
+// For example:
+//
+//   MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; }
+//
+// will allow you to write:
+//
+//   EXPECT_THAT(Blah("a"), HasAbsoluteValue(n));
+//
+// which may lead to this message (assuming n is 10):
+//
+//   Value of: Blah("a")
+//   Expected: has absolute value 10
+//     Actual: -9
+//
+// Note that both the matcher description and its parameter are
+// printed, making the message human-friendly.
+//
+// In the matcher definition body, you can write 'foo_type' to
+// reference the type of a parameter named 'foo'.  For example, in the
+// body of MATCHER_P(HasAbsoluteValue, value) above, you can write
+// 'value_type' to refer to the type of 'value'.
+//
+// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to
+// support multi-parameter matchers.
+//
+// Describing Parameterized Matchers
+// =================================
+//
+// The last argument to MATCHER*() is a string-typed expression.  The
+// expression can reference all of the matcher's parameters and a
+// special bool-typed variable named 'negation'.  When 'negation' is
+// false, the expression should evaluate to the matcher's description;
+// otherwise it should evaluate to the description of the negation of
+// the matcher.  For example,
+//
+//   using testing::PrintToString;
+//
+//   MATCHER_P2(InClosedRange, low, hi,
+//       std::string(negation ? "is not" : "is") + " in range [" +
+//       PrintToString(low) + ", " + PrintToString(hi) + "]") {
+//     return low <= arg && arg <= hi;
+//   }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//   EXPECT_THAT(3, Not(InClosedRange(2, 4)));
+//
+// would generate two failures that contain the text:
+//
+//   Expected: is in range [4, 6]
+//   ...
+//   Expected: is not in range [2, 4]
+//
+// If you specify "" as the description, the failure message will
+// contain the sequence of words in the matcher name followed by the
+// parameter values printed as a tuple.  For example,
+//
+//   MATCHER_P2(InClosedRange, low, hi, "") { ... }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//   EXPECT_THAT(3, Not(InClosedRange(2, 4)));
+//
+// would generate two failures that contain the text:
+//
+//   Expected: in closed range (4, 6)
+//   ...
+//   Expected: not (in closed range (2, 4))
+//
+// Types of Matcher Parameters
+// ===========================
+//
+// For the purpose of typing, you can view
+//
+//   MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... }
+//
+// as shorthand for
+//
+//   template <typename p1_type, ..., typename pk_type>
+//   FooMatcherPk<p1_type, ..., pk_type>
+//   Foo(p1_type p1, ..., pk_type pk) { ... }
+//
+// When you write Foo(v1, ..., vk), the compiler infers the types of
+// the parameters v1, ..., and vk for you.  If you are not happy with
+// the result of the type inference, you can specify the types by
+// explicitly instantiating the template, as in Foo<long, bool>(5,
+// false).  As said earlier, you don't get to (or need to) specify
+// 'arg_type' as that's determined by the context in which the matcher
+// is used.  You can assign the result of expression Foo(p1, ..., pk)
+// to a variable of type FooMatcherPk<p1_type, ..., pk_type>.  This
+// can be useful when composing matchers.
+//
+// While you can instantiate a matcher template with reference types,
+// passing the parameters by pointer usually makes your code more
+// readable.  If, however, you still want to pass a parameter by
+// reference, be aware that in the failure message generated by the
+// matcher you will see the value of the referenced object but not its
+// address.
+//
+// Explaining Match Results
+// ========================
+//
+// Sometimes the matcher description alone isn't enough to explain why
+// the match has failed or succeeded.  For example, when expecting a
+// long string, it can be very helpful to also print the diff between
+// the expected string and the actual one.  To achieve that, you can
+// optionally stream additional information to a special variable
+// named result_listener, whose type is a pointer to class
+// MatchResultListener:
+//
+//   MATCHER_P(EqualsLongString, str, "") {
+//     if (arg == str) return true;
+//
+//     *result_listener << "the difference: "
+///                     << DiffStrings(str, arg);
+//     return false;
+//   }
+//
+// Overloading Matchers
+// ====================
+//
+// You can overload matchers with different numbers of parameters:
+//
+//   MATCHER_P(Blah, a, description_string1) { ... }
+//   MATCHER_P2(Blah, a, b, description_string2) { ... }
+//
+// Caveats
+// =======
+//
+// When defining a new matcher, you should also consider implementing
+// MatcherInterface or using MakePolymorphicMatcher().  These
+// approaches require more work than the MATCHER* macros, but also
+// give you more control on the types of the value being matched and
+// the matcher parameters, which may leads to better compiler error
+// messages when the matcher is used wrong.  They also allow
+// overloading matchers based on parameter types (as opposed to just
+// based on the number of parameters).
+//
+// MATCHER*() can only be used in a namespace scope as templates cannot be
+// declared inside of a local class.
+//
+// More Information
+// ================
+//
+// To learn more about using these macros, please search for 'MATCHER'
+// on
+// https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md
+//
+// This file also implements some commonly used argument matchers.  More
 // matchers can be defined by the user implementing the
 // MatcherInterface<T> interface if necessary.
 //
@@ -39,11 +252,11 @@
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
 
-#include <math.h>
 #include <algorithm>
+#include <cmath>
 #include <initializer_list>
 #include <iterator>
 #include <limits>
@@ -54,8 +267,10 @@
 #include <type_traits>
 #include <utility>
 #include <vector>
+
 #include "gmock/internal/gmock-internal-utils.h"
 #include "gmock/internal/gmock-port.h"
+#include "gmock/internal/gmock-pp.h"
 #include "gtest/gtest.h"
 
 // MSVC warning C5046 is new as of VS2017 version 15.8.
@@ -128,7 +343,7 @@
     // constructor from M (this usually happens when T has an implicit
     // constructor from any type).
     //
-    // It won't work to unconditionally implict_cast
+    // It won't work to unconditionally implicit_cast
     // polymorphic_matcher_or_value to Matcher<T> because it won't trigger
     // a user-defined conversion from M to T if one exists (assuming M is
     // a value).
@@ -141,7 +356,7 @@
   template <bool Ignore>
   static Matcher<T> CastImpl(const M& polymorphic_matcher_or_value,
                              std::true_type /* convertible_to_matcher */,
-                             bool_constant<Ignore>) {
+                             std::integral_constant<bool, Ignore>) {
     // M is implicitly convertible to Matcher<T>, which means that either
     // M is a polymorphic matcher or Matcher<T> has an implicit constructor
     // from M.  In both cases using the implicit conversion will produce a
@@ -209,7 +424,14 @@
               !std::is_base_of<FromType, ToType>::value,
           "Can't implicitly convert from <base> to <derived>");
 
-      return source_matcher_.MatchAndExplain(static_cast<U>(x), listener);
+      // Do the cast to `U` explicitly if necessary.
+      // Otherwise, let implicit conversions do the trick.
+      using CastType =
+          typename std::conditional<std::is_convertible<T&, const U&>::value,
+                                    T&, U>::type;
+
+      return source_matcher_.MatchAndExplain(static_cast<CastType>(x),
+                                             listener);
     }
 
     void DescribeTo(::std::ostream* os) const override {
@@ -222,8 +444,6 @@
 
    private:
     const Matcher<U> source_matcher_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 };
 
@@ -235,6 +455,50 @@
   static Matcher<T> Cast(const Matcher<T>& matcher) { return matcher; }
 };
 
+// Template specialization for parameterless Matcher.
+template <typename Derived>
+class MatcherBaseImpl {
+ public:
+  MatcherBaseImpl() = default;
+
+  template <typename T>
+  operator ::testing::Matcher<T>() const {  // NOLINT(runtime/explicit)
+    return ::testing::Matcher<T>(new
+                                 typename Derived::template gmock_Impl<T>());
+  }
+};
+
+// Template specialization for Matcher with parameters.
+template <template <typename...> class Derived, typename... Ts>
+class MatcherBaseImpl<Derived<Ts...>> {
+ public:
+  // Mark the constructor explicit for single argument T to avoid implicit
+  // conversions.
+  template <typename E = std::enable_if<sizeof...(Ts) == 1>,
+            typename E::type* = nullptr>
+  explicit MatcherBaseImpl(Ts... params)
+      : params_(std::forward<Ts>(params)...) {}
+  template <typename E = std::enable_if<sizeof...(Ts) != 1>,
+            typename = typename E::type>
+  MatcherBaseImpl(Ts... params)  // NOLINT
+      : params_(std::forward<Ts>(params)...) {}
+
+  template <typename F>
+  operator ::testing::Matcher<F>() const {  // NOLINT(runtime/explicit)
+    return Apply<F>(MakeIndexSequence<sizeof...(Ts)>{});
+  }
+
+ private:
+  template <typename F, std::size_t... tuple_ids>
+  ::testing::Matcher<F> Apply(IndexSequence<tuple_ids...>) const {
+    return ::testing::Matcher<F>(
+        new typename Derived<Ts...>::template gmock_Impl<F>(
+            std::get<tuple_ids>(params_)...));
+  }
+
+  const std::tuple<Ts...> params_;
+};
+
 }  // namespace internal
 
 // In order to be safe and clear, casting between different matcher
@@ -246,56 +510,43 @@
   return internal::MatcherCastImpl<T, M>::Cast(matcher);
 }
 
-// Implements SafeMatcherCast().
-//
-// FIXME: The intermediate SafeMatcherCastImpl class was introduced as a
-// workaround for a compiler bug, and can now be removed.
-template <typename T>
-class SafeMatcherCastImpl {
- public:
-  // This overload handles polymorphic matchers and values only since
-  // monomorphic matchers are handled by the next one.
-  template <typename M>
-  static inline Matcher<T> Cast(const M& polymorphic_matcher_or_value) {
-    return internal::MatcherCastImpl<T, M>::Cast(polymorphic_matcher_or_value);
-  }
-
-  // This overload handles monomorphic matchers.
-  //
-  // In general, if type T can be implicitly converted to type U, we can
-  // safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
-  // contravariant): just keep a copy of the original Matcher<U>, convert the
-  // argument from type T to U, and then pass it to the underlying Matcher<U>.
-  // The only exception is when U is a reference and T is not, as the
-  // underlying Matcher<U> may be interested in the argument's address, which
-  // is not preserved in the conversion from T to U.
-  template <typename U>
-  static inline Matcher<T> Cast(const Matcher<U>& matcher) {
-    // Enforce that T can be implicitly converted to U.
-    GTEST_COMPILE_ASSERT_((std::is_convertible<T, U>::value),
-                          "T must be implicitly convertible to U");
-    // Enforce that we are not converting a non-reference type T to a reference
-    // type U.
-    GTEST_COMPILE_ASSERT_(
-        std::is_reference<T>::value || !std::is_reference<U>::value,
-        cannot_convert_non_reference_arg_to_reference);
-    // In case both T and U are arithmetic types, enforce that the
-    // conversion is not lossy.
-    typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
-    typedef GTEST_REMOVE_REFERENCE_AND_CONST_(U) RawU;
-    const bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther;
-    const bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther;
-    GTEST_COMPILE_ASSERT_(
-        kTIsOther || kUIsOther ||
-        (internal::LosslessArithmeticConvertible<RawT, RawU>::value),
-        conversion_of_arithmetic_types_must_be_lossless);
-    return MatcherCast<T>(matcher);
-  }
-};
-
+// This overload handles polymorphic matchers and values only since
+// monomorphic matchers are handled by the next one.
 template <typename T, typename M>
-inline Matcher<T> SafeMatcherCast(const M& polymorphic_matcher) {
-  return SafeMatcherCastImpl<T>::Cast(polymorphic_matcher);
+inline Matcher<T> SafeMatcherCast(const M& polymorphic_matcher_or_value) {
+  return MatcherCast<T>(polymorphic_matcher_or_value);
+}
+
+// This overload handles monomorphic matchers.
+//
+// In general, if type T can be implicitly converted to type U, we can
+// safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
+// contravariant): just keep a copy of the original Matcher<U>, convert the
+// argument from type T to U, and then pass it to the underlying Matcher<U>.
+// The only exception is when U is a reference and T is not, as the
+// underlying Matcher<U> may be interested in the argument's address, which
+// is not preserved in the conversion from T to U.
+template <typename T, typename U>
+inline Matcher<T> SafeMatcherCast(const Matcher<U>& matcher) {
+  // Enforce that T can be implicitly converted to U.
+  static_assert(std::is_convertible<const T&, const U&>::value,
+                "T must be implicitly convertible to U");
+  // Enforce that we are not converting a non-reference type T to a reference
+  // type U.
+  GTEST_COMPILE_ASSERT_(
+      std::is_reference<T>::value || !std::is_reference<U>::value,
+      cannot_convert_non_reference_arg_to_reference);
+  // In case both T and U are arithmetic types, enforce that the
+  // conversion is not lossy.
+  typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
+  typedef GTEST_REMOVE_REFERENCE_AND_CONST_(U) RawU;
+  constexpr bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther;
+  constexpr bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther;
+  GTEST_COMPILE_ASSERT_(
+      kTIsOther || kUIsOther ||
+      (internal::LosslessArithmeticConvertible<RawT, RawU>::value),
+      conversion_of_arithmetic_types_must_be_lossless);
+  return MatcherCast<T>(matcher);
 }
 
 // A<T>() returns a matcher that matches any value of type T.
@@ -484,31 +735,25 @@
   return TransformTupleValuesHelper<Tuple, Func, OutIter>::Run(f, t, out);
 }
 
-// Implements A<T>().
-template <typename T>
-class AnyMatcherImpl : public MatcherInterface<const T&> {
- public:
-  bool MatchAndExplain(const T& /* x */,
-                       MatchResultListener* /* listener */) const override {
-    return true;
-  }
-  void DescribeTo(::std::ostream* os) const override { *os << "is anything"; }
-  void DescribeNegationTo(::std::ostream* os) const override {
-    // This is mostly for completeness' safe, as it's not very useful
-    // to write Not(A<bool>()).  However we cannot completely rule out
-    // such a possibility, and it doesn't hurt to be prepared.
-    *os << "never matches";
-  }
-};
-
 // Implements _, a matcher that matches any value of any
 // type.  This is a polymorphic matcher, so we need a template type
 // conversion operator to make it appearing as a Matcher<T> for any
 // type T.
 class AnythingMatcher {
  public:
+  using is_gtest_matcher = void;
+
   template <typename T>
-  operator Matcher<T>() const { return A<T>(); }
+  bool MatchAndExplain(const T& /* x */, std::ostream* /* listener */) const {
+    return true;
+  }
+  void DescribeTo(std::ostream* os) const { *os << "is anything"; }
+  void DescribeNegationTo(::std::ostream* os) const {
+    // This is mostly for completeness' sake, as it's not very useful
+    // to write Not(A<bool>()).  However we cannot completely rule out
+    // such a possibility, and it doesn't hurt to be prepared.
+    *os << "never matches";
+  }
 };
 
 // Implements the polymorphic IsNull() matcher, which matches any raw or smart
@@ -608,13 +853,9 @@
 
    private:
     const Super& object_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
   T& object_;
-
-  GTEST_DISALLOW_ASSIGN_(RefMatcher);
 };
 
 // Polymorphic helper functions for narrow and wide string matchers.
@@ -656,19 +897,20 @@
 template <typename StringType>
 class StrEqualityMatcher {
  public:
-  StrEqualityMatcher(const StringType& str, bool expect_eq,
-                     bool case_sensitive)
-      : string_(str), expect_eq_(expect_eq), case_sensitive_(case_sensitive) {}
+  StrEqualityMatcher(StringType str, bool expect_eq, bool case_sensitive)
+      : string_(std::move(str)),
+        expect_eq_(expect_eq),
+        case_sensitive_(case_sensitive) {}
 
-#if GTEST_HAS_ABSL
-  bool MatchAndExplain(const absl::string_view& s,
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
                        MatchResultListener* listener) const {
-    // This should fail to compile if absl::string_view is used with wide
+    // This should fail to compile if StringView is used with wide
     // strings.
     const StringType& str = std::string(s);
     return MatchAndExplain(str, listener);
   }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
   // Accepts pointer types, particularly:
   //   const char*
@@ -686,11 +928,11 @@
   // Matches anything that can convert to StringType.
   //
   // This is a template, not just a plain function with const StringType&,
-  // because absl::string_view has some interfering non-explicit constructors.
+  // because StringView has some interfering non-explicit constructors.
   template <typename MatcheeStringType>
   bool MatchAndExplain(const MatcheeStringType& s,
                        MatchResultListener* /* listener */) const {
-    const StringType& s2(s);
+    const StringType s2(s);
     const bool eq = case_sensitive_ ? s2 == string_ :
         CaseInsensitiveStringEquals(s2, string_);
     return expect_eq_ == eq;
@@ -717,8 +959,6 @@
   const StringType string_;
   const bool expect_eq_;
   const bool case_sensitive_;
-
-  GTEST_DISALLOW_ASSIGN_(StrEqualityMatcher);
 };
 
 // Implements the polymorphic HasSubstr(substring) matcher, which
@@ -730,15 +970,15 @@
   explicit HasSubstrMatcher(const StringType& substring)
       : substring_(substring) {}
 
-#if GTEST_HAS_ABSL
-  bool MatchAndExplain(const absl::string_view& s,
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
                        MatchResultListener* listener) const {
-    // This should fail to compile if absl::string_view is used with wide
+    // This should fail to compile if StringView is used with wide
     // strings.
     const StringType& str = std::string(s);
     return MatchAndExplain(str, listener);
   }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
   // Accepts pointer types, particularly:
   //   const char*
@@ -753,12 +993,11 @@
   // Matches anything that can convert to StringType.
   //
   // This is a template, not just a plain function with const StringType&,
-  // because absl::string_view has some interfering non-explicit constructors.
+  // because StringView has some interfering non-explicit constructors.
   template <typename MatcheeStringType>
   bool MatchAndExplain(const MatcheeStringType& s,
                        MatchResultListener* /* listener */) const {
-    const StringType& s2(s);
-    return s2.find(substring_) != StringType::npos;
+    return StringType(s).find(substring_) != StringType::npos;
   }
 
   // Describes what this matcher matches.
@@ -774,8 +1013,6 @@
 
  private:
   const StringType substring_;
-
-  GTEST_DISALLOW_ASSIGN_(HasSubstrMatcher);
 };
 
 // Implements the polymorphic StartsWith(substring) matcher, which
@@ -787,15 +1024,15 @@
   explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) {
   }
 
-#if GTEST_HAS_ABSL
-  bool MatchAndExplain(const absl::string_view& s,
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
                        MatchResultListener* listener) const {
-    // This should fail to compile if absl::string_view is used with wide
+    // This should fail to compile if StringView is used with wide
     // strings.
     const StringType& str = std::string(s);
     return MatchAndExplain(str, listener);
   }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
   // Accepts pointer types, particularly:
   //   const char*
@@ -810,7 +1047,7 @@
   // Matches anything that can convert to StringType.
   //
   // This is a template, not just a plain function with const StringType&,
-  // because absl::string_view has some interfering non-explicit constructors.
+  // because StringView has some interfering non-explicit constructors.
   template <typename MatcheeStringType>
   bool MatchAndExplain(const MatcheeStringType& s,
                        MatchResultListener* /* listener */) const {
@@ -831,8 +1068,6 @@
 
  private:
   const StringType prefix_;
-
-  GTEST_DISALLOW_ASSIGN_(StartsWithMatcher);
 };
 
 // Implements the polymorphic EndsWith(substring) matcher, which
@@ -843,15 +1078,15 @@
  public:
   explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {}
 
-#if GTEST_HAS_ABSL
-  bool MatchAndExplain(const absl::string_view& s,
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
                        MatchResultListener* listener) const {
-    // This should fail to compile if absl::string_view is used with wide
+    // This should fail to compile if StringView is used with wide
     // strings.
     const StringType& str = std::string(s);
     return MatchAndExplain(str, listener);
   }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
   // Accepts pointer types, particularly:
   //   const char*
@@ -866,7 +1101,7 @@
   // Matches anything that can convert to StringType.
   //
   // This is a template, not just a plain function with const StringType&,
-  // because absl::string_view has some interfering non-explicit constructors.
+  // because StringView has some interfering non-explicit constructors.
   template <typename MatcheeStringType>
   bool MatchAndExplain(const MatcheeStringType& s,
                        MatchResultListener* /* listener */) const {
@@ -887,8 +1122,6 @@
 
  private:
   const StringType suffix_;
-
-  GTEST_DISALLOW_ASSIGN_(EndsWithMatcher);
 };
 
 // Implements a matcher that compares the two fields of a 2-tuple
@@ -982,8 +1215,6 @@
 
  private:
   const Matcher<T> matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(NotMatcherImpl);
 };
 
 // Implements the Not(m) matcher, which matches a value that doesn't
@@ -1002,8 +1233,6 @@
 
  private:
   InnerMatcher matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(NotMatcher);
 };
 
 // Implements the AllOf(m1, m2) matcher for a particular argument type
@@ -1065,8 +1294,6 @@
 
  private:
   const std::vector<Matcher<T> > matchers_;
-
-  GTEST_DISALLOW_ASSIGN_(AllOfMatcherImpl);
 };
 
 // VariadicMatcher is used for the variadic implementation of
@@ -1081,6 +1308,9 @@
     static_assert(sizeof...(Args) > 0, "Must have at least one matcher.");
   }
 
+  VariadicMatcher(const VariadicMatcher&) = default;
+  VariadicMatcher& operator=(const VariadicMatcher&) = delete;
+
   // This template type conversion operator allows an
   // VariadicMatcher<Matcher1, Matcher2...> object to match any type that
   // all of the provided matchers (Matcher1, Matcher2, ...) can match.
@@ -1105,8 +1335,6 @@
       std::integral_constant<size_t, sizeof...(Args)>) const {}
 
   std::tuple<Args...> matchers_;
-
-  GTEST_DISALLOW_ASSIGN_(VariadicMatcher);
 };
 
 template <typename... Args>
@@ -1171,14 +1399,36 @@
 
  private:
   const std::vector<Matcher<T> > matchers_;
-
-  GTEST_DISALLOW_ASSIGN_(AnyOfMatcherImpl);
 };
 
 // AnyOfMatcher is used for the variadic implementation of AnyOf(m_1, m_2, ...).
 template <typename... Args>
 using AnyOfMatcher = VariadicMatcher<AnyOfMatcherImpl, Args...>;
 
+// ConditionalMatcher is the implementation of Conditional(cond, m1, m2)
+template <typename MatcherTrue, typename MatcherFalse>
+class ConditionalMatcher {
+ public:
+  ConditionalMatcher(bool condition, MatcherTrue matcher_true,
+                     MatcherFalse matcher_false)
+      : condition_(condition),
+        matcher_true_(std::move(matcher_true)),
+        matcher_false_(std::move(matcher_false)) {}
+
+  template <typename T>
+  operator Matcher<T>() const {  // NOLINT(runtime/explicit)
+    return condition_ ? SafeMatcherCast<T>(matcher_true_)
+                      : SafeMatcherCast<T>(matcher_false_);
+  }
+
+ private:
+  bool condition_;
+  MatcherTrue matcher_true_;
+  MatcherFalse matcher_false_;
+
+  GTEST_DISALLOW_ASSIGN_(ConditionalMatcher);
+};
+
 // Wrapper for implementation of Any/AllOfArray().
 template <template <class> class MatcherImpl, typename T>
 class SomeOfArrayMatcher {
@@ -1200,8 +1450,6 @@
 
  private:
   const ::std::vector<T> matchers_;
-
-  GTEST_DISALLOW_ASSIGN_(SomeOfArrayMatcher);
 };
 
 template <typename T>
@@ -1223,7 +1471,7 @@
   // interested in the address of the argument.
   template <typename T>
   bool MatchAndExplain(T& x,  // NOLINT
-                       MatchResultListener* /* listener */) const {
+                       MatchResultListener* listener) const {
     // Without the if-statement, MSVC sometimes warns about converting
     // a value to bool (warning 4800).
     //
@@ -1232,6 +1480,7 @@
     // having no operator!().
     if (predicate_(x))
       return true;
+    *listener << "didn't satisfy the given predicate";
     return false;
   }
 
@@ -1245,8 +1494,6 @@
 
  private:
   Predicate predicate_;
-
-  GTEST_DISALLOW_ASSIGN_(TrulyMatcher);
 };
 
 // Used for implementing Matches(matcher), which turns a matcher into
@@ -1283,8 +1530,6 @@
 
  private:
   M matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(MatcherAsPredicate);
 };
 
 // For implementing ASSERT_THAT() and EXPECT_THAT().  The template
@@ -1323,7 +1568,7 @@
        << "Expected: ";
     matcher.DescribeTo(&ss);
 
-    // Rerun the matcher to "PrintAndExain" the failure.
+    // Rerun the matcher to "PrintAndExplain" the failure.
     StringMatchResultListener listener;
     if (MatchPrintAndExplain(x, matcher, &listener)) {
       ss << "\n  The matcher failed on the initial attempt; but passed when "
@@ -1335,8 +1580,6 @@
 
  private:
   const M matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(PredicateFormatterFromMatcher);
 };
 
 // A helper function for converting a matcher to a predicate-formatter
@@ -1349,6 +1592,22 @@
   return PredicateFormatterFromMatcher<M>(std::move(matcher));
 }
 
+// Implements the polymorphic IsNan() matcher, which matches any floating type
+// value that is Nan.
+class IsNanMatcher {
+ public:
+  template <typename FloatType>
+  bool MatchAndExplain(const FloatType& f,
+                       MatchResultListener* /* listener */) const {
+    return (::std::isnan)(f);
+  }
+
+  void DescribeTo(::std::ostream* os) const { *os << "is NaN"; }
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "isn't NaN";
+  }
+};
+
 // Implements the polymorphic floating point equality matcher, which matches
 // two float values using ULP-based approximation or, optionally, a
 // user-specified epsilon.  The template is meant to be instantiated with
@@ -1409,7 +1668,7 @@
         }
 
         const FloatType diff = value - expected_;
-        if (fabs(diff) <= max_abs_error_) {
+        if (::std::fabs(diff) <= max_abs_error_) {
           return true;
         }
 
@@ -1472,16 +1731,11 @@
     const bool nan_eq_nan_;
     // max_abs_error will be used for value comparison when >= 0.
     const FloatType max_abs_error_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
   // The following 3 type conversion operators allow FloatEq(expected) and
   // NanSensitiveFloatEq(expected) to be used as a Matcher<float>, a
   // Matcher<const float&>, or a Matcher<float&>, but nothing else.
-  // (While Google's C++ coding style doesn't allow arguments passed
-  // by non-const reference, we may see them in code not conforming to
-  // the style.  Therefore Google Mock needs to support them.)
   operator Matcher<FloatType>() const {
     return MakeMatcher(
         new Impl<FloatType>(expected_, nan_eq_nan_, max_abs_error_));
@@ -1502,8 +1756,6 @@
   const bool nan_eq_nan_;
   // max_abs_error will be used for value comparison when >= 0.
   const FloatType max_abs_error_;
-
-  GTEST_DISALLOW_ASSIGN_(FloatingEqMatcher);
 };
 
 // A 2-tuple ("binary") wrapper around FloatingEqMatcher:
@@ -1607,8 +1859,9 @@
   template <typename Pointer>
   class Impl : public MatcherInterface<Pointer> {
    public:
-    typedef typename PointeeOf<typename std::remove_const<
-        typename std::remove_reference<Pointer>::type>::type>::type Pointee;
+    using Pointee =
+        typename std::pointer_traits<GTEST_REMOVE_REFERENCE_AND_CONST_(
+            Pointer)>::element_type;
 
     explicit Impl(const InnerMatcher& matcher)
         : matcher_(MatcherCast<const Pointee&>(matcher)) {}
@@ -1633,13 +1886,67 @@
 
    private:
     const Matcher<const Pointee&> matcher_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
   const InnerMatcher matcher_;
+};
 
-  GTEST_DISALLOW_ASSIGN_(PointeeMatcher);
+// Implements the Pointer(m) matcher
+// Implements the Pointer(m) matcher for matching a pointer that matches matcher
+// m.  The pointer can be either raw or smart, and will match `m` against the
+// raw pointer.
+template <typename InnerMatcher>
+class PointerMatcher {
+ public:
+  explicit PointerMatcher(const InnerMatcher& matcher) : matcher_(matcher) {}
+
+  // This type conversion operator template allows Pointer(m) to be
+  // used as a matcher for any pointer type whose pointer type is
+  // compatible with the inner matcher, where type PointerType can be
+  // either a raw pointer or a smart pointer.
+  //
+  // The reason we do this instead of relying on
+  // MakePolymorphicMatcher() is that the latter is not flexible
+  // enough for implementing the DescribeTo() method of Pointer().
+  template <typename PointerType>
+  operator Matcher<PointerType>() const {  // NOLINT
+    return Matcher<PointerType>(new Impl<const PointerType&>(matcher_));
+  }
+
+ private:
+  // The monomorphic implementation that works for a particular pointer type.
+  template <typename PointerType>
+  class Impl : public MatcherInterface<PointerType> {
+   public:
+    using Pointer =
+        const typename std::pointer_traits<GTEST_REMOVE_REFERENCE_AND_CONST_(
+            PointerType)>::element_type*;
+
+    explicit Impl(const InnerMatcher& matcher)
+        : matcher_(MatcherCast<Pointer>(matcher)) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "is a pointer that ";
+      matcher_.DescribeTo(os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "is not a pointer that ";
+      matcher_.DescribeTo(os);
+    }
+
+    bool MatchAndExplain(PointerType pointer,
+                         MatchResultListener* listener) const override {
+      *listener << "which is a pointer that ";
+      Pointer p = GetRawPointer(pointer);
+      return MatchPrintAndExplain(p, matcher_, listener);
+    }
+
+   private:
+    Matcher<Pointer> matcher_;
+  };
+
+  const InnerMatcher matcher_;
 };
 
 #if GTEST_HAS_RTTI
@@ -1676,8 +1983,6 @@
   static void GetCastTypeDescription(::std::ostream* os) {
     *os << "when dynamic_cast to " << GetToName() << ", ";
   }
-
-  GTEST_DISALLOW_ASSIGN_(WhenDynamicCastToMatcherBase);
 };
 
 // Primary template.
@@ -1775,8 +2080,6 @@
   // Contains either "whose given field " if the name of the field is unknown
   // or "whose field `name_of_field` " if the name is known.
   const std::string whose_field_;
-
-  GTEST_DISALLOW_ASSIGN_(FieldMatcher);
 };
 
 // Implements the Property() matcher for matching a property
@@ -1845,8 +2148,6 @@
   // Contains either "whose given property " if the name of the property is
   // unknown or "whose property `name_of_property` " if the name is known.
   const std::string whose_property_;
-
-  GTEST_DISALLOW_ASSIGN_(PropertyMatcher);
 };
 
 // Type traits specifying various features of different functors for ResultOf.
@@ -1936,14 +2237,10 @@
     // how many times the callable will be invoked.
     mutable CallableStorageType callable_;
     const Matcher<ResultType> matcher_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };  // class Impl
 
   const CallableStorageType callable_;
   const InnerMatcher matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(ResultOfMatcher);
 };
 
 // Implements a matcher that checks the size of an STL-style container.
@@ -1988,12 +2285,10 @@
 
    private:
     const Matcher<SizeType> size_matcher_;
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
  private:
   const SizeMatcher size_matcher_;
-  GTEST_DISALLOW_ASSIGN_(SizeIsMatcher);
 };
 
 // Implements a matcher that checks the begin()..end() distance of an STL-style
@@ -2045,12 +2340,10 @@
 
    private:
     const Matcher<DistanceType> distance_matcher_;
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
  private:
   const DistanceMatcher distance_matcher_;
-  GTEST_DISALLOW_ASSIGN_(BeginEndDistanceIsMatcher);
 };
 
 // Implements an equality matcher for any STL-style container whose elements
@@ -2143,8 +2436,6 @@
 
  private:
   const StlContainer expected_;
-
-  GTEST_DISALLOW_ASSIGN_(ContainerEqMatcher);
 };
 
 // A comparator functor that uses the < operator to compare two values.
@@ -2226,8 +2517,6 @@
  private:
   const Comparator comparator_;
   const ContainerMatcher matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(WhenSortedByMatcher);
 };
 
 // Implements Pointwise(tuple_matcher, rhs_container).  tuple_matcher
@@ -2316,7 +2605,7 @@
           StringMatchResultListener inner_listener;
           // Create InnerMatcherArg as a temporarily object to avoid it outlives
           // *left and *right. Dereference or the conversion to `const T&` may
-          // return temp objects, e.g for vector<bool>.
+          // return temp objects, e.g. for vector<bool>.
           if (!mono_tuple_matcher_.MatchAndExplain(
                   InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left),
                                   ImplicitCast_<const RhsValue&>(*right)),
@@ -2343,15 +2632,11 @@
    private:
     const Matcher<InnerMatcherArg> mono_tuple_matcher_;
     const RhsStlContainer rhs_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
  private:
   const TupleMatcher tuple_matcher_;
   const RhsStlContainer rhs_;
-
-  GTEST_DISALLOW_ASSIGN_(PointwiseMatcher);
 };
 
 // Holds the logic common to ContainsMatcherImpl and EachMatcherImpl.
@@ -2392,10 +2677,56 @@
     return all_elements_should_match;
   }
 
+  bool MatchAndExplainImpl(const Matcher<size_t>& count_matcher,
+                           Container container,
+                           MatchResultListener* listener) const {
+    StlContainerReference stl_container = View::ConstReference(container);
+    size_t i = 0;
+    std::vector<size_t> match_elements;
+    for (auto it = stl_container.begin(); it != stl_container.end();
+         ++it, ++i) {
+      StringMatchResultListener inner_listener;
+      const bool matches = inner_matcher_.MatchAndExplain(*it, &inner_listener);
+      if (matches) {
+        match_elements.push_back(i);
+      }
+    }
+    if (listener->IsInterested()) {
+      if (match_elements.empty()) {
+        *listener << "has no element that matches";
+      } else if (match_elements.size() == 1) {
+        *listener << "whose element #" << match_elements[0] << " matches";
+      } else {
+        *listener << "whose elements (";
+        std::string sep = "";
+        for (size_t e : match_elements) {
+          *listener << sep << e;
+          sep = ", ";
+        }
+        *listener << ") match";
+      }
+    }
+    StringMatchResultListener count_listener;
+    if (count_matcher.MatchAndExplain(match_elements.size(), &count_listener)) {
+      *listener << " and whose match quantity of " << match_elements.size()
+                << " matches";
+      PrintIfNotEmpty(count_listener.str(), listener->stream());
+      return true;
+    } else {
+      if (match_elements.empty()) {
+        *listener << " and";
+      } else {
+        *listener << " but";
+      }
+      *listener << " whose match quantity of " << match_elements.size()
+                << " does not match";
+      PrintIfNotEmpty(count_listener.str(), listener->stream());
+      return false;
+    }
+  }
+
  protected:
   const Matcher<const Element&> inner_matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(QuantifierMatcherImpl);
 };
 
 // Implements Contains(element_matcher) for the given argument type Container.
@@ -2422,9 +2753,6 @@
                        MatchResultListener* listener) const override {
     return this->MatchAndExplainImpl(false, container, listener);
   }
-
- private:
-  GTEST_DISALLOW_ASSIGN_(ContainsMatcherImpl);
 };
 
 // Implements Each(element_matcher) for the given argument type Container.
@@ -2451,9 +2779,58 @@
                        MatchResultListener* listener) const override {
     return this->MatchAndExplainImpl(true, container, listener);
   }
+};
+
+// Implements Contains(element_matcher).Times(n) for the given argument type
+// Container.
+template <typename Container>
+class ContainsTimesMatcherImpl : public QuantifierMatcherImpl<Container> {
+ public:
+  template <typename InnerMatcher>
+  explicit ContainsTimesMatcherImpl(InnerMatcher inner_matcher,
+                                    Matcher<size_t> count_matcher)
+      : QuantifierMatcherImpl<Container>(inner_matcher),
+        count_matcher_(std::move(count_matcher)) {}
+
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "quantity of elements that match ";
+    this->inner_matcher_.DescribeTo(os);
+    *os << " ";
+    count_matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "quantity of elements that match ";
+    this->inner_matcher_.DescribeTo(os);
+    *os << " ";
+    count_matcher_.DescribeNegationTo(os);
+  }
+
+  bool MatchAndExplain(Container container,
+                       MatchResultListener* listener) const override {
+    return this->MatchAndExplainImpl(count_matcher_, container, listener);
+  }
 
  private:
-  GTEST_DISALLOW_ASSIGN_(EachMatcherImpl);
+  const Matcher<size_t> count_matcher_;
+};
+
+// Implements polymorphic Contains(element_matcher).Times(n).
+template <typename M>
+class ContainsTimesMatcher {
+ public:
+  explicit ContainsTimesMatcher(M m, Matcher<size_t> count_matcher)
+      : inner_matcher_(m), count_matcher_(std::move(count_matcher)) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {  // NOLINT
+    return Matcher<Container>(new ContainsTimesMatcherImpl<const Container&>(
+        inner_matcher_, count_matcher_));
+  }
+
+ private:
+  const M inner_matcher_;
+  const Matcher<size_t> count_matcher_;
 };
 
 // Implements polymorphic Contains(element_matcher).
@@ -2463,15 +2840,17 @@
   explicit ContainsMatcher(M m) : inner_matcher_(m) {}
 
   template <typename Container>
-  operator Matcher<Container>() const {
+  operator Matcher<Container>() const {  // NOLINT
     return Matcher<Container>(
         new ContainsMatcherImpl<const Container&>(inner_matcher_));
   }
 
+  ContainsTimesMatcher<M> Times(Matcher<size_t> count_matcher) const {
+    return ContainsTimesMatcher<M>(inner_matcher_, std::move(count_matcher));
+  }
+
  private:
   const M inner_matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(ContainsMatcher);
 };
 
 // Implements polymorphic Each(element_matcher).
@@ -2481,15 +2860,13 @@
   explicit EachMatcher(M m) : inner_matcher_(m) {}
 
   template <typename Container>
-  operator Matcher<Container>() const {
+  operator Matcher<Container>() const {  // NOLINT
     return Matcher<Container>(
         new EachMatcherImpl<const Container&>(inner_matcher_));
   }
 
  private:
   const M inner_matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(EachMatcher);
 };
 
 struct Rank1 {};
@@ -2560,8 +2937,6 @@
 
  private:
   const Matcher<const KeyType&> inner_matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(KeyMatcherImpl);
 };
 
 // Implements polymorphic Key(matcher_for_key).
@@ -2578,8 +2953,49 @@
 
  private:
   const M matcher_for_key_;
+};
 
-  GTEST_DISALLOW_ASSIGN_(KeyMatcher);
+// Implements polymorphic Address(matcher_for_address).
+template <typename InnerMatcher>
+class AddressMatcher {
+ public:
+  explicit AddressMatcher(InnerMatcher m) : matcher_(m) {}
+
+  template <typename Type>
+  operator Matcher<Type>() const {  // NOLINT
+    return Matcher<Type>(new Impl<const Type&>(matcher_));
+  }
+
+ private:
+  // The monomorphic implementation that works for a particular object type.
+  template <typename Type>
+  class Impl : public MatcherInterface<Type> {
+   public:
+    using Address = const GTEST_REMOVE_REFERENCE_AND_CONST_(Type) *;
+    explicit Impl(const InnerMatcher& matcher)
+        : matcher_(MatcherCast<Address>(matcher)) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "has address that ";
+      matcher_.DescribeTo(os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "does not have address that ";
+      matcher_.DescribeTo(os);
+    }
+
+    bool MatchAndExplain(Type object,
+                         MatchResultListener* listener) const override {
+      *listener << "which has address ";
+      Address address = std::addressof(object);
+      return MatchPrintAndExplain(address, matcher_, listener);
+    }
+
+   private:
+    const Matcher<Address> matcher_;
+  };
+  const InnerMatcher matcher_;
 };
 
 // Implements Pair(first_matcher, second_matcher) for the given argument pair
@@ -2665,8 +3081,6 @@
 
   const Matcher<const FirstType&> first_matcher_;
   const Matcher<const SecondType&> second_matcher_;
-
-  GTEST_DISALLOW_ASSIGN_(PairMatcherImpl);
 };
 
 // Implements polymorphic Pair(first_matcher, second_matcher).
@@ -2685,8 +3099,203 @@
  private:
   const FirstMatcher first_matcher_;
   const SecondMatcher second_matcher_;
+};
 
-  GTEST_DISALLOW_ASSIGN_(PairMatcher);
+template <typename T, size_t... I>
+auto UnpackStructImpl(const T& t, IndexSequence<I...>, int)
+    -> decltype(std::tie(get<I>(t)...)) {
+  static_assert(std::tuple_size<T>::value == sizeof...(I),
+                "Number of arguments doesn't match the number of fields.");
+  return std::tie(get<I>(t)...);
+}
+
+#if defined(__cpp_structured_bindings) && __cpp_structured_bindings >= 201606
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<1>, char) {
+  const auto& [a] = t;
+  return std::tie(a);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<2>, char) {
+  const auto& [a, b] = t;
+  return std::tie(a, b);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<3>, char) {
+  const auto& [a, b, c] = t;
+  return std::tie(a, b, c);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<4>, char) {
+  const auto& [a, b, c, d] = t;
+  return std::tie(a, b, c, d);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<5>, char) {
+  const auto& [a, b, c, d, e] = t;
+  return std::tie(a, b, c, d, e);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<6>, char) {
+  const auto& [a, b, c, d, e, f] = t;
+  return std::tie(a, b, c, d, e, f);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<7>, char) {
+  const auto& [a, b, c, d, e, f, g] = t;
+  return std::tie(a, b, c, d, e, f, g);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<8>, char) {
+  const auto& [a, b, c, d, e, f, g, h] = t;
+  return std::tie(a, b, c, d, e, f, g, h);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<9>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<10>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<11>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<12>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<13>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<14>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<15>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<16>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
+}
+#endif  // defined(__cpp_structured_bindings)
+
+template <size_t I, typename T>
+auto UnpackStruct(const T& t)
+    -> decltype((UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0)) {
+  return (UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0);
+}
+
+// Helper function to do comma folding in C++11.
+// The array ensures left-to-right order of evaluation.
+// Usage: VariadicExpand({expr...});
+template <typename T, size_t N>
+void VariadicExpand(const T (&)[N]) {}
+
+template <typename Struct, typename StructSize>
+class FieldsAreMatcherImpl;
+
+template <typename Struct, size_t... I>
+class FieldsAreMatcherImpl<Struct, IndexSequence<I...>>
+    : public MatcherInterface<Struct> {
+  using UnpackedType =
+      decltype(UnpackStruct<sizeof...(I)>(std::declval<const Struct&>()));
+  using MatchersType = std::tuple<
+      Matcher<const typename std::tuple_element<I, UnpackedType>::type&>...>;
+
+ public:
+  template <typename Inner>
+  explicit FieldsAreMatcherImpl(const Inner& matchers)
+      : matchers_(testing::SafeMatcherCast<
+                  const typename std::tuple_element<I, UnpackedType>::type&>(
+            std::get<I>(matchers))...) {}
+
+  void DescribeTo(::std::ostream* os) const override {
+    const char* separator = "";
+    VariadicExpand(
+        {(*os << separator << "has field #" << I << " that ",
+          std::get<I>(matchers_).DescribeTo(os), separator = ", and ")...});
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    const char* separator = "";
+    VariadicExpand({(*os << separator << "has field #" << I << " that ",
+                     std::get<I>(matchers_).DescribeNegationTo(os),
+                     separator = ", or ")...});
+  }
+
+  bool MatchAndExplain(Struct t, MatchResultListener* listener) const override {
+    return MatchInternal((UnpackStruct<sizeof...(I)>)(t), listener);
+  }
+
+ private:
+  bool MatchInternal(UnpackedType tuple, MatchResultListener* listener) const {
+    if (!listener->IsInterested()) {
+      // If the listener is not interested, we don't need to construct the
+      // explanation.
+      bool good = true;
+      VariadicExpand({good = good && std::get<I>(matchers_).Matches(
+                                         std::get<I>(tuple))...});
+      return good;
+    }
+
+    size_t failed_pos = ~size_t{};
+
+    std::vector<StringMatchResultListener> inner_listener(sizeof...(I));
+
+    VariadicExpand(
+        {failed_pos == ~size_t{} && !std::get<I>(matchers_).MatchAndExplain(
+                                        std::get<I>(tuple), &inner_listener[I])
+             ? failed_pos = I
+             : 0 ...});
+    if (failed_pos != ~size_t{}) {
+      *listener << "whose field #" << failed_pos << " does not match";
+      PrintIfNotEmpty(inner_listener[failed_pos].str(), listener->stream());
+      return false;
+    }
+
+    *listener << "whose all elements match";
+    const char* separator = ", where";
+    for (size_t index = 0; index < sizeof...(I); ++index) {
+      const std::string str = inner_listener[index].str();
+      if (!str.empty()) {
+        *listener << separator << " field #" << index << " is a value " << str;
+        separator = ", and";
+      }
+    }
+
+    return true;
+  }
+
+  MatchersType matchers_;
+};
+
+template <typename... Inner>
+class FieldsAreMatcher {
+ public:
+  explicit FieldsAreMatcher(Inner... inner) : matchers_(std::move(inner)...) {}
+
+  template <typename Struct>
+  operator Matcher<Struct>() const {  // NOLINT
+    return Matcher<Struct>(
+        new FieldsAreMatcherImpl<const Struct&, IndexSequenceFor<Inner...>>(
+            matchers_));
+  }
+
+ private:
+  std::tuple<Inner...> matchers_;
 };
 
 // Implements ElementsAre() and ElementsAreArray().
@@ -2832,8 +3441,6 @@
   size_t count() const { return matchers_.size(); }
 
   ::std::vector<Matcher<const Element&> > matchers_;
-
-  GTEST_DISALLOW_ASSIGN_(ElementsAreMatcherImpl);
 };
 
 // Connectivity matrix of (elements X matchers), in element-major order.
@@ -2936,8 +3543,6 @@
  private:
   UnorderedMatcherRequire::Flags match_flags_;
   MatcherDescriberVec matcher_describers_;
-
-  GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImplBase);
 };
 
 // Implements UnorderedElementsAre, UnorderedElementsAreArray, IsSubsetOf, and
@@ -2960,7 +3565,9 @@
       : UnorderedElementsAreMatcherImplBase(matcher_flags) {
     for (; first != last; ++first) {
       matchers_.push_back(MatcherCast<const Element&>(*first));
-      matcher_describers().push_back(matchers_.back().GetDescriber());
+    }
+    for (const auto& m : matchers_) {
+      matcher_describers().push_back(m.GetDescriber());
     }
   }
 
@@ -3011,12 +3618,14 @@
     element_printouts->clear();
     ::std::vector<char> did_match;
     size_t num_elements = 0;
+    DummyMatchResultListener dummy;
     for (; elem_first != elem_last; ++num_elements, ++elem_first) {
       if (listener->IsInterested()) {
         element_printouts->push_back(PrintToString(*elem_first));
       }
       for (size_t irhs = 0; irhs != matchers_.size(); ++irhs) {
-        did_match.push_back(Matches(matchers_[irhs])(*elem_first));
+        did_match.push_back(
+            matchers_[irhs].MatchAndExplain(*elem_first, &dummy));
       }
     }
 
@@ -3031,8 +3640,6 @@
   }
 
   ::std::vector<Matcher<const Element&> > matchers_;
-
-  GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImpl);
 };
 
 // Functor for use in TransformTuple.
@@ -3070,7 +3677,6 @@
 
  private:
   const MatcherTuple matchers_;
-  GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcher);
 };
 
 // Implements ElementsAre.
@@ -3100,7 +3706,6 @@
 
  private:
   const MatcherTuple matchers_;
-  GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher);
 };
 
 // Implements UnorderedElementsAreArray(), IsSubsetOf(), and IsSupersetOf().
@@ -3122,8 +3727,6 @@
  private:
   UnorderedMatcherRequire::Flags match_flags_;
   ::std::vector<T> matchers_;
-
-  GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreArrayMatcher);
 };
 
 // Implements ElementsAreArray().
@@ -3145,8 +3748,6 @@
 
  private:
   const ::std::vector<T> matchers_;
-
-  GTEST_DISALLOW_ASSIGN_(ElementsAreArrayMatcher);
 };
 
 // Given a 2-tuple matcher tm of type Tuple2Matcher and a value second
@@ -3164,6 +3765,8 @@
   BoundSecondMatcher(const Tuple2Matcher& tm, const Second& second)
       : tuple2_matcher_(tm), second_value_(second) {}
 
+  BoundSecondMatcher(const BoundSecondMatcher& other) = default;
+
   template <typename T>
   operator Matcher<T>() const {
     return MakeMatcher(new Impl<T>(tuple2_matcher_, second_value_));
@@ -3206,8 +3809,6 @@
    private:
     const Matcher<const ArgTuple&> mono_tuple2_matcher_;
     const Second second_value_;
-
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
   const Tuple2Matcher tuple2_matcher_;
@@ -3280,12 +3881,10 @@
 
    private:
     const Matcher<ValueType> value_matcher_;
-    GTEST_DISALLOW_ASSIGN_(Impl);
   };
 
  private:
   const ValueMatcher value_matcher_;
-  GTEST_DISALLOW_ASSIGN_(OptionalMatcher);
 };
 
 namespace variant_matcher {
@@ -3510,26 +4109,26 @@
 }
 
 template <typename T>
-inline internal::ElementsAreArrayMatcher<T> ElementsAreArray(
-    const T* pointer, size_t count) {
+inline auto ElementsAreArray(const T* pointer, size_t count)
+    -> decltype(ElementsAreArray(pointer, pointer + count)) {
   return ElementsAreArray(pointer, pointer + count);
 }
 
 template <typename T, size_t N>
-inline internal::ElementsAreArrayMatcher<T> ElementsAreArray(
-    const T (&array)[N]) {
+inline auto ElementsAreArray(const T (&array)[N])
+    -> decltype(ElementsAreArray(array, N)) {
   return ElementsAreArray(array, N);
 }
 
 template <typename Container>
-inline internal::ElementsAreArrayMatcher<typename Container::value_type>
-ElementsAreArray(const Container& container) {
+inline auto ElementsAreArray(const Container& container)
+    -> decltype(ElementsAreArray(container.begin(), container.end())) {
   return ElementsAreArray(container.begin(), container.end());
 }
 
 template <typename T>
-inline internal::ElementsAreArrayMatcher<T>
-ElementsAreArray(::std::initializer_list<T> xs) {
+inline auto ElementsAreArray(::std::initializer_list<T> xs)
+    -> decltype(ElementsAreArray(xs.begin(), xs.end())) {
   return ElementsAreArray(xs.begin(), xs.end());
 }
 
@@ -3593,12 +4192,14 @@
 // Creates a matcher that matches any value of the given type T.
 template <typename T>
 inline Matcher<T> A() {
-  return Matcher<T>(new internal::AnyMatcherImpl<T>());
+  return _;
 }
 
 // Creates a matcher that matches any value of the given type T.
 template <typename T>
-inline Matcher<T> An() { return A<T>(); }
+inline Matcher<T> An() {
+  return _;
+}
 
 template <typename T, typename M>
 Matcher<T> internal::MatcherCastImpl<T, M>::CastImpl(
@@ -3626,6 +4227,11 @@
   return internal::RefMatcher<T&>(x);
 }
 
+// Creates a polymorphic matcher that matches any NaN floating point.
+inline PolymorphicMatcher<internal::IsNanMatcher> IsNan() {
+  return MakePolymorphicMatcher(internal::IsNanMatcher());
+}
+
 // Creates a matcher that matches any double argument approximately
 // equal to rhs, where two NANs are considered unequal.
 inline internal::FloatingEqMatcher<double> DoubleEq(double rhs) {
@@ -3808,52 +4414,60 @@
 // String matchers.
 
 // Matches a string equal to str.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrEq(
-    const std::string& str) {
+template <typename T = std::string>
+PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrEq(
+    const internal::StringLike<T>& str) {
   return MakePolymorphicMatcher(
-      internal::StrEqualityMatcher<std::string>(str, true, true));
+      internal::StrEqualityMatcher<std::string>(std::string(str), true, true));
 }
 
 // Matches a string not equal to str.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrNe(
-    const std::string& str) {
+template <typename T = std::string>
+PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrNe(
+    const internal::StringLike<T>& str) {
   return MakePolymorphicMatcher(
-      internal::StrEqualityMatcher<std::string>(str, false, true));
+      internal::StrEqualityMatcher<std::string>(std::string(str), false, true));
 }
 
 // Matches a string equal to str, ignoring case.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseEq(
-    const std::string& str) {
+template <typename T = std::string>
+PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseEq(
+    const internal::StringLike<T>& str) {
   return MakePolymorphicMatcher(
-      internal::StrEqualityMatcher<std::string>(str, true, false));
+      internal::StrEqualityMatcher<std::string>(std::string(str), true, false));
 }
 
 // Matches a string not equal to str, ignoring case.
-inline PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseNe(
-    const std::string& str) {
-  return MakePolymorphicMatcher(
-      internal::StrEqualityMatcher<std::string>(str, false, false));
+template <typename T = std::string>
+PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseNe(
+    const internal::StringLike<T>& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<std::string>(
+      std::string(str), false, false));
 }
 
 // Creates a matcher that matches any string, std::string, or C string
 // that contains the given substring.
-inline PolymorphicMatcher<internal::HasSubstrMatcher<std::string> > HasSubstr(
-    const std::string& substring) {
+template <typename T = std::string>
+PolymorphicMatcher<internal::HasSubstrMatcher<std::string> > HasSubstr(
+    const internal::StringLike<T>& substring) {
   return MakePolymorphicMatcher(
-      internal::HasSubstrMatcher<std::string>(substring));
+      internal::HasSubstrMatcher<std::string>(std::string(substring)));
 }
 
 // Matches a string that starts with 'prefix' (case-sensitive).
-inline PolymorphicMatcher<internal::StartsWithMatcher<std::string> > StartsWith(
-    const std::string& prefix) {
+template <typename T = std::string>
+PolymorphicMatcher<internal::StartsWithMatcher<std::string> > StartsWith(
+    const internal::StringLike<T>& prefix) {
   return MakePolymorphicMatcher(
-      internal::StartsWithMatcher<std::string>(prefix));
+      internal::StartsWithMatcher<std::string>(std::string(prefix)));
 }
 
 // Matches a string that ends with 'suffix' (case-sensitive).
-inline PolymorphicMatcher<internal::EndsWithMatcher<std::string> > EndsWith(
-    const std::string& suffix) {
-  return MakePolymorphicMatcher(internal::EndsWithMatcher<std::string>(suffix));
+template <typename T = std::string>
+PolymorphicMatcher<internal::EndsWithMatcher<std::string> > EndsWith(
+    const internal::StringLike<T>& suffix) {
+  return MakePolymorphicMatcher(
+      internal::EndsWithMatcher<std::string>(std::string(suffix)));
 }
 
 #if GTEST_HAS_STD_WSTRING
@@ -4034,11 +4648,7 @@
 inline PolymorphicMatcher<internal::ContainerEqMatcher<
     typename std::remove_const<Container>::type>>
 ContainerEq(const Container& rhs) {
-  // This following line is for working around a bug in MSVC 8.0,
-  // which causes Container to be a const type sometimes.
-  typedef typename std::remove_const<Container>::type RawContainer;
-  return MakePolymorphicMatcher(
-      internal::ContainerEqMatcher<RawContainer>(rhs));
+  return MakePolymorphicMatcher(internal::ContainerEqMatcher<Container>(rhs));
 }
 
 // Returns a matcher that matches a container that, when sorted using
@@ -4071,12 +4681,8 @@
 inline internal::PointwiseMatcher<TupleMatcher,
                                   typename std::remove_const<Container>::type>
 Pointwise(const TupleMatcher& tuple_matcher, const Container& rhs) {
-  // This following line is for working around a bug in MSVC 8.0,
-  // which causes Container to be a const type sometimes (e.g. when
-  // rhs is a const int[])..
-  typedef typename std::remove_const<Container>::type RawContainer;
-  return internal::PointwiseMatcher<TupleMatcher, RawContainer>(
-      tuple_matcher, rhs);
+  return internal::PointwiseMatcher<TupleMatcher, Container>(tuple_matcher,
+                                                             rhs);
 }
 
 
@@ -4107,14 +4713,9 @@
             typename std::remove_const<RhsContainer>::type>::type::value_type>>
 UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
                    const RhsContainer& rhs_container) {
-  // This following line is for working around a bug in MSVC 8.0,
-  // which causes RhsContainer to be a const type sometimes (e.g. when
-  // rhs_container is a const int[]).
-  typedef typename std::remove_const<RhsContainer>::type RawRhsContainer;
-
   // RhsView allows the same code to handle RhsContainer being a
   // STL-style container and it being a native C-style array.
-  typedef typename internal::StlContainerView<RawRhsContainer> RhsView;
+  typedef typename internal::StlContainerView<RhsContainer> RhsView;
   typedef typename RhsView::type RhsStlContainer;
   typedef typename RhsStlContainer::value_type Second;
   const RhsStlContainer& rhs_stl_container =
@@ -4142,7 +4743,6 @@
   return UnorderedPointwise(tuple2_matcher, std::vector<T>(rhs));
 }
 
-
 // Matches an STL-style container or a native array that contains at
 // least one element matching the given value or matcher.
 //
@@ -4152,7 +4752,7 @@
 //   page_ids.insert(1);
 //   EXPECT_THAT(page_ids, Contains(1));
 //   EXPECT_THAT(page_ids, Contains(Gt(2)));
-//   EXPECT_THAT(page_ids, Not(Contains(4)));
+//   EXPECT_THAT(page_ids, Not(Contains(4)));  // See below for Times(0)
 //
 //   ::std::map<int, size_t> page_lengths;
 //   page_lengths[1] = 100;
@@ -4161,6 +4761,19 @@
 //
 //   const char* user_ids[] = { "joe", "mike", "tom" };
 //   EXPECT_THAT(user_ids, Contains(Eq(::std::string("tom"))));
+//
+// The matcher supports a modifier `Times` that allows to check for arbitrary
+// occurrences including testing for absence with Times(0).
+//
+// Examples:
+//   ::std::vector<int> ids;
+//   ids.insert(1);
+//   ids.insert(1);
+//   ids.insert(3);
+//   EXPECT_THAT(ids, Contains(1).Times(2));      // 1 occurs 2 times
+//   EXPECT_THAT(ids, Contains(2).Times(0));      // 2 is not present
+//   EXPECT_THAT(ids, Contains(3).Times(Ge(1)));  // 3 occurs at least once
+
 template <typename M>
 inline internal::ContainsMatcher<M> Contains(M matcher) {
   return internal::ContainsMatcher<M>(matcher);
@@ -4287,7 +4900,7 @@
 // Matches an STL-style container or a native array that contains only
 // elements matching the given value or matcher.
 //
-// Each(m) is semantically equivalent to Not(Contains(Not(m))). Only
+// Each(m) is semantically equivalent to `Not(Contains(Not(m)))`. Only
 // the messages are different.
 //
 // Examples:
@@ -4336,6 +4949,47 @@
       first_matcher, second_matcher);
 }
 
+namespace no_adl {
+// Conditional() creates a matcher that conditionally uses either the first or
+// second matcher provided. For example, we could create an `equal if, and only
+// if' matcher using the Conditonal wrapper as follows:
+//
+//   EXPECT_THAT(result, Conditional(condition, Eq(expected), Ne(expected)));
+template <typename MatcherTrue, typename MatcherFalse>
+internal::ConditionalMatcher<MatcherTrue, MatcherFalse> Conditional(
+    bool condition, MatcherTrue matcher_true, MatcherFalse matcher_false) {
+  return internal::ConditionalMatcher<MatcherTrue, MatcherFalse>(
+      condition, std::move(matcher_true), std::move(matcher_false));
+}
+
+// FieldsAre(matchers...) matches piecewise the fields of compatible structs.
+// These include those that support `get<I>(obj)`, and when structured bindings
+// are enabled any class that supports them.
+// In particular, `std::tuple`, `std::pair`, `std::array` and aggregate types.
+template <typename... M>
+internal::FieldsAreMatcher<typename std::decay<M>::type...> FieldsAre(
+    M&&... matchers) {
+  return internal::FieldsAreMatcher<typename std::decay<M>::type...>(
+      std::forward<M>(matchers)...);
+}
+
+// Creates a matcher that matches a pointer (raw or smart) that matches
+// inner_matcher.
+template <typename InnerMatcher>
+inline internal::PointerMatcher<InnerMatcher> Pointer(
+    const InnerMatcher& inner_matcher) {
+  return internal::PointerMatcher<InnerMatcher>(inner_matcher);
+}
+
+// Creates a matcher that matches an object that has an address that matches
+// inner_matcher.
+template <typename InnerMatcher>
+inline internal::AddressMatcher<InnerMatcher> Address(
+    const InnerMatcher& inner_matcher) {
+  return internal::AddressMatcher<InnerMatcher>(inner_matcher);
+}
+}  // namespace no_adl
+
 // Returns a predicate that is satisfied by anything that matches the
 // given matcher.
 template <typename M>
@@ -4520,7 +5174,7 @@
 // and is printable using 'PrintToString'. It is compatible with
 // std::optional/std::experimental::optional.
 // Note that to compare an optional type variable against nullopt you should
-// use Eq(nullopt) and not Optional(Eq(nullopt)). The latter implies that the
+// use Eq(nullopt) and not Eq(Optional(nullopt)). The latter implies that the
 // optional value contains an optional itself.
 template <typename ValueMatcher>
 inline internal::OptionalMatcher<ValueMatcher> Optional(
@@ -4547,6 +5201,175 @@
       internal::variant_matcher::VariantMatcher<T>(matcher));
 }
 
+#if GTEST_HAS_EXCEPTIONS
+
+// Anything inside the `internal` namespace is internal to the implementation
+// and must not be used in user code!
+namespace internal {
+
+class WithWhatMatcherImpl {
+ public:
+  WithWhatMatcherImpl(Matcher<std::string> matcher)
+      : matcher_(std::move(matcher)) {}
+
+  void DescribeTo(std::ostream* os) const {
+    *os << "contains .what() that ";
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(std::ostream* os) const {
+    *os << "contains .what() that does not ";
+    matcher_.DescribeTo(os);
+  }
+
+  template <typename Err>
+  bool MatchAndExplain(const Err& err, MatchResultListener* listener) const {
+    *listener << "which contains .what() that ";
+    return matcher_.MatchAndExplain(err.what(), listener);
+  }
+
+ private:
+  const Matcher<std::string> matcher_;
+};
+
+inline PolymorphicMatcher<WithWhatMatcherImpl> WithWhat(
+    Matcher<std::string> m) {
+  return MakePolymorphicMatcher(WithWhatMatcherImpl(std::move(m)));
+}
+
+template <typename Err>
+class ExceptionMatcherImpl {
+  class NeverThrown {
+   public:
+    const char* what() const noexcept {
+      return "this exception should never be thrown";
+    }
+  };
+
+  // If the matchee raises an exception of a wrong type, we'd like to
+  // catch it and print its message and type. To do that, we add an additional
+  // catch clause:
+  //
+  //     try { ... }
+  //     catch (const Err&) { /* an expected exception */ }
+  //     catch (const std::exception&) { /* exception of a wrong type */ }
+  //
+  // However, if the `Err` itself is `std::exception`, we'd end up with two
+  // identical `catch` clauses:
+  //
+  //     try { ... }
+  //     catch (const std::exception&) { /* an expected exception */ }
+  //     catch (const std::exception&) { /* exception of a wrong type */ }
+  //
+  // This can cause a warning or an error in some compilers. To resolve
+  // the issue, we use a fake error type whenever `Err` is `std::exception`:
+  //
+  //     try { ... }
+  //     catch (const std::exception&) { /* an expected exception */ }
+  //     catch (const NeverThrown&) { /* exception of a wrong type */ }
+  using DefaultExceptionType = typename std::conditional<
+      std::is_same<typename std::remove_cv<
+                       typename std::remove_reference<Err>::type>::type,
+                   std::exception>::value,
+      const NeverThrown&, const std::exception&>::type;
+
+ public:
+  ExceptionMatcherImpl(Matcher<const Err&> matcher)
+      : matcher_(std::move(matcher)) {}
+
+  void DescribeTo(std::ostream* os) const {
+    *os << "throws an exception which is a " << GetTypeName<Err>();
+    *os << " which ";
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(std::ostream* os) const {
+    *os << "throws an exception which is not a " << GetTypeName<Err>();
+    *os << " which ";
+    matcher_.DescribeNegationTo(os);
+  }
+
+  template <typename T>
+  bool MatchAndExplain(T&& x, MatchResultListener* listener) const {
+    try {
+      (void)(std::forward<T>(x)());
+    } catch (const Err& err) {
+      *listener << "throws an exception which is a " << GetTypeName<Err>();
+      *listener << " ";
+      return matcher_.MatchAndExplain(err, listener);
+    } catch (DefaultExceptionType err) {
+#if GTEST_HAS_RTTI
+      *listener << "throws an exception of type " << GetTypeName(typeid(err));
+      *listener << " ";
+#else
+      *listener << "throws an std::exception-derived type ";
+#endif
+      *listener << "with description \"" << err.what() << "\"";
+      return false;
+    } catch (...) {
+      *listener << "throws an exception of an unknown type";
+      return false;
+    }
+
+    *listener << "does not throw any exception";
+    return false;
+  }
+
+ private:
+  const Matcher<const Err&> matcher_;
+};
+
+}  // namespace internal
+
+// Throws()
+// Throws(exceptionMatcher)
+// ThrowsMessage(messageMatcher)
+//
+// This matcher accepts a callable and verifies that when invoked, it throws
+// an exception with the given type and properties.
+//
+// Examples:
+//
+//   EXPECT_THAT(
+//       []() { throw std::runtime_error("message"); },
+//       Throws<std::runtime_error>());
+//
+//   EXPECT_THAT(
+//       []() { throw std::runtime_error("message"); },
+//       ThrowsMessage<std::runtime_error>(HasSubstr("message")));
+//
+//   EXPECT_THAT(
+//       []() { throw std::runtime_error("message"); },
+//       Throws<std::runtime_error>(
+//           Property(&std::runtime_error::what, HasSubstr("message"))));
+
+template <typename Err>
+PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> Throws() {
+  return MakePolymorphicMatcher(
+      internal::ExceptionMatcherImpl<Err>(A<const Err&>()));
+}
+
+template <typename Err, typename ExceptionMatcher>
+PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> Throws(
+    const ExceptionMatcher& exception_matcher) {
+  // Using matcher cast allows users to pass a matcher of a more broad type.
+  // For example user may want to pass Matcher<std::exception>
+  // to Throws<std::runtime_error>, or Matcher<int64> to Throws<int32>.
+  return MakePolymorphicMatcher(internal::ExceptionMatcherImpl<Err>(
+      SafeMatcherCast<const Err&>(exception_matcher)));
+}
+
+template <typename Err, typename MessageMatcher>
+PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
+    MessageMatcher&& message_matcher) {
+  static_assert(std::is_base_of<std::exception, Err>::value,
+                "expected an std::exception-derived type");
+  return Throws<Err>(internal::WithWhat(
+      MatcherCast<std::string>(std::forward<MessageMatcher>(message_matcher))));
+}
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
 // These macros allow using matchers to check values in Google Test
 // tests.  ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher)
 // succeed if and only if the value matches the matcher.  If the assertion
@@ -4556,6 +5379,159 @@
 #define EXPECT_THAT(value, matcher) EXPECT_PRED_FORMAT1(\
     ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)
 
+// MATCHER* macroses itself are listed below.
+#define MATCHER(name, description)                                             \
+  class name##Matcher                                                          \
+      : public ::testing::internal::MatcherBaseImpl<name##Matcher> {           \
+   public:                                                                     \
+    template <typename arg_type>                                               \
+    class gmock_Impl : public ::testing::MatcherInterface<const arg_type&> {   \
+     public:                                                                   \
+      gmock_Impl() {}                                                          \
+      bool MatchAndExplain(                                                    \
+          const arg_type& arg,                                                 \
+          ::testing::MatchResultListener* result_listener) const override;     \
+      void DescribeTo(::std::ostream* gmock_os) const override {               \
+        *gmock_os << FormatDescription(false);                                 \
+      }                                                                        \
+      void DescribeNegationTo(::std::ostream* gmock_os) const override {       \
+        *gmock_os << FormatDescription(true);                                  \
+      }                                                                        \
+                                                                               \
+     private:                                                                  \
+      ::std::string FormatDescription(bool negation) const {                   \
+        ::std::string gmock_description = (description);                       \
+        if (!gmock_description.empty()) {                                      \
+          return gmock_description;                                            \
+        }                                                                      \
+        return ::testing::internal::FormatMatcherDescription(negation, #name,  \
+                                                             {});              \
+      }                                                                        \
+    };                                                                         \
+  };                                                                           \
+  GTEST_ATTRIBUTE_UNUSED_ inline name##Matcher name() { return {}; }           \
+  template <typename arg_type>                                                 \
+  bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain(                   \
+      const arg_type& arg,                                                     \
+      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_) \
+      const
+
+#define MATCHER_P(name, p0, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP, description, (p0))
+#define MATCHER_P2(name, p0, p1, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP2, description, (p0, p1))
+#define MATCHER_P3(name, p0, p1, p2, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP3, description, (p0, p1, p2))
+#define MATCHER_P4(name, p0, p1, p2, p3, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP4, description, (p0, p1, p2, p3))
+#define MATCHER_P5(name, p0, p1, p2, p3, p4, description)    \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP5, description, \
+                         (p0, p1, p2, p3, p4))
+#define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP6, description,  \
+                         (p0, p1, p2, p3, p4, p5))
+#define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP7, description,      \
+                         (p0, p1, p2, p3, p4, p5, p6))
+#define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP8, description,          \
+                         (p0, p1, p2, p3, p4, p5, p6, p7))
+#define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP9, description,              \
+                         (p0, p1, p2, p3, p4, p5, p6, p7, p8))
+#define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP10, description,                  \
+                         (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9))
+
+#define GMOCK_INTERNAL_MATCHER(name, full_name, description, args)             \
+  template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)>                      \
+  class full_name : public ::testing::internal::MatcherBaseImpl<               \
+                        full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>> { \
+   public:                                                                     \
+    using full_name::MatcherBaseImpl::MatcherBaseImpl;                         \
+    template <typename arg_type>                                               \
+    class gmock_Impl : public ::testing::MatcherInterface<const arg_type&> {   \
+     public:                                                                   \
+      explicit gmock_Impl(GMOCK_INTERNAL_MATCHER_FUNCTION_ARGS(args))          \
+          : GMOCK_INTERNAL_MATCHER_FORWARD_ARGS(args) {}                       \
+      bool MatchAndExplain(                                                    \
+          const arg_type& arg,                                                 \
+          ::testing::MatchResultListener* result_listener) const override;     \
+      void DescribeTo(::std::ostream* gmock_os) const override {               \
+        *gmock_os << FormatDescription(false);                                 \
+      }                                                                        \
+      void DescribeNegationTo(::std::ostream* gmock_os) const override {       \
+        *gmock_os << FormatDescription(true);                                  \
+      }                                                                        \
+      GMOCK_INTERNAL_MATCHER_MEMBERS(args)                                     \
+                                                                               \
+     private:                                                                  \
+      ::std::string FormatDescription(bool negation) const {                   \
+        ::std::string gmock_description = (description);                       \
+        if (!gmock_description.empty()) {                                      \
+          return gmock_description;                                            \
+        }                                                                      \
+        return ::testing::internal::FormatMatcherDescription(                  \
+            negation, #name,                                                   \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(      \
+                ::std::tuple<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>(        \
+                    GMOCK_INTERNAL_MATCHER_MEMBERS_USAGE(args))));             \
+      }                                                                        \
+    };                                                                         \
+  };                                                                           \
+  template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)>                      \
+  inline full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)> name(             \
+      GMOCK_INTERNAL_MATCHER_FUNCTION_ARGS(args)) {                            \
+    return full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>(                \
+        GMOCK_INTERNAL_MATCHER_ARGS_USAGE(args));                              \
+  }                                                                            \
+  template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)>                      \
+  template <typename arg_type>                                                 \
+  bool full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>::gmock_Impl<        \
+      arg_type>::MatchAndExplain(const arg_type& arg,                          \
+                                 ::testing::MatchResultListener*               \
+                                     result_listener GTEST_ATTRIBUTE_UNUSED_)  \
+      const
+
+#define GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args) \
+  GMOCK_PP_TAIL(                                     \
+      GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAM, , args))
+#define GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAM(i_unused, data_unused, arg) \
+  , typename arg##_type
+
+#define GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_TYPE_PARAM, , args))
+#define GMOCK_INTERNAL_MATCHER_TYPE_PARAM(i_unused, data_unused, arg) \
+  , arg##_type
+
+#define GMOCK_INTERNAL_MATCHER_FUNCTION_ARGS(args) \
+  GMOCK_PP_TAIL(dummy_first GMOCK_PP_FOR_EACH(     \
+      GMOCK_INTERNAL_MATCHER_FUNCTION_ARG, , args))
+#define GMOCK_INTERNAL_MATCHER_FUNCTION_ARG(i, data_unused, arg) \
+  , arg##_type gmock_p##i
+
+#define GMOCK_INTERNAL_MATCHER_FORWARD_ARGS(args) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_FORWARD_ARG, , args))
+#define GMOCK_INTERNAL_MATCHER_FORWARD_ARG(i, data_unused, arg) \
+  , arg(::std::forward<arg##_type>(gmock_p##i))
+
+#define GMOCK_INTERNAL_MATCHER_MEMBERS(args) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_MEMBER, , args)
+#define GMOCK_INTERNAL_MATCHER_MEMBER(i_unused, data_unused, arg) \
+  const arg##_type arg;
+
+#define GMOCK_INTERNAL_MATCHER_MEMBERS_USAGE(args) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_MEMBER_USAGE, , args))
+#define GMOCK_INTERNAL_MATCHER_MEMBER_USAGE(i_unused, data_unused, arg) , arg
+
+#define GMOCK_INTERNAL_MATCHER_ARGS_USAGE(args) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_ARG_USAGE, , args))
+#define GMOCK_INTERNAL_MATCHER_ARG_USAGE(i, data_unused, arg_unused) \
+  , gmock_p##i
+
+// To prevent ADL on certain functions we put them on a separate namespace.
+using namespace no_adl;  // NOLINT
+
 }  // namespace testing
 
 GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251 5046
@@ -4565,4 +5541,4 @@
 // declarations from this file.
 #include "gmock/internal/custom/gmock-matchers.h"
 
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-more-actions.h b/ext/googletest/googlemock/include/gmock/gmock-more-actions.h
index d42484a..fd293358 100644
--- a/ext/googletest/googlemock/include/gmock/gmock-more-actions.h
+++ b/ext/googletest/googlemock/include/gmock/gmock-more-actions.h
@@ -30,38 +30,464 @@
 
 // Google Mock - a framework for writing C++ mock classes.
 //
-// This file implements some actions that depend on gmock-generated-actions.h.
+// This file implements some commonly used variadic actions.
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
 
-#include <algorithm>
-#include <type_traits>
+#include <memory>
+#include <utility>
 
-#include "gmock/gmock-generated-actions.h"
+#include "gmock/gmock-actions.h"
+#include "gmock/internal/gmock-port.h"
+
+// Include any custom callback actions added by the local installation.
+#include "gmock/internal/custom/gmock-generated-actions.h"
+
+// Sometimes you want to give an action explicit template parameters
+// that cannot be inferred from its value parameters.  ACTION() and
+// ACTION_P*() don't support that.  ACTION_TEMPLATE() remedies that
+// and can be viewed as an extension to ACTION() and ACTION_P*().
+//
+// The syntax:
+//
+//   ACTION_TEMPLATE(ActionName,
+//                   HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m),
+//                   AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; }
+//
+// defines an action template that takes m explicit template
+// parameters and n value parameters.  name_i is the name of the i-th
+// template parameter, and kind_i specifies whether it's a typename,
+// an integral constant, or a template.  p_i is the name of the i-th
+// value parameter.
+//
+// Example:
+//
+//   // DuplicateArg<k, T>(output) converts the k-th argument of the mock
+//   // function to type T and copies it to *output.
+//   ACTION_TEMPLATE(DuplicateArg,
+//                   HAS_2_TEMPLATE_PARAMS(int, k, typename, T),
+//                   AND_1_VALUE_PARAMS(output)) {
+//     *output = T(::std::get<k>(args));
+//   }
+//   ...
+//     int n;
+//     EXPECT_CALL(mock, Foo(_, _))
+//         .WillOnce(DuplicateArg<1, unsigned char>(&n));
+//
+// To create an instance of an action template, write:
+//
+//   ActionName<t1, ..., t_m>(v1, ..., v_n)
+//
+// where the ts are the template arguments and the vs are the value
+// arguments.  The value argument types are inferred by the compiler.
+// If you want to explicitly specify the value argument types, you can
+// provide additional template arguments:
+//
+//   ActionName<t1, ..., t_m, u1, ..., u_k>(v1, ..., v_n)
+//
+// where u_i is the desired type of v_i.
+//
+// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the
+// number of value parameters, but not on the number of template
+// parameters.  Without the restriction, the meaning of the following
+// is unclear:
+//
+//   OverloadedAction<int, bool>(x);
+//
+// Are we using a single-template-parameter action where 'bool' refers
+// to the type of x, or are we using a two-template-parameter action
+// where the compiler is asked to infer the type of x?
+//
+// Implementation notes:
+//
+// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and
+// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for
+// implementing ACTION_TEMPLATE.  The main trick we use is to create
+// new macro invocations when expanding a macro.  For example, we have
+//
+//   #define ACTION_TEMPLATE(name, template_params, value_params)
+//       ... GMOCK_INTERNAL_DECL_##template_params ...
+//
+// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...)
+// to expand to
+//
+//       ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ...
+//
+// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the
+// preprocessor will continue to expand it to
+//
+//       ... typename T ...
+//
+// This technique conforms to the C++ standard and is portable.  It
+// allows us to implement action templates using O(N) code, where N is
+// the maximum number of template/value parameters supported.  Without
+// using it, we'd have to devote O(N^2) amount of code to implement all
+// combinations of m and n.
+
+// Declares the template parameters.
+#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0
+#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1) kind0 name0, kind1 name1
+#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2) kind0 name0, kind1 name1, kind2 name2
+#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \
+    kind3 name3
+#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \
+    kind2 name2, kind3 name3, kind4 name4
+#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \
+    kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5
+#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \
+    kind5 name5, kind6 name6
+#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \
+    kind4 name4, kind5 name5, kind6 name6, kind7 name7
+#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \
+    kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \
+    kind8 name8
+#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \
+    kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \
+    kind6 name6, kind7 name7, kind8 name8, kind9 name9
+
+// Lists the template parameters.
+#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0
+#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1) name0, name1
+#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2) name0, name1, name2
+#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3) name0, name1, name2, name3
+#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \
+    name4
+#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \
+    name2, name3, name4, name5
+#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6) name0, name1, name2, name3, name4, name5, name6
+#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7
+#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \
+    name6, name7, name8
+#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \
+    name3, name4, name5, name6, name7, name8, name9
+
+// Declares the types of value parameters.
+#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \
+    typename p0##_type, typename p1##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \
+    typename p0##_type, typename p1##_type, typename p2##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
+    typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
+    typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
+    typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) , typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type, \
+    typename p6##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type, \
+    typename p6##_type, typename p7##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type, \
+    typename p6##_type, typename p7##_type, typename p8##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \
+    typename p2##_type, typename p3##_type, typename p4##_type, \
+    typename p5##_type, typename p6##_type, typename p7##_type, \
+    typename p8##_type, typename p9##_type
+
+// Initializes the value parameters.
+#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\
+    ()
+#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\
+    (p0##_type gmock_p0) : p0(::std::move(gmock_p0))
+#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\
+    (p0##_type gmock_p0, p1##_type gmock_p1) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1))
+#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2))
+#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3))
+#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4))
+#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5))
+#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6))
+#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6, p7##_type gmock_p7) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+        p7(::std::move(gmock_p7))
+#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6, p7##_type gmock_p7, \
+        p8##_type gmock_p8) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+        p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8))
+#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+        p9##_type gmock_p9) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+        p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)), \
+        p9(::std::move(gmock_p9))
+
+// Defines the copy constructor
+#define GMOCK_INTERNAL_DEFN_COPY_AND_0_VALUE_PARAMS() \
+    {}  // Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82134
+#define GMOCK_INTERNAL_DEFN_COPY_AND_1_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_2_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_3_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_4_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_5_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_6_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_7_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_8_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_9_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_10_VALUE_PARAMS(...) = default;
+
+// Declares the fields for storing the value parameters.
+#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0;
+#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \
+    p1##_type p1;
+#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \
+    p1##_type p1; p2##_type p2;
+#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \
+    p1##_type p1; p2##_type p2; p3##_type p3;
+#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
+    p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4;
+#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
+    p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+    p5##_type p5;
+#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+    p5##_type p5; p6##_type p6;
+#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+    p5##_type p5; p6##_type p6; p7##_type p7;
+#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
+    p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8;
+#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
+    p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \
+    p9##_type p9;
+
+// Lists the value parameters.
+#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0
+#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1
+#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2
+#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3
+#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \
+    p2, p3, p4
+#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \
+    p1, p2, p3, p4, p5
+#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) p0, p1, p2, p3, p4, p5, p6
+#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) p0, p1, p2, p3, p4, p5, p6, p7
+#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8
+#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9
+
+// Lists the value parameter types.
+#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \
+    p1##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \
+    p1##_type, p2##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
+    p0##_type, p1##_type, p2##_type, p3##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
+    p0##_type, p1##_type, p2##_type, p3##_type, p4##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
+    p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \
+    p6##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+    p5##_type, p6##_type, p7##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+    p5##_type, p6##_type, p7##_type, p8##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+    p5##_type, p6##_type, p7##_type, p8##_type, p9##_type
+
+// Declares the value parameters.
+#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0
+#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \
+    p1##_type p1
+#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \
+    p1##_type p1, p2##_type p2
+#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \
+    p1##_type p1, p2##_type p2, p3##_type p3
+#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
+    p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4
+#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
+    p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+    p5##_type p5
+#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+    p5##_type p5, p6##_type p6
+#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+    p5##_type p5, p6##_type p6, p7##_type p7
+#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+    p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8
+#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+    p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
+    p9##_type p9
+
+// The suffix of the class template implementing the action template.
+#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P
+#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2
+#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3
+#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4
+#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5
+#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6
+#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7
+#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) P8
+#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) P9
+#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) P10
+
+// The name of the class template implementing the action template.
+#define GMOCK_ACTION_CLASS_(name, value_params)\
+    GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params)
+
+#define ACTION_TEMPLATE(name, template_params, value_params)                   \
+  template <GMOCK_INTERNAL_DECL_##template_params                              \
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>                           \
+  class GMOCK_ACTION_CLASS_(name, value_params) {                              \
+   public:                                                                     \
+    explicit GMOCK_ACTION_CLASS_(name, value_params)(                          \
+        GMOCK_INTERNAL_DECL_##value_params)                                    \
+        GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params),    \
+                    = default; ,                                               \
+                    : impl_(std::make_shared<gmock_Impl>(                      \
+                                GMOCK_INTERNAL_LIST_##value_params)) { })      \
+    GMOCK_ACTION_CLASS_(name, value_params)(                                   \
+        const GMOCK_ACTION_CLASS_(name, value_params)&) noexcept               \
+        GMOCK_INTERNAL_DEFN_COPY_##value_params                                \
+    GMOCK_ACTION_CLASS_(name, value_params)(                                   \
+        GMOCK_ACTION_CLASS_(name, value_params)&&) noexcept                    \
+        GMOCK_INTERNAL_DEFN_COPY_##value_params                                \
+    template <typename F>                                                      \
+    operator ::testing::Action<F>() const {                                    \
+      return GMOCK_PP_IF(                                                      \
+          GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params),              \
+                      (::testing::internal::MakeAction<F, gmock_Impl>()),      \
+                      (::testing::internal::MakeAction<F>(impl_)));            \
+    }                                                                          \
+   private:                                                                    \
+    class gmock_Impl {                                                         \
+     public:                                                                   \
+      explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}                \
+      template <typename function_type, typename return_type,                  \
+                typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>         \
+      return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const;  \
+      GMOCK_INTERNAL_DEFN_##value_params                                       \
+    };                                                                         \
+    GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params),        \
+                , std::shared_ptr<const gmock_Impl> impl_;)                    \
+  };                                                                           \
+  template <GMOCK_INTERNAL_DECL_##template_params                              \
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>                           \
+  GMOCK_ACTION_CLASS_(name, value_params)<                                     \
+      GMOCK_INTERNAL_LIST_##template_params                                    \
+      GMOCK_INTERNAL_LIST_TYPE_##value_params> name(                           \
+          GMOCK_INTERNAL_DECL_##value_params) GTEST_MUST_USE_RESULT_;          \
+  template <GMOCK_INTERNAL_DECL_##template_params                              \
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>                           \
+  inline GMOCK_ACTION_CLASS_(name, value_params)<                              \
+      GMOCK_INTERNAL_LIST_##template_params                                    \
+      GMOCK_INTERNAL_LIST_TYPE_##value_params> name(                           \
+          GMOCK_INTERNAL_DECL_##value_params) {                                \
+    return GMOCK_ACTION_CLASS_(name, value_params)<                            \
+        GMOCK_INTERNAL_LIST_##template_params                                  \
+        GMOCK_INTERNAL_LIST_TYPE_##value_params>(                              \
+            GMOCK_INTERNAL_LIST_##value_params);                               \
+  }                                                                            \
+  template <GMOCK_INTERNAL_DECL_##template_params                              \
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>                           \
+  template <typename function_type, typename return_type, typename args_type,  \
+            GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>                                 \
+  return_type GMOCK_ACTION_CLASS_(name, value_params)<                         \
+      GMOCK_INTERNAL_LIST_##template_params                                    \
+      GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::gmock_PerformImpl( \
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
 
 namespace testing {
-namespace internal {
-
-// An internal replacement for std::copy which mimics its behavior. This is
-// necessary because Visual Studio deprecates ::std::copy, issuing warning 4996.
-// However Visual Studio 2010 and later do not honor #pragmas which disable that
-// warning.
-template<typename InputIterator, typename OutputIterator>
-inline OutputIterator CopyElements(InputIterator first,
-                                   InputIterator last,
-                                   OutputIterator output) {
-  for (; first != last; ++first, ++output) {
-    *output = *first;
-  }
-  return output;
-}
-
-}  // namespace internal
-
-// Various overloads for Invoke().
 
 // The ACTION*() macros trigger warning C4100 (unreferenced formal
 // parameter) in MSVC with -W4.  Unfortunately they cannot be fixed in
@@ -73,90 +499,75 @@
 # pragma warning(disable:4100)
 #endif
 
-// Action ReturnArg<k>() returns the k-th argument of the mock function.
-ACTION_TEMPLATE(ReturnArg,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_0_VALUE_PARAMS()) {
-  return ::std::get<k>(args);
+namespace internal {
+
+// internal::InvokeArgument - a helper for InvokeArgument action.
+// The basic overloads are provided here for generic functors.
+// Overloads for other custom-callables are provided in the
+// internal/custom/gmock-generated-actions.h header.
+template <typename F, typename... Args>
+auto InvokeArgument(F f, Args... args) -> decltype(f(args...)) {
+  return f(args...);
 }
 
-// Action SaveArg<k>(pointer) saves the k-th (0-based) argument of the
-// mock function to *pointer.
-ACTION_TEMPLATE(SaveArg,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_1_VALUE_PARAMS(pointer)) {
-  *pointer = ::std::get<k>(args);
+template <std::size_t index, typename... Params>
+struct InvokeArgumentAction {
+  template <typename... Args>
+  auto operator()(Args&&... args) const -> decltype(internal::InvokeArgument(
+      std::get<index>(std::forward_as_tuple(std::forward<Args>(args)...)),
+      std::declval<const Params&>()...)) {
+    internal::FlatTuple<Args&&...> args_tuple(FlatTupleConstructTag{},
+                                              std::forward<Args>(args)...);
+    return params.Apply([&](const Params&... unpacked_params) {
+      auto&& callable = args_tuple.template Get<index>();
+      return internal::InvokeArgument(
+          std::forward<decltype(callable)>(callable), unpacked_params...);
+    });
+  }
+
+  internal::FlatTuple<Params...> params;
+};
+
+}  // namespace internal
+
+// The InvokeArgument<N>(a1, a2, ..., a_k) action invokes the N-th
+// (0-based) argument, which must be a k-ary callable, of the mock
+// function, with arguments a1, a2, ..., a_k.
+//
+// Notes:
+//
+//   1. The arguments are passed by value by default.  If you need to
+//   pass an argument by reference, wrap it inside std::ref().  For
+//   example,
+//
+//     InvokeArgument<1>(5, string("Hello"), std::ref(foo))
+//
+//   passes 5 and string("Hello") by value, and passes foo by
+//   reference.
+//
+//   2. If the callable takes an argument by reference but std::ref() is
+//   not used, it will receive the reference to a copy of the value,
+//   instead of the original value.  For example, when the 0-th
+//   argument of the mock function takes a const string&, the action
+//
+//     InvokeArgument<0>(string("Hello"))
+//
+//   makes a copy of the temporary string("Hello") object and passes a
+//   reference of the copy, instead of the original temporary object,
+//   to the callable.  This makes it easy for a user to define an
+//   InvokeArgument action from temporary values and have it performed
+//   later.
+template <std::size_t index, typename... Params>
+internal::InvokeArgumentAction<index, typename std::decay<Params>::type...>
+InvokeArgument(Params&&... params) {
+  return {internal::FlatTuple<typename std::decay<Params>::type...>(
+      internal::FlatTupleConstructTag{}, std::forward<Params>(params)...)};
 }
 
-// Action SaveArgPointee<k>(pointer) saves the value pointed to
-// by the k-th (0-based) argument of the mock function to *pointer.
-ACTION_TEMPLATE(SaveArgPointee,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_1_VALUE_PARAMS(pointer)) {
-  *pointer = *::std::get<k>(args);
-}
-
-// Action SetArgReferee<k>(value) assigns 'value' to the variable
-// referenced by the k-th (0-based) argument of the mock function.
-ACTION_TEMPLATE(SetArgReferee,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_1_VALUE_PARAMS(value)) {
-  typedef typename ::std::tuple_element<k, args_type>::type argk_type;
-  // Ensures that argument #k is a reference.  If you get a compiler
-  // error on the next line, you are using SetArgReferee<k>(value) in
-  // a mock function whose k-th (0-based) argument is not a reference.
-  GTEST_COMPILE_ASSERT_(std::is_reference<argk_type>::value,
-                        SetArgReferee_must_be_used_with_a_reference_argument);
-  ::std::get<k>(args) = value;
-}
-
-// Action SetArrayArgument<k>(first, last) copies the elements in
-// source range [first, last) to the array pointed to by the k-th
-// (0-based) argument, which can be either a pointer or an
-// iterator. The action does not take ownership of the elements in the
-// source range.
-ACTION_TEMPLATE(SetArrayArgument,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_2_VALUE_PARAMS(first, last)) {
-  // Visual Studio deprecates ::std::copy, so we use our own copy in that case.
-#ifdef _MSC_VER
-  internal::CopyElements(first, last, ::std::get<k>(args));
-#else
-  ::std::copy(first, last, ::std::get<k>(args));
-#endif
-}
-
-// Action DeleteArg<k>() deletes the k-th (0-based) argument of the mock
-// function.
-ACTION_TEMPLATE(DeleteArg,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_0_VALUE_PARAMS()) {
-  delete ::std::get<k>(args);
-}
-
-// This action returns the value pointed to by 'pointer'.
-ACTION_P(ReturnPointee, pointer) { return *pointer; }
-
-// Action Throw(exception) can be used in a mock function of any type
-// to throw the given exception.  Any copyable value can be thrown.
-#if GTEST_HAS_EXCEPTIONS
-
-// Suppresses the 'unreachable code' warning that VC generates in opt modes.
-# ifdef _MSC_VER
-#  pragma warning(push)          // Saves the current warning state.
-#  pragma warning(disable:4702)  // Temporarily disables warning 4702.
-# endif
-ACTION_P(Throw, exception) { throw exception; }
-# ifdef _MSC_VER
-#  pragma warning(pop)           // Restores the warning state.
-# endif
-
-#endif  // GTEST_HAS_EXCEPTIONS
-
 #ifdef _MSC_VER
 # pragma warning(pop)
 #endif
 
 }  // namespace testing
 
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-more-matchers.h b/ext/googletest/googlemock/include/gmock/gmock-more-matchers.h
index 1c9a399..dfc77e3 100644
--- a/ext/googletest/googlemock/include/gmock/gmock-more-matchers.h
+++ b/ext/googletest/googlemock/include/gmock/gmock-more-matchers.h
@@ -30,17 +30,17 @@
 
 // Google Mock - a framework for writing C++ mock classes.
 //
-// This file implements some matchers that depend on gmock-generated-matchers.h.
+// This file implements some matchers that depend on gmock-matchers.h.
 //
 // Note that tests are implemented in gmock-matchers_test.cc rather than
 // gmock-more-matchers-test.cc.
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_MORE_MATCHERS_H_
-#define GMOCK_INCLUDE_GMOCK_MORE_MATCHERS_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_MATCHERS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_MATCHERS_H_
 
-#include "gmock/gmock-generated-matchers.h"
+#include "gmock/gmock-matchers.h"
 
 namespace testing {
 
@@ -89,4 +89,4 @@
 
 }  // namespace testing
 
-#endif  // GMOCK_INCLUDE_GMOCK_MORE_MATCHERS_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_MATCHERS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-nice-strict.h b/ext/googletest/googlemock/include/gmock/gmock-nice-strict.h
index 5495a98..b03b770 100644
--- a/ext/googletest/googlemock/include/gmock/gmock-nice-strict.h
+++ b/ext/googletest/googlemock/include/gmock/gmock-nice-strict.h
@@ -60,20 +60,91 @@
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
+
+#include <type_traits>
 
 #include "gmock/gmock-spec-builders.h"
 #include "gmock/internal/gmock-port.h"
 
 namespace testing {
+template <class MockClass>
+class NiceMock;
+template <class MockClass>
+class NaggyMock;
+template <class MockClass>
+class StrictMock;
+
+namespace internal {
+template <typename T>
+std::true_type StrictnessModifierProbe(const NiceMock<T>&);
+template <typename T>
+std::true_type StrictnessModifierProbe(const NaggyMock<T>&);
+template <typename T>
+std::true_type StrictnessModifierProbe(const StrictMock<T>&);
+std::false_type StrictnessModifierProbe(...);
+
+template <typename T>
+constexpr bool HasStrictnessModifier() {
+  return decltype(StrictnessModifierProbe(std::declval<const T&>()))::value;
+}
+
+// Base classes that register and deregister with testing::Mock to alter the
+// default behavior around uninteresting calls. Inheriting from one of these
+// classes first and then MockClass ensures the MockClass constructor is run
+// after registration, and that the MockClass destructor runs before
+// deregistration. This guarantees that MockClass's constructor and destructor
+// run with the same level of strictness as its instance methods.
+
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW && \
+    (defined(_MSC_VER) || defined(__clang__))
+// We need to mark these classes with this declspec to ensure that
+// the empty base class optimization is performed.
+#define GTEST_INTERNAL_EMPTY_BASE_CLASS __declspec(empty_bases)
+#else
+#define GTEST_INTERNAL_EMPTY_BASE_CLASS
+#endif
+
+template <typename Base>
+class NiceMockImpl {
+ public:
+  NiceMockImpl() { ::testing::Mock::AllowUninterestingCalls(this); }
+
+  ~NiceMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
+};
+
+template <typename Base>
+class NaggyMockImpl {
+ public:
+  NaggyMockImpl() { ::testing::Mock::WarnUninterestingCalls(this); }
+
+  ~NaggyMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
+};
+
+template <typename Base>
+class StrictMockImpl {
+ public:
+  StrictMockImpl() { ::testing::Mock::FailUninterestingCalls(this); }
+
+  ~StrictMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
+};
+
+}  // namespace internal
 
 template <class MockClass>
-class NiceMock : public MockClass {
+class GTEST_INTERNAL_EMPTY_BASE_CLASS NiceMock
+    : private internal::NiceMockImpl<MockClass>,
+      public MockClass {
  public:
+  static_assert(!internal::HasStrictnessModifier<MockClass>(),
+                "Can't apply NiceMock to a class hierarchy that already has a "
+                "strictness modifier. See "
+                "https://google.github.io/googletest/"
+                "gmock_cook_book.html#NiceStrictNaggy");
   NiceMock() : MockClass() {
-    ::testing::Mock::AllowUninterestingCalls(
-        internal::ImplicitCast_<MockClass*>(this));
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
   }
 
   // Ideally, we would inherit base class's constructors through a using
@@ -85,21 +156,16 @@
   // made explicit.
   template <typename A>
   explicit NiceMock(A&& arg) : MockClass(std::forward<A>(arg)) {
-    ::testing::Mock::AllowUninterestingCalls(
-        internal::ImplicitCast_<MockClass*>(this));
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
   }
 
-  template <typename A1, typename A2, typename... An>
-  NiceMock(A1&& arg1, A2&& arg2, An&&... args)
-      : MockClass(std::forward<A1>(arg1), std::forward<A2>(arg2),
+  template <typename TArg1, typename TArg2, typename... An>
+  NiceMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
+      : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
                   std::forward<An>(args)...) {
-    ::testing::Mock::AllowUninterestingCalls(
-        internal::ImplicitCast_<MockClass*>(this));
-  }
-
-  ~NiceMock() {  // NOLINT
-    ::testing::Mock::UnregisterCallReaction(
-        internal::ImplicitCast_<MockClass*>(this));
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
   }
 
  private:
@@ -107,11 +173,19 @@
 };
 
 template <class MockClass>
-class NaggyMock : public MockClass {
+class GTEST_INTERNAL_EMPTY_BASE_CLASS NaggyMock
+    : private internal::NaggyMockImpl<MockClass>,
+      public MockClass {
+  static_assert(!internal::HasStrictnessModifier<MockClass>(),
+                "Can't apply NaggyMock to a class hierarchy that already has a "
+                "strictness modifier. See "
+                "https://google.github.io/googletest/"
+                "gmock_cook_book.html#NiceStrictNaggy");
+
  public:
   NaggyMock() : MockClass() {
-    ::testing::Mock::WarnUninterestingCalls(
-        internal::ImplicitCast_<MockClass*>(this));
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
   }
 
   // Ideally, we would inherit base class's constructors through a using
@@ -123,21 +197,16 @@
   // made explicit.
   template <typename A>
   explicit NaggyMock(A&& arg) : MockClass(std::forward<A>(arg)) {
-    ::testing::Mock::WarnUninterestingCalls(
-        internal::ImplicitCast_<MockClass*>(this));
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
   }
 
-  template <typename A1, typename A2, typename... An>
-  NaggyMock(A1&& arg1, A2&& arg2, An&&... args)
-      : MockClass(std::forward<A1>(arg1), std::forward<A2>(arg2),
+  template <typename TArg1, typename TArg2, typename... An>
+  NaggyMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
+      : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
                   std::forward<An>(args)...) {
-    ::testing::Mock::WarnUninterestingCalls(
-        internal::ImplicitCast_<MockClass*>(this));
-  }
-
-  ~NaggyMock() {  // NOLINT
-    ::testing::Mock::UnregisterCallReaction(
-        internal::ImplicitCast_<MockClass*>(this));
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
   }
 
  private:
@@ -145,11 +214,19 @@
 };
 
 template <class MockClass>
-class StrictMock : public MockClass {
+class GTEST_INTERNAL_EMPTY_BASE_CLASS StrictMock
+    : private internal::StrictMockImpl<MockClass>,
+      public MockClass {
  public:
+  static_assert(
+      !internal::HasStrictnessModifier<MockClass>(),
+      "Can't apply StrictMock to a class hierarchy that already has a "
+      "strictness modifier. See "
+      "https://google.github.io/googletest/"
+      "gmock_cook_book.html#NiceStrictNaggy");
   StrictMock() : MockClass() {
-    ::testing::Mock::FailUninterestingCalls(
-        internal::ImplicitCast_<MockClass*>(this));
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
   }
 
   // Ideally, we would inherit base class's constructors through a using
@@ -161,55 +238,24 @@
   // made explicit.
   template <typename A>
   explicit StrictMock(A&& arg) : MockClass(std::forward<A>(arg)) {
-    ::testing::Mock::FailUninterestingCalls(
-        internal::ImplicitCast_<MockClass*>(this));
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
   }
 
-  template <typename A1, typename A2, typename... An>
-  StrictMock(A1&& arg1, A2&& arg2, An&&... args)
-      : MockClass(std::forward<A1>(arg1), std::forward<A2>(arg2),
+  template <typename TArg1, typename TArg2, typename... An>
+  StrictMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
+      : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
                   std::forward<An>(args)...) {
-    ::testing::Mock::FailUninterestingCalls(
-        internal::ImplicitCast_<MockClass*>(this));
-  }
-
-  ~StrictMock() {  // NOLINT
-    ::testing::Mock::UnregisterCallReaction(
-        internal::ImplicitCast_<MockClass*>(this));
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
   }
 
  private:
   GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock);
 };
 
-// The following specializations catch some (relatively more common)
-// user errors of nesting nice and strict mocks.  They do NOT catch
-// all possible errors.
-
-// These specializations are declared but not defined, as NiceMock,
-// NaggyMock, and StrictMock cannot be nested.
-
-template <typename MockClass>
-class NiceMock<NiceMock<MockClass> >;
-template <typename MockClass>
-class NiceMock<NaggyMock<MockClass> >;
-template <typename MockClass>
-class NiceMock<StrictMock<MockClass> >;
-
-template <typename MockClass>
-class NaggyMock<NiceMock<MockClass> >;
-template <typename MockClass>
-class NaggyMock<NaggyMock<MockClass> >;
-template <typename MockClass>
-class NaggyMock<StrictMock<MockClass> >;
-
-template <typename MockClass>
-class StrictMock<NiceMock<MockClass> >;
-template <typename MockClass>
-class StrictMock<NaggyMock<MockClass> >;
-template <typename MockClass>
-class StrictMock<StrictMock<MockClass> >;
+#undef GTEST_INTERNAL_EMPTY_BASE_CLASS
 
 }  // namespace testing
 
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock-spec-builders.h b/ext/googletest/googlemock/include/gmock/gmock-spec-builders.h
index 80c13b5..41323c1 100644
--- a/ext/googletest/googlemock/include/gmock/gmock-spec-builders.h
+++ b/ext/googletest/googlemock/include/gmock/gmock-spec-builders.h
@@ -58,8 +58,8 @@
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
 
 #include <functional>
 #include <map>
@@ -108,6 +108,14 @@
 // Helper class for testing the Expectation class template.
 class ExpectationTester;
 
+// Helper classes for implementing NiceMock, StrictMock, and NaggyMock.
+template <typename MockClass>
+class NiceMockImpl;
+template <typename MockClass>
+class StrictMockImpl;
+template <typename MockClass>
+class NaggyMockImpl;
+
 // Protects the mock object registry (in class Mock), all function
 // mockers, and all expectations.
 //
@@ -413,14 +421,12 @@
   template <typename F>
   friend class internal::FunctionMocker;
 
-  template <typename M>
-  friend class NiceMock;
-
-  template <typename M>
-  friend class NaggyMock;
-
-  template <typename M>
-  friend class StrictMock;
+  template <typename MockClass>
+  friend class internal::NiceMockImpl;
+  template <typename MockClass>
+  friend class internal::NaggyMockImpl;
+  template <typename MockClass>
+  friend class internal::StrictMockImpl;
 
   // Tells Google Mock to allow uninteresting calls on the given mock
   // object.
@@ -499,7 +505,10 @@
  public:
   // Constructs a null object that doesn't reference any expectation.
   Expectation();
-
+  Expectation(Expectation&&) = default;
+  Expectation(const Expectation&) = default;
+  Expectation& operator=(Expectation&&) = default;
+  Expectation& operator=(const Expectation&) = default;
   ~Expectation();
 
   // This single-argument ctor must not be explicit, in order to support the
@@ -879,8 +888,6 @@
   Clause last_clause_;
   mutable bool action_count_checked_;  // Under mutex_.
   mutable Mutex mutex_;  // Protects action_count_checked_.
-
-  GTEST_DISALLOW_ASSIGN_(ExpectationBase);
 };  // class ExpectationBase
 
 // Impements an expectation for the given function type.
@@ -1295,8 +1302,6 @@
   internal::FunctionMocker<F>* const function_mocker_;
   // The argument matchers specified in the spec.
   ArgumentMatcherTuple matchers_;
-
-  GTEST_DISALLOW_ASSIGN_(MockSpec);
 };  // class MockSpec
 
 // Wrapper type for generically holding an ordinary value or lvalue reference.
@@ -1350,12 +1355,6 @@
   T* value_ptr_;
 };
 
-// MSVC warns about using 'this' in base member initializer list, so
-// we need to temporarily disable the warning.  We have to do it for
-// the entire class to suppress the warning, even though it's about
-// the constructor only.
-GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355)
-
 // C++ treats the void type specially.  For example, you cannot define
 // a void-typed variable or pass a void value to a function.
 // ActionResultHolder<T> holds a value of type T, where T must be a
@@ -1786,18 +1785,87 @@
   }
 };  // class FunctionMocker
 
-GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4355
-
 // Reports an uninteresting call (whose description is in msg) in the
 // manner specified by 'reaction'.
 void ReportUninterestingCall(CallReaction reaction, const std::string& msg);
 
 }  // namespace internal
 
-// A MockFunction<F> class has one mock method whose type is F.  It is
-// useful when you just want your test code to emit some messages and
-// have Google Mock verify the right messages are sent (and perhaps at
-// the right times).  For example, if you are exercising code:
+namespace internal {
+
+template <typename F>
+class MockFunction;
+
+template <typename R, typename... Args>
+class MockFunction<R(Args...)> {
+ public:
+  MockFunction(const MockFunction&) = delete;
+  MockFunction& operator=(const MockFunction&) = delete;
+
+  std::function<R(Args...)> AsStdFunction() {
+    return [this](Args... args) -> R {
+      return this->Call(std::forward<Args>(args)...);
+    };
+  }
+
+  // Implementation detail: the expansion of the MOCK_METHOD macro.
+  R Call(Args... args) {
+    mock_.SetOwnerAndName(this, "Call");
+    return mock_.Invoke(std::forward<Args>(args)...);
+  }
+
+  MockSpec<R(Args...)> gmock_Call(Matcher<Args>... m) {
+    mock_.RegisterOwner(this);
+    return mock_.With(std::move(m)...);
+  }
+
+  MockSpec<R(Args...)> gmock_Call(const WithoutMatchers&, R (*)(Args...)) {
+    return this->gmock_Call(::testing::A<Args>()...);
+  }
+
+ protected:
+  MockFunction() = default;
+  ~MockFunction() = default;
+
+ private:
+  FunctionMocker<R(Args...)> mock_;
+};
+
+/*
+The SignatureOf<F> struct is a meta-function returning function signature
+corresponding to the provided F argument.
+
+It makes use of MockFunction easier by allowing it to accept more F arguments
+than just function signatures.
+
+Specializations provided here cover a signature type itself and any template
+that can be parameterized with a signature, including std::function and
+boost::function.
+*/
+
+template <typename F, typename = void>
+struct SignatureOf;
+
+template <typename R, typename... Args>
+struct SignatureOf<R(Args...)> {
+  using type = R(Args...);
+};
+
+template <template <typename> class C, typename F>
+struct SignatureOf<C<F>,
+                   typename std::enable_if<std::is_function<F>::value>::type>
+    : SignatureOf<F> {};
+
+template <typename F>
+using SignatureOfT = typename SignatureOf<F>::type;
+
+}  // namespace internal
+
+// A MockFunction<F> type has one mock method whose type is
+// internal::SignatureOfT<F>.  It is useful when you just want your
+// test code to emit some messages and have Google Mock verify the
+// right messages are sent (and perhaps at the right times).  For
+// example, if you are exercising code:
 //
 //   Foo(1);
 //   Foo(2);
@@ -1831,49 +1899,34 @@
 // Bar("a") is called by which call to Foo().
 //
 // MockFunction<F> can also be used to exercise code that accepts
-// std::function<F> callbacks. To do so, use AsStdFunction() method
-// to create std::function proxy forwarding to original object's Call.
-// Example:
+// std::function<internal::SignatureOfT<F>> callbacks. To do so, use
+// AsStdFunction() method to create std::function proxy forwarding to
+// original object's Call. Example:
 //
 // TEST(FooTest, RunsCallbackWithBarArgument) {
 //   MockFunction<int(string)> callback;
 //   EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1));
 //   Foo(callback.AsStdFunction());
 // }
+//
+// The internal::SignatureOfT<F> indirection allows to use other types
+// than just function signature type. This is typically useful when
+// providing a mock for a predefined std::function type. Example:
+//
+// using FilterPredicate = std::function<bool(string)>;
+// void MyFilterAlgorithm(FilterPredicate predicate);
+//
+// TEST(FooTest, FilterPredicateAlwaysAccepts) {
+//   MockFunction<FilterPredicate> predicateMock;
+//   EXPECT_CALL(predicateMock, Call(_)).WillRepeatedly(Return(true));
+//   MyFilterAlgorithm(predicateMock.AsStdFunction());
+// }
 template <typename F>
-class MockFunction;
+class MockFunction : public internal::MockFunction<internal::SignatureOfT<F>> {
+  using Base = internal::MockFunction<internal::SignatureOfT<F>>;
 
-template <typename R, typename... Args>
-class MockFunction<R(Args...)> {
  public:
-  MockFunction() {}
-  MockFunction(const MockFunction&) = delete;
-  MockFunction& operator=(const MockFunction&) = delete;
-
-  std::function<R(Args...)> AsStdFunction() {
-    return [this](Args... args) -> R {
-      return this->Call(std::forward<Args>(args)...);
-    };
-  }
-
-  // Implementation detail: the expansion of the MOCK_METHOD macro.
-  R Call(Args... args) {
-    mock_.SetOwnerAndName(this, "Call");
-    return mock_.Invoke(std::forward<Args>(args)...);
-  }
-
-  internal::MockSpec<R(Args...)> gmock_Call(Matcher<Args>... m) {
-    mock_.RegisterOwner(this);
-    return mock_.With(std::move(m)...);
-  }
-
-  internal::MockSpec<R(Args...)> gmock_Call(const internal::WithoutMatchers&,
-                                            R (*)(Args...)) {
-    return this->gmock_Call(::testing::A<Args>()...);
-  }
-
- private:
-  internal::FunctionMocker<R(Args...)> mock_;
+  using Base::Base;
 };
 
 // The style guide prohibits "using" statements in a namespace scope
@@ -1982,4 +2035,4 @@
 #define EXPECT_CALL(obj, call) \
   GMOCK_ON_CALL_IMPL_(obj, InternalExpectedAt, call)
 
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
diff --git a/ext/googletest/googlemock/include/gmock/gmock.h b/ext/googletest/googlemock/include/gmock/gmock.h
index 99c3d78..12469bc 100644
--- a/ext/googletest/googlemock/include/gmock/gmock.h
+++ b/ext/googletest/googlemock/include/gmock/gmock.h
@@ -34,8 +34,8 @@
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_H_
-#define GMOCK_INCLUDE_GMOCK_GMOCK_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_H_
 
 // This file implements the following syntax:
 //
@@ -59,9 +59,6 @@
 #include "gmock/gmock-actions.h"
 #include "gmock/gmock-cardinalities.h"
 #include "gmock/gmock-function-mocker.h"
-#include "gmock/gmock-generated-actions.h"
-#include "gmock/gmock-generated-function-mockers.h"
-#include "gmock/gmock-generated-matchers.h"
 #include "gmock/gmock-matchers.h"
 #include "gmock/gmock-more-actions.h"
 #include "gmock/gmock-more-matchers.h"
@@ -98,4 +95,4 @@
 
 }  // namespace testing
 
-#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_H_
diff --git a/ext/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h b/ext/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h
index 92d910c..63f8999 100644
--- a/ext/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h
+++ b/ext/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h
@@ -1,10 +1,6 @@
-// This file was GENERATED by command:
-//     pump.py gmock-generated-actions.h.pump
-// DO NOT EDIT BY HAND!!!
-
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
-#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
 
-#endif  // GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
diff --git a/ext/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump b/ext/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump
deleted file mode 100644
index 67c221f..0000000
--- a/ext/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump
+++ /dev/null
@@ -1,12 +0,0 @@
-$$ -*- mode: c++; -*-
-$$ This is a Pump source file. Please use Pump to convert
-$$ it to callback-actions.h.
-$$
-$var max_callback_arity = 5
-$$}} This meta comment fixes auto-indentation in editors.
-
-// GOOGLETEST_CM0002 DO NOT DELETE
-#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
-#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
-
-#endif  // GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
diff --git a/ext/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h b/ext/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h
index 14aafaa..6384294 100644
--- a/ext/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h
+++ b/ext/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h
@@ -31,6 +31,6 @@
 //
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
-#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
-#endif  // GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
diff --git a/ext/googletest/googlemock/include/gmock/internal/custom/gmock-port.h b/ext/googletest/googlemock/include/gmock/internal/custom/gmock-port.h
index 0030fe9..1437869 100644
--- a/ext/googletest/googlemock/include/gmock/internal/custom/gmock-port.h
+++ b/ext/googletest/googlemock/include/gmock/internal/custom/gmock-port.h
@@ -33,7 +33,7 @@
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
-#define GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
 
-#endif  // GMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
diff --git a/ext/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h b/ext/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h
index fdc049c..317544a 100644
--- a/ext/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h
+++ b/ext/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h
@@ -36,8 +36,8 @@
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
-#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
 
 #include <stdio.h>
 #include <ostream>  // NOLINT
@@ -71,20 +71,6 @@
 // "foo_bar_123" are converted to "foo bar 123".
 GTEST_API_ std::string ConvertIdentifierNameToWords(const char* id_name);
 
-// PointeeOf<Pointer>::type is the type of a value pointed to by a
-// Pointer, which can be either a smart pointer or a raw pointer.  The
-// following default implementation is for the case where Pointer is a
-// smart pointer.
-template <typename Pointer>
-struct PointeeOf {
-  // Smart pointer classes define type element_type as the type of
-  // their pointees.
-  typedef typename Pointer::element_type type;
-};
-// This specialization is for the raw pointer case.
-template <typename T>
-struct PointeeOf<T*> { typedef T type; };  // NOLINT
-
 // GetRawPointer(p) returns the raw pointer underlying p when p is a
 // smart pointer, or returns p itself when p is already a raw pointer.
 // The following default implementation is for the smart pointer case.
@@ -136,15 +122,13 @@
 GMOCK_DECLARE_KIND_(unsigned int, kInteger);
 GMOCK_DECLARE_KIND_(long, kInteger);  // NOLINT
 GMOCK_DECLARE_KIND_(unsigned long, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(long long, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(unsigned long long, kInteger);  // NOLINT
 
 #if GMOCK_WCHAR_T_IS_NATIVE_
 GMOCK_DECLARE_KIND_(wchar_t, kInteger);
 #endif
 
-// Non-standard integer types.
-GMOCK_DECLARE_KIND_(Int64, kInteger);
-GMOCK_DECLARE_KIND_(UInt64, kInteger);
-
 // All standard floating-point types.
 GMOCK_DECLARE_KIND_(float, kFloatingPoint);
 GMOCK_DECLARE_KIND_(double, kFloatingPoint);
@@ -157,9 +141,6 @@
   static_cast< ::testing::internal::TypeKind>( \
       ::testing::internal::KindOf<type>::value)
 
-// Evaluates to true if and only if integer type T is signed.
-#define GMOCK_IS_SIGNED_(T) (static_cast<T>(-1) < 0)
-
 // LosslessArithmeticConvertibleImpl<kFromKind, From, kToKind, To>::value
 // is true if and only if arithmetic type From can be losslessly converted to
 // arithmetic type To.
@@ -170,65 +151,30 @@
 // From, and kToKind is the kind of To; the value is
 // implementation-defined when the above pre-condition is violated.
 template <TypeKind kFromKind, typename From, TypeKind kToKind, typename To>
-struct LosslessArithmeticConvertibleImpl : public std::false_type {};
-
-// Converting bool to bool is lossless.
-template <>
-struct LosslessArithmeticConvertibleImpl<kBool, bool, kBool, bool>
-    : public std::true_type {};
-
-// Converting bool to any integer type is lossless.
-template <typename To>
-struct LosslessArithmeticConvertibleImpl<kBool, bool, kInteger, To>
-    : public std::true_type {};
-
-// Converting bool to any floating-point type is lossless.
-template <typename To>
-struct LosslessArithmeticConvertibleImpl<kBool, bool, kFloatingPoint, To>
-    : public std::true_type {};
-
-// Converting an integer to bool is lossy.
-template <typename From>
-struct LosslessArithmeticConvertibleImpl<kInteger, From, kBool, bool>
-    : public std::false_type {};
-
-// Converting an integer to another non-bool integer is lossless
-// if and only if the target type's range encloses the source type's range.
-template <typename From, typename To>
-struct LosslessArithmeticConvertibleImpl<kInteger, From, kInteger, To>
-    : public bool_constant<
-      // When converting from a smaller size to a larger size, we are
-      // fine as long as we are not converting from signed to unsigned.
-      ((sizeof(From) < sizeof(To)) &&
-       (!GMOCK_IS_SIGNED_(From) || GMOCK_IS_SIGNED_(To))) ||
-      // When converting between the same size, the signedness must match.
-      ((sizeof(From) == sizeof(To)) &&
-       (GMOCK_IS_SIGNED_(From) == GMOCK_IS_SIGNED_(To)))> {};  // NOLINT
-
-#undef GMOCK_IS_SIGNED_
-
-// Converting an integer to a floating-point type may be lossy, since
-// the format of a floating-point number is implementation-defined.
-template <typename From, typename To>
-struct LosslessArithmeticConvertibleImpl<kInteger, From, kFloatingPoint, To>
-    : public std::false_type {};
-
-// Converting a floating-point to bool is lossy.
-template <typename From>
-struct LosslessArithmeticConvertibleImpl<kFloatingPoint, From, kBool, bool>
-    : public std::false_type {};
-
-// Converting a floating-point to an integer is lossy.
-template <typename From, typename To>
-struct LosslessArithmeticConvertibleImpl<kFloatingPoint, From, kInteger, To>
-    : public std::false_type {};
-
-// Converting a floating-point to another floating-point is lossless
-// if and only if the target type is at least as big as the source type.
-template <typename From, typename To>
-struct LosslessArithmeticConvertibleImpl<
-  kFloatingPoint, From, kFloatingPoint, To>
-    : public bool_constant<sizeof(From) <= sizeof(To)> {};  // NOLINT
+using LosslessArithmeticConvertibleImpl = std::integral_constant<
+    bool,
+    // clang-format off
+      // Converting from bool is always lossless
+      (kFromKind == kBool) ? true
+      // Converting between any other type kinds will be lossy if the type
+      // kinds are not the same.
+    : (kFromKind != kToKind) ? false
+    : (kFromKind == kInteger &&
+       // Converting between integers of different widths is allowed so long
+       // as the conversion does not go from signed to unsigned.
+      (((sizeof(From) < sizeof(To)) &&
+        !(std::is_signed<From>::value && !std::is_signed<To>::value)) ||
+       // Converting between integers of the same width only requires the
+       // two types to have the same signedness.
+       ((sizeof(From) == sizeof(To)) &&
+        (std::is_signed<From>::value == std::is_signed<To>::value)))
+       ) ? true
+      // Floating point conversions are lossless if and only if `To` is at least
+      // as wide as `From`.
+    : (kFromKind == kFloatingPoint && (sizeof(From) <= sizeof(To))) ? true
+    : false
+    // clang-format on
+    >;
 
 // LosslessArithmeticConvertible<From, To>::value is true if and only if
 // arithmetic type From can be losslessly converted to arithmetic type To.
@@ -238,9 +184,9 @@
 // reference) built-in arithmetic types; the value is
 // implementation-defined when the above pre-condition is violated.
 template <typename From, typename To>
-struct LosslessArithmeticConvertible
-    : public LosslessArithmeticConvertibleImpl<
-  GMOCK_KIND_OF_(From), From, GMOCK_KIND_OF_(To), To> {};  // NOLINT
+using LosslessArithmeticConvertible =
+    LosslessArithmeticConvertibleImpl<GMOCK_KIND_OF_(From), From,
+                                      GMOCK_KIND_OF_(To), To>;
 
 // This interface knows how to report a Google Mock failure (either
 // non-fatal or fatal).
@@ -334,8 +280,6 @@
 // Internal use only: access the singleton instance of WithoutMatchers.
 GTEST_API_ WithoutMatchers GetWithoutMatchers();
 
-// Type traits.
-
 // Disable MSVC warnings for infinite recursion, since in this case the
 // the recursion is unreachable.
 #ifdef _MSC_VER
@@ -420,7 +364,8 @@
 class StlContainerView< ::std::tuple<ElementPointer, Size> > {
  public:
   typedef typename std::remove_const<
-      typename internal::PointeeOf<ElementPointer>::type>::type RawElement;
+      typename std::pointer_traits<ElementPointer>::element_type>::type
+      RawElement;
   typedef internal::NativeArray<RawElement> type;
   typedef const type const_reference;
 
@@ -464,11 +409,13 @@
 
 // Apply the function to a tuple of arguments.
 template <typename F, typename Tuple>
-auto Apply(F&& f, Tuple&& args)
-    -> decltype(ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args),
-                          MakeIndexSequence<std::tuple_size<Tuple>::value>())) {
+auto Apply(F&& f, Tuple&& args) -> decltype(
+    ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args),
+              MakeIndexSequence<std::tuple_size<
+                  typename std::remove_reference<Tuple>::type>::value>())) {
   return ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args),
-                   MakeIndexSequence<std::tuple_size<Tuple>::value>());
+                   MakeIndexSequence<std::tuple_size<
+                       typename std::remove_reference<Tuple>::type>::value>());
 }
 
 // Template struct Function<F>, where F must be a function type, contains
@@ -492,8 +439,7 @@
   using Result = R;
   static constexpr size_t ArgumentCount = sizeof...(Args);
   template <size_t I>
-  using Arg = ElemFromList<I, typename MakeIndexSequence<sizeof...(Args)>::type,
-                           Args...>;
+  using Arg = ElemFromList<I, Args...>;
   using ArgumentTuple = std::tuple<Args...>;
   using ArgumentMatcherTuple = std::tuple<Matcher<Args>...>;
   using MakeResultVoid = void(Args...);
@@ -510,4 +456,4 @@
 }  // namespace internal
 }  // namespace testing
 
-#endif  // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
diff --git a/ext/googletest/googlemock/include/gmock/internal/gmock-port.h b/ext/googletest/googlemock/include/gmock/internal/gmock-port.h
index 063e292..367a44d 100644
--- a/ext/googletest/googlemock/include/gmock/internal/gmock-port.h
+++ b/ext/googletest/googlemock/include/gmock/internal/gmock-port.h
@@ -37,11 +37,12 @@
 
 // GOOGLETEST_CM0002 DO NOT DELETE
 
-#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
-#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
 
 #include <assert.h>
 #include <stdlib.h>
+#include <cstdint>
 #include <iostream>
 
 // Most of the utilities needed for porting Google Mock are also
@@ -69,8 +70,7 @@
 
 // Macros for declaring flags.
 # define GMOCK_DECLARE_bool_(name) extern GTEST_API_ bool GMOCK_FLAG(name)
-# define GMOCK_DECLARE_int32_(name) \
-    extern GTEST_API_ ::testing::internal::Int32 GMOCK_FLAG(name)
+# define GMOCK_DECLARE_int32_(name) extern GTEST_API_ int32_t GMOCK_FLAG(name)
 # define GMOCK_DECLARE_string_(name) \
     extern GTEST_API_ ::std::string GMOCK_FLAG(name)
 
@@ -78,10 +78,10 @@
 # define GMOCK_DEFINE_bool_(name, default_val, doc) \
     GTEST_API_ bool GMOCK_FLAG(name) = (default_val)
 # define GMOCK_DEFINE_int32_(name, default_val, doc) \
-    GTEST_API_ ::testing::internal::Int32 GMOCK_FLAG(name) = (default_val)
+    GTEST_API_ int32_t GMOCK_FLAG(name) = (default_val)
 # define GMOCK_DEFINE_string_(name, default_val, doc) \
     GTEST_API_ ::std::string GMOCK_FLAG(name) = (default_val)
 
 #endif  // !defined(GMOCK_DECLARE_bool_)
 
-#endif  // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
diff --git a/ext/googletest/googlemock/include/gmock/internal/gmock-pp.h b/ext/googletest/googlemock/include/gmock/internal/gmock-pp.h
index 1ab80e1..94d61c0 100644
--- a/ext/googletest/googlemock/include/gmock/internal/gmock-pp.h
+++ b/ext/googletest/googlemock/include/gmock/internal/gmock-pp.h
@@ -1,18 +1,5 @@
-#ifndef THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_PP_H_
-#define THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_PP_H_
-
-#undef GMOCK_PP_INTERNAL_USE_MSVC
-#if defined(__clang__)
-#define GMOCK_PP_INTERNAL_USE_MSVC 0
-#elif defined(_MSC_VER)
-// TODO(iserna): Also verify tradional versus comformant preprocessor.
-static_assert(
-    _MSC_VER >= 1900,
-    "MSVC version not supported. There is support for MSVC 14.0 and above.");
-#define GMOCK_PP_INTERNAL_USE_MSVC 1
-#else
-#define GMOCK_PP_INTERNAL_USE_MSVC 0
-#endif
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_
 
 // Expands and concatenates the arguments. Constructed macros reevaluate.
 #define GMOCK_PP_CAT(_1, _2) GMOCK_PP_INTERNAL_CAT(_1, _2)
@@ -29,10 +16,6 @@
 // Returns the only argument.
 #define GMOCK_PP_IDENTITY(_1) _1
 
-// MSVC preprocessor collapses __VA_ARGS__ in a single argument, we use a
-// CAT-like directive to force correct evaluation. Each macro has its own.
-#if GMOCK_PP_INTERNAL_USE_MSVC
-
 // Evaluates to the number of arguments after expansion.
 //
 //   #define PAIR x, y
@@ -43,45 +26,27 @@
 //   GMOCK_PP_NARG(PAIR) => 2
 //
 // Requires: the number of arguments after expansion is at most 15.
-#define GMOCK_PP_NARG(...)                                                    \
-  GMOCK_PP_INTERNAL_NARG_CAT(                                                 \
-      GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, \
-                                      8, 7, 6, 5, 4, 3, 2, 1), )
+#define GMOCK_PP_NARG(...) \
+  GMOCK_PP_INTERNAL_16TH(  \
+      (__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
 
 // Returns 1 if the expansion of arguments has an unprotected comma. Otherwise
 // returns 0. Requires no more than 15 unprotected commas.
-#define GMOCK_PP_HAS_COMMA(...)                                               \
-  GMOCK_PP_INTERNAL_HAS_COMMA_CAT(                                            \
-      GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
-                                      1, 1, 1, 1, 1, 0), )
+#define GMOCK_PP_HAS_COMMA(...) \
+  GMOCK_PP_INTERNAL_16TH(       \
+      (__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0))
+
 // Returns the first argument.
-#define GMOCK_PP_HEAD(...) \
-  GMOCK_PP_INTERNAL_HEAD_CAT(GMOCK_PP_INTERNAL_HEAD(__VA_ARGS__), )
+#define GMOCK_PP_HEAD(...) GMOCK_PP_INTERNAL_HEAD((__VA_ARGS__, unusedArg))
 
 // Returns the tail. A variadic list of all arguments minus the first. Requires
 // at least one argument.
-#define GMOCK_PP_TAIL(...) \
-  GMOCK_PP_INTERNAL_TAIL_CAT(GMOCK_PP_INTERNAL_TAIL(__VA_ARGS__), )
+#define GMOCK_PP_TAIL(...) GMOCK_PP_INTERNAL_TAIL((__VA_ARGS__))
 
 // Calls CAT(_Macro, NARG(__VA_ARGS__))(__VA_ARGS__)
 #define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \
-  GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT(      \
-      GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__), )
-
-#else  // GMOCK_PP_INTERNAL_USE_MSVC
-
-#define GMOCK_PP_NARG(...)                                                   \
-  GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, \
-                                  7, 6, 5, 4, 3, 2, 1)
-#define GMOCK_PP_HAS_COMMA(...)                                              \
-  GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
-                                  1, 1, 1, 1, 0)
-#define GMOCK_PP_HEAD(...) GMOCK_PP_INTERNAL_HEAD(__VA_ARGS__)
-#define GMOCK_PP_TAIL(...) GMOCK_PP_INTERNAL_TAIL(__VA_ARGS__)
-#define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \
-  GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__)
-
-#endif  // GMOCK_PP_INTERNAL_USE_MSVC
+  GMOCK_PP_IDENTITY(                        \
+      GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__))
 
 // If the arguments after expansion have no tokens, evaluates to `1`. Otherwise
 // evaluates to `0`.
@@ -121,6 +86,14 @@
 #define GMOCK_PP_IF(_Cond, _Then, _Else) \
   GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IF_, _Cond)(_Then, _Else)
 
+// Similar to GMOCK_PP_IF but takes _Then and _Else in parentheses.
+//
+// GMOCK_PP_GENERIC_IF(1, (a, b, c), (d, e, f)) => a, b, c
+// GMOCK_PP_GENERIC_IF(0, (a, b, c), (d, e, f)) => d, e, f
+//
+#define GMOCK_PP_GENERIC_IF(_Cond, _Then, _Else) \
+  GMOCK_PP_REMOVE_PARENS(GMOCK_PP_IF(_Cond, _Then, _Else))
+
 // Evaluates to the number of arguments after expansion. Identifies 'empty' as
 // 0.
 //
@@ -139,10 +112,9 @@
 
 // Expands to 1 if the first argument starts with something in parentheses,
 // otherwise to 0.
-#define GMOCK_PP_IS_BEGIN_PARENS(...)                    \
-  GMOCK_PP_INTERNAL_ALTERNATE_HEAD(                      \
-      GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_, \
-                   GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C __VA_ARGS__))
+#define GMOCK_PP_IS_BEGIN_PARENS(...)                              \
+  GMOCK_PP_HEAD(GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_, \
+                             GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C __VA_ARGS__))
 
 // Expands to 1 is there is only one argument and it is enclosed in parentheses.
 #define GMOCK_PP_IS_ENCLOSED_PARENS(...)             \
@@ -179,10 +151,6 @@
 #define GMOCK_PP_INTENRAL_EMPTY_TUPLE (, , , , , , , , , , , , , , , )
 #define GMOCK_PP_INTERNAL_CAT(_1, _2) _1##_2
 #define GMOCK_PP_INTERNAL_STRINGIZE(...) #__VA_ARGS__
-#define GMOCK_PP_INTERNAL_INTERNAL_16TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, \
-                                        _10, _11, _12, _13, _14, _15, _16,  \
-                                        ...)                                \
-  _16
 #define GMOCK_PP_INTERNAL_CAT_5(_1, _2, _3, _4, _5) _1##_2##_3##_4##_5
 #define GMOCK_PP_INTERNAL_IS_EMPTY(_1, _2, _3, _4)                             \
   GMOCK_PP_HAS_COMMA(GMOCK_PP_INTERNAL_CAT_5(GMOCK_PP_INTERNAL_IS_EMPTY_CASE_, \
@@ -190,30 +158,24 @@
 #define GMOCK_PP_INTERNAL_IS_EMPTY_CASE_0001 ,
 #define GMOCK_PP_INTERNAL_IF_1(_Then, _Else) _Then
 #define GMOCK_PP_INTERNAL_IF_0(_Then, _Else) _Else
-#define GMOCK_PP_INTERNAL_HEAD(_1, ...) _1
-#define GMOCK_PP_INTERNAL_TAIL(_1, ...) __VA_ARGS__
 
-#if GMOCK_PP_INTERNAL_USE_MSVC
-#define GMOCK_PP_INTERNAL_NARG_CAT(_1, _2) GMOCK_PP_INTERNAL_NARG_CAT_I(_1, _2)
-#define GMOCK_PP_INTERNAL_HEAD_CAT(_1, _2) GMOCK_PP_INTERNAL_HEAD_CAT_I(_1, _2)
-#define GMOCK_PP_INTERNAL_HAS_COMMA_CAT(_1, _2) \
-  GMOCK_PP_INTERNAL_HAS_COMMA_CAT_I(_1, _2)
-#define GMOCK_PP_INTERNAL_TAIL_CAT(_1, _2) GMOCK_PP_INTERNAL_TAIL_CAT_I(_1, _2)
-#define GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT(_1, _2) \
-  GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT_I(_1, _2)
-#define GMOCK_PP_INTERNAL_NARG_CAT_I(_1, _2) _1##_2
-#define GMOCK_PP_INTERNAL_HEAD_CAT_I(_1, _2) _1##_2
-#define GMOCK_PP_INTERNAL_HAS_COMMA_CAT_I(_1, _2) _1##_2
-#define GMOCK_PP_INTERNAL_TAIL_CAT_I(_1, _2) _1##_2
-#define GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT_I(_1, _2) _1##_2
-#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD(...) \
-  GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT(GMOCK_PP_HEAD(__VA_ARGS__), )
-#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT(_1, _2) \
-  GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT_I(_1, _2)
-#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT_I(_1, _2) _1##_2
-#else  // GMOCK_PP_INTERNAL_USE_MSVC
-#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD(...) GMOCK_PP_HEAD(__VA_ARGS__)
-#endif  // GMOCK_PP_INTERNAL_USE_MSVC
+// Because of MSVC treating a token with a comma in it as a single token when
+// passed to another macro, we need to force it to evaluate it as multiple
+// tokens. We do that by using a "IDENTITY(MACRO PARENTHESIZED_ARGS)" macro. We
+// define one per possible macro that relies on this behavior. Note "_Args" must
+// be parenthesized.
+#define GMOCK_PP_INTERNAL_INTERNAL_16TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, \
+                                        _10, _11, _12, _13, _14, _15, _16,  \
+                                        ...)                                \
+  _16
+#define GMOCK_PP_INTERNAL_16TH(_Args) \
+  GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_16TH _Args)
+#define GMOCK_PP_INTERNAL_INTERNAL_HEAD(_1, ...) _1
+#define GMOCK_PP_INTERNAL_HEAD(_Args) \
+  GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_HEAD _Args)
+#define GMOCK_PP_INTERNAL_INTERNAL_TAIL(_1, ...) __VA_ARGS__
+#define GMOCK_PP_INTERNAL_TAIL(_Args) \
+  GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_TAIL _Args)
 
 #define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C(...) 1 _
 #define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_1 1,
@@ -314,4 +276,4 @@
   GMOCK_PP_INTERNAL_FOR_EACH_IMPL_14(GMOCK_PP_INC(_i), _Macro, _Data,   \
                                      (GMOCK_PP_TAIL _Tuple))
 
-#endif  // THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_
diff --git a/ext/googletest/googlemock/scripts/README.md b/ext/googletest/googlemock/scripts/README.md
new file mode 100644
index 0000000..a3301e5
--- /dev/null
+++ b/ext/googletest/googlemock/scripts/README.md
@@ -0,0 +1,5 @@
+# Please Note:
+
+Files in this directory are no longer supported by the maintainers. They
+represent mostly historical artifacts and supported by the community only. There
+is no guarantee whatsoever that these scripts still work.
diff --git a/ext/googletest/googlemock/scripts/fuse_gmock_files.py b/ext/googletest/googlemock/scripts/fuse_gmock_files.py
index c33c725..7fa9b3a 100755
--- a/ext/googletest/googlemock/scripts/fuse_gmock_files.py
+++ b/ext/googletest/googlemock/scripts/fuse_gmock_files.py
@@ -28,8 +28,8 @@
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""fuse_gmock_files.py v0.1.0.
 
-"""fuse_gmock_files.py v0.1.0
 Fuses Google Mock and Google Test source code into two .h files and a .cc file.
 
 SYNOPSIS
@@ -55,27 +55,29 @@
 This tool is experimental.  In particular, it assumes that there is no
 conditional inclusion of Google Mock or Google Test headers.  Please
 report any problems to googlemock@googlegroups.com.  You can read
-https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md for more
+https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md
+for more
 information.
 """
 
-__author__ = 'wan@google.com (Zhanyong Wan)'
+from __future__ import print_function
 
 import os
 import re
-import sets
 import sys
 
+__author__ = 'wan@google.com (Zhanyong Wan)'
+
 # We assume that this file is in the scripts/ directory in the Google
 # Mock root directory.
 DEFAULT_GMOCK_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..')
 
 # We need to call into googletest/scripts/fuse_gtest_files.py.
 sys.path.append(os.path.join(DEFAULT_GMOCK_ROOT_DIR, '../googletest/scripts'))
-import fuse_gtest_files
-gtest = fuse_gtest_files
+import fuse_gtest_files as gtest  # pylint:disable=g-import-not-at-top
 
-# Regex for matching '#include "gmock/..."'.
+# Regex for matching
+# '#include "gmock/..."'.
 INCLUDE_GMOCK_FILE_REGEX = re.compile(r'^\s*#\s*include\s*"(gmock/.+)"')
 
 # Where to find the source seed files.
@@ -98,6 +100,9 @@
   """Makes sure gmock_root points to a valid gmock root directory.
 
   The function aborts the program on failure.
+
+  Args:
+    gmock_root: A string with the mock root directory.
   """
 
   gtest.ValidateGTestRootDir(GetGTestRootDir(gmock_root))
@@ -109,6 +114,9 @@
   """Makes sure output_dir points to a valid output directory.
 
   The function aborts the program on failure.
+
+  Args:
+    output_dir: A string representing the output directory.
   """
 
   gtest.VerifyOutputFile(output_dir, gtest.GTEST_H_OUTPUT)
@@ -119,8 +127,8 @@
 def FuseGMockH(gmock_root, output_dir):
   """Scans folder gmock_root to generate gmock/gmock.h in output_dir."""
 
-  output_file = file(os.path.join(output_dir, GMOCK_H_OUTPUT), 'w')
-  processed_files = sets.Set()  # Holds all gmock headers we've processed.
+  output_file = open(os.path.join(output_dir, GMOCK_H_OUTPUT), 'w')
+  processed_files = set()  # Holds all gmock headers we've processed.
 
   def ProcessFile(gmock_header_path):
     """Processes the given gmock header file."""
@@ -132,25 +140,28 @@
     processed_files.add(gmock_header_path)
 
     # Reads each line in the given gmock header.
-    for line in file(os.path.join(gmock_root, gmock_header_path), 'r'):
-      m = INCLUDE_GMOCK_FILE_REGEX.match(line)
-      if m:
-        # It's '#include "gmock/..."' - let's process it recursively.
-        ProcessFile('include/' + m.group(1))
-      else:
-        m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line)
-        if m:
-          # It's '#include "gtest/foo.h"'.  We translate it to
-          # "gtest/gtest.h", regardless of what foo is, since all
-          # gtest headers are fused into gtest/gtest.h.
 
-          # There is no need to #include gtest.h twice.
-          if not gtest.GTEST_H_SEED in processed_files:
-            processed_files.add(gtest.GTEST_H_SEED)
-            output_file.write('#include "%s"\n' % (gtest.GTEST_H_OUTPUT,))
+    with open(os.path.join(gmock_root, gmock_header_path), 'r') as fh:
+      for line in fh:
+        m = INCLUDE_GMOCK_FILE_REGEX.match(line)
+        if m:
+          # '#include "gmock/..."'
+          # - let's process it recursively.
+          ProcessFile('include/' + m.group(1))
         else:
-          # Otherwise we copy the line unchanged to the output file.
-          output_file.write(line)
+          m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line)
+          if m:
+            # '#include "gtest/foo.h"'
+            # We translate it to "gtest/gtest.h", regardless of what foo is,
+            # since all gtest headers are fused into gtest/gtest.h.
+
+            # There is no need to #include gtest.h twice.
+            if gtest.GTEST_H_SEED not in processed_files:
+              processed_files.add(gtest.GTEST_H_SEED)
+              output_file.write('#include "%s"\n' % (gtest.GTEST_H_OUTPUT,))
+          else:
+            # Otherwise we copy the line unchanged to the output file.
+            output_file.write(line)
 
   ProcessFile(GMOCK_H_SEED)
   output_file.close()
@@ -159,7 +170,7 @@
 def FuseGMockAllCcToFile(gmock_root, output_file):
   """Scans folder gmock_root to fuse gmock-all.cc into output_file."""
 
-  processed_files = sets.Set()
+  processed_files = set()
 
   def ProcessFile(gmock_source_file):
     """Processes the given gmock source file."""
@@ -171,32 +182,37 @@
     processed_files.add(gmock_source_file)
 
     # Reads each line in the given gmock source file.
-    for line in file(os.path.join(gmock_root, gmock_source_file), 'r'):
-      m = INCLUDE_GMOCK_FILE_REGEX.match(line)
-      if m:
-        # It's '#include "gmock/foo.h"'.  We treat it as '#include
-        # "gmock/gmock.h"', as all other gmock headers are being fused
-        # into gmock.h and cannot be #included directly.
 
-        # There is no need to #include "gmock/gmock.h" more than once.
-        if not GMOCK_H_SEED in processed_files:
-          processed_files.add(GMOCK_H_SEED)
-          output_file.write('#include "%s"\n' % (GMOCK_H_OUTPUT,))
-      else:
-        m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line)
+    with open(os.path.join(gmock_root, gmock_source_file), 'r') as fh:
+      for line in fh:
+        m = INCLUDE_GMOCK_FILE_REGEX.match(line)
         if m:
-          # It's '#include "gtest/..."'.
-          # There is no need to #include gtest.h as it has been
-          # #included by gtest-all.cc.
-          pass
+          # '#include "gmock/foo.h"'
+          # We treat it as '#include  "gmock/gmock.h"', as all other gmock
+          # headers are being fused into gmock.h and cannot be
+          # included directly.  No need to
+          # #include "gmock/gmock.h"
+          # more than once.
+
+          if GMOCK_H_SEED not in processed_files:
+            processed_files.add(GMOCK_H_SEED)
+            output_file.write('#include "%s"\n' % (GMOCK_H_OUTPUT,))
         else:
-          m = gtest.INCLUDE_SRC_FILE_REGEX.match(line)
+          m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line)
           if m:
-            # It's '#include "src/foo"' - let's process it recursively.
-            ProcessFile(m.group(1))
+            # '#include "gtest/..."'
+            # There is no need to #include gtest.h as it has been
+            # #included by gtest-all.cc.
+
+            pass
           else:
-            # Otherwise we copy the line unchanged to the output file.
-            output_file.write(line)
+            m = gtest.INCLUDE_SRC_FILE_REGEX.match(line)
+            if m:
+              # It's '#include "src/foo"' - let's process it recursively.
+              ProcessFile(m.group(1))
+            else:
+              # Otherwise we copy the line unchanged to the output file.
+              output_file.write(line)
 
   ProcessFile(GMOCK_ALL_CC_SEED)
 
@@ -204,12 +220,12 @@
 def FuseGMockGTestAllCc(gmock_root, output_dir):
   """Scans folder gmock_root to generate gmock-gtest-all.cc in output_dir."""
 
-  output_file = file(os.path.join(output_dir, GMOCK_GTEST_ALL_CC_OUTPUT), 'w')
-  # First, fuse gtest-all.cc into gmock-gtest-all.cc.
-  gtest.FuseGTestAllCcToFile(GetGTestRootDir(gmock_root), output_file)
-  # Next, append fused gmock-all.cc to gmock-gtest-all.cc.
-  FuseGMockAllCcToFile(gmock_root, output_file)
-  output_file.close()
+  with open(os.path.join(output_dir, GMOCK_GTEST_ALL_CC_OUTPUT),
+            'w') as output_file:
+    # First, fuse gtest-all.cc into gmock-gtest-all.cc.
+    gtest.FuseGTestAllCcToFile(GetGTestRootDir(gmock_root), output_file)
+    # Next, append fused gmock-all.cc to gmock-gtest-all.cc.
+    FuseGMockAllCcToFile(gmock_root, output_file)
 
 
 def FuseGMock(gmock_root, output_dir):
@@ -232,7 +248,7 @@
     # fuse_gmock_files.py GMOCK_ROOT_DIR OUTPUT_DIR
     FuseGMock(sys.argv[1], sys.argv[2])
   else:
-    print __doc__
+    print(__doc__)
     sys.exit(1)
 
 
diff --git a/ext/googletest/googlemock/scripts/generator/cpp/ast.py b/ext/googletest/googlemock/scripts/generator/cpp/ast.py
index f14728b..0e77016 100755
--- a/ext/googletest/googlemock/scripts/generator/cpp/ast.py
+++ b/ext/googletest/googlemock/scripts/generator/cpp/ast.py
@@ -17,10 +17,7 @@
 
 """Generate an Abstract Syntax Tree (AST) for C++."""
 
-__author__ = 'nnorwitz@google.com (Neal Norwitz)'
-
-
-# TODO:
+# FIXME:
 #  * Tokens should never be exported, need to convert to Nodes
 #    (return types, parameters, etc.)
 #  * Handle static class data for templatized classes
@@ -33,12 +30,13 @@
 
 
 try:
-    # Python 3.x
-    import builtins
+  # Python 3.x
+  import builtins
 except ImportError:
-    # Python 2.x
-    import __builtin__ as builtins
+  # Python 2.x
+  import __builtin__ as builtins
 
+import collections
 import sys
 import traceback
 
@@ -48,15 +46,15 @@
 
 
 if not hasattr(builtins, 'reversed'):
-    # Support Python 2.3 and earlier.
-    def reversed(seq):
-        for i in range(len(seq)-1, -1, -1):
-            yield seq[i]
+  # Support Python 2.3 and earlier.
+  def reversed(seq):
+    for i in range(len(seq)-1, -1, -1):
+      yield seq[i]
 
 if not hasattr(builtins, 'next'):
-    # Support Python 2.5 and earlier.
-    def next(obj):
-        return obj.next()
+  # Support Python 2.5 and earlier.
+  def next(obj):
+    return obj.next()
 
 
 VISIBILITY_PUBLIC, VISIBILITY_PROTECTED, VISIBILITY_PRIVATE = range(3)
@@ -101,1570 +99,1609 @@
 # TODO(nnorwitz): use this as a singleton for templated_types, etc
 # where we don't want to create a new empty dict each time.  It is also const.
 class _NullDict(object):
-    __contains__ = lambda self: False
-    keys = values = items = iterkeys = itervalues = iteritems = lambda self: ()
+  __contains__ = lambda self: False
+  keys = values = items = iterkeys = itervalues = iteritems = lambda self: ()
 
 
 # TODO(nnorwitz): move AST nodes into a separate module.
 class Node(object):
-    """Base AST node."""
+  """Base AST node."""
 
-    def __init__(self, start, end):
-        self.start = start
-        self.end = end
+  def __init__(self, start, end):
+    self.start = start
+    self.end = end
 
-    def IsDeclaration(self):
-        """Returns bool if this node is a declaration."""
-        return False
+  def IsDeclaration(self):
+    """Returns bool if this node is a declaration."""
+    return False
 
-    def IsDefinition(self):
-        """Returns bool if this node is a definition."""
-        return False
+  def IsDefinition(self):
+    """Returns bool if this node is a definition."""
+    return False
 
-    def IsExportable(self):
-        """Returns bool if this node exportable from a header file."""
-        return False
+  def IsExportable(self):
+    """Returns bool if this node exportable from a header file."""
+    return False
 
-    def Requires(self, node):
-        """Does this AST node require the definition of the node passed in?"""
-        return False
+  def Requires(self, node):
+    """Does this AST node require the definition of the node passed in?"""
+    return False
 
-    def XXX__str__(self):
-        return self._StringHelper(self.__class__.__name__, '')
+  def XXX__str__(self):
+    return self._StringHelper(self.__class__.__name__, '')
 
-    def _StringHelper(self, name, suffix):
-        if not utils.DEBUG:
-            return '%s(%s)' % (name, suffix)
-        return '%s(%d, %d, %s)' % (name, self.start, self.end, suffix)
+  def _StringHelper(self, name, suffix):
+    if not utils.DEBUG:
+      return '%s(%s)' % (name, suffix)
+    return '%s(%d, %d, %s)' % (name, self.start, self.end, suffix)
 
-    def __repr__(self):
-        return str(self)
+  def __repr__(self):
+    return str(self)
 
 
 class Define(Node):
-    def __init__(self, start, end, name, definition):
-        Node.__init__(self, start, end)
-        self.name = name
-        self.definition = definition
+  def __init__(self, start, end, name, definition):
+    Node.__init__(self, start, end)
+    self.name = name
+    self.definition = definition
 
-    def __str__(self):
-        value = '%s %s' % (self.name, self.definition)
-        return self._StringHelper(self.__class__.__name__, value)
+  def __str__(self):
+    value = '%s %s' % (self.name, self.definition)
+    return self._StringHelper(self.__class__.__name__, value)
 
 
 class Include(Node):
-    def __init__(self, start, end, filename, system):
-        Node.__init__(self, start, end)
-        self.filename = filename
-        self.system = system
+  def __init__(self, start, end, filename, system):
+    Node.__init__(self, start, end)
+    self.filename = filename
+    self.system = system
 
-    def __str__(self):
-        fmt = '"%s"'
-        if self.system:
-            fmt = '<%s>'
-        return self._StringHelper(self.__class__.__name__, fmt % self.filename)
+  def __str__(self):
+    fmt = '"%s"'
+    if self.system:
+      fmt = '<%s>'
+    return self._StringHelper(self.__class__.__name__, fmt % self.filename)
 
 
 class Goto(Node):
-    def __init__(self, start, end, label):
-        Node.__init__(self, start, end)
-        self.label = label
+  def __init__(self, start, end, label):
+    Node.__init__(self, start, end)
+    self.label = label
 
-    def __str__(self):
-        return self._StringHelper(self.__class__.__name__, str(self.label))
+  def __str__(self):
+    return self._StringHelper(self.__class__.__name__, str(self.label))
 
 
 class Expr(Node):
-    def __init__(self, start, end, expr):
-        Node.__init__(self, start, end)
-        self.expr = expr
+  def __init__(self, start, end, expr):
+    Node.__init__(self, start, end)
+    self.expr = expr
 
-    def Requires(self, node):
-        # TODO(nnorwitz): impl.
-        return False
+  def Requires(self, node):
+    # TODO(nnorwitz): impl.
+    return False
 
-    def __str__(self):
-        return self._StringHelper(self.__class__.__name__, str(self.expr))
+  def __str__(self):
+    return self._StringHelper(self.__class__.__name__, str(self.expr))
 
 
 class Return(Expr):
-    pass
+  pass
 
 
 class Delete(Expr):
-    pass
+  pass
 
 
 class Friend(Expr):
-    def __init__(self, start, end, expr, namespace):
-        Expr.__init__(self, start, end, expr)
-        self.namespace = namespace[:]
+  def __init__(self, start, end, expr, namespace):
+    Expr.__init__(self, start, end, expr)
+    self.namespace = namespace[:]
 
 
 class Using(Node):
-    def __init__(self, start, end, names):
-        Node.__init__(self, start, end)
-        self.names = names
+  def __init__(self, start, end, names):
+    Node.__init__(self, start, end)
+    self.names = names
 
-    def __str__(self):
-        return self._StringHelper(self.__class__.__name__, str(self.names))
+  def __str__(self):
+    return self._StringHelper(self.__class__.__name__, str(self.names))
 
 
 class Parameter(Node):
-    def __init__(self, start, end, name, parameter_type, default):
-        Node.__init__(self, start, end)
-        self.name = name
-        self.type = parameter_type
-        self.default = default
+  def __init__(self, start, end, name, parameter_type, default):
+    Node.__init__(self, start, end)
+    self.name = name
+    self.type = parameter_type
+    self.default = default
 
-    def Requires(self, node):
-        # TODO(nnorwitz): handle namespaces, etc.
-        return self.type.name == node.name
+  def Requires(self, node):
+    # TODO(nnorwitz): handle namespaces, etc.
+    return self.type.name == node.name
 
-    def __str__(self):
-        name = str(self.type)
-        suffix = '%s %s' % (name, self.name)
-        if self.default:
-            suffix += ' = ' + ''.join([d.name for d in self.default])
-        return self._StringHelper(self.__class__.__name__, suffix)
+  def __str__(self):
+    name = str(self.type)
+    suffix = '%s %s' % (name, self.name)
+    if self.default:
+      suffix += ' = ' + ''.join([d.name for d in self.default])
+    return self._StringHelper(self.__class__.__name__, suffix)
 
 
 class _GenericDeclaration(Node):
-    def __init__(self, start, end, name, namespace):
-        Node.__init__(self, start, end)
-        self.name = name
-        self.namespace = namespace[:]
+  def __init__(self, start, end, name, namespace):
+    Node.__init__(self, start, end)
+    self.name = name
+    self.namespace = namespace[:]
 
-    def FullName(self):
-        prefix = ''
-        if self.namespace and self.namespace[-1]:
-            prefix = '::'.join(self.namespace) + '::'
-        return prefix + self.name
+  def FullName(self):
+    prefix = ''
+    if self.namespace and self.namespace[-1]:
+      prefix = '::'.join(self.namespace) + '::'
+    return prefix + self.name
 
-    def _TypeStringHelper(self, suffix):
-        if self.namespace:
-            names = [n or '<anonymous>' for n in self.namespace]
-            suffix += ' in ' + '::'.join(names)
-        return self._StringHelper(self.__class__.__name__, suffix)
+  def _TypeStringHelper(self, suffix):
+    if self.namespace:
+      names = [n or '<anonymous>' for n in self.namespace]
+      suffix += ' in ' + '::'.join(names)
+    return self._StringHelper(self.__class__.__name__, suffix)
 
 
 # TODO(nnorwitz): merge with Parameter in some way?
 class VariableDeclaration(_GenericDeclaration):
-    def __init__(self, start, end, name, var_type, initial_value, namespace):
-        _GenericDeclaration.__init__(self, start, end, name, namespace)
-        self.type = var_type
-        self.initial_value = initial_value
+  def __init__(self, start, end, name, var_type, initial_value, namespace):
+    _GenericDeclaration.__init__(self, start, end, name, namespace)
+    self.type = var_type
+    self.initial_value = initial_value
 
-    def Requires(self, node):
-        # TODO(nnorwitz): handle namespaces, etc.
-        return self.type.name == node.name
+  def Requires(self, node):
+    # TODO(nnorwitz): handle namespaces, etc.
+    return self.type.name == node.name
 
-    def ToString(self):
-        """Return a string that tries to reconstitute the variable decl."""
-        suffix = '%s %s' % (self.type, self.name)
-        if self.initial_value:
-            suffix += ' = ' + self.initial_value
-        return suffix
+  def ToString(self):
+    """Return a string that tries to reconstitute the variable decl."""
+    suffix = '%s %s' % (self.type, self.name)
+    if self.initial_value:
+      suffix += ' = ' + self.initial_value
+    return suffix
 
-    def __str__(self):
-        return self._StringHelper(self.__class__.__name__, self.ToString())
+  def __str__(self):
+    return self._StringHelper(self.__class__.__name__, self.ToString())
 
 
 class Typedef(_GenericDeclaration):
-    def __init__(self, start, end, name, alias, namespace):
-        _GenericDeclaration.__init__(self, start, end, name, namespace)
-        self.alias = alias
+  def __init__(self, start, end, name, alias, namespace):
+    _GenericDeclaration.__init__(self, start, end, name, namespace)
+    self.alias = alias
 
-    def IsDefinition(self):
+  def IsDefinition(self):
+    return True
+
+  def IsExportable(self):
+    return True
+
+  def Requires(self, node):
+    # TODO(nnorwitz): handle namespaces, etc.
+    name = node.name
+    for token in self.alias:
+      if token is not None and name == token.name:
         return True
+    return False
 
-    def IsExportable(self):
-        return True
-
-    def Requires(self, node):
-        # TODO(nnorwitz): handle namespaces, etc.
-        name = node.name
-        for token in self.alias:
-            if token is not None and name == token.name:
-                return True
-        return False
-
-    def __str__(self):
-        suffix = '%s, %s' % (self.name, self.alias)
-        return self._TypeStringHelper(suffix)
+  def __str__(self):
+    suffix = '%s, %s' % (self.name, self.alias)
+    return self._TypeStringHelper(suffix)
 
 
 class _NestedType(_GenericDeclaration):
-    def __init__(self, start, end, name, fields, namespace):
-        _GenericDeclaration.__init__(self, start, end, name, namespace)
-        self.fields = fields
+  def __init__(self, start, end, name, fields, namespace):
+    _GenericDeclaration.__init__(self, start, end, name, namespace)
+    self.fields = fields
 
-    def IsDefinition(self):
-        return True
+  def IsDefinition(self):
+    return True
 
-    def IsExportable(self):
-        return True
+  def IsExportable(self):
+    return True
 
-    def __str__(self):
-        suffix = '%s, {%s}' % (self.name, self.fields)
-        return self._TypeStringHelper(suffix)
+  def __str__(self):
+    suffix = '%s, {%s}' % (self.name, self.fields)
+    return self._TypeStringHelper(suffix)
 
 
 class Union(_NestedType):
-    pass
+  pass
 
 
 class Enum(_NestedType):
-    pass
+  pass
 
 
 class Class(_GenericDeclaration):
-    def __init__(self, start, end, name, bases, templated_types, body, namespace):
-        _GenericDeclaration.__init__(self, start, end, name, namespace)
-        self.bases = bases
-        self.body = body
-        self.templated_types = templated_types
+  def __init__(self, start, end, name, bases, templated_types, body, namespace):
+    _GenericDeclaration.__init__(self, start, end, name, namespace)
+    self.bases = bases
+    self.body = body
+    self.templated_types = templated_types
 
-    def IsDeclaration(self):
-        return self.bases is None and self.body is None
+  def IsDeclaration(self):
+    return self.bases is None and self.body is None
 
-    def IsDefinition(self):
-        return not self.IsDeclaration()
+  def IsDefinition(self):
+    return not self.IsDeclaration()
 
-    def IsExportable(self):
-        return not self.IsDeclaration()
+  def IsExportable(self):
+    return not self.IsDeclaration()
 
-    def Requires(self, node):
-        # TODO(nnorwitz): handle namespaces, etc.
-        if self.bases:
-            for token_list in self.bases:
-                # TODO(nnorwitz): bases are tokens, do name comparison.
-                for token in token_list:
-                    if token.name == node.name:
-                        return True
-        # TODO(nnorwitz): search in body too.
-        return False
+  def Requires(self, node):
+    # TODO(nnorwitz): handle namespaces, etc.
+    if self.bases:
+      for token_list in self.bases:
+        # TODO(nnorwitz): bases are tokens, do name comparison.
+        for token in token_list:
+          if token.name == node.name:
+            return True
+    # TODO(nnorwitz): search in body too.
+    return False
 
-    def __str__(self):
-        name = self.name
-        if self.templated_types:
-            name += '<%s>' % self.templated_types
-        suffix = '%s, %s, %s' % (name, self.bases, self.body)
-        return self._TypeStringHelper(suffix)
+  def __str__(self):
+    name = self.name
+    if self.templated_types:
+      name += '<%s>' % self.templated_types
+    suffix = '%s, %s, %s' % (name, self.bases, self.body)
+    return self._TypeStringHelper(suffix)
 
 
 class Struct(Class):
-    pass
+  pass
 
 
 class Function(_GenericDeclaration):
-    def __init__(self, start, end, name, return_type, parameters,
-                 modifiers, templated_types, body, namespace):
-        _GenericDeclaration.__init__(self, start, end, name, namespace)
-        converter = TypeConverter(namespace)
-        self.return_type = converter.CreateReturnType(return_type)
-        self.parameters = converter.ToParameters(parameters)
-        self.modifiers = modifiers
-        self.body = body
-        self.templated_types = templated_types
+  def __init__(self, start, end, name, return_type, parameters,
+               modifiers, templated_types, body, namespace):
+    _GenericDeclaration.__init__(self, start, end, name, namespace)
+    converter = TypeConverter(namespace)
+    self.return_type = converter.CreateReturnType(return_type)
+    self.parameters = converter.ToParameters(parameters)
+    self.modifiers = modifiers
+    self.body = body
+    self.templated_types = templated_types
 
-    def IsDeclaration(self):
-        return self.body is None
+  def IsDeclaration(self):
+    return self.body is None
 
-    def IsDefinition(self):
-        return self.body is not None
+  def IsDefinition(self):
+    return self.body is not None
 
-    def IsExportable(self):
-        if self.return_type and 'static' in self.return_type.modifiers:
-            return False
-        return None not in self.namespace
+  def IsExportable(self):
+    if self.return_type and 'static' in self.return_type.modifiers:
+      return False
+    return None not in self.namespace
 
-    def Requires(self, node):
-        if self.parameters:
-            # TODO(nnorwitz): parameters are tokens, do name comparison.
-            for p in self.parameters:
-                if p.name == node.name:
-                    return True
-        # TODO(nnorwitz): search in body too.
-        return False
+  def Requires(self, node):
+    if self.parameters:
+      # TODO(nnorwitz): parameters are tokens, do name comparison.
+      for p in self.parameters:
+        if p.name == node.name:
+          return True
+    # TODO(nnorwitz): search in body too.
+    return False
 
-    def __str__(self):
-        # TODO(nnorwitz): add templated_types.
-        suffix = ('%s %s(%s), 0x%02x, %s' %
-                  (self.return_type, self.name, self.parameters,
-                   self.modifiers, self.body))
-        return self._TypeStringHelper(suffix)
+  def __str__(self):
+    # TODO(nnorwitz): add templated_types.
+    suffix = ('%s %s(%s), 0x%02x, %s' %
+              (self.return_type, self.name, self.parameters,
+               self.modifiers, self.body))
+    return self._TypeStringHelper(suffix)
 
 
 class Method(Function):
-    def __init__(self, start, end, name, in_class, return_type, parameters,
-                 modifiers, templated_types, body, namespace):
-        Function.__init__(self, start, end, name, return_type, parameters,
-                          modifiers, templated_types, body, namespace)
-        # TODO(nnorwitz): in_class could also be a namespace which can
-        # mess up finding functions properly.
-        self.in_class = in_class
+  def __init__(self, start, end, name, in_class, return_type, parameters,
+               modifiers, templated_types, body, namespace):
+    Function.__init__(self, start, end, name, return_type, parameters,
+                      modifiers, templated_types, body, namespace)
+    # TODO(nnorwitz): in_class could also be a namespace which can
+    # mess up finding functions properly.
+    self.in_class = in_class
 
 
 class Type(_GenericDeclaration):
-    """Type used for any variable (eg class, primitive, struct, etc)."""
+  """Type used for any variable (eg class, primitive, struct, etc)."""
 
-    def __init__(self, start, end, name, templated_types, modifiers,
-                 reference, pointer, array):
-        """
+  def __init__(self, start, end, name, templated_types, modifiers,
+               reference, pointer, array):
+    """
         Args:
           name: str name of main type
           templated_types: [Class (Type?)] template type info between <>
           modifiers: [str] type modifiers (keywords) eg, const, mutable, etc.
           reference, pointer, array: bools
         """
-        _GenericDeclaration.__init__(self, start, end, name, [])
-        self.templated_types = templated_types
-        if not name and modifiers:
-            self.name = modifiers.pop()
-        self.modifiers = modifiers
-        self.reference = reference
-        self.pointer = pointer
-        self.array = array
+    _GenericDeclaration.__init__(self, start, end, name, [])
+    self.templated_types = templated_types
+    if not name and modifiers:
+      self.name = modifiers.pop()
+    self.modifiers = modifiers
+    self.reference = reference
+    self.pointer = pointer
+    self.array = array
 
-    def __str__(self):
-        prefix = ''
-        if self.modifiers:
-            prefix = ' '.join(self.modifiers) + ' '
-        name = str(self.name)
-        if self.templated_types:
-            name += '<%s>' % self.templated_types
-        suffix = prefix + name
-        if self.reference:
-            suffix += '&'
-        if self.pointer:
-            suffix += '*'
-        if self.array:
-            suffix += '[]'
-        return self._TypeStringHelper(suffix)
+  def __str__(self):
+    prefix = ''
+    if self.modifiers:
+      prefix = ' '.join(self.modifiers) + ' '
+    name = str(self.name)
+    if self.templated_types:
+      name += '<%s>' % self.templated_types
+    suffix = prefix + name
+    if self.reference:
+      suffix += '&'
+    if self.pointer:
+      suffix += '*'
+    if self.array:
+      suffix += '[]'
+    return self._TypeStringHelper(suffix)
 
-    # By definition, Is* are always False.  A Type can only exist in
-    # some sort of variable declaration, parameter, or return value.
-    def IsDeclaration(self):
-        return False
+  # By definition, Is* are always False.  A Type can only exist in
+  # some sort of variable declaration, parameter, or return value.
+  def IsDeclaration(self):
+    return False
 
-    def IsDefinition(self):
-        return False
+  def IsDefinition(self):
+    return False
 
-    def IsExportable(self):
-        return False
+  def IsExportable(self):
+    return False
 
 
 class TypeConverter(object):
 
-    def __init__(self, namespace_stack):
-        self.namespace_stack = namespace_stack
+  def __init__(self, namespace_stack):
+    self.namespace_stack = namespace_stack
 
-    def _GetTemplateEnd(self, tokens, start):
-        count = 1
-        end = start
-        while 1:
-            token = tokens[end]
-            end += 1
-            if token.name == '<':
-                count += 1
-            elif token.name == '>':
-                count -= 1
-                if count == 0:
-                    break
-        return tokens[start:end-1], end
+  def _GetTemplateEnd(self, tokens, start):
+    count = 1
+    end = start
+    while 1:
+      token = tokens[end]
+      end += 1
+      if token.name == '<':
+        count += 1
+      elif token.name == '>':
+        count -= 1
+        if count == 0:
+          break
+    return tokens[start:end-1], end
 
-    def ToType(self, tokens):
-        """Convert [Token,...] to [Class(...), ] useful for base classes.
+  def ToType(self, tokens):
+    """Convert [Token,...] to [Class(...), ] useful for base classes.
         For example, code like class Foo : public Bar<x, y> { ... };
         the "Bar<x, y>" portion gets converted to an AST.
 
         Returns:
           [Class(...), ...]
         """
-        result = []
-        name_tokens = []
+    result = []
+    name_tokens = []
+    reference = pointer = array = False
+
+    def AddType(templated_types):
+      # Partition tokens into name and modifier tokens.
+      names = []
+      modifiers = []
+      for t in name_tokens:
+        if keywords.IsKeyword(t.name):
+          modifiers.append(t.name)
+        else:
+          names.append(t.name)
+      name = ''.join(names)
+      if name_tokens:
+        result.append(Type(name_tokens[0].start, name_tokens[-1].end,
+                           name, templated_types, modifiers,
+                           reference, pointer, array))
+      del name_tokens[:]
+
+    i = 0
+    end = len(tokens)
+    while i < end:
+      token = tokens[i]
+      if token.name == '<':
+        new_tokens, new_end = self._GetTemplateEnd(tokens, i+1)
+        AddType(self.ToType(new_tokens))
+        # If there is a comma after the template, we need to consume
+        # that here otherwise it becomes part of the name.
+        i = new_end
         reference = pointer = array = False
+      elif token.name == ',':
+        AddType([])
+        reference = pointer = array = False
+      elif token.name == '*':
+        pointer = True
+      elif token.name == '&':
+        reference = True
+      elif token.name == '[':
+        pointer = True
+      elif token.name == ']':
+        pass
+      else:
+        name_tokens.append(token)
+      i += 1
 
-        def AddType(templated_types):
-            # Partition tokens into name and modifier tokens.
-            names = []
-            modifiers = []
-            for t in name_tokens:
-                if keywords.IsKeyword(t.name):
-                    modifiers.append(t.name)
-                else:
-                    names.append(t.name)
-            name = ''.join(names)
-            if name_tokens:
-                result.append(Type(name_tokens[0].start, name_tokens[-1].end,
-                                   name, templated_types, modifiers,
-                                   reference, pointer, array))
-            del name_tokens[:]
+    if name_tokens:
+      # No '<' in the tokens, just a simple name and no template.
+      AddType([])
+    return result
 
-        i = 0
-        end = len(tokens)
-        while i < end:
-            token = tokens[i]
-            if token.name == '<':
-                new_tokens, new_end = self._GetTemplateEnd(tokens, i+1)
-                AddType(self.ToType(new_tokens))
-                # If there is a comma after the template, we need to consume
-                # that here otherwise it becomes part of the name.
-                i = new_end
-                reference = pointer = array = False
-            elif token.name == ',':
-                AddType([])
-                reference = pointer = array = False
-            elif token.name == '*':
-                pointer = True
-            elif token.name == '&':
-                reference = True
-            elif token.name == '[':
-               pointer = True
-            elif token.name == ']':
-                pass
-            else:
-                name_tokens.append(token)
-            i += 1
+  def DeclarationToParts(self, parts, needs_name_removed):
+    name = None
+    default = []
+    if needs_name_removed:
+      # Handle default (initial) values properly.
+      for i, t in enumerate(parts):
+        if t.name == '=':
+          default = parts[i+1:]
+          name = parts[i-1].name
+          if name == ']' and parts[i-2].name == '[':
+            name = parts[i-3].name
+            i -= 1
+          parts = parts[:i-1]
+          break
+      else:
+        if parts[-1].token_type == tokenize.NAME:
+          name = parts.pop().name
+        else:
+          # TODO(nnorwitz): this is a hack that happens for code like
+          # Register(Foo<T>); where it thinks this is a function call
+          # but it's actually a declaration.
+          name = '???'
+    modifiers = []
+    type_name = []
+    other_tokens = []
+    templated_types = []
+    i = 0
+    end = len(parts)
+    while i < end:
+      p = parts[i]
+      if keywords.IsKeyword(p.name):
+        modifiers.append(p.name)
+      elif p.name == '<':
+        templated_tokens, new_end = self._GetTemplateEnd(parts, i+1)
+        templated_types = self.ToType(templated_tokens)
+        i = new_end - 1
+        # Don't add a spurious :: to data members being initialized.
+        next_index = i + 1
+        if next_index < end and parts[next_index].name == '::':
+          i += 1
+      elif p.name in ('[', ']', '='):
+        # These are handled elsewhere.
+        other_tokens.append(p)
+      elif p.name not in ('*', '&', '>'):
+        # Ensure that names have a space between them.
+        if (type_name and type_name[-1].token_type == tokenize.NAME and
+                p.token_type == tokenize.NAME):
+          type_name.append(tokenize.Token(tokenize.SYNTAX, ' ', 0, 0))
+        type_name.append(p)
+      else:
+        other_tokens.append(p)
+      i += 1
+    type_name = ''.join([t.name for t in type_name])
+    return name, type_name, templated_types, modifiers, default, other_tokens
 
-        if name_tokens:
-            # No '<' in the tokens, just a simple name and no template.
-            AddType([])
-        return result
+  def ToParameters(self, tokens):
+    if not tokens:
+      return []
 
-    def DeclarationToParts(self, parts, needs_name_removed):
-        name = None
-        default = []
-        if needs_name_removed:
-            # Handle default (initial) values properly.
-            for i, t in enumerate(parts):
-                if t.name == '=':
-                    default = parts[i+1:]
-                    name = parts[i-1].name
-                    if name == ']' and parts[i-2].name == '[':
-                        name = parts[i-3].name
-                        i -= 1
-                    parts = parts[:i-1]
-                    break
-            else:
-                if parts[-1].token_type == tokenize.NAME:
-                    name = parts.pop().name
-                else:
-                    # TODO(nnorwitz): this is a hack that happens for code like
-                    # Register(Foo<T>); where it thinks this is a function call
-                    # but it's actually a declaration.
-                    name = '???'
-        modifiers = []
-        type_name = []
-        other_tokens = []
-        templated_types = []
-        i = 0
-        end = len(parts)
-        while i < end:
-            p = parts[i]
-            if keywords.IsKeyword(p.name):
-                modifiers.append(p.name)
-            elif p.name == '<':
-                templated_tokens, new_end = self._GetTemplateEnd(parts, i+1)
-                templated_types = self.ToType(templated_tokens)
-                i = new_end - 1
-                # Don't add a spurious :: to data members being initialized.
-                next_index = i + 1
-                if next_index < end and parts[next_index].name == '::':
-                    i += 1
-            elif p.name in ('[', ']', '='):
-                # These are handled elsewhere.
-                other_tokens.append(p)
-            elif p.name not in ('*', '&', '>'):
-                # Ensure that names have a space between them.
-                if (type_name and type_name[-1].token_type == tokenize.NAME and
-                    p.token_type == tokenize.NAME):
-                    type_name.append(tokenize.Token(tokenize.SYNTAX, ' ', 0, 0))
-                type_name.append(p)
-            else:
-                other_tokens.append(p)
-            i += 1
-        type_name = ''.join([t.name for t in type_name])
-        return name, type_name, templated_types, modifiers, default, other_tokens
+    result = []
+    name = type_name = ''
+    type_modifiers = []
+    pointer = reference = array = False
+    first_token = None
+    default = []
 
-    def ToParameters(self, tokens):
-        if not tokens:
-            return []
+    def AddParameter(end):
+      if default:
+        del default[0]  # Remove flag.
+      parts = self.DeclarationToParts(type_modifiers, True)
+      (name, type_name, templated_types, modifiers,
+       unused_default, unused_other_tokens) = parts
+      parameter_type = Type(first_token.start, first_token.end,
+                            type_name, templated_types, modifiers,
+                            reference, pointer, array)
+      p = Parameter(first_token.start, end, name,
+                    parameter_type, default)
+      result.append(p)
 
-        result = []
+    template_count = 0
+    brace_count = 0
+    for s in tokens:
+      if not first_token:
+        first_token = s
+
+      # Check for braces before templates, as we can have unmatched '<>'
+      # inside default arguments.
+      if s.name == '{':
+        brace_count += 1
+      elif s.name == '}':
+        brace_count -= 1
+      if brace_count > 0:
+        type_modifiers.append(s)
+        continue
+
+      if s.name == '<':
+        template_count += 1
+      elif s.name == '>':
+        template_count -= 1
+      if template_count > 0:
+        type_modifiers.append(s)
+        continue
+
+      if s.name == ',':
+        AddParameter(s.start)
         name = type_name = ''
         type_modifiers = []
         pointer = reference = array = False
         first_token = None
         default = []
+      elif s.name == '*':
+        pointer = True
+      elif s.name == '&':
+        reference = True
+      elif s.name == '[':
+        array = True
+      elif s.name == ']':
+        pass  # Just don't add to type_modifiers.
+      elif s.name == '=':
+        # Got a default value.  Add any value (None) as a flag.
+        default.append(None)
+      elif default:
+        default.append(s)
+      else:
+        type_modifiers.append(s)
+    AddParameter(tokens[-1].end)
+    return result
 
-        def AddParameter(end):
-            if default:
-                del default[0]  # Remove flag.
-            parts = self.DeclarationToParts(type_modifiers, True)
-            (name, type_name, templated_types, modifiers,
-             unused_default, unused_other_tokens) = parts
-            parameter_type = Type(first_token.start, first_token.end,
-                                  type_name, templated_types, modifiers,
-                                  reference, pointer, array)
-            p = Parameter(first_token.start, end, name,
-                          parameter_type, default)
-            result.append(p)
+  def CreateReturnType(self, return_type_seq):
+    if not return_type_seq:
+      return None
+    start = return_type_seq[0].start
+    end = return_type_seq[-1].end
+    _, name, templated_types, modifiers, default, other_tokens = \
+        self.DeclarationToParts(return_type_seq, False)
+    names = [n.name for n in other_tokens]
+    reference = '&' in names
+    pointer = '*' in names
+    array = '[' in names
+    return Type(start, end, name, templated_types, modifiers,
+                reference, pointer, array)
 
-        template_count = 0
-        for s in tokens:
-            if not first_token:
-                first_token = s
-            if s.name == '<':
-                template_count += 1
-            elif s.name == '>':
-                template_count -= 1
-            if template_count > 0:
-                type_modifiers.append(s)
-                continue
-
-            if s.name == ',':
-                AddParameter(s.start)
-                name = type_name = ''
-                type_modifiers = []
-                pointer = reference = array = False
-                first_token = None
-                default = []
-            elif s.name == '*':
-                pointer = True
-            elif s.name == '&':
-                reference = True
-            elif s.name == '[':
-                array = True
-            elif s.name == ']':
-                pass  # Just don't add to type_modifiers.
-            elif s.name == '=':
-                # Got a default value.  Add any value (None) as a flag.
-                default.append(None)
-            elif default:
-                default.append(s)
-            else:
-                type_modifiers.append(s)
-        AddParameter(tokens[-1].end)
-        return result
-
-    def CreateReturnType(self, return_type_seq):
-        if not return_type_seq:
-            return None
-        start = return_type_seq[0].start
-        end = return_type_seq[-1].end
-        _, name, templated_types, modifiers, default, other_tokens = \
-           self.DeclarationToParts(return_type_seq, False)
-        names = [n.name for n in other_tokens]
-        reference = '&' in names
-        pointer = '*' in names
-        array = '[' in names
-        return Type(start, end, name, templated_types, modifiers,
-                    reference, pointer, array)
-
-    def GetTemplateIndices(self, names):
-        # names is a list of strings.
-        start = names.index('<')
-        end = len(names) - 1
-        while end > 0:
-            if names[end] == '>':
-                break
-            end -= 1
-        return start, end+1
+  def GetTemplateIndices(self, names):
+    # names is a list of strings.
+    start = names.index('<')
+    end = len(names) - 1
+    while end > 0:
+      if names[end] == '>':
+        break
+      end -= 1
+    return start, end+1
 
 class AstBuilder(object):
-    def __init__(self, token_stream, filename, in_class='', visibility=None,
-                 namespace_stack=[]):
-        self.tokens = token_stream
-        self.filename = filename
-        # TODO(nnorwitz): use a better data structure (deque) for the queue.
-        # Switching directions of the "queue" improved perf by about 25%.
-        # Using a deque should be even better since we access from both sides.
-        self.token_queue = []
-        self.namespace_stack = namespace_stack[:]
-        self.in_class = in_class
-        if in_class is None:
-            self.in_class_name_only = None
+  def __init__(self, token_stream, filename, in_class='', visibility=None,
+               namespace_stack=[]):
+    self.tokens = token_stream
+    self.filename = filename
+    # TODO(nnorwitz): use a better data structure (deque) for the queue.
+    # Switching directions of the "queue" improved perf by about 25%.
+    # Using a deque should be even better since we access from both sides.
+    self.token_queue = []
+    self.namespace_stack = namespace_stack[:]
+    self.in_class = in_class
+    if in_class is None:
+      self.in_class_name_only = None
+    else:
+      self.in_class_name_only = in_class.split('::')[-1]
+    self.visibility = visibility
+    self.in_function = False
+    self.current_token = None
+    # Keep the state whether we are currently handling a typedef or not.
+    self._handling_typedef = False
+
+    self.converter = TypeConverter(self.namespace_stack)
+
+  def HandleError(self, msg, token):
+    printable_queue = list(reversed(self.token_queue[-20:]))
+    sys.stderr.write('Got %s in %s @ %s %s\n' %
+                     (msg, self.filename, token, printable_queue))
+
+  def Generate(self):
+    while 1:
+      token = self._GetNextToken()
+      if not token:
+        break
+
+      # Get the next token.
+      self.current_token = token
+
+      # Dispatch on the next token type.
+      if token.token_type == _INTERNAL_TOKEN:
+        if token.name == _NAMESPACE_POP:
+          self.namespace_stack.pop()
+        continue
+
+      try:
+        result = self._GenerateOne(token)
+        if result is not None:
+          yield result
+      except:
+        self.HandleError('exception', token)
+        raise
+
+  def _CreateVariable(self, pos_token, name, type_name, type_modifiers,
+                      ref_pointer_name_seq, templated_types, value=None):
+    reference = '&' in ref_pointer_name_seq
+    pointer = '*' in ref_pointer_name_seq
+    array = '[' in ref_pointer_name_seq
+    var_type = Type(pos_token.start, pos_token.end, type_name,
+                    templated_types, type_modifiers,
+                    reference, pointer, array)
+    return VariableDeclaration(pos_token.start, pos_token.end,
+                               name, var_type, value, self.namespace_stack)
+
+  def _GenerateOne(self, token):
+    if token.token_type == tokenize.NAME:
+      if (keywords.IsKeyword(token.name) and
+          not keywords.IsBuiltinType(token.name)):
+        if token.name == 'enum':
+          # Pop the next token and only put it back if it's not
+          # 'class'.  This allows us to support the two-token
+          # 'enum class' keyword as if it were simply 'enum'.
+          next = self._GetNextToken()
+          if next.name != 'class':
+            self._AddBackToken(next)
+
+        method = getattr(self, 'handle_' + token.name)
+        return method()
+      elif token.name == self.in_class_name_only:
+        # The token name is the same as the class, must be a ctor if
+        # there is a paren.  Otherwise, it's the return type.
+        # Peek ahead to get the next token to figure out which.
+        next = self._GetNextToken()
+        self._AddBackToken(next)
+        if next.token_type == tokenize.SYNTAX and next.name == '(':
+          return self._GetMethod([token], FUNCTION_CTOR, None, True)
+        # Fall through--handle like any other method.
+
+      # Handle data or function declaration/definition.
+      syntax = tokenize.SYNTAX
+      temp_tokens, last_token = \
+          self._GetVarTokensUpToIgnoringTemplates(syntax,
+                                                  '(', ';', '{', '[')
+      temp_tokens.insert(0, token)
+      if last_token.name == '(':
+        # If there is an assignment before the paren,
+        # this is an expression, not a method.
+        expr = bool([e for e in temp_tokens if e.name == '='])
+        if expr:
+          new_temp = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+          temp_tokens.append(last_token)
+          temp_tokens.extend(new_temp)
+          last_token = tokenize.Token(tokenize.SYNTAX, ';', 0, 0)
+
+      if last_token.name == '[':
+        # Handle array, this isn't a method, unless it's an operator.
+        # TODO(nnorwitz): keep the size somewhere.
+        # unused_size = self._GetTokensUpTo(tokenize.SYNTAX, ']')
+        temp_tokens.append(last_token)
+        if temp_tokens[-2].name == 'operator':
+          temp_tokens.append(self._GetNextToken())
         else:
-            self.in_class_name_only = in_class.split('::')[-1]
-        self.visibility = visibility
-        self.in_function = False
-        self.current_token = None
-        # Keep the state whether we are currently handling a typedef or not.
-        self._handling_typedef = False
+          temp_tokens2, last_token = \
+              self._GetVarTokensUpTo(tokenize.SYNTAX, ';')
+          temp_tokens.extend(temp_tokens2)
 
-        self.converter = TypeConverter(self.namespace_stack)
+      if last_token.name == ';':
+        # Handle data, this isn't a method.
+        parts = self.converter.DeclarationToParts(temp_tokens, True)
+        (name, type_name, templated_types, modifiers, default,
+         unused_other_tokens) = parts
 
-    def HandleError(self, msg, token):
-        printable_queue = list(reversed(self.token_queue[-20:]))
-        sys.stderr.write('Got %s in %s @ %s %s\n' %
-                         (msg, self.filename, token, printable_queue))
-
-    def Generate(self):
-        while 1:
-            token = self._GetNextToken()
-            if not token:
-                break
-
-            # Get the next token.
-            self.current_token = token
-
-            # Dispatch on the next token type.
-            if token.token_type == _INTERNAL_TOKEN:
-                if token.name == _NAMESPACE_POP:
-                    self.namespace_stack.pop()
-                continue
-
-            try:
-                result = self._GenerateOne(token)
-                if result is not None:
-                    yield result
-            except:
-                self.HandleError('exception', token)
-                raise
-
-    def _CreateVariable(self, pos_token, name, type_name, type_modifiers,
-                        ref_pointer_name_seq, templated_types, value=None):
-        reference = '&' in ref_pointer_name_seq
-        pointer = '*' in ref_pointer_name_seq
-        array = '[' in ref_pointer_name_seq
-        var_type = Type(pos_token.start, pos_token.end, type_name,
-                        templated_types, type_modifiers,
-                        reference, pointer, array)
-        return VariableDeclaration(pos_token.start, pos_token.end,
-                                   name, var_type, value, self.namespace_stack)
-
-    def _GenerateOne(self, token):
-        if token.token_type == tokenize.NAME:
-            if (keywords.IsKeyword(token.name) and
-                not keywords.IsBuiltinType(token.name)):
-                method = getattr(self, 'handle_' + token.name)
-                return method()
-            elif token.name == self.in_class_name_only:
-                # The token name is the same as the class, must be a ctor if
-                # there is a paren.  Otherwise, it's the return type.
-                # Peek ahead to get the next token to figure out which.
-                next = self._GetNextToken()
-                self._AddBackToken(next)
-                if next.token_type == tokenize.SYNTAX and next.name == '(':
-                    return self._GetMethod([token], FUNCTION_CTOR, None, True)
-                # Fall through--handle like any other method.
-
-            # Handle data or function declaration/definition.
-            syntax = tokenize.SYNTAX
-            temp_tokens, last_token = \
-                self._GetVarTokensUpTo(syntax, '(', ';', '{', '[')
-            temp_tokens.insert(0, token)
-            if last_token.name == '(':
-                # If there is an assignment before the paren,
-                # this is an expression, not a method.
-                expr = bool([e for e in temp_tokens if e.name == '='])
-                if expr:
-                    new_temp = self._GetTokensUpTo(tokenize.SYNTAX, ';')
-                    temp_tokens.append(last_token)
-                    temp_tokens.extend(new_temp)
-                    last_token = tokenize.Token(tokenize.SYNTAX, ';', 0, 0)
-
-            if last_token.name == '[':
-                # Handle array, this isn't a method, unless it's an operator.
-                # TODO(nnorwitz): keep the size somewhere.
-                # unused_size = self._GetTokensUpTo(tokenize.SYNTAX, ']')
-                temp_tokens.append(last_token)
-                if temp_tokens[-2].name == 'operator':
-                    temp_tokens.append(self._GetNextToken())
-                else:
-                    temp_tokens2, last_token = \
-                        self._GetVarTokensUpTo(tokenize.SYNTAX, ';')
-                    temp_tokens.extend(temp_tokens2)
-
-            if last_token.name == ';':
-                # Handle data, this isn't a method.
-                parts = self.converter.DeclarationToParts(temp_tokens, True)
-                (name, type_name, templated_types, modifiers, default,
-                 unused_other_tokens) = parts
-
-                t0 = temp_tokens[0]
-                names = [t.name for t in temp_tokens]
-                if templated_types:
-                    start, end = self.converter.GetTemplateIndices(names)
-                    names = names[:start] + names[end:]
-                default = ''.join([t.name for t in default])
-                return self._CreateVariable(t0, name, type_name, modifiers,
-                                            names, templated_types, default)
-            if last_token.name == '{':
-                self._AddBackTokens(temp_tokens[1:])
-                self._AddBackToken(last_token)
-                method_name = temp_tokens[0].name
-                method = getattr(self, 'handle_' + method_name, None)
-                if not method:
-                    # Must be declaring a variable.
-                    # TODO(nnorwitz): handle the declaration.
-                    return None
-                return method()
-            return self._GetMethod(temp_tokens, 0, None, False)
-        elif token.token_type == tokenize.SYNTAX:
-            if token.name == '~' and self.in_class:
-                # Must be a dtor (probably not in method body).
-                token = self._GetNextToken()
-                # self.in_class can contain A::Name, but the dtor will only
-                # be Name.  Make sure to compare against the right value.
-                if (token.token_type == tokenize.NAME and
-                    token.name == self.in_class_name_only):
-                    return self._GetMethod([token], FUNCTION_DTOR, None, True)
-            # TODO(nnorwitz): handle a lot more syntax.
-        elif token.token_type == tokenize.PREPROCESSOR:
-            # TODO(nnorwitz): handle more preprocessor directives.
-            # token starts with a #, so remove it and strip whitespace.
-            name = token.name[1:].lstrip()
-            if name.startswith('include'):
-                # Remove "include".
-                name = name[7:].strip()
-                assert name
-                # Handle #include \<newline> "header-on-second-line.h".
-                if name.startswith('\\'):
-                    name = name[1:].strip()
-                assert name[0] in '<"', token
-                assert name[-1] in '>"', token
-                system = name[0] == '<'
-                filename = name[1:-1]
-                return Include(token.start, token.end, filename, system)
-            if name.startswith('define'):
-                # Remove "define".
-                name = name[6:].strip()
-                assert name
-                value = ''
-                for i, c in enumerate(name):
-                    if c.isspace():
-                        value = name[i:].lstrip()
-                        name = name[:i]
-                        break
-                return Define(token.start, token.end, name, value)
-            if name.startswith('if') and name[2:3].isspace():
-                condition = name[3:].strip()
-                if condition.startswith('0') or condition.startswith('(0)'):
-                    self._SkipIf0Blocks()
-        return None
-
-    def _GetTokensUpTo(self, expected_token_type, expected_token):
-        return self._GetVarTokensUpTo(expected_token_type, expected_token)[0]
-
-    def _GetVarTokensUpTo(self, expected_token_type, *expected_tokens):
-        last_token = self._GetNextToken()
-        tokens = []
-        while (last_token.token_type != expected_token_type or
-               last_token.name not in expected_tokens):
-            tokens.append(last_token)
-            last_token = self._GetNextToken()
-        return tokens, last_token
-
-    # TODO(nnorwitz): remove _IgnoreUpTo() it shouldn't be necessary.
-    def _IgnoreUpTo(self, token_type, token):
-        unused_tokens = self._GetTokensUpTo(token_type, token)
-
-    def _SkipIf0Blocks(self):
-        count = 1
-        while 1:
-            token = self._GetNextToken()
-            if token.token_type != tokenize.PREPROCESSOR:
-                continue
-
-            name = token.name[1:].lstrip()
-            if name.startswith('endif'):
-                count -= 1
-                if count == 0:
-                    break
-            elif name.startswith('if'):
-                count += 1
-
-    def _GetMatchingChar(self, open_paren, close_paren, GetNextToken=None):
-        if GetNextToken is None:
-            GetNextToken = self._GetNextToken
-        # Assumes the current token is open_paren and we will consume
-        # and return up to the close_paren.
-        count = 1
-        token = GetNextToken()
-        while 1:
-            if token.token_type == tokenize.SYNTAX:
-                if token.name == open_paren:
-                    count += 1
-                elif token.name == close_paren:
-                    count -= 1
-                    if count == 0:
-                        break
-            yield token
-            token = GetNextToken()
-        yield token
-
-    def _GetParameters(self):
-        return self._GetMatchingChar('(', ')')
-
-    def GetScope(self):
-        return self._GetMatchingChar('{', '}')
-
-    def _GetNextToken(self):
-        if self.token_queue:
-            return self.token_queue.pop()
-        return next(self.tokens)
-
-    def _AddBackToken(self, token):
-        if token.whence == tokenize.WHENCE_STREAM:
-            token.whence = tokenize.WHENCE_QUEUE
-            self.token_queue.insert(0, token)
-        else:
-            assert token.whence == tokenize.WHENCE_QUEUE, token
-            self.token_queue.append(token)
-
-    def _AddBackTokens(self, tokens):
-        if tokens:
-            if tokens[-1].whence == tokenize.WHENCE_STREAM:
-                for token in tokens:
-                    token.whence = tokenize.WHENCE_QUEUE
-                self.token_queue[:0] = reversed(tokens)
-            else:
-                assert tokens[-1].whence == tokenize.WHENCE_QUEUE, tokens
-                self.token_queue.extend(reversed(tokens))
-
-    def GetName(self, seq=None):
-        """Returns ([tokens], next_token_info)."""
-        GetNextToken = self._GetNextToken
-        if seq is not None:
-            it = iter(seq)
-            GetNextToken = lambda: next(it)
-        next_token = GetNextToken()
-        tokens = []
-        last_token_was_name = False
-        while (next_token.token_type == tokenize.NAME or
-               (next_token.token_type == tokenize.SYNTAX and
-                next_token.name in ('::', '<'))):
-            # Two NAMEs in a row means the identifier should terminate.
-            # It's probably some sort of variable declaration.
-            if last_token_was_name and next_token.token_type == tokenize.NAME:
-                break
-            last_token_was_name = next_token.token_type == tokenize.NAME
-            tokens.append(next_token)
-            # Handle templated names.
-            if next_token.name == '<':
-                tokens.extend(self._GetMatchingChar('<', '>', GetNextToken))
-                last_token_was_name = True
-            next_token = GetNextToken()
-        return tokens, next_token
-
-    def GetMethod(self, modifiers, templated_types):
-        return_type_and_name = self._GetTokensUpTo(tokenize.SYNTAX, '(')
-        assert len(return_type_and_name) >= 1
-        return self._GetMethod(return_type_and_name, modifiers, templated_types,
-                               False)
-
-    def _GetMethod(self, return_type_and_name, modifiers, templated_types,
-                   get_paren):
-        template_portion = None
-        if get_paren:
-            token = self._GetNextToken()
-            assert token.token_type == tokenize.SYNTAX, token
-            if token.name == '<':
-                # Handle templatized dtors.
-                template_portion = [token]
-                template_portion.extend(self._GetMatchingChar('<', '>'))
-                token = self._GetNextToken()
-            assert token.token_type == tokenize.SYNTAX, token
-            assert token.name == '(', token
-
-        name = return_type_and_name.pop()
-        # Handle templatized ctors.
-        if name.name == '>':
-            index = 1
-            while return_type_and_name[index].name != '<':
-                index += 1
-            template_portion = return_type_and_name[index:] + [name]
-            del return_type_and_name[index:]
-            name = return_type_and_name.pop()
-        elif name.name == ']':
-            rt = return_type_and_name
-            assert rt[-1].name == '[', return_type_and_name
-            assert rt[-2].name == 'operator', return_type_and_name
-            name_seq = return_type_and_name[-2:]
-            del return_type_and_name[-2:]
-            name = tokenize.Token(tokenize.NAME, 'operator[]',
-                                  name_seq[0].start, name.end)
-            # Get the open paren so _GetParameters() below works.
-            unused_open_paren = self._GetNextToken()
-
-        # TODO(nnorwitz): store template_portion.
-        return_type = return_type_and_name
-        indices = name
-        if return_type:
-            indices = return_type[0]
-
-        # Force ctor for templatized ctors.
-        if name.name == self.in_class and not modifiers:
-            modifiers |= FUNCTION_CTOR
-        parameters = list(self._GetParameters())
-        del parameters[-1]              # Remove trailing ')'.
-
-        # Handling operator() is especially weird.
-        if name.name == 'operator' and not parameters:
-            token = self._GetNextToken()
-            assert token.name == '(', token
-            parameters = list(self._GetParameters())
-            del parameters[-1]          # Remove trailing ')'.
-
+        t0 = temp_tokens[0]
+        names = [t.name for t in temp_tokens]
+        if templated_types:
+          start, end = self.converter.GetTemplateIndices(names)
+          names = names[:start] + names[end:]
+        default = ''.join([t.name for t in default])
+        return self._CreateVariable(t0, name, type_name, modifiers,
+                                    names, templated_types, default)
+      if last_token.name == '{':
+        self._AddBackTokens(temp_tokens[1:])
+        self._AddBackToken(last_token)
+        method_name = temp_tokens[0].name
+        method = getattr(self, 'handle_' + method_name, None)
+        if not method:
+          # Must be declaring a variable.
+          # TODO(nnorwitz): handle the declaration.
+          return None
+        return method()
+      return self._GetMethod(temp_tokens, 0, None, False)
+    elif token.token_type == tokenize.SYNTAX:
+      if token.name == '~' and self.in_class:
+        # Must be a dtor (probably not in method body).
         token = self._GetNextToken()
-        while token.token_type == tokenize.NAME:
-            modifier_token = token
-            token = self._GetNextToken()
-            if modifier_token.name == 'const':
-                modifiers |= FUNCTION_CONST
-            elif modifier_token.name == '__attribute__':
-                # TODO(nnorwitz): handle more __attribute__ details.
-                modifiers |= FUNCTION_ATTRIBUTE
-                assert token.name == '(', token
-                # Consume everything between the (parens).
-                unused_tokens = list(self._GetMatchingChar('(', ')'))
-                token = self._GetNextToken()
-            elif modifier_token.name == 'throw':
-                modifiers |= FUNCTION_THROW
-                assert token.name == '(', token
-                # Consume everything between the (parens).
-                unused_tokens = list(self._GetMatchingChar('(', ')'))
-                token = self._GetNextToken()
-            elif modifier_token.name == 'override':
-                modifiers |= FUNCTION_OVERRIDE
-            elif modifier_token.name == modifier_token.name.upper():
-                # HACK(nnorwitz):  assume that all upper-case names
-                # are some macro we aren't expanding.
-                modifiers |= FUNCTION_UNKNOWN_ANNOTATION
-            else:
-                self.HandleError('unexpected token', modifier_token)
-
-        assert token.token_type == tokenize.SYNTAX, token
-        # Handle ctor initializers.
-        if token.name == ':':
-            # TODO(nnorwitz): anything else to handle for initializer list?
-            while token.name != ';' and token.name != '{':
-                token = self._GetNextToken()
-
-        # Handle pointer to functions that are really data but look
-        # like method declarations.
-        if token.name == '(':
-            if parameters[0].name == '*':
-                # name contains the return type.
-                name = parameters.pop()
-                # parameters contains the name of the data.
-                modifiers = [p.name for p in parameters]
-                # Already at the ( to open the parameter list.
-                function_parameters = list(self._GetMatchingChar('(', ')'))
-                del function_parameters[-1]  # Remove trailing ')'.
-                # TODO(nnorwitz): store the function_parameters.
-                token = self._GetNextToken()
-                assert token.token_type == tokenize.SYNTAX, token
-                assert token.name == ';', token
-                return self._CreateVariable(indices, name.name, indices.name,
-                                            modifiers, '', None)
-            # At this point, we got something like:
-            #  return_type (type::*name_)(params);
-            # This is a data member called name_ that is a function pointer.
-            # With this code: void (sq_type::*field_)(string&);
-            # We get: name=void return_type=[] parameters=sq_type ... field_
-            # TODO(nnorwitz): is return_type always empty?
-            # TODO(nnorwitz): this isn't even close to being correct.
-            # Just put in something so we don't crash and can move on.
-            real_name = parameters[-1]
-            modifiers = [p.name for p in self._GetParameters()]
-            del modifiers[-1]           # Remove trailing ')'.
-            return self._CreateVariable(indices, real_name.name, indices.name,
-                                        modifiers, '', None)
-
-        if token.name == '{':
-            body = list(self.GetScope())
-            del body[-1]                # Remove trailing '}'.
-        else:
-            body = None
-            if token.name == '=':
-                token = self._GetNextToken()
-
-                if token.name == 'default' or token.name == 'delete':
-                    # Ignore explicitly defaulted and deleted special members
-                    # in C++11.
-                    token = self._GetNextToken()
-                else:
-                    # Handle pure-virtual declarations.
-                    assert token.token_type == tokenize.CONSTANT, token
-                    assert token.name == '0', token
-                    modifiers |= FUNCTION_PURE_VIRTUAL
-                    token = self._GetNextToken()
-
-            if token.name == '[':
-                # TODO(nnorwitz): store tokens and improve parsing.
-                # template <typename T, size_t N> char (&ASH(T (&seq)[N]))[N];
-                tokens = list(self._GetMatchingChar('[', ']'))
-                token = self._GetNextToken()
-
-            assert token.name == ';', (token, return_type_and_name, parameters)
-
-        # Looks like we got a method, not a function.
-        if len(return_type) > 2 and return_type[-1].name == '::':
-            return_type, in_class = \
-                         self._GetReturnTypeAndClassName(return_type)
-            return Method(indices.start, indices.end, name.name, in_class,
-                          return_type, parameters, modifiers, templated_types,
-                          body, self.namespace_stack)
-        return Function(indices.start, indices.end, name.name, return_type,
-                        parameters, modifiers, templated_types, body,
-                        self.namespace_stack)
-
-    def _GetReturnTypeAndClassName(self, token_seq):
-        # Splitting the return type from the class name in a method
-        # can be tricky.  For example, Return::Type::Is::Hard::To::Find().
-        # Where is the return type and where is the class name?
-        # The heuristic used is to pull the last name as the class name.
-        # This includes all the templated type info.
-        # TODO(nnorwitz): if there is only One name like in the
-        # example above, punt and assume the last bit is the class name.
-
-        # Ignore a :: prefix, if exists so we can find the first real name.
-        i = 0
-        if token_seq[0].name == '::':
-            i = 1
-        # Ignore a :: suffix, if exists.
-        end = len(token_seq) - 1
-        if token_seq[end-1].name == '::':
-            end -= 1
-
-        # Make a copy of the sequence so we can append a sentinel
-        # value. This is required for GetName will has to have some
-        # terminating condition beyond the last name.
-        seq_copy = token_seq[i:end]
-        seq_copy.append(tokenize.Token(tokenize.SYNTAX, '', 0, 0))
-        names = []
-        while i < end:
-            # Iterate through the sequence parsing out each name.
-            new_name, next = self.GetName(seq_copy[i:])
-            assert new_name, 'Got empty new_name, next=%s' % next
-            # We got a pointer or ref.  Add it to the name.
-            if next and next.token_type == tokenize.SYNTAX:
-                new_name.append(next)
-            names.append(new_name)
-            i += len(new_name)
-
-        # Now that we have the names, it's time to undo what we did.
-
-        # Remove the sentinel value.
-        names[-1].pop()
-        # Flatten the token sequence for the return type.
-        return_type = [e for seq in names[:-1] for e in seq]
-        # The class name is the last name.
-        class_name = names[-1]
-        return return_type, class_name
-
-    def handle_bool(self):
-        pass
-
-    def handle_char(self):
-        pass
-
-    def handle_int(self):
-        pass
-
-    def handle_long(self):
-        pass
-
-    def handle_short(self):
-        pass
-
-    def handle_double(self):
-        pass
-
-    def handle_float(self):
-        pass
-
-    def handle_void(self):
-        pass
-
-    def handle_wchar_t(self):
-        pass
-
-    def handle_unsigned(self):
-        pass
-
-    def handle_signed(self):
-        pass
-
-    def _GetNestedType(self, ctor):
-        name = None
-        name_tokens, token = self.GetName()
-        if name_tokens:
-            name = ''.join([t.name for t in name_tokens])
-
-        # Handle forward declarations.
-        if token.token_type == tokenize.SYNTAX and token.name == ';':
-            return ctor(token.start, token.end, name, None,
-                        self.namespace_stack)
-
-        if token.token_type == tokenize.NAME and self._handling_typedef:
-            self._AddBackToken(token)
-            return ctor(token.start, token.end, name, None,
-                        self.namespace_stack)
-
-        # Must be the type declaration.
-        fields = list(self._GetMatchingChar('{', '}'))
-        del fields[-1]                  # Remove trailing '}'.
-        if token.token_type == tokenize.SYNTAX and token.name == '{':
-            next = self._GetNextToken()
-            new_type = ctor(token.start, token.end, name, fields,
-                            self.namespace_stack)
-            # A name means this is an anonymous type and the name
-            # is the variable declaration.
-            if next.token_type != tokenize.NAME:
-                return new_type
-            name = new_type
-            token = next
-
-        # Must be variable declaration using the type prefixed with keyword.
-        assert token.token_type == tokenize.NAME, token
-        return self._CreateVariable(token, token.name, name, [], '', None)
-
-    def handle_struct(self):
-        # Special case the handling typedef/aliasing of structs here.
-        # It would be a pain to handle in the class code.
-        name_tokens, var_token = self.GetName()
-        if name_tokens:
-            next_token = self._GetNextToken()
-            is_syntax = (var_token.token_type == tokenize.SYNTAX and
-                         var_token.name[0] in '*&')
-            is_variable = (var_token.token_type == tokenize.NAME and
-                           next_token.name == ';')
-            variable = var_token
-            if is_syntax and not is_variable:
-                variable = next_token
-                temp = self._GetNextToken()
-                if temp.token_type == tokenize.SYNTAX and temp.name == '(':
-                    # Handle methods declared to return a struct.
-                    t0 = name_tokens[0]
-                    struct = tokenize.Token(tokenize.NAME, 'struct',
-                                            t0.start-7, t0.start-2)
-                    type_and_name = [struct]
-                    type_and_name.extend(name_tokens)
-                    type_and_name.extend((var_token, next_token))
-                    return self._GetMethod(type_and_name, 0, None, False)
-                assert temp.name == ';', (temp, name_tokens, var_token)
-            if is_syntax or (is_variable and not self._handling_typedef):
-                modifiers = ['struct']
-                type_name = ''.join([t.name for t in name_tokens])
-                position = name_tokens[0]
-                return self._CreateVariable(position, variable.name, type_name,
-                                            modifiers, var_token.name, None)
-            name_tokens.extend((var_token, next_token))
-            self._AddBackTokens(name_tokens)
-        else:
-            self._AddBackToken(var_token)
-        return self._GetClass(Struct, VISIBILITY_PUBLIC, None)
-
-    def handle_union(self):
-        return self._GetNestedType(Union)
-
-    def handle_enum(self):
-        token = self._GetNextToken()
-        if not (token.token_type == tokenize.NAME and token.name == 'class'):
-            self._AddBackToken(token)
-        return self._GetNestedType(Enum)
-
-    def handle_auto(self):
-        # TODO(nnorwitz): warn about using auto?  Probably not since it
-        # will be reclaimed and useful for C++0x.
-        pass
-
-    def handle_register(self):
-        pass
-
-    def handle_const(self):
-        pass
-
-    def handle_inline(self):
-        pass
-
-    def handle_extern(self):
-        pass
-
-    def handle_static(self):
-        pass
-
-    def handle_virtual(self):
-        # What follows must be a method.
-        token = token2 = self._GetNextToken()
-        if token.name == 'inline':
-            # HACK(nnorwitz): handle inline dtors by ignoring 'inline'.
-            token2 = self._GetNextToken()
-        if token2.token_type == tokenize.SYNTAX and token2.name == '~':
-            return self.GetMethod(FUNCTION_VIRTUAL + FUNCTION_DTOR, None)
-        assert token.token_type == tokenize.NAME or token.name == '::', token
-        return_type_and_name = self._GetTokensUpTo(tokenize.SYNTAX, '(')  # )
-        return_type_and_name.insert(0, token)
-        if token2 is not token:
-            return_type_and_name.insert(1, token2)
-        return self._GetMethod(return_type_and_name, FUNCTION_VIRTUAL,
-                               None, False)
-
-    def handle_volatile(self):
-        pass
-
-    def handle_mutable(self):
-        pass
-
-    def handle_public(self):
-        assert self.in_class
-        self.visibility = VISIBILITY_PUBLIC
-
-    def handle_protected(self):
-        assert self.in_class
-        self.visibility = VISIBILITY_PROTECTED
-
-    def handle_private(self):
-        assert self.in_class
-        self.visibility = VISIBILITY_PRIVATE
-
-    def handle_friend(self):
-        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
-        assert tokens
-        t0 = tokens[0]
-        return Friend(t0.start, t0.end, tokens, self.namespace_stack)
-
-    def handle_static_cast(self):
-        pass
-
-    def handle_const_cast(self):
-        pass
-
-    def handle_dynamic_cast(self):
-        pass
-
-    def handle_reinterpret_cast(self):
-        pass
-
-    def handle_new(self):
-        pass
-
-    def handle_delete(self):
-        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
-        assert tokens
-        return Delete(tokens[0].start, tokens[0].end, tokens)
-
-    def handle_typedef(self):
-        token = self._GetNextToken()
+        # self.in_class can contain A::Name, but the dtor will only
+        # be Name.  Make sure to compare against the right value.
         if (token.token_type == tokenize.NAME and
+                token.name == self.in_class_name_only):
+          return self._GetMethod([token], FUNCTION_DTOR, None, True)
+      # TODO(nnorwitz): handle a lot more syntax.
+    elif token.token_type == tokenize.PREPROCESSOR:
+      # TODO(nnorwitz): handle more preprocessor directives.
+      # token starts with a #, so remove it and strip whitespace.
+      name = token.name[1:].lstrip()
+      if name.startswith('include'):
+        # Remove "include".
+        name = name[7:].strip()
+        assert name
+        # Handle #include \<newline> "header-on-second-line.h".
+        if name.startswith('\\'):
+          name = name[1:].strip()
+        assert name[0] in '<"', token
+        assert name[-1] in '>"', token
+        system = name[0] == '<'
+        filename = name[1:-1]
+        return Include(token.start, token.end, filename, system)
+      if name.startswith('define'):
+        # Remove "define".
+        name = name[6:].strip()
+        assert name
+        value = ''
+        for i, c in enumerate(name):
+          if c.isspace():
+            value = name[i:].lstrip()
+            name = name[:i]
+            break
+        return Define(token.start, token.end, name, value)
+      if name.startswith('if') and name[2:3].isspace():
+        condition = name[3:].strip()
+        if condition.startswith('0') or condition.startswith('(0)'):
+          self._SkipIf0Blocks()
+    return None
+
+  def _GetTokensUpTo(self, expected_token_type, expected_token):
+    return self._GetVarTokensUpTo(expected_token_type, expected_token)[0]
+
+  def _GetVarTokensUpTo(self, expected_token_type, *expected_tokens):
+    last_token = self._GetNextToken()
+    tokens = []
+    while (last_token.token_type != expected_token_type or
+           last_token.name not in expected_tokens):
+      tokens.append(last_token)
+      last_token = self._GetNextToken()
+    return tokens, last_token
+
+  # Same as _GetVarTokensUpTo, but skips over '<...>' which could contain an
+  # expected token.
+  def _GetVarTokensUpToIgnoringTemplates(self, expected_token_type,
+                                         *expected_tokens):
+    last_token = self._GetNextToken()
+    tokens = []
+    nesting = 0
+    while (nesting > 0 or
+           last_token.token_type != expected_token_type or
+           last_token.name not in expected_tokens):
+      tokens.append(last_token)
+      last_token = self._GetNextToken()
+      if last_token.name == '<':
+        nesting += 1
+      elif last_token.name == '>':
+        nesting -= 1
+    return tokens, last_token
+
+  # TODO(nnorwitz): remove _IgnoreUpTo() it shouldn't be necessary.
+  def _IgnoreUpTo(self, token_type, token):
+    unused_tokens = self._GetTokensUpTo(token_type, token)
+
+  def _SkipIf0Blocks(self):
+    count = 1
+    while 1:
+      token = self._GetNextToken()
+      if token.token_type != tokenize.PREPROCESSOR:
+        continue
+
+      name = token.name[1:].lstrip()
+      if name.startswith('endif'):
+        count -= 1
+        if count == 0:
+          break
+      elif name.startswith('if'):
+        count += 1
+
+  def _GetMatchingChar(self, open_paren, close_paren, GetNextToken=None):
+    if GetNextToken is None:
+      GetNextToken = self._GetNextToken
+    # Assumes the current token is open_paren and we will consume
+    # and return up to the close_paren.
+    count = 1
+    token = GetNextToken()
+    while 1:
+      if token.token_type == tokenize.SYNTAX:
+        if token.name == open_paren:
+          count += 1
+        elif token.name == close_paren:
+          count -= 1
+          if count == 0:
+            break
+      yield token
+      token = GetNextToken()
+    yield token
+
+  def _GetParameters(self):
+    return self._GetMatchingChar('(', ')')
+
+  def GetScope(self):
+    return self._GetMatchingChar('{', '}')
+
+  def _GetNextToken(self):
+    if self.token_queue:
+      return self.token_queue.pop()
+    try:
+      return next(self.tokens)
+    except StopIteration:
+      return
+
+  def _AddBackToken(self, token):
+    if token.whence == tokenize.WHENCE_STREAM:
+      token.whence = tokenize.WHENCE_QUEUE
+      self.token_queue.insert(0, token)
+    else:
+      assert token.whence == tokenize.WHENCE_QUEUE, token
+      self.token_queue.append(token)
+
+  def _AddBackTokens(self, tokens):
+    if tokens:
+      if tokens[-1].whence == tokenize.WHENCE_STREAM:
+        for token in tokens:
+          token.whence = tokenize.WHENCE_QUEUE
+        self.token_queue[:0] = reversed(tokens)
+      else:
+        assert tokens[-1].whence == tokenize.WHENCE_QUEUE, tokens
+        self.token_queue.extend(reversed(tokens))
+
+  def GetName(self, seq=None):
+    """Returns ([tokens], next_token_info)."""
+    GetNextToken = self._GetNextToken
+    if seq is not None:
+      it = iter(seq)
+      GetNextToken = lambda: next(it)
+    next_token = GetNextToken()
+    tokens = []
+    last_token_was_name = False
+    while (next_token.token_type == tokenize.NAME or
+           (next_token.token_type == tokenize.SYNTAX and
+            next_token.name in ('::', '<'))):
+      # Two NAMEs in a row means the identifier should terminate.
+      # It's probably some sort of variable declaration.
+      if last_token_was_name and next_token.token_type == tokenize.NAME:
+        break
+      last_token_was_name = next_token.token_type == tokenize.NAME
+      tokens.append(next_token)
+      # Handle templated names.
+      if next_token.name == '<':
+        tokens.extend(self._GetMatchingChar('<', '>', GetNextToken))
+        last_token_was_name = True
+      next_token = GetNextToken()
+    return tokens, next_token
+
+  def GetMethod(self, modifiers, templated_types):
+    return_type_and_name = self._GetTokensUpTo(tokenize.SYNTAX, '(')
+    assert len(return_type_and_name) >= 1
+    return self._GetMethod(return_type_and_name, modifiers, templated_types,
+                           False)
+
+  def _GetMethod(self, return_type_and_name, modifiers, templated_types,
+                 get_paren):
+    template_portion = None
+    if get_paren:
+      token = self._GetNextToken()
+      assert token.token_type == tokenize.SYNTAX, token
+      if token.name == '<':
+        # Handle templatized dtors.
+        template_portion = [token]
+        template_portion.extend(self._GetMatchingChar('<', '>'))
+        token = self._GetNextToken()
+      assert token.token_type == tokenize.SYNTAX, token
+      assert token.name == '(', token
+
+    name = return_type_and_name.pop()
+    # Handle templatized ctors.
+    if name.name == '>':
+      index = 1
+      while return_type_and_name[index].name != '<':
+        index += 1
+      template_portion = return_type_and_name[index:] + [name]
+      del return_type_and_name[index:]
+      name = return_type_and_name.pop()
+    elif name.name == ']':
+      rt = return_type_and_name
+      assert rt[-1].name == '[', return_type_and_name
+      assert rt[-2].name == 'operator', return_type_and_name
+      name_seq = return_type_and_name[-2:]
+      del return_type_and_name[-2:]
+      name = tokenize.Token(tokenize.NAME, 'operator[]',
+                            name_seq[0].start, name.end)
+      # Get the open paren so _GetParameters() below works.
+      unused_open_paren = self._GetNextToken()
+
+    # TODO(nnorwitz): store template_portion.
+    return_type = return_type_and_name
+    indices = name
+    if return_type:
+      indices = return_type[0]
+
+    # Force ctor for templatized ctors.
+    if name.name == self.in_class and not modifiers:
+      modifiers |= FUNCTION_CTOR
+    parameters = list(self._GetParameters())
+    del parameters[-1]              # Remove trailing ')'.
+
+    # Handling operator() is especially weird.
+    if name.name == 'operator' and not parameters:
+      token = self._GetNextToken()
+      assert token.name == '(', token
+      parameters = list(self._GetParameters())
+      del parameters[-1]          # Remove trailing ')'.
+
+    token = self._GetNextToken()
+    while token.token_type == tokenize.NAME:
+      modifier_token = token
+      token = self._GetNextToken()
+      if modifier_token.name == 'const':
+        modifiers |= FUNCTION_CONST
+      elif modifier_token.name == '__attribute__':
+        # TODO(nnorwitz): handle more __attribute__ details.
+        modifiers |= FUNCTION_ATTRIBUTE
+        assert token.name == '(', token
+        # Consume everything between the (parens).
+        unused_tokens = list(self._GetMatchingChar('(', ')'))
+        token = self._GetNextToken()
+      elif modifier_token.name == 'throw':
+        modifiers |= FUNCTION_THROW
+        assert token.name == '(', token
+        # Consume everything between the (parens).
+        unused_tokens = list(self._GetMatchingChar('(', ')'))
+        token = self._GetNextToken()
+      elif modifier_token.name == 'override':
+        modifiers |= FUNCTION_OVERRIDE
+      elif modifier_token.name == modifier_token.name.upper():
+        # HACK(nnorwitz):  assume that all upper-case names
+        # are some macro we aren't expanding.
+        modifiers |= FUNCTION_UNKNOWN_ANNOTATION
+      else:
+        self.HandleError('unexpected token', modifier_token)
+
+    assert token.token_type == tokenize.SYNTAX, token
+    # Handle ctor initializers.
+    if token.name == ':':
+      # TODO(nnorwitz): anything else to handle for initializer list?
+      while token.name != ';' and token.name != '{':
+        token = self._GetNextToken()
+
+    # Handle pointer to functions that are really data but look
+    # like method declarations.
+    if token.name == '(':
+      if parameters[0].name == '*':
+        # name contains the return type.
+        name = parameters.pop()
+        # parameters contains the name of the data.
+        modifiers = [p.name for p in parameters]
+        # Already at the ( to open the parameter list.
+        function_parameters = list(self._GetMatchingChar('(', ')'))
+        del function_parameters[-1]  # Remove trailing ')'.
+        # TODO(nnorwitz): store the function_parameters.
+        token = self._GetNextToken()
+        assert token.token_type == tokenize.SYNTAX, token
+        assert token.name == ';', token
+        return self._CreateVariable(indices, name.name, indices.name,
+                                    modifiers, '', None)
+      # At this point, we got something like:
+      #  return_type (type::*name_)(params);
+      # This is a data member called name_ that is a function pointer.
+      # With this code: void (sq_type::*field_)(string&);
+      # We get: name=void return_type=[] parameters=sq_type ... field_
+      # TODO(nnorwitz): is return_type always empty?
+      # TODO(nnorwitz): this isn't even close to being correct.
+      # Just put in something so we don't crash and can move on.
+      real_name = parameters[-1]
+      modifiers = [p.name for p in self._GetParameters()]
+      del modifiers[-1]           # Remove trailing ')'.
+      return self._CreateVariable(indices, real_name.name, indices.name,
+                                  modifiers, '', None)
+
+    if token.name == '{':
+      body = list(self.GetScope())
+      del body[-1]                # Remove trailing '}'.
+    else:
+      body = None
+      if token.name == '=':
+        token = self._GetNextToken()
+
+        if token.name == 'default' or token.name == 'delete':
+          # Ignore explicitly defaulted and deleted special members
+          # in C++11.
+          token = self._GetNextToken()
+        else:
+          # Handle pure-virtual declarations.
+          assert token.token_type == tokenize.CONSTANT, token
+          assert token.name == '0', token
+          modifiers |= FUNCTION_PURE_VIRTUAL
+          token = self._GetNextToken()
+
+      if token.name == '[':
+        # TODO(nnorwitz): store tokens and improve parsing.
+        # template <typename T, size_t N> char (&ASH(T (&seq)[N]))[N];
+        tokens = list(self._GetMatchingChar('[', ']'))
+        token = self._GetNextToken()
+
+      assert token.name == ';', (token, return_type_and_name, parameters)
+
+    # Looks like we got a method, not a function.
+    if len(return_type) > 2 and return_type[-1].name == '::':
+      return_type, in_class = \
+          self._GetReturnTypeAndClassName(return_type)
+      return Method(indices.start, indices.end, name.name, in_class,
+                    return_type, parameters, modifiers, templated_types,
+                    body, self.namespace_stack)
+    return Function(indices.start, indices.end, name.name, return_type,
+                    parameters, modifiers, templated_types, body,
+                    self.namespace_stack)
+
+  def _GetReturnTypeAndClassName(self, token_seq):
+    # Splitting the return type from the class name in a method
+    # can be tricky.  For example, Return::Type::Is::Hard::To::Find().
+    # Where is the return type and where is the class name?
+    # The heuristic used is to pull the last name as the class name.
+    # This includes all the templated type info.
+    # TODO(nnorwitz): if there is only One name like in the
+    # example above, punt and assume the last bit is the class name.
+
+    # Ignore a :: prefix, if exists so we can find the first real name.
+    i = 0
+    if token_seq[0].name == '::':
+      i = 1
+    # Ignore a :: suffix, if exists.
+    end = len(token_seq) - 1
+    if token_seq[end-1].name == '::':
+      end -= 1
+
+    # Make a copy of the sequence so we can append a sentinel
+    # value. This is required for GetName will has to have some
+    # terminating condition beyond the last name.
+    seq_copy = token_seq[i:end]
+    seq_copy.append(tokenize.Token(tokenize.SYNTAX, '', 0, 0))
+    names = []
+    while i < end:
+      # Iterate through the sequence parsing out each name.
+      new_name, next = self.GetName(seq_copy[i:])
+      assert new_name, 'Got empty new_name, next=%s' % next
+      # We got a pointer or ref.  Add it to the name.
+      if next and next.token_type == tokenize.SYNTAX:
+        new_name.append(next)
+      names.append(new_name)
+      i += len(new_name)
+
+    # Now that we have the names, it's time to undo what we did.
+
+    # Remove the sentinel value.
+    names[-1].pop()
+    # Flatten the token sequence for the return type.
+    return_type = [e for seq in names[:-1] for e in seq]
+    # The class name is the last name.
+    class_name = names[-1]
+    return return_type, class_name
+
+  def handle_bool(self):
+    pass
+
+  def handle_char(self):
+    pass
+
+  def handle_int(self):
+    pass
+
+  def handle_long(self):
+    pass
+
+  def handle_short(self):
+    pass
+
+  def handle_double(self):
+    pass
+
+  def handle_float(self):
+    pass
+
+  def handle_void(self):
+    pass
+
+  def handle_wchar_t(self):
+    pass
+
+  def handle_unsigned(self):
+    pass
+
+  def handle_signed(self):
+    pass
+
+  def _GetNestedType(self, ctor):
+    name = None
+    name_tokens, token = self.GetName()
+    if name_tokens:
+      name = ''.join([t.name for t in name_tokens])
+
+    # Handle forward declarations.
+    if token.token_type == tokenize.SYNTAX and token.name == ';':
+      return ctor(token.start, token.end, name, None,
+                  self.namespace_stack)
+
+    if token.token_type == tokenize.NAME and self._handling_typedef:
+      self._AddBackToken(token)
+      return ctor(token.start, token.end, name, None,
+                  self.namespace_stack)
+
+    # Must be the type declaration.
+    fields = list(self._GetMatchingChar('{', '}'))
+    del fields[-1]                  # Remove trailing '}'.
+    if token.token_type == tokenize.SYNTAX and token.name == '{':
+      next = self._GetNextToken()
+      new_type = ctor(token.start, token.end, name, fields,
+                      self.namespace_stack)
+      # A name means this is an anonymous type and the name
+      # is the variable declaration.
+      if next.token_type != tokenize.NAME:
+        return new_type
+      name = new_type
+      token = next
+
+    # Must be variable declaration using the type prefixed with keyword.
+    assert token.token_type == tokenize.NAME, token
+    return self._CreateVariable(token, token.name, name, [], '', None)
+
+  def handle_struct(self):
+    # Special case the handling typedef/aliasing of structs here.
+    # It would be a pain to handle in the class code.
+    name_tokens, var_token = self.GetName()
+    if name_tokens:
+      next_token = self._GetNextToken()
+      is_syntax = (var_token.token_type == tokenize.SYNTAX and
+                   var_token.name[0] in '*&')
+      is_variable = (var_token.token_type == tokenize.NAME and
+                     next_token.name == ';')
+      variable = var_token
+      if is_syntax and not is_variable:
+        variable = next_token
+        temp = self._GetNextToken()
+        if temp.token_type == tokenize.SYNTAX and temp.name == '(':
+          # Handle methods declared to return a struct.
+          t0 = name_tokens[0]
+          struct = tokenize.Token(tokenize.NAME, 'struct',
+                                  t0.start-7, t0.start-2)
+          type_and_name = [struct]
+          type_and_name.extend(name_tokens)
+          type_and_name.extend((var_token, next_token))
+          return self._GetMethod(type_and_name, 0, None, False)
+        assert temp.name == ';', (temp, name_tokens, var_token)
+      if is_syntax or (is_variable and not self._handling_typedef):
+        modifiers = ['struct']
+        type_name = ''.join([t.name for t in name_tokens])
+        position = name_tokens[0]
+        return self._CreateVariable(position, variable.name, type_name,
+                                    modifiers, var_token.name, None)
+      name_tokens.extend((var_token, next_token))
+      self._AddBackTokens(name_tokens)
+    else:
+      self._AddBackToken(var_token)
+    return self._GetClass(Struct, VISIBILITY_PUBLIC, None)
+
+  def handle_union(self):
+    return self._GetNestedType(Union)
+
+  def handle_enum(self):
+    return self._GetNestedType(Enum)
+
+  def handle_auto(self):
+    # TODO(nnorwitz): warn about using auto?  Probably not since it
+    # will be reclaimed and useful for C++0x.
+    pass
+
+  def handle_register(self):
+    pass
+
+  def handle_const(self):
+    pass
+
+  def handle_inline(self):
+    pass
+
+  def handle_extern(self):
+    pass
+
+  def handle_static(self):
+    pass
+
+  def handle_virtual(self):
+    # What follows must be a method.
+    token = token2 = self._GetNextToken()
+    if token.name == 'inline':
+      # HACK(nnorwitz): handle inline dtors by ignoring 'inline'.
+      token2 = self._GetNextToken()
+    if token2.token_type == tokenize.SYNTAX and token2.name == '~':
+      return self.GetMethod(FUNCTION_VIRTUAL + FUNCTION_DTOR, None)
+    assert token.token_type == tokenize.NAME or token.name == '::', token
+    return_type_and_name, _ = self._GetVarTokensUpToIgnoringTemplates(
+        tokenize.SYNTAX, '(')  # )
+    return_type_and_name.insert(0, token)
+    if token2 is not token:
+      return_type_and_name.insert(1, token2)
+    return self._GetMethod(return_type_and_name, FUNCTION_VIRTUAL,
+                           None, False)
+
+  def handle_volatile(self):
+    pass
+
+  def handle_mutable(self):
+    pass
+
+  def handle_public(self):
+    assert self.in_class
+    self.visibility = VISIBILITY_PUBLIC
+
+  def handle_protected(self):
+    assert self.in_class
+    self.visibility = VISIBILITY_PROTECTED
+
+  def handle_private(self):
+    assert self.in_class
+    self.visibility = VISIBILITY_PRIVATE
+
+  def handle_friend(self):
+    tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+    assert tokens
+    t0 = tokens[0]
+    return Friend(t0.start, t0.end, tokens, self.namespace_stack)
+
+  def handle_static_cast(self):
+    pass
+
+  def handle_const_cast(self):
+    pass
+
+  def handle_dynamic_cast(self):
+    pass
+
+  def handle_reinterpret_cast(self):
+    pass
+
+  def handle_new(self):
+    pass
+
+  def handle_delete(self):
+    tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+    assert tokens
+    return Delete(tokens[0].start, tokens[0].end, tokens)
+
+  def handle_typedef(self):
+    token = self._GetNextToken()
+    if (token.token_type == tokenize.NAME and
             keywords.IsKeyword(token.name)):
-            # Token must be struct/enum/union/class.
-            method = getattr(self, 'handle_' + token.name)
-            self._handling_typedef = True
-            tokens = [method()]
-            self._handling_typedef = False
+      # Token must be struct/enum/union/class.
+      method = getattr(self, 'handle_' + token.name)
+      self._handling_typedef = True
+      tokens = [method()]
+      self._handling_typedef = False
+    else:
+      tokens = [token]
+
+    # Get the remainder of the typedef up to the semi-colon.
+    tokens.extend(self._GetTokensUpTo(tokenize.SYNTAX, ';'))
+
+    # TODO(nnorwitz): clean all this up.
+    assert tokens
+    name = tokens.pop()
+    indices = name
+    if tokens:
+      indices = tokens[0]
+    if not indices:
+      indices = token
+    if name.name == ')':
+      # HACK(nnorwitz): Handle pointers to functions "properly".
+      if (len(tokens) >= 4 and
+              tokens[1].name == '(' and tokens[2].name == '*'):
+        tokens.append(name)
+        name = tokens[3]
+    elif name.name == ']':
+      # HACK(nnorwitz): Handle arrays properly.
+      if len(tokens) >= 2:
+        tokens.append(name)
+        name = tokens[1]
+    new_type = tokens
+    if tokens and isinstance(tokens[0], tokenize.Token):
+      new_type = self.converter.ToType(tokens)[0]
+    return Typedef(indices.start, indices.end, name.name,
+                   new_type, self.namespace_stack)
+
+  def handle_typeid(self):
+    pass  # Not needed yet.
+
+  def handle_typename(self):
+    pass  # Not needed yet.
+
+  def _GetTemplatedTypes(self):
+    result = collections.OrderedDict()
+    tokens = list(self._GetMatchingChar('<', '>'))
+    len_tokens = len(tokens) - 1    # Ignore trailing '>'.
+    i = 0
+    while i < len_tokens:
+      key = tokens[i].name
+      i += 1
+      if keywords.IsKeyword(key) or key == ',':
+        continue
+      type_name = default = None
+      if i < len_tokens:
+        i += 1
+        if tokens[i-1].name == '=':
+          assert i < len_tokens, '%s %s' % (i, tokens)
+          default, unused_next_token = self.GetName(tokens[i:])
+          i += len(default)
         else:
-            tokens = [token]
+          if tokens[i-1].name != ',':
+            # We got something like: Type variable.
+            # Re-adjust the key (variable) and type_name (Type).
+            key = tokens[i-1].name
+            type_name = tokens[i-2]
 
-        # Get the remainder of the typedef up to the semi-colon.
-        tokens.extend(self._GetTokensUpTo(tokenize.SYNTAX, ';'))
+      result[key] = (type_name, default)
+    return result
 
-        # TODO(nnorwitz): clean all this up.
-        assert tokens
-        name = tokens.pop()
-        indices = name
-        if tokens:
-            indices = tokens[0]
-        if not indices:
-            indices = token
-        if name.name == ')':
-            # HACK(nnorwitz): Handle pointers to functions "properly".
-            if (len(tokens) >= 4 and
-                tokens[1].name == '(' and tokens[2].name == '*'):
-                tokens.append(name)
-                name = tokens[3]
-        elif name.name == ']':
-            # HACK(nnorwitz): Handle arrays properly.
-            if len(tokens) >= 2:
-                tokens.append(name)
-                name = tokens[1]
-        new_type = tokens
-        if tokens and isinstance(tokens[0], tokenize.Token):
-            new_type = self.converter.ToType(tokens)[0]
-        return Typedef(indices.start, indices.end, name.name,
-                       new_type, self.namespace_stack)
+  def handle_template(self):
+    token = self._GetNextToken()
+    assert token.token_type == tokenize.SYNTAX, token
+    assert token.name == '<', token
+    templated_types = self._GetTemplatedTypes()
+    # TODO(nnorwitz): for now, just ignore the template params.
+    token = self._GetNextToken()
+    if token.token_type == tokenize.NAME:
+      if token.name == 'class':
+        return self._GetClass(Class, VISIBILITY_PRIVATE, templated_types)
+      elif token.name == 'struct':
+        return self._GetClass(Struct, VISIBILITY_PUBLIC, templated_types)
+      elif token.name == 'friend':
+        return self.handle_friend()
+    self._AddBackToken(token)
+    tokens, last = self._GetVarTokensUpTo(tokenize.SYNTAX, '(', ';')
+    tokens.append(last)
+    self._AddBackTokens(tokens)
+    if last.name == '(':
+      return self.GetMethod(FUNCTION_NONE, templated_types)
+    # Must be a variable definition.
+    return None
 
-    def handle_typeid(self):
-        pass  # Not needed yet.
+  def handle_true(self):
+    pass  # Nothing to do.
 
-    def handle_typename(self):
-        pass  # Not needed yet.
+  def handle_false(self):
+    pass  # Nothing to do.
 
-    def _GetTemplatedTypes(self):
-        result = {}
-        tokens = list(self._GetMatchingChar('<', '>'))
-        len_tokens = len(tokens) - 1    # Ignore trailing '>'.
-        i = 0
-        while i < len_tokens:
-            key = tokens[i].name
-            i += 1
-            if keywords.IsKeyword(key) or key == ',':
-                continue
-            type_name = default = None
-            if i < len_tokens:
-                i += 1
-                if tokens[i-1].name == '=':
-                    assert i < len_tokens, '%s %s' % (i, tokens)
-                    default, unused_next_token = self.GetName(tokens[i:])
-                    i += len(default)
-                else:
-                    if tokens[i-1].name != ',':
-                        # We got something like: Type variable.
-                        # Re-adjust the key (variable) and type_name (Type).
-                        key = tokens[i-1].name
-                        type_name = tokens[i-2]
+  def handle_asm(self):
+    pass  # Not needed yet.
 
-            result[key] = (type_name, default)
-        return result
+  def handle_class(self):
+    return self._GetClass(Class, VISIBILITY_PRIVATE, None)
 
-    def handle_template(self):
-        token = self._GetNextToken()
-        assert token.token_type == tokenize.SYNTAX, token
-        assert token.name == '<', token
-        templated_types = self._GetTemplatedTypes()
-        # TODO(nnorwitz): for now, just ignore the template params.
-        token = self._GetNextToken()
-        if token.token_type == tokenize.NAME:
-            if token.name == 'class':
-                return self._GetClass(Class, VISIBILITY_PRIVATE, templated_types)
-            elif token.name == 'struct':
-                return self._GetClass(Struct, VISIBILITY_PUBLIC, templated_types)
-            elif token.name == 'friend':
-                return self.handle_friend()
+  def _GetBases(self):
+    # Get base classes.
+    bases = []
+    while 1:
+      token = self._GetNextToken()
+      assert token.token_type == tokenize.NAME, token
+      # TODO(nnorwitz): store kind of inheritance...maybe.
+      if token.name not in ('public', 'protected', 'private'):
+        # If inheritance type is not specified, it is private.
+        # Just put the token back so we can form a name.
+        # TODO(nnorwitz): it would be good to warn about this.
         self._AddBackToken(token)
-        tokens, last = self._GetVarTokensUpTo(tokenize.SYNTAX, '(', ';')
-        tokens.append(last)
-        self._AddBackTokens(tokens)
-        if last.name == '(':
-            return self.GetMethod(FUNCTION_NONE, templated_types)
-        # Must be a variable definition.
-        return None
-
-    def handle_true(self):
-        pass  # Nothing to do.
-
-    def handle_false(self):
-        pass  # Nothing to do.
-
-    def handle_asm(self):
-        pass  # Not needed yet.
-
-    def handle_class(self):
-        return self._GetClass(Class, VISIBILITY_PRIVATE, None)
-
-    def _GetBases(self):
-        # Get base classes.
-        bases = []
-        while 1:
-            token = self._GetNextToken()
-            assert token.token_type == tokenize.NAME, token
-            # TODO(nnorwitz): store kind of inheritance...maybe.
-            if token.name not in ('public', 'protected', 'private'):
-                # If inheritance type is not specified, it is private.
-                # Just put the token back so we can form a name.
-                # TODO(nnorwitz): it would be good to warn about this.
-                self._AddBackToken(token)
-            else:
-                # Check for virtual inheritance.
-                token = self._GetNextToken()
-                if token.name != 'virtual':
-                    self._AddBackToken(token)
-                else:
-                    # TODO(nnorwitz): store that we got virtual for this base.
-                    pass
-            base, next_token = self.GetName()
-            bases_ast = self.converter.ToType(base)
-            assert len(bases_ast) == 1, bases_ast
-            bases.append(bases_ast[0])
-            assert next_token.token_type == tokenize.SYNTAX, next_token
-            if next_token.name == '{':
-                token = next_token
-                break
-            # Support multiple inheritance.
-            assert next_token.name == ',', next_token
-        return bases, token
-
-    def _GetClass(self, class_type, visibility, templated_types):
-        class_name = None
-        class_token = self._GetNextToken()
-        if class_token.token_type != tokenize.NAME:
-            assert class_token.token_type == tokenize.SYNTAX, class_token
-            token = class_token
-        else:
-            # Skip any macro (e.g. storage class specifiers) after the
-            # 'class' keyword.
-            next_token = self._GetNextToken()
-            if next_token.token_type == tokenize.NAME:
-                self._AddBackToken(next_token)
-            else:
-                self._AddBackTokens([class_token, next_token])
-            name_tokens, token = self.GetName()
-            class_name = ''.join([t.name for t in name_tokens])
-        bases = None
-        if token.token_type == tokenize.SYNTAX:
-            if token.name == ';':
-                # Forward declaration.
-                return class_type(class_token.start, class_token.end,
-                                  class_name, None, templated_types, None,
-                                  self.namespace_stack)
-            if token.name in '*&':
-                # Inline forward declaration.  Could be method or data.
-                name_token = self._GetNextToken()
-                next_token = self._GetNextToken()
-                if next_token.name == ';':
-                    # Handle data
-                    modifiers = ['class']
-                    return self._CreateVariable(class_token, name_token.name,
-                                                class_name,
-                                                modifiers, token.name, None)
-                else:
-                    # Assume this is a method.
-                    tokens = (class_token, token, name_token, next_token)
-                    self._AddBackTokens(tokens)
-                    return self.GetMethod(FUNCTION_NONE, None)
-            if token.name == ':':
-                bases, token = self._GetBases()
-
-        body = None
-        if token.token_type == tokenize.SYNTAX and token.name == '{':
-            assert token.token_type == tokenize.SYNTAX, token
-            assert token.name == '{', token
-
-            ast = AstBuilder(self.GetScope(), self.filename, class_name,
-                             visibility, self.namespace_stack)
-            body = list(ast.Generate())
-
-            if not self._handling_typedef:
-                token = self._GetNextToken()
-                if token.token_type != tokenize.NAME:
-                    assert token.token_type == tokenize.SYNTAX, token
-                    assert token.name == ';', token
-                else:
-                    new_class = class_type(class_token.start, class_token.end,
-                                           class_name, bases, None,
-                                           body, self.namespace_stack)
-
-                    modifiers = []
-                    return self._CreateVariable(class_token,
-                                                token.name, new_class,
-                                                modifiers, token.name, None)
-        else:
-            if not self._handling_typedef:
-                self.HandleError('non-typedef token', token)
-            self._AddBackToken(token)
-
-        return class_type(class_token.start, class_token.end, class_name,
-                          bases, templated_types, body, self.namespace_stack)
-
-    def handle_namespace(self):
+      else:
+        # Check for virtual inheritance.
         token = self._GetNextToken()
-        # Support anonymous namespaces.
-        name = None
-        if token.token_type == tokenize.NAME:
-            name = token.name
-            token = self._GetNextToken()
-        self.namespace_stack.append(name)
-        assert token.token_type == tokenize.SYNTAX, token
-        # Create an internal token that denotes when the namespace is complete.
-        internal_token = tokenize.Token(_INTERNAL_TOKEN, _NAMESPACE_POP,
-                                        None, None)
-        internal_token.whence = token.whence
-        if token.name == '=':
-            # TODO(nnorwitz): handle aliasing namespaces.
-            name, next_token = self.GetName()
-            assert next_token.name == ';', next_token
-            self._AddBackToken(internal_token)
+        if token.name != 'virtual':
+          self._AddBackToken(token)
         else:
-            assert token.name == '{', token
-            tokens = list(self.GetScope())
-            # Replace the trailing } with the internal namespace pop token.
-            tokens[-1] = internal_token
-            # Handle namespace with nothing in it.
-            self._AddBackTokens(tokens)
-        return None
+          # TODO(nnorwitz): store that we got virtual for this base.
+          pass
+      base, next_token = self.GetName()
+      bases_ast = self.converter.ToType(base)
+      assert len(bases_ast) == 1, bases_ast
+      bases.append(bases_ast[0])
+      assert next_token.token_type == tokenize.SYNTAX, next_token
+      if next_token.name == '{':
+        token = next_token
+        break
+      # Support multiple inheritance.
+      assert next_token.name == ',', next_token
+    return bases, token
 
-    def handle_using(self):
-        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
-        assert tokens
-        return Using(tokens[0].start, tokens[0].end, tokens)
+  def _GetClass(self, class_type, visibility, templated_types):
+    class_name = None
+    class_token = self._GetNextToken()
+    if class_token.token_type != tokenize.NAME:
+      assert class_token.token_type == tokenize.SYNTAX, class_token
+      token = class_token
+    else:
+      # Skip any macro (e.g. storage class specifiers) after the
+      # 'class' keyword.
+      next_token = self._GetNextToken()
+      if next_token.token_type == tokenize.NAME:
+        self._AddBackToken(next_token)
+      else:
+        self._AddBackTokens([class_token, next_token])
+      name_tokens, token = self.GetName()
+      class_name = ''.join([t.name for t in name_tokens])
+    bases = None
+    if token.token_type == tokenize.SYNTAX:
+      if token.name == ';':
+        # Forward declaration.
+        return class_type(class_token.start, class_token.end,
+                          class_name, None, templated_types, None,
+                          self.namespace_stack)
+      if token.name in '*&':
+        # Inline forward declaration.  Could be method or data.
+        name_token = self._GetNextToken()
+        next_token = self._GetNextToken()
+        if next_token.name == ';':
+          # Handle data
+          modifiers = ['class']
+          return self._CreateVariable(class_token, name_token.name,
+                                      class_name,
+                                      modifiers, token.name, None)
+        else:
+          # Assume this is a method.
+          tokens = (class_token, token, name_token, next_token)
+          self._AddBackTokens(tokens)
+          return self.GetMethod(FUNCTION_NONE, None)
+      if token.name == ':':
+        bases, token = self._GetBases()
 
-    def handle_explicit(self):
-        assert self.in_class
-        # Nothing much to do.
-        # TODO(nnorwitz): maybe verify the method name == class name.
-        # This must be a ctor.
-        return self.GetMethod(FUNCTION_CTOR, None)
+    body = None
+    if token.token_type == tokenize.SYNTAX and token.name == '{':
+      assert token.token_type == tokenize.SYNTAX, token
+      assert token.name == '{', token
 
-    def handle_this(self):
-        pass  # Nothing to do.
+      ast = AstBuilder(self.GetScope(), self.filename, class_name,
+                       visibility, self.namespace_stack)
+      body = list(ast.Generate())
 
-    def handle_operator(self):
-        # Pull off the next token(s?) and make that part of the method name.
-        pass
-
-    def handle_sizeof(self):
-        pass
-
-    def handle_case(self):
-        pass
-
-    def handle_switch(self):
-        pass
-
-    def handle_default(self):
+      if not self._handling_typedef:
         token = self._GetNextToken()
-        assert token.token_type == tokenize.SYNTAX
-        assert token.name == ':'
+        if token.token_type != tokenize.NAME:
+          assert token.token_type == tokenize.SYNTAX, token
+          assert token.name == ';', token
+        else:
+          new_class = class_type(class_token.start, class_token.end,
+                                 class_name, bases, None,
+                                 body, self.namespace_stack)
 
-    def handle_if(self):
-        pass
+          modifiers = []
+          return self._CreateVariable(class_token,
+                                      token.name, new_class,
+                                      modifiers, token.name, None)
+    else:
+      if not self._handling_typedef:
+        self.HandleError('non-typedef token', token)
+      self._AddBackToken(token)
 
-    def handle_else(self):
-        pass
+    return class_type(class_token.start, class_token.end, class_name,
+                      bases, templated_types, body, self.namespace_stack)
 
-    def handle_return(self):
-        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
-        if not tokens:
-            return Return(self.current_token.start, self.current_token.end, None)
-        return Return(tokens[0].start, tokens[0].end, tokens)
+  def handle_namespace(self):
+    # Support anonymous namespaces.
+    name = None
+    name_tokens, token = self.GetName()
+    if name_tokens:
+      name = ''.join([t.name for t in name_tokens])
+    self.namespace_stack.append(name)
+    assert token.token_type == tokenize.SYNTAX, token
+    # Create an internal token that denotes when the namespace is complete.
+    internal_token = tokenize.Token(_INTERNAL_TOKEN, _NAMESPACE_POP,
+                                    None, None)
+    internal_token.whence = token.whence
+    if token.name == '=':
+      # TODO(nnorwitz): handle aliasing namespaces.
+      name, next_token = self.GetName()
+      assert next_token.name == ';', next_token
+      self._AddBackToken(internal_token)
+    else:
+      assert token.name == '{', token
+      tokens = list(self.GetScope())
+      # Replace the trailing } with the internal namespace pop token.
+      tokens[-1] = internal_token
+      # Handle namespace with nothing in it.
+      self._AddBackTokens(tokens)
+    return None
 
-    def handle_goto(self):
-        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
-        assert len(tokens) == 1, str(tokens)
-        return Goto(tokens[0].start, tokens[0].end, tokens[0].name)
+  def handle_using(self):
+    tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+    assert tokens
+    return Using(tokens[0].start, tokens[0].end, tokens)
 
-    def handle_try(self):
-        pass  # Not needed yet.
+  def handle_explicit(self):
+    assert self.in_class
+    # Nothing much to do.
+    # TODO(nnorwitz): maybe verify the method name == class name.
+    # This must be a ctor.
+    return self.GetMethod(FUNCTION_CTOR, None)
 
-    def handle_catch(self):
-        pass  # Not needed yet.
+  def handle_this(self):
+    pass  # Nothing to do.
 
-    def handle_throw(self):
-        pass  # Not needed yet.
+  def handle_operator(self):
+    # Pull off the next token(s?) and make that part of the method name.
+    pass
 
-    def handle_while(self):
-        pass
+  def handle_sizeof(self):
+    pass
 
-    def handle_do(self):
-        pass
+  def handle_case(self):
+    pass
 
-    def handle_for(self):
-        pass
+  def handle_switch(self):
+    pass
 
-    def handle_break(self):
-        self._IgnoreUpTo(tokenize.SYNTAX, ';')
+  def handle_default(self):
+    token = self._GetNextToken()
+    assert token.token_type == tokenize.SYNTAX
+    assert token.name == ':'
 
-    def handle_continue(self):
-        self._IgnoreUpTo(tokenize.SYNTAX, ';')
+  def handle_if(self):
+    pass
+
+  def handle_else(self):
+    pass
+
+  def handle_return(self):
+    tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+    if not tokens:
+      return Return(self.current_token.start, self.current_token.end, None)
+    return Return(tokens[0].start, tokens[0].end, tokens)
+
+  def handle_goto(self):
+    tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+    assert len(tokens) == 1, str(tokens)
+    return Goto(tokens[0].start, tokens[0].end, tokens[0].name)
+
+  def handle_try(self):
+    pass  # Not needed yet.
+
+  def handle_catch(self):
+    pass  # Not needed yet.
+
+  def handle_throw(self):
+    pass  # Not needed yet.
+
+  def handle_while(self):
+    pass
+
+  def handle_do(self):
+    pass
+
+  def handle_for(self):
+    pass
+
+  def handle_break(self):
+    self._IgnoreUpTo(tokenize.SYNTAX, ';')
+
+  def handle_continue(self):
+    self._IgnoreUpTo(tokenize.SYNTAX, ';')
 
 
 def BuilderFromSource(source, filename):
-    """Utility method that returns an AstBuilder from source code.
+  """Utility method that returns an AstBuilder from source code.
 
     Args:
       source: 'C++ source code'
@@ -1673,64 +1710,64 @@
     Returns:
       AstBuilder
     """
-    return AstBuilder(tokenize.GetTokens(source), filename)
+  return AstBuilder(tokenize.GetTokens(source), filename)
 
 
 def PrintIndentifiers(filename, should_print):
-    """Prints all identifiers for a C++ source file.
+  """Prints all identifiers for a C++ source file.
 
     Args:
       filename: 'file1'
       should_print: predicate with signature: bool Function(token)
     """
-    source = utils.ReadFile(filename, False)
-    if source is None:
-        sys.stderr.write('Unable to find: %s\n' % filename)
-        return
+  source = utils.ReadFile(filename, False)
+  if source is None:
+    sys.stderr.write('Unable to find: %s\n' % filename)
+    return
 
-    #print('Processing %s' % actual_filename)
-    builder = BuilderFromSource(source, filename)
-    try:
-        for node in builder.Generate():
-            if should_print(node):
-                print(node.name)
-    except KeyboardInterrupt:
-        return
-    except:
-        pass
+  #print('Processing %s' % actual_filename)
+  builder = BuilderFromSource(source, filename)
+  try:
+    for node in builder.Generate():
+      if should_print(node):
+        print(node.name)
+  except KeyboardInterrupt:
+    return
+  except:
+    pass
 
 
 def PrintAllIndentifiers(filenames, should_print):
-    """Prints all identifiers for each C++ source file in filenames.
+  """Prints all identifiers for each C++ source file in filenames.
 
     Args:
       filenames: ['file1', 'file2', ...]
       should_print: predicate with signature: bool Function(token)
     """
-    for path in filenames:
-        PrintIndentifiers(path, should_print)
+  for path in filenames:
+    PrintIndentifiers(path, should_print)
 
 
 def main(argv):
-    for filename in argv[1:]:
-        source = utils.ReadFile(filename)
-        if source is None:
-            continue
+  for filename in argv[1:]:
+    source = utils.ReadFile(filename)
+    if source is None:
+      continue
 
-        print('Processing %s' % filename)
-        builder = BuilderFromSource(source, filename)
-        try:
-            entire_ast = filter(None, builder.Generate())
-        except KeyboardInterrupt:
-            return
-        except:
-            # Already printed a warning, print the traceback and continue.
-            traceback.print_exc()
-        else:
-            if utils.DEBUG:
-                for ast in entire_ast:
-                    print(ast)
+    print('Processing %s' % filename)
+    builder = BuilderFromSource(source, filename)
+    try:
+      entire_ast = filter(None, builder.Generate())
+    except KeyboardInterrupt:
+      return
+    except:
+      # Already printed a warning, print the traceback and continue.
+      traceback.print_exc()
+    else:
+      if utils.DEBUG:
+        for ast in entire_ast:
+          print(ast)
 
 
 if __name__ == '__main__':
-    main(sys.argv)
+  main(sys.argv)
diff --git a/ext/googletest/googlemock/scripts/generator/cpp/gmock_class.py b/ext/googletest/googlemock/scripts/generator/cpp/gmock_class.py
index f9966cb..3e21022 100755
--- a/ext/googletest/googlemock/scripts/generator/cpp/gmock_class.py
+++ b/ext/googletest/googlemock/scripts/generator/cpp/gmock_class.py
@@ -26,9 +26,6 @@
 Output is sent to stdout.
 """
 
-__author__ = 'nnorwitz@google.com (Neal Norwitz)'
-
-
 import os
 import re
 import sys
@@ -41,6 +38,7 @@
   _dummy = set
 except NameError:
   import sets
+
   set = sets.Set
 
 _VERSION = (1, 0, 1)  # The version of this script.
@@ -48,79 +46,100 @@
 _INDENT = 2
 
 
+def _RenderType(ast_type):
+  """Renders the potentially recursively templated type into a string.
+
+  Args:
+    ast_type: The AST of the type.
+
+  Returns:
+    Rendered string of the type.
+  """
+  # Add modifiers like 'const'.
+  modifiers = ''
+  if ast_type.modifiers:
+    modifiers = ' '.join(ast_type.modifiers) + ' '
+  return_type = modifiers + ast_type.name
+  if ast_type.templated_types:
+    # Collect template args.
+    template_args = []
+    for arg in ast_type.templated_types:
+      rendered_arg = _RenderType(arg)
+      template_args.append(rendered_arg)
+    return_type += '<' + ', '.join(template_args) + '>'
+  if ast_type.pointer:
+    return_type += '*'
+  if ast_type.reference:
+    return_type += '&'
+  return return_type
+
+
+def _GenerateArg(source):
+  """Strips out comments, default arguments, and redundant spaces from a single argument.
+
+  Args:
+    source: A string for a single argument.
+
+  Returns:
+    Rendered string of the argument.
+  """
+  # Remove end of line comments before eliminating newlines.
+  arg = re.sub(r'//.*', '', source)
+
+  # Remove c-style comments.
+  arg = re.sub(r'/\*.*\*/', '', arg)
+
+  # Remove default arguments.
+  arg = re.sub(r'=.*', '', arg)
+
+  # Collapse spaces and newlines into a single space.
+  arg = re.sub(r'\s+', ' ', arg)
+  return arg.strip()
+
+
+def _EscapeForMacro(s):
+  """Escapes a string for use as an argument to a C++ macro."""
+  paren_count = 0
+  for c in s:
+    if c == '(':
+      paren_count += 1
+    elif c == ')':
+      paren_count -= 1
+    elif c == ',' and paren_count == 0:
+      return '(' + s + ')'
+  return s
+
+
 def _GenerateMethods(output_lines, source, class_node):
-  function_type = (ast.FUNCTION_VIRTUAL | ast.FUNCTION_PURE_VIRTUAL |
-                   ast.FUNCTION_OVERRIDE)
+  function_type = (
+      ast.FUNCTION_VIRTUAL | ast.FUNCTION_PURE_VIRTUAL | ast.FUNCTION_OVERRIDE)
   ctor_or_dtor = ast.FUNCTION_CTOR | ast.FUNCTION_DTOR
   indent = ' ' * _INDENT
 
   for node in class_node.body:
     # We only care about virtual functions.
-    if (isinstance(node, ast.Function) and
-        node.modifiers & function_type and
+    if (isinstance(node, ast.Function) and node.modifiers & function_type and
         not node.modifiers & ctor_or_dtor):
       # Pick out all the elements we need from the original function.
-      const = ''
+      modifiers = 'override'
       if node.modifiers & ast.FUNCTION_CONST:
-        const = 'CONST_'
+        modifiers = 'const, ' + modifiers
+
       return_type = 'void'
       if node.return_type:
-        # Add modifiers like 'const'.
-        modifiers = ''
-        if node.return_type.modifiers:
-          modifiers = ' '.join(node.return_type.modifiers) + ' '
-        return_type = modifiers + node.return_type.name
-        template_args = [arg.name for arg in node.return_type.templated_types]
-        if template_args:
-          return_type += '<' + ', '.join(template_args) + '>'
-          if len(template_args) > 1:
-            for line in [
-                '// The following line won\'t really compile, as the return',
-                '// type has multiple template arguments.  To fix it, use a',
-                '// typedef for the return type.']:
-              output_lines.append(indent + line)
-        if node.return_type.pointer:
-          return_type += '*'
-        if node.return_type.reference:
-          return_type += '&'
-        num_parameters = len(node.parameters)
-        if len(node.parameters) == 1:
-          first_param = node.parameters[0]
-          if source[first_param.start:first_param.end].strip() == 'void':
-            # We must treat T(void) as a function with no parameters.
-            num_parameters = 0
-      tmpl = ''
-      if class_node.templated_types:
-        tmpl = '_T'
-      mock_method_macro = 'MOCK_%sMETHOD%d%s' % (const, num_parameters, tmpl)
+        return_type = _EscapeForMacro(_RenderType(node.return_type))
 
-      args = ''
-      if node.parameters:
-        # Due to the parser limitations, it is impossible to keep comments
-        # while stripping the default parameters.  When defaults are
-        # present, we choose to strip them and comments (and produce
-        # compilable code).
-        # TODO(nnorwitz@google.com): Investigate whether it is possible to
-        # preserve parameter name when reconstructing parameter text from
-        # the AST.
-        if len([param for param in node.parameters if param.default]) > 0:
-          args = ', '.join(param.type.name for param in node.parameters)
-        else:
-          # Get the full text of the parameters from the start
-          # of the first parameter to the end of the last parameter.
-          start = node.parameters[0].start
-          end = node.parameters[-1].end
-          # Remove // comments.
-          args_strings = re.sub(r'//.*', '', source[start:end])
-          # Condense multiple spaces and eliminate newlines putting the
-          # parameters together on a single line.  Ensure there is a
-          # space in an argument which is split by a newline without
-          # intervening whitespace, e.g.: int\nBar
-          args = re.sub('  +', ' ', args_strings.replace('\n', ' '))
+      args = []
+      for p in node.parameters:
+        arg = _GenerateArg(source[p.start:p.end])
+        if arg != 'void':
+          args.append(_EscapeForMacro(arg))
 
       # Create the mock method definition.
-      output_lines.extend(['%s%s(%s,' % (indent, mock_method_macro, node.name),
-                           '%s%s(%s));' % (indent*3, return_type, args)])
+      output_lines.extend([
+          '%sMOCK_METHOD(%s, %s, (%s), (%s));' %
+          (indent, return_type, node.name, ', '.join(args), modifiers)
+      ])
 
 
 def _GenerateMocks(filename, source, ast_list, desired_class_names):
@@ -141,12 +160,13 @@
 
       # Add template args for templated classes.
       if class_node.templated_types:
-        # TODO(paulchang): The AST doesn't preserve template argument order,
-        # so we have to make up names here.
         # TODO(paulchang): Handle non-type template arguments (e.g.
         # template<typename T, int N>).
-        template_arg_count = len(class_node.templated_types.keys())
-        template_args = ['T%d' % n for n in range(template_arg_count)]
+
+        # class_node.templated_types is an OrderedDict from strings to a tuples.
+        # The key is the name of the template, and the value is
+        # (type_name, default). Both type_name and default could be None.
+        template_args = class_node.templated_types.keys()
         template_decls = ['typename ' + arg for arg in template_args]
         lines.append('template <' + ', '.join(template_decls) + '>')
         parent_name += '<' + ', '.join(template_args) + '>'
@@ -171,7 +191,7 @@
 
       # Close the namespace.
       if class_node.namespace:
-        for i in range(len(class_node.namespace)-1, -1, -1):
+        for i in range(len(class_node.namespace) - 1, -1, -1):
           lines.append('}  // namespace %s' % class_node.namespace[i])
         lines.append('')  # Add an extra newline.
 
diff --git a/ext/googletest/googlemock/scripts/generator/cpp/gmock_class_test.py b/ext/googletest/googlemock/scripts/generator/cpp/gmock_class_test.py
index c53e600..eff475f 100755
--- a/ext/googletest/googlemock/scripts/generator/cpp/gmock_class_test.py
+++ b/ext/googletest/googlemock/scripts/generator/cpp/gmock_class_test.py
@@ -17,9 +17,6 @@
 
 """Tests for gmock.scripts.generator.cpp.gmock_class."""
 
-__author__ = 'nnorwitz@google.com (Neal Norwitz)'
-
-
 import os
 import sys
 import unittest
@@ -34,7 +31,8 @@
 class TestCase(unittest.TestCase):
   """Helper class that adds assert methods."""
 
-  def StripLeadingWhitespace(self, lines):
+  @staticmethod
+  def StripLeadingWhitespace(lines):
     """Strip leading whitespace in each line in 'lines'."""
     return '\n'.join([s.lstrip() for s in lines.split('\n')])
 
@@ -45,7 +43,8 @@
 
 class GenerateMethodsTest(TestCase):
 
-  def GenerateMethodSource(self, cpp_source):
+  @staticmethod
+  def GenerateMethodSource(cpp_source):
     """Convert C++ source to Google Mock output source lines."""
     method_source_lines = []
     # <test> is a pseudo-filename, it is not read or written.
@@ -62,7 +61,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD0(Bar,\nint());',
+        'MOCK_METHOD(int, Bar, (), (override));',
         self.GenerateMethodSource(source))
 
   def testSimpleConstructorsAndDestructor(self):
@@ -79,7 +78,7 @@
 """
     # The constructors and destructor should be ignored.
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD0(Bar,\nint());',
+        'MOCK_METHOD(int, Bar, (), (override));',
         self.GenerateMethodSource(source))
 
   def testVirtualDestructor(self):
@@ -92,7 +91,7 @@
 """
     # The destructor should be ignored.
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD0(Bar,\nint());',
+        'MOCK_METHOD(int, Bar, (), (override));',
         self.GenerateMethodSource(source))
 
   def testExplicitlyDefaultedConstructorsAndDestructor(self):
@@ -108,7 +107,7 @@
 """
     # The constructors and destructor should be ignored.
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD0(Bar,\nint());',
+        'MOCK_METHOD(int, Bar, (), (override));',
         self.GenerateMethodSource(source))
 
   def testExplicitlyDeletedConstructorsAndDestructor(self):
@@ -124,7 +123,7 @@
 """
     # The constructors and destructor should be ignored.
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD0(Bar,\nint());',
+        'MOCK_METHOD(int, Bar, (), (override));',
         self.GenerateMethodSource(source))
 
   def testSimpleOverrideMethod(self):
@@ -135,7 +134,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD0(Bar,\nint());',
+        'MOCK_METHOD(int, Bar, (), (override));',
         self.GenerateMethodSource(source))
 
   def testSimpleConstMethod(self):
@@ -146,7 +145,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_CONST_METHOD1(Bar,\nvoid(bool flag));',
+        'MOCK_METHOD(void, Bar, (bool flag), (const, override));',
         self.GenerateMethodSource(source))
 
   def testExplicitVoid(self):
@@ -157,7 +156,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD0(Bar,\nint(void));',
+        'MOCK_METHOD(int, Bar, (), (override));',
         self.GenerateMethodSource(source))
 
   def testStrangeNewlineInParameter(self):
@@ -169,7 +168,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD1(Bar,\nvoid(int a));',
+        'MOCK_METHOD(void, Bar, (int a), (override));',
         self.GenerateMethodSource(source))
 
   def testDefaultParameters(self):
@@ -180,18 +179,58 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD2(Bar,\nvoid(int, char));',
+        'MOCK_METHOD(void, Bar, (int a, char c), (override));',
         self.GenerateMethodSource(source))
 
   def testMultipleDefaultParameters(self):
     source = """
 class Foo {
  public:
-  virtual void Bar(int a = 42, char c = 'x') = 0;
+  virtual void Bar(
+        int a = 42, 
+        char c = 'x', 
+        const int* const p = nullptr, 
+        const std::string& s = "42",
+        char tab[] = {'4','2'},
+        int const *& rp = aDefaultPointer) = 0;
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD2(Bar,\nvoid(int, char));',
+        'MOCK_METHOD(void, Bar, '
+        '(int a, char c, const int* const p, const std::string& s, char tab[], int const *& rp), '
+        '(override));', self.GenerateMethodSource(source))
+
+  def testMultipleSingleLineDefaultParameters(self):
+    source = """
+class Foo {
+ public:
+  virtual void Bar(int a = 42, int b = 43, int c = 44) = 0;
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(
+        'MOCK_METHOD(void, Bar, (int a, int b, int c), (override));',
+        self.GenerateMethodSource(source))
+
+  def testConstDefaultParameter(self):
+    source = """
+class Test {
+ public:
+  virtual bool Bar(const int test_arg = 42) = 0;
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(
+        'MOCK_METHOD(bool, Bar, (const int test_arg), (override));',
+        self.GenerateMethodSource(source))
+
+  def testConstRefDefaultParameter(self):
+    source = """
+class Test {
+ public:
+  virtual bool Bar(const std::string& test_arg = "42" ) = 0;
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(
+        'MOCK_METHOD(bool, Bar, (const std::string& test_arg), (override));',
         self.GenerateMethodSource(source))
 
   def testRemovesCommentsWhenDefaultsArePresent(self):
@@ -203,7 +242,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD2(Bar,\nvoid(int, char));',
+        'MOCK_METHOD(void, Bar, (int a, char c), (override));',
         self.GenerateMethodSource(source))
 
   def testDoubleSlashCommentsInParameterListAreRemoved(self):
@@ -216,7 +255,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_CONST_METHOD2(Bar,\nvoid(int a, int b));',
+        'MOCK_METHOD(void, Bar, (int a, int b), (const, override));',
         self.GenerateMethodSource(source))
 
   def testCStyleCommentsInParameterListAreNotRemoved(self):
@@ -230,7 +269,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD2(Bar,\nconst string&(int /* keeper */, int b));',
+        'MOCK_METHOD(const string&, Bar, (int, int b), (override));',
         self.GenerateMethodSource(source))
 
   def testArgsOfTemplateTypes(self):
@@ -240,8 +279,7 @@
   virtual int Bar(const vector<int>& v, map<int, string>* output);
 };"""
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD2(Bar,\n'
-        'int(const vector<int>& v, map<int, string>* output));',
+        'MOCK_METHOD(int, Bar, (const vector<int>& v, (map<int, string>* output)), (override));',
         self.GenerateMethodSource(source))
 
   def testReturnTypeWithOneTemplateArg(self):
@@ -251,7 +289,7 @@
   virtual vector<int>* Bar(int n);
 };"""
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD1(Bar,\nvector<int>*(int n));',
+        'MOCK_METHOD(vector<int>*, Bar, (int n), (override));',
         self.GenerateMethodSource(source))
 
   def testReturnTypeWithManyTemplateArgs(self):
@@ -260,13 +298,8 @@
  public:
   virtual map<int, string> Bar();
 };"""
-    # Comparing the comment text is brittle - we'll think of something
-    # better in case this gets annoying, but for now let's keep it simple.
     self.assertEqualIgnoreLeadingWhitespace(
-        '// The following line won\'t really compile, as the return\n'
-        '// type has multiple template arguments.  To fix it, use a\n'
-        '// typedef for the return type.\n'
-        'MOCK_METHOD0(Bar,\nmap<int, string>());',
+        'MOCK_METHOD((map<int, string>), Bar, (), (override));',
         self.GenerateMethodSource(source))
 
   def testSimpleMethodInTemplatedClass(self):
@@ -278,7 +311,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD0_T(Bar,\nint());',
+        'MOCK_METHOD(int, Bar, (), (override));',
         self.GenerateMethodSource(source))
 
   def testPointerArgWithoutNames(self):
@@ -288,7 +321,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD1(Bar,\nint(C*));',
+        'MOCK_METHOD(int, Bar, (C*), (override));',
         self.GenerateMethodSource(source))
 
   def testReferenceArgWithoutNames(self):
@@ -298,7 +331,7 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD1(Bar,\nint(C&));',
+        'MOCK_METHOD(int, Bar, (C&), (override));',
         self.GenerateMethodSource(source))
 
   def testArrayArgWithoutNames(self):
@@ -308,13 +341,14 @@
 };
 """
     self.assertEqualIgnoreLeadingWhitespace(
-        'MOCK_METHOD1(Bar,\nint(C[]));',
+        'MOCK_METHOD(int, Bar, (C[]), (override));',
         self.GenerateMethodSource(source))
 
 
 class GenerateMocksTest(TestCase):
 
-  def GenerateMocks(self, cpp_source):
+  @staticmethod
+  def GenerateMocks(cpp_source):
     """Convert C++ source to complete Google Mock output source."""
     # <test> is a pseudo-filename, it is not read or written.
     filename = '<test>'
@@ -327,31 +361,30 @@
     source = """
 namespace Foo {
 namespace Bar { class Forward; }
-namespace Baz {
+namespace Baz::Qux {
 
 class Test {
  public:
   virtual void Foo();
 };
 
-}  // namespace Baz
+}  // namespace Baz::Qux
 }  // namespace Foo
 """
     expected = """\
 namespace Foo {
-namespace Baz {
+namespace Baz::Qux {
 
 class MockTest : public Test {
 public:
-MOCK_METHOD0(Foo,
-void());
+MOCK_METHOD(void, Foo, (), (override));
 };
 
-}  // namespace Baz
+}  // namespace Baz::Qux
 }  // namespace Foo
 """
-    self.assertEqualIgnoreLeadingWhitespace(
-        expected, self.GenerateMocks(source))
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
 
   def testClassWithStorageSpecifierMacro(self):
     source = """
@@ -363,12 +396,11 @@
     expected = """\
 class MockTest : public Test {
 public:
-MOCK_METHOD0(Foo,
-void());
+MOCK_METHOD(void, Foo, (), (override));
 };
 """
-    self.assertEqualIgnoreLeadingWhitespace(
-        expected, self.GenerateMocks(source))
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
 
   def testTemplatedForwardDeclaration(self):
     source = """
@@ -381,12 +413,11 @@
     expected = """\
 class MockTest : public Test {
 public:
-MOCK_METHOD0(Foo,
-void());
+MOCK_METHOD(void, Foo, (), (override));
 };
 """
-    self.assertEqualIgnoreLeadingWhitespace(
-        expected, self.GenerateMocks(source))
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
 
   def testTemplatedClass(self):
     source = """
@@ -397,15 +428,14 @@
 };
 """
     expected = """\
-template <typename T0, typename T1>
-class MockTest : public Test<T0, T1> {
+template <typename S, typename T>
+class MockTest : public Test<S, T> {
 public:
-MOCK_METHOD0_T(Foo,
-void());
+MOCK_METHOD(void, Foo, (), (override));
 };
 """
-    self.assertEqualIgnoreLeadingWhitespace(
-        expected, self.GenerateMocks(source))
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
 
   def testTemplateInATemplateTypedef(self):
     source = """
@@ -418,12 +448,29 @@
     expected = """\
 class MockTest : public Test {
 public:
-MOCK_METHOD1(Bar,
-void(const FooType& test_arg));
+MOCK_METHOD(void, Bar, (const FooType& test_arg), (override));
 };
 """
-    self.assertEqualIgnoreLeadingWhitespace(
-        expected, self.GenerateMocks(source))
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
+
+  def testTemplatedClassWithTemplatedArguments(self):
+    source = """
+template <typename S, typename T, typename U, typename V, typename W>
+class Test {
+ public:
+  virtual U Foo(T some_arg);
+};
+"""
+    expected = """\
+template <typename S, typename T, typename U, typename V, typename W>
+class MockTest : public Test<S, T, U, V, W> {
+public:
+MOCK_METHOD(U, Foo, (T some_arg), (override));
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
 
   def testTemplateInATemplateTypedefWithComma(self):
     source = """
@@ -437,30 +484,87 @@
     expected = """\
 class MockTest : public Test {
 public:
-MOCK_METHOD1(Bar,
-void(const FooType& test_arg));
+MOCK_METHOD(void, Bar, (const FooType& test_arg), (override));
 };
 """
-    self.assertEqualIgnoreLeadingWhitespace(
-        expected, self.GenerateMocks(source))
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
 
-  def testEnumClass(self):
+  def testParenthesizedCommaInArg(self):
     source = """
 class Test {
  public:
-  enum class Baz { BAZINGA };
-  virtual void Bar(const FooType& test_arg);
+   virtual void Bar(std::function<void(int, int)> f);
 };
 """
     expected = """\
 class MockTest : public Test {
 public:
-MOCK_METHOD1(Bar,
-void(const FooType& test_arg));
+MOCK_METHOD(void, Bar, (std::function<void(int, int)> f), (override));
 };
 """
-    self.assertEqualIgnoreLeadingWhitespace(
-        expected, self.GenerateMocks(source))
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
+
+  def testEnumType(self):
+    source = """
+class Test {
+ public:
+  enum Bar {
+    BAZ, QUX, QUUX, QUUUX
+  };
+  virtual void Foo();
+};
+"""
+    expected = """\
+class MockTest : public Test {
+public:
+MOCK_METHOD(void, Foo, (), (override));
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
+
+  def testEnumClassType(self):
+    source = """
+class Test {
+ public:
+  enum class Bar {
+    BAZ, QUX, QUUX, QUUUX
+  };
+  virtual void Foo();
+};
+"""
+    expected = """\
+class MockTest : public Test {
+public:
+MOCK_METHOD(void, Foo, (), (override));
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
+
+  def testStdFunction(self):
+    source = """
+class Test {
+ public:
+  Test(std::function<int(std::string)> foo) : foo_(foo) {}
+
+  virtual std::function<int(std::string)> foo();
+
+ private:
+  std::function<int(std::string)> foo_;
+};
+"""
+    expected = """\
+class MockTest : public Test {
+public:
+MOCK_METHOD(std::function<int (std::string)>, foo, (), (override));
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(expected,
+                                            self.GenerateMocks(source))
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/ext/googletest/googlemock/scripts/generator/cpp/keywords.py b/ext/googletest/googlemock/scripts/generator/cpp/keywords.py
index f694450..e428271 100755
--- a/ext/googletest/googlemock/scripts/generator/cpp/keywords.py
+++ b/ext/googletest/googlemock/scripts/generator/cpp/keywords.py
@@ -17,9 +17,6 @@
 
 """C++ keywords and helper utilities for determining keywords."""
 
-__author__ = 'nnorwitz@google.com (Neal Norwitz)'
-
-
 try:
     # Python 3.x
     import builtins
diff --git a/ext/googletest/googlemock/scripts/generator/cpp/tokenize.py b/ext/googletest/googlemock/scripts/generator/cpp/tokenize.py
index 359d556..a75edcb 100755
--- a/ext/googletest/googlemock/scripts/generator/cpp/tokenize.py
+++ b/ext/googletest/googlemock/scripts/generator/cpp/tokenize.py
@@ -17,9 +17,6 @@
 
 """Tokenize C++ source code."""
 
-__author__ = 'nnorwitz@google.com (Neal Norwitz)'
-
-
 try:
     # Python 3.x
     import builtins
diff --git a/ext/googletest/googlemock/scripts/generator/cpp/utils.py b/ext/googletest/googlemock/scripts/generator/cpp/utils.py
index eab36ee..6f5fc09 100755
--- a/ext/googletest/googlemock/scripts/generator/cpp/utils.py
+++ b/ext/googletest/googlemock/scripts/generator/cpp/utils.py
@@ -17,12 +17,8 @@
 
 """Generic utilities for C++ parsing."""
 
-__author__ = 'nnorwitz@google.com (Neal Norwitz)'
-
-
 import sys
 
-
 # Set to True to see the start/end token indices.
 DEBUG = True
 
diff --git a/ext/googletest/googlemock/scripts/generator/gmock_gen.py b/ext/googletest/googlemock/scripts/generator/gmock_gen.py
index 8cc0d13..9d528a5 100755
--- a/ext/googletest/googlemock/scripts/generator/gmock_gen.py
+++ b/ext/googletest/googlemock/scripts/generator/gmock_gen.py
@@ -16,7 +16,6 @@
 
 """Driver for starting up Google Mock class generator."""
 
-__author__ = 'nnorwitz@google.com (Neal Norwitz)'
 
 import os
 import sys
diff --git a/ext/googletest/googlemock/scripts/gmock-config.in b/ext/googletest/googlemock/scripts/gmock-config.in
deleted file mode 100755
index 2baefe9..0000000
--- a/ext/googletest/googlemock/scripts/gmock-config.in
+++ /dev/null
@@ -1,303 +0,0 @@
-#!/bin/sh
-
-# These variables are automatically filled in by the configure script.
-name="@PACKAGE_TARNAME@"
-version="@PACKAGE_VERSION@"
-
-show_usage()
-{
-  echo "Usage: gmock-config [OPTIONS...]"
-}
-
-show_help()
-{
-  show_usage
-  cat <<\EOF
-
-The `gmock-config' script provides access to the necessary compile and linking
-flags to connect with Google C++ Mocking Framework, both in a build prior to
-installation, and on the system proper after installation. The installation
-overrides may be issued in combination with any other queries, but will only
-affect installation queries if called on a built but not installed gmock. The
-installation queries may not be issued with any other types of queries, and
-only one installation query may be made at a time. The version queries and
-compiler flag queries may be combined as desired but not mixed. Different
-version queries are always combined with logical "and" semantics, and only the
-last of any particular query is used while all previous ones ignored. All
-versions must be specified as a sequence of numbers separated by periods.
-Compiler flag queries output the union of the sets of flags when combined.
-
- Examples:
-  gmock-config --min-version=1.0 || echo "Insufficient Google Mock version."
-
-  g++ $(gmock-config --cppflags --cxxflags) -o foo.o -c foo.cpp
-  g++ $(gmock-config --ldflags --libs) -o foo foo.o
-
-  # When using a built but not installed Google Mock:
-  g++ $(../../my_gmock_build/scripts/gmock-config ...) ...
-
-  # When using an installed Google Mock, but with installation overrides:
-  export GMOCK_PREFIX="/opt"
-  g++ $(gmock-config --libdir="/opt/lib64" ...) ...
-
- Help:
-  --usage                    brief usage information
-  --help                     display this help message
-
- Installation Overrides:
-  --prefix=<dir>             overrides the installation prefix
-  --exec-prefix=<dir>        overrides the executable installation prefix
-  --libdir=<dir>             overrides the library installation prefix
-  --includedir=<dir>         overrides the header file installation prefix
-
- Installation Queries:
-  --prefix                   installation prefix
-  --exec-prefix              executable installation prefix
-  --libdir                   library installation directory
-  --includedir               header file installation directory
-  --version                  the version of the Google Mock installation
-
- Version Queries:
-  --min-version=VERSION      return 0 if the version is at least VERSION
-  --exact-version=VERSION    return 0 if the version is exactly VERSION
-  --max-version=VERSION      return 0 if the version is at most VERSION
-
- Compilation Flag Queries:
-  --cppflags                 compile flags specific to the C-like preprocessors
-  --cxxflags                 compile flags appropriate for C++ programs
-  --ldflags                  linker flags
-  --libs                     libraries for linking
-
-EOF
-}
-
-# This function bounds our version with a min and a max. It uses some clever
-# POSIX-compliant variable expansion to portably do all the work in the shell
-# and avoid any dependency on a particular "sed" or "awk" implementation.
-# Notable is that it will only ever compare the first 3 components of versions.
-# Further components will be cleanly stripped off. All versions must be
-# unadorned, so "v1.0" will *not* work. The minimum version must be in $1, and
-# the max in $2. TODO(chandlerc@google.com): If this ever breaks, we should
-# investigate expanding this via autom4te from AS_VERSION_COMPARE rather than
-# continuing to maintain our own shell version.
-check_versions()
-{
-  major_version=${version%%.*}
-  minor_version="0"
-  point_version="0"
-  if test "${version#*.}" != "${version}"; then
-    minor_version=${version#*.}
-    minor_version=${minor_version%%.*}
-  fi
-  if test "${version#*.*.}" != "${version}"; then
-    point_version=${version#*.*.}
-    point_version=${point_version%%.*}
-  fi
-
-  min_version="$1"
-  min_major_version=${min_version%%.*}
-  min_minor_version="0"
-  min_point_version="0"
-  if test "${min_version#*.}" != "${min_version}"; then
-    min_minor_version=${min_version#*.}
-    min_minor_version=${min_minor_version%%.*}
-  fi
-  if test "${min_version#*.*.}" != "${min_version}"; then
-    min_point_version=${min_version#*.*.}
-    min_point_version=${min_point_version%%.*}
-  fi
-
-  max_version="$2"
-  max_major_version=${max_version%%.*}
-  max_minor_version="0"
-  max_point_version="0"
-  if test "${max_version#*.}" != "${max_version}"; then
-    max_minor_version=${max_version#*.}
-    max_minor_version=${max_minor_version%%.*}
-  fi
-  if test "${max_version#*.*.}" != "${max_version}"; then
-    max_point_version=${max_version#*.*.}
-    max_point_version=${max_point_version%%.*}
-  fi
-
-  test $(($major_version)) -lt $(($min_major_version)) && exit 1
-  if test $(($major_version)) -eq $(($min_major_version)); then
-    test $(($minor_version)) -lt $(($min_minor_version)) && exit 1
-    if test $(($minor_version)) -eq $(($min_minor_version)); then
-      test $(($point_version)) -lt $(($min_point_version)) && exit 1
-    fi
-  fi
-
-  test $(($major_version)) -gt $(($max_major_version)) && exit 1
-  if test $(($major_version)) -eq $(($max_major_version)); then
-    test $(($minor_version)) -gt $(($max_minor_version)) && exit 1
-    if test $(($minor_version)) -eq $(($max_minor_version)); then
-      test $(($point_version)) -gt $(($max_point_version)) && exit 1
-    fi
-  fi
-
-  exit 0
-}
-
-# Show the usage line when no arguments are specified.
-if test $# -eq 0; then
-  show_usage
-  exit 1
-fi
-
-while test $# -gt 0; do
-  case $1 in
-    --usage)          show_usage;         exit 0;;
-    --help)           show_help;          exit 0;;
-
-    # Installation overrides
-    --prefix=*)       GMOCK_PREFIX=${1#--prefix=};;
-    --exec-prefix=*)  GMOCK_EXEC_PREFIX=${1#--exec-prefix=};;
-    --libdir=*)       GMOCK_LIBDIR=${1#--libdir=};;
-    --includedir=*)   GMOCK_INCLUDEDIR=${1#--includedir=};;
-
-    # Installation queries
-    --prefix|--exec-prefix|--libdir|--includedir|--version)
-      if test -n "${do_query}"; then
-        show_usage
-        exit 1
-      fi
-      do_query=${1#--}
-      ;;
-
-    # Version checking
-    --min-version=*)
-      do_check_versions=yes
-      min_version=${1#--min-version=}
-      ;;
-    --max-version=*)
-      do_check_versions=yes
-      max_version=${1#--max-version=}
-      ;;
-    --exact-version=*)
-      do_check_versions=yes
-      exact_version=${1#--exact-version=}
-      ;;
-
-    # Compiler flag output
-    --cppflags)       echo_cppflags=yes;;
-    --cxxflags)       echo_cxxflags=yes;;
-    --ldflags)        echo_ldflags=yes;;
-    --libs)           echo_libs=yes;;
-
-    # Everything else is an error
-    *)                show_usage;         exit 1;;
-  esac
-  shift
-done
-
-# These have defaults filled in by the configure script but can also be
-# overridden by environment variables or command line parameters.
-prefix="${GMOCK_PREFIX:-@prefix@}"
-exec_prefix="${GMOCK_EXEC_PREFIX:-@exec_prefix@}"
-libdir="${GMOCK_LIBDIR:-@libdir@}"
-includedir="${GMOCK_INCLUDEDIR:-@includedir@}"
-
-# We try and detect if our binary is not located at its installed location. If
-# it's not, we provide variables pointing to the source and build tree rather
-# than to the install tree. We also locate Google Test using the configured
-# gtest-config script rather than searching the PATH and our bindir for one.
-# This allows building against a just-built gmock rather than an installed
-# gmock.
-bindir="@bindir@"
-this_relative_bindir=`dirname $0`
-this_bindir=`cd ${this_relative_bindir}; pwd -P`
-if test "${this_bindir}" = "${this_bindir%${bindir}}"; then
-  # The path to the script doesn't end in the bindir sequence from Autoconf,
-  # assume that we are in a build tree.
-  build_dir=`dirname ${this_bindir}`
-  src_dir=`cd ${this_bindir}/@top_srcdir@; pwd -P`
-
-  # TODO(chandlerc@google.com): This is a dangerous dependency on libtool, we
-  # should work to remove it, and/or remove libtool altogether, replacing it
-  # with direct references to the library and a link path.
-  gmock_libs="${build_dir}/lib/libgmock.la"
-  gmock_ldflags=""
-
-  # We provide hooks to include from either the source or build dir, where the
-  # build dir is always preferred. This will potentially allow us to write
-  # build rules for generated headers and have them automatically be preferred
-  # over provided versions.
-  gmock_cppflags="-I${build_dir}/include -I${src_dir}/include"
-  gmock_cxxflags=""
-
-  # Directly invoke the gtest-config script used during the build process.
-  gtest_config="@GTEST_CONFIG@"
-else
-  # We're using an installed gmock, although it may be staged under some
-  # prefix. Assume (as our own libraries do) that we can resolve the prefix,
-  # and are present in the dynamic link paths.
-  gmock_ldflags="-L${libdir}"
-  gmock_libs="-l${name}"
-  gmock_cppflags="-I${includedir}"
-  gmock_cxxflags=""
-
-  # We also prefer any gtest-config script installed in our prefix. Lacking
-  # one, we look in the PATH for one.
-  gtest_config="${bindir}/gtest-config"
-  if test ! -x "${gtest_config}"; then
-    gtest_config=`which gtest-config`
-  fi
-fi
-
-# Ensure that we have located a Google Test to link against.
-if ! test -x "${gtest_config}"; then
-  echo "Unable to locate Google Test, check your Google Mock configuration" \
-       "and installation" >&2
-  exit 1
-elif ! "${gtest_config}" "--exact-version=@GTEST_VERSION@"; then
-  echo "The Google Test found is not the same version as Google Mock was " \
-       "built against" >&2
-  exit 1
-fi
-
-# Add the necessary Google Test bits into the various flag variables
-gmock_cppflags="${gmock_cppflags} `${gtest_config} --cppflags`"
-gmock_cxxflags="${gmock_cxxflags} `${gtest_config} --cxxflags`"
-gmock_ldflags="${gmock_ldflags} `${gtest_config} --ldflags`"
-gmock_libs="${gmock_libs} `${gtest_config} --libs`"
-
-# Do an installation query if requested.
-if test -n "$do_query"; then
-  case $do_query in
-    prefix)           echo $prefix;       exit 0;;
-    exec-prefix)      echo $exec_prefix;  exit 0;;
-    libdir)           echo $libdir;       exit 0;;
-    includedir)       echo $includedir;   exit 0;;
-    version)          echo $version;      exit 0;;
-    *)                show_usage;         exit 1;;
-  esac
-fi
-
-# Do a version check if requested.
-if test "$do_check_versions" = "yes"; then
-  # Make sure we didn't receive a bad combination of parameters.
-  test "$echo_cppflags" = "yes" && show_usage && exit 1
-  test "$echo_cxxflags" = "yes" && show_usage && exit 1
-  test "$echo_ldflags" = "yes"  && show_usage && exit 1
-  test "$echo_libs" = "yes"     && show_usage && exit 1
-
-  if test "$exact_version" != ""; then
-    check_versions $exact_version $exact_version
-    # unreachable
-  else
-    check_versions ${min_version:-0.0.0} ${max_version:-9999.9999.9999}
-    # unreachable
-  fi
-fi
-
-# Do the output in the correct order so that these can be used in-line of
-# a compiler invocation.
-output=""
-test "$echo_cppflags" = "yes" && output="$output $gmock_cppflags"
-test "$echo_cxxflags" = "yes" && output="$output $gmock_cxxflags"
-test "$echo_ldflags" = "yes"  && output="$output $gmock_ldflags"
-test "$echo_libs" = "yes"     && output="$output $gmock_libs"
-echo $output
-
-exit 0
diff --git a/ext/googletest/googlemock/scripts/gmock_doctor.py b/ext/googletest/googlemock/scripts/gmock_doctor.py
deleted file mode 100755
index 74992bc..0000000
--- a/ext/googletest/googlemock/scripts/gmock_doctor.py
+++ /dev/null
@@ -1,640 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2008, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""Converts compiler's errors in code using Google Mock to plain English."""
-
-__author__ = 'wan@google.com (Zhanyong Wan)'
-
-import re
-import sys
-
-_VERSION = '1.0.3'
-
-_EMAIL = 'googlemock@googlegroups.com'
-
-_COMMON_GMOCK_SYMBOLS = [
-    # Matchers
-    '_',
-    'A',
-    'AddressSatisfies',
-    'AllOf',
-    'An',
-    'AnyOf',
-    'ContainerEq',
-    'Contains',
-    'ContainsRegex',
-    'DoubleEq',
-    'ElementsAre',
-    'ElementsAreArray',
-    'EndsWith',
-    'Eq',
-    'Field',
-    'FloatEq',
-    'Ge',
-    'Gt',
-    'HasSubstr',
-    'IsInitializedProto',
-    'Le',
-    'Lt',
-    'MatcherCast',
-    'Matches',
-    'MatchesRegex',
-    'NanSensitiveDoubleEq',
-    'NanSensitiveFloatEq',
-    'Ne',
-    'Not',
-    'NotNull',
-    'Pointee',
-    'Property',
-    'Ref',
-    'ResultOf',
-    'SafeMatcherCast',
-    'StartsWith',
-    'StrCaseEq',
-    'StrCaseNe',
-    'StrEq',
-    'StrNe',
-    'Truly',
-    'TypedEq',
-    'Value',
-
-    # Actions
-    'Assign',
-    'ByRef',
-    'DeleteArg',
-    'DoAll',
-    'DoDefault',
-    'IgnoreResult',
-    'Invoke',
-    'InvokeArgument',
-    'InvokeWithoutArgs',
-    'Return',
-    'ReturnNew',
-    'ReturnNull',
-    'ReturnRef',
-    'SaveArg',
-    'SetArgReferee',
-    'SetArgPointee',
-    'SetArgumentPointee',
-    'SetArrayArgument',
-    'SetErrnoAndReturn',
-    'Throw',
-    'WithArg',
-    'WithArgs',
-    'WithoutArgs',
-
-    # Cardinalities
-    'AnyNumber',
-    'AtLeast',
-    'AtMost',
-    'Between',
-    'Exactly',
-
-    # Sequences
-    'InSequence',
-    'Sequence',
-
-    # Misc
-    'DefaultValue',
-    'Mock',
-    ]
-
-# Regex for matching source file path and line number in the compiler's errors.
-_GCC_FILE_LINE_RE = r'(?P<file>.*):(?P<line>\d+):(\d+:)?\s+'
-_CLANG_FILE_LINE_RE = r'(?P<file>.*):(?P<line>\d+):(?P<column>\d+):\s+'
-_CLANG_NON_GMOCK_FILE_LINE_RE = (
-    r'(?P<file>.*[/\\^](?!gmock-)[^/\\]+):(?P<line>\d+):(?P<column>\d+):\s+')
-
-
-def _FindAllMatches(regex, s):
-  """Generates all matches of regex in string s."""
-
-  r = re.compile(regex)
-  return r.finditer(s)
-
-
-def _GenericDiagnoser(short_name, long_name, diagnoses, msg):
-  """Diagnoses the given disease by pattern matching.
-
-  Can provide different diagnoses for different patterns.
-
-  Args:
-    short_name: Short name of the disease.
-    long_name:  Long name of the disease.
-    diagnoses:  A list of pairs (regex, pattern for formatting the diagnosis
-                for matching regex).
-    msg:        Compiler's error messages.
-  Yields:
-    Tuples of the form
-      (short name of disease, long name of disease, diagnosis).
-  """
-  for regex, diagnosis in diagnoses:
-    if re.search(regex, msg):
-      diagnosis = '%(file)s:%(line)s:' + diagnosis
-      for m in _FindAllMatches(regex, msg):
-        yield (short_name, long_name, diagnosis % m.groupdict())
-
-
-def _NeedToReturnReferenceDiagnoser(msg):
-  """Diagnoses the NRR disease, given the error messages by the compiler."""
-
-  gcc_regex = (r'In member function \'testing::internal::ReturnAction<R>.*\n'
-               + _GCC_FILE_LINE_RE + r'instantiated from here\n'
-               r'.*gmock-actions\.h.*error: creating array with negative size')
-  clang_regex = (r'error:.*array.*negative.*\r?\n'
-                 r'(.*\n)*?' +
-                 _CLANG_NON_GMOCK_FILE_LINE_RE +
-                 r'note: in instantiation of function template specialization '
-                 r'\'testing::internal::ReturnAction<(?P<type>.*)>'
-                 r'::operator Action<.*>\' requested here')
-  clang11_re = (r'use_ReturnRef_instead_of_Return_to_return_a_reference.*'
-                r'(.*\n)*?' + _CLANG_NON_GMOCK_FILE_LINE_RE)
-
-  diagnosis = """
-You are using a Return() action in a function that returns a reference to
-%(type)s.  Please use ReturnRef() instead."""
-  return _GenericDiagnoser('NRR', 'Need to Return Reference',
-                           [(clang_regex, diagnosis),
-                            (clang11_re, diagnosis % {'type': 'a type'}),
-                            (gcc_regex, diagnosis % {'type': 'a type'})],
-                           msg)
-
-
-def _NeedToReturnSomethingDiagnoser(msg):
-  """Diagnoses the NRS disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE + r'(instantiated from here\n.'
-               r'*gmock.*actions\.h.*error: void value not ignored)'
-               r'|(error: control reaches end of non-void function)')
-  clang_regex1 = (_CLANG_FILE_LINE_RE +
-                  r'error: cannot initialize return object '
-                  r'of type \'Result\' \(aka \'(?P<return_type>.*)\'\) '
-                  r'with an rvalue of type \'void\'')
-  clang_regex2 = (_CLANG_FILE_LINE_RE +
-                  r'error: cannot initialize return object '
-                  r'of type \'(?P<return_type>.*)\' '
-                  r'with an rvalue of type \'void\'')
-  diagnosis = """
-You are using an action that returns void, but it needs to return
-%(return_type)s.  Please tell it *what* to return.  Perhaps you can use
-the pattern DoAll(some_action, Return(some_value))?"""
-  return _GenericDiagnoser(
-      'NRS',
-      'Need to Return Something',
-      [(gcc_regex, diagnosis % {'return_type': '*something*'}),
-       (clang_regex1, diagnosis),
-       (clang_regex2, diagnosis)],
-      msg)
-
-
-def _NeedToReturnNothingDiagnoser(msg):
-  """Diagnoses the NRN disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE + r'instantiated from here\n'
-               r'.*gmock-actions\.h.*error: instantiation of '
-               r'\'testing::internal::ReturnAction<R>::Impl<F>::value_\' '
-               r'as type \'void\'')
-  clang_regex1 = (r'error: field has incomplete type '
-                  r'\'Result\' \(aka \'void\'\)(\r)?\n'
-                  r'(.*\n)*?' +
-                  _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
-                  r'of function template specialization '
-                  r'\'testing::internal::ReturnAction<(?P<return_type>.*)>'
-                  r'::operator Action<void \(.*\)>\' requested here')
-  clang_regex2 = (r'error: field has incomplete type '
-                  r'\'Result\' \(aka \'void\'\)(\r)?\n'
-                  r'(.*\n)*?' +
-                  _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
-                  r'of function template specialization '
-                  r'\'testing::internal::DoBothAction<.*>'
-                  r'::operator Action<(?P<return_type>.*) \(.*\)>\' '
-                  r'requested here')
-  diagnosis = """
-You are using an action that returns %(return_type)s, but it needs to return
-void.  Please use a void-returning action instead.
-
-All actions but the last in DoAll(...) must return void.  Perhaps you need
-to re-arrange the order of actions in a DoAll(), if you are using one?"""
-  return _GenericDiagnoser(
-      'NRN',
-      'Need to Return Nothing',
-      [(gcc_regex, diagnosis % {'return_type': '*something*'}),
-       (clang_regex1, diagnosis),
-       (clang_regex2, diagnosis)],
-      msg)
-
-
-def _IncompleteByReferenceArgumentDiagnoser(msg):
-  """Diagnoses the IBRA disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE + r'instantiated from here\n'
-               r'.*gtest-printers\.h.*error: invalid application of '
-               r'\'sizeof\' to incomplete type \'(?P<type>.*)\'')
-
-  clang_regex = (r'.*gtest-printers\.h.*error: invalid application of '
-                 r'\'sizeof\' to an incomplete type '
-                 r'\'(?P<type>.*)( const)?\'\r?\n'
-                 r'(.*\n)*?' +
-                 _CLANG_NON_GMOCK_FILE_LINE_RE +
-                 r'note: in instantiation of member function '
-                 r'\'testing::internal2::TypeWithoutFormatter<.*>::'
-                 r'PrintValue\' requested here')
-  diagnosis = """
-In order to mock this function, Google Mock needs to see the definition
-of type "%(type)s" - declaration alone is not enough.  Either #include
-the header that defines it, or change the argument to be passed
-by pointer."""
-
-  return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
-                           [(gcc_regex, diagnosis),
-                            (clang_regex, diagnosis)],
-                           msg)
-
-
-def _OverloadedFunctionMatcherDiagnoser(msg):
-  """Diagnoses the OFM disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for '
-               r'call to \'Truly\(<unresolved overloaded function type>\)')
-  clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching function for '
-                 r'call to \'Truly')
-  diagnosis = """
-The argument you gave to Truly() is an overloaded function.  Please tell
-your compiler which overloaded version you want to use.
-
-For example, if you want to use the version whose signature is
-  bool Foo(int n);
-you should write
-  Truly(static_cast<bool (*)(int n)>(Foo))"""
-  return _GenericDiagnoser('OFM', 'Overloaded Function Matcher',
-                           [(gcc_regex, diagnosis),
-                            (clang_regex, diagnosis)],
-                           msg)
-
-
-def _OverloadedFunctionActionDiagnoser(msg):
-  """Diagnoses the OFA disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for call to '
-               r'\'Invoke\(<unresolved overloaded function type>')
-  clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching '
-                 r'function for call to \'Invoke\'\r?\n'
-                 r'(.*\n)*?'
-                 r'.*\bgmock-generated-actions\.h:\d+:\d+:\s+'
-                 r'note: candidate template ignored:\s+'
-                 r'couldn\'t infer template argument \'FunctionImpl\'')
-  diagnosis = """
-Function you are passing to Invoke is overloaded.  Please tell your compiler
-which overloaded version you want to use.
-
-For example, if you want to use the version whose signature is
-  bool MyFunction(int n, double x);
-you should write something like
-  Invoke(static_cast<bool (*)(int n, double x)>(MyFunction))"""
-  return _GenericDiagnoser('OFA', 'Overloaded Function Action',
-                           [(gcc_regex, diagnosis),
-                            (clang_regex, diagnosis)],
-                           msg)
-
-
-def _OverloadedMethodActionDiagnoser(msg):
-  """Diagnoses the OMA disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for '
-               r'call to \'Invoke\(.+, <unresolved overloaded function '
-               r'type>\)')
-  clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching function '
-                 r'for call to \'Invoke\'\r?\n'
-                 r'(.*\n)*?'
-                 r'.*\bgmock-generated-actions\.h:\d+:\d+: '
-                 r'note: candidate function template not viable: '
-                 r'requires .*, but 2 (arguments )?were provided')
-  diagnosis = """
-The second argument you gave to Invoke() is an overloaded method.  Please
-tell your compiler which overloaded version you want to use.
-
-For example, if you want to use the version whose signature is
-  class Foo {
-    ...
-    bool Bar(int n, double x);
-  };
-you should write something like
-  Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
-  return _GenericDiagnoser('OMA', 'Overloaded Method Action',
-                           [(gcc_regex, diagnosis),
-                            (clang_regex, diagnosis)],
-                           msg)
-
-
-def _MockObjectPointerDiagnoser(msg):
-  """Diagnoses the MOP disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE + r'error: request for member '
-               r'\'gmock_(?P<method>.+)\' in \'(?P<mock_object>.+)\', '
-               r'which is of non-class type \'(.*::)*(?P<class_name>.+)\*\'')
-  clang_regex = (_CLANG_FILE_LINE_RE + r'error: member reference type '
-                 r'\'(?P<class_name>.*?) *\' is a pointer; '
-                 r'(did you mean|maybe you meant) to use \'->\'\?')
-  diagnosis = """
-The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
-not a *pointer* to it.  Please write '*(%(mock_object)s)' instead of
-'%(mock_object)s' as your first argument.
-
-For example, given the mock class:
-
-  class %(class_name)s : public ... {
-    ...
-    MOCK_METHOD0(%(method)s, ...);
-  };
-
-and the following mock instance:
-
-  %(class_name)s* mock_ptr = ...
-
-you should use the EXPECT_CALL like this:
-
-  EXPECT_CALL(*mock_ptr, %(method)s(...));"""
-
-  return _GenericDiagnoser(
-      'MOP',
-      'Mock Object Pointer',
-      [(gcc_regex, diagnosis),
-       (clang_regex, diagnosis % {'mock_object': 'mock_object',
-                                  'method': 'method',
-                                  'class_name': '%(class_name)s'})],
-       msg)
-
-
-def _NeedToUseSymbolDiagnoser(msg):
-  """Diagnoses the NUS disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE + r'error: \'(?P<symbol>.+)\' '
-               r'(was not declared in this scope|has not been declared)')
-  clang_regex = (_CLANG_FILE_LINE_RE +
-                 r'error: (use of undeclared identifier|unknown type name|'
-                 r'no template named) \'(?P<symbol>[^\']+)\'')
-  diagnosis = """
-'%(symbol)s' is defined by Google Mock in the testing namespace.
-Did you forget to write
-  using testing::%(symbol)s;
-?"""
-  for m in (list(_FindAllMatches(gcc_regex, msg)) +
-            list(_FindAllMatches(clang_regex, msg))):
-    symbol = m.groupdict()['symbol']
-    if symbol in _COMMON_GMOCK_SYMBOLS:
-      yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
-
-
-def _NeedToUseReturnNullDiagnoser(msg):
-  """Diagnoses the NRNULL disease, given the error messages by the compiler."""
-
-  gcc_regex = ('instantiated from \'testing::internal::ReturnAction<R>'
-               '::operator testing::Action<Func>\(\) const.*\n' +
-               _GCC_FILE_LINE_RE + r'instantiated from here\n'
-               r'.*error: no matching function for call to \'ImplicitCast_\('
-               r'(:?long )?int&\)')
-  clang_regex = (r'\bgmock-actions.h:.* error: no matching function for '
-                 r'call to \'ImplicitCast_\'\r?\n'
-                 r'(.*\n)*?' +
-                 _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
-                 r'of function template specialization '
-                 r'\'testing::internal::ReturnAction<(int|long)>::operator '
-                 r'Action<(?P<type>.*)\(\)>\' requested here')
-  diagnosis = """
-You are probably calling Return(NULL) and the compiler isn't sure how to turn
-NULL into %(type)s. Use ReturnNull() instead.
-Note: the line number may be off; please fix all instances of Return(NULL)."""
-  return _GenericDiagnoser(
-      'NRNULL', 'Need to use ReturnNull',
-      [(clang_regex, diagnosis),
-       (gcc_regex, diagnosis % {'type': 'the right type'})],
-      msg)
-
-
-def _TypeInTemplatedBaseDiagnoser(msg):
-  """Diagnoses the TTB disease, given the error messages by the compiler."""
-
-  # This version works when the type is used as the mock function's return
-  # type.
-  gcc_4_3_1_regex_type_in_retval = (
-      r'In member function \'int .*\n' + _GCC_FILE_LINE_RE +
-      r'error: a function call cannot appear in a constant-expression')
-  gcc_4_4_0_regex_type_in_retval = (
-      r'error: a function call cannot appear in a constant-expression'
-      + _GCC_FILE_LINE_RE + r'error: template argument 1 is invalid\n')
-  # This version works when the type is used as the mock function's sole
-  # parameter type.
-  gcc_regex_type_of_sole_param = (
-      _GCC_FILE_LINE_RE +
-      r'error: \'(?P<type>.+)\' was not declared in this scope\n'
-      r'.*error: template argument 1 is invalid\n')
-  # This version works when the type is used as a parameter of a mock
-  # function that has multiple parameters.
-  gcc_regex_type_of_a_param = (
-      r'error: expected `;\' before \'::\' token\n'
-      + _GCC_FILE_LINE_RE +
-      r'error: \'(?P<type>.+)\' was not declared in this scope\n'
-      r'.*error: template argument 1 is invalid\n'
-      r'.*error: \'.+\' was not declared in this scope')
-  clang_regex_type_of_retval_or_sole_param = (
-      _CLANG_FILE_LINE_RE +
-      r'error: use of undeclared identifier \'(?P<type>.*)\'\n'
-      r'(.*\n)*?'
-      r'(?P=file):(?P=line):\d+: error: '
-      r'non-friend class member \'Result\' cannot have a qualified name'
-      )
-  clang_regex_type_of_a_param = (
-      _CLANG_FILE_LINE_RE +
-      r'error: C\+\+ requires a type specifier for all declarations\n'
-      r'(.*\n)*?'
-      r'(?P=file):(?P=line):(?P=column): error: '
-      r'C\+\+ requires a type specifier for all declarations'
-      )
-  clang_regex_unknown_type = (
-      _CLANG_FILE_LINE_RE +
-      r'error: unknown type name \'(?P<type>[^\']+)\''
-      )
-
-  diagnosis = """
-In a mock class template, types or typedefs defined in the base class
-template are *not* automatically visible.  This is how C++ works.  Before
-you can use a type or typedef named %(type)s defined in base class Base<T>, you
-need to make it visible.  One way to do it is:
-
-  typedef typename Base<T>::%(type)s %(type)s;"""
-
-  for diag in _GenericDiagnoser(
-      'TTB', 'Type in Template Base',
-      [(gcc_4_3_1_regex_type_in_retval, diagnosis % {'type': 'Foo'}),
-       (gcc_4_4_0_regex_type_in_retval, diagnosis % {'type': 'Foo'}),
-       (gcc_regex_type_of_sole_param, diagnosis),
-       (gcc_regex_type_of_a_param, diagnosis),
-       (clang_regex_type_of_retval_or_sole_param, diagnosis),
-       (clang_regex_type_of_a_param, diagnosis % {'type': 'Foo'})],
-      msg):
-    yield diag
-  # Avoid overlap with the NUS pattern.
-  for m in _FindAllMatches(clang_regex_unknown_type, msg):
-    type_ = m.groupdict()['type']
-    if type_ not in _COMMON_GMOCK_SYMBOLS:
-      yield ('TTB', 'Type in Template Base', diagnosis % m.groupdict())
-
-
-def _WrongMockMethodMacroDiagnoser(msg):
-  """Diagnoses the WMM disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE +
-               r'.*this_method_does_not_take_(?P<wrong_args>\d+)_argument.*\n'
-               r'.*\n'
-               r'.*candidates are.*FunctionMocker<[^>]+A(?P<args>\d+)\)>')
-  clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE +
-                 r'error:.*array.*negative.*r?\n'
-                 r'(.*\n)*?'
-                 r'(?P=file):(?P=line):(?P=column): error: too few arguments '
-                 r'to function call, expected (?P<args>\d+), '
-                 r'have (?P<wrong_args>\d+)')
-  clang11_re = (_CLANG_NON_GMOCK_FILE_LINE_RE +
-                r'.*this_method_does_not_take_'
-                r'(?P<wrong_args>\d+)_argument.*')
-  diagnosis = """
-You are using MOCK_METHOD%(wrong_args)s to define a mock method that has
-%(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s,
-MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead."""
-  return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn Macro',
-                           [(gcc_regex, diagnosis),
-                            (clang11_re, diagnosis % {'wrong_args': 'm',
-                                                      'args': 'n'}),
-                            (clang_regex, diagnosis)],
-                           msg)
-
-
-def _WrongParenPositionDiagnoser(msg):
-  """Diagnoses the WPP disease, given the error messages by the compiler."""
-
-  gcc_regex = (_GCC_FILE_LINE_RE +
-               r'error:.*testing::internal::MockSpec<.* has no member named \''
-               r'(?P<method>\w+)\'')
-  clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE +
-                 r'error: no member named \'(?P<method>\w+)\' in '
-                 r'\'testing::internal::MockSpec<.*>\'')
-  diagnosis = """
-The closing parenthesis of ON_CALL or EXPECT_CALL should be *before*
-".%(method)s".  For example, you should write:
-  EXPECT_CALL(my_mock, Foo(_)).%(method)s(...);
-instead of:
-  EXPECT_CALL(my_mock, Foo(_).%(method)s(...));"""
-  return _GenericDiagnoser('WPP', 'Wrong Parenthesis Position',
-                           [(gcc_regex, diagnosis),
-                            (clang_regex, diagnosis)],
-                           msg)
-
-
-_DIAGNOSERS = [
-    _IncompleteByReferenceArgumentDiagnoser,
-    _MockObjectPointerDiagnoser,
-    _NeedToReturnNothingDiagnoser,
-    _NeedToReturnReferenceDiagnoser,
-    _NeedToReturnSomethingDiagnoser,
-    _NeedToUseReturnNullDiagnoser,
-    _NeedToUseSymbolDiagnoser,
-    _OverloadedFunctionActionDiagnoser,
-    _OverloadedFunctionMatcherDiagnoser,
-    _OverloadedMethodActionDiagnoser,
-    _TypeInTemplatedBaseDiagnoser,
-    _WrongMockMethodMacroDiagnoser,
-    _WrongParenPositionDiagnoser,
-    ]
-
-
-def Diagnose(msg):
-  """Generates all possible diagnoses given the compiler error message."""
-
-  msg = re.sub(r'\x1b\[[^m]*m', '', msg)  # Strips all color formatting.
-  # Assuming the string is using the UTF-8 encoding, replaces the left and
-  # the right single quote characters with apostrophes.
-  msg = re.sub(r'(\xe2\x80\x98|\xe2\x80\x99)', "'", msg)
-
-  diagnoses = []
-  for diagnoser in _DIAGNOSERS:
-    for diag in diagnoser(msg):
-      diagnosis = '[%s - %s]\n%s' % diag
-      if not diagnosis in diagnoses:
-        diagnoses.append(diagnosis)
-  return diagnoses
-
-
-def main():
-  print ('Google Mock Doctor v%s - '
-         'diagnoses problems in code using Google Mock.' % _VERSION)
-
-  if sys.stdin.isatty():
-    print ('Please copy and paste the compiler errors here.  Press c-D when '
-           'you are done:')
-  else:
-    print ('Waiting for compiler errors on stdin . . .')
-
-  msg = sys.stdin.read().strip()
-  diagnoses = Diagnose(msg)
-  count = len(diagnoses)
-  if not count:
-    print ("""
-Your compiler complained:
-8<------------------------------------------------------------
-%s
------------------------------------------------------------->8
-
-Uh-oh, I'm not smart enough to figure out what the problem is. :-(
-However...
-If you send your source code and the compiler's error messages to
-%s, you can be helped and I can get smarter --
-win-win for us!""" % (msg, _EMAIL))
-  else:
-    print ('------------------------------------------------------------')
-    print ('Your code appears to have the following',)
-    if count > 1:
-      print ('%s diseases:' % (count,))
-    else:
-      print ('disease:')
-    i = 0
-    for d in diagnoses:
-      i += 1
-      if count > 1:
-        print ('\n#%s:' % (i,))
-      print (d)
-    print ("""
-How did I do?  If you think I'm wrong or unhelpful, please send your
-source code and the compiler's error messages to %s.
-Then you can be helped and I can get smarter -- I promise I won't be upset!""" %
-           _EMAIL)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/ext/googletest/googlemock/scripts/upload.py b/ext/googletest/googlemock/scripts/upload.py
deleted file mode 100755
index 95239dc..0000000
--- a/ext/googletest/googlemock/scripts/upload.py
+++ /dev/null
@@ -1,1387 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2007 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Tool for uploading diffs from a version control system to the codereview app.
-
-Usage summary: upload.py [options] [-- diff_options]
-
-Diff options are passed to the diff command of the underlying system.
-
-Supported version control systems:
-  Git
-  Mercurial
-  Subversion
-
-It is important for Git/Mercurial users to specify a tree/node/branch to diff
-against by using the '--rev' option.
-"""
-# This code is derived from appcfg.py in the App Engine SDK (open source),
-# and from ASPN recipe #146306.
-
-import cookielib
-import getpass
-import logging
-import md5
-import mimetypes
-import optparse
-import os
-import re
-import socket
-import subprocess
-import sys
-import urllib
-import urllib2
-import urlparse
-
-try:
-  import readline
-except ImportError:
-  pass
-
-# The logging verbosity:
-#  0: Errors only.
-#  1: Status messages.
-#  2: Info logs.
-#  3: Debug logs.
-verbosity = 1
-
-# Max size of patch or base file.
-MAX_UPLOAD_SIZE = 900 * 1024
-
-
-def GetEmail(prompt):
-  """Prompts the user for their email address and returns it.
-
-  The last used email address is saved to a file and offered up as a suggestion
-  to the user. If the user presses enter without typing in anything the last
-  used email address is used. If the user enters a new address, it is saved
-  for next time we prompt.
-
-  """
-  last_email_file_name = os.path.expanduser("~/.last_codereview_email_address")
-  last_email = ""
-  if os.path.exists(last_email_file_name):
-    try:
-      last_email_file = open(last_email_file_name, "r")
-      last_email = last_email_file.readline().strip("\n")
-      last_email_file.close()
-      prompt += " [%s]" % last_email
-    except IOError, e:
-      pass
-  email = raw_input(prompt + ": ").strip()
-  if email:
-    try:
-      last_email_file = open(last_email_file_name, "w")
-      last_email_file.write(email)
-      last_email_file.close()
-    except IOError, e:
-      pass
-  else:
-    email = last_email
-  return email
-
-
-def StatusUpdate(msg):
-  """Print a status message to stdout.
-
-  If 'verbosity' is greater than 0, print the message.
-
-  Args:
-    msg: The string to print.
-  """
-  if verbosity > 0:
-    print msg
-
-
-def ErrorExit(msg):
-  """Print an error message to stderr and exit."""
-  print >>sys.stderr, msg
-  sys.exit(1)
-
-
-class ClientLoginError(urllib2.HTTPError):
-  """Raised to indicate there was an error authenticating with ClientLogin."""
-
-  def __init__(self, url, code, msg, headers, args):
-    urllib2.HTTPError.__init__(self, url, code, msg, headers, None)
-    self.args = args
-    self.reason = args["Error"]
-
-
-class AbstractRpcServer(object):
-  """Provides a common interface for a simple RPC server."""
-
-  def __init__(self, host, auth_function, host_override=None, extra_headers={},
-               save_cookies=False):
-    """Creates a new HttpRpcServer.
-
-    Args:
-      host: The host to send requests to.
-      auth_function: A function that takes no arguments and returns an
-        (email, password) tuple when called. Will be called if authentication
-        is required.
-      host_override: The host header to send to the server (defaults to host).
-      extra_headers: A dict of extra headers to append to every request.
-      save_cookies: If True, save the authentication cookies to local disk.
-        If False, use an in-memory cookiejar instead.  Subclasses must
-        implement this functionality.  Defaults to False.
-    """
-    self.host = host
-    self.host_override = host_override
-    self.auth_function = auth_function
-    self.authenticated = False
-    self.extra_headers = extra_headers
-    self.save_cookies = save_cookies
-    self.opener = self._GetOpener()
-    if self.host_override:
-      logging.info("Server: %s; Host: %s", self.host, self.host_override)
-    else:
-      logging.info("Server: %s", self.host)
-
-  def _GetOpener(self):
-    """Returns an OpenerDirector for making HTTP requests.
-
-    Returns:
-      A urllib2.OpenerDirector object.
-    """
-    raise NotImplementedError()
-
-  def _CreateRequest(self, url, data=None):
-    """Creates a new urllib request."""
-    logging.debug("Creating request for: '%s' with payload:\n%s", url, data)
-    req = urllib2.Request(url, data=data)
-    if self.host_override:
-      req.add_header("Host", self.host_override)
-    for key, value in self.extra_headers.iteritems():
-      req.add_header(key, value)
-    return req
-
-  def _GetAuthToken(self, email, password):
-    """Uses ClientLogin to authenticate the user, returning an auth token.
-
-    Args:
-      email:    The user's email address
-      password: The user's password
-
-    Raises:
-      ClientLoginError: If there was an error authenticating with ClientLogin.
-      HTTPError: If there was some other form of HTTP error.
-
-    Returns:
-      The authentication token returned by ClientLogin.
-    """
-    account_type = "GOOGLE"
-    if self.host.endswith(".google.com"):
-      # Needed for use inside Google.
-      account_type = "HOSTED"
-    req = self._CreateRequest(
-        url="https://www.google.com/accounts/ClientLogin",
-        data=urllib.urlencode({
-            "Email": email,
-            "Passwd": password,
-            "service": "ah",
-            "source": "rietveld-codereview-upload",
-            "accountType": account_type,
-        }),
-    )
-    try:
-      response = self.opener.open(req)
-      response_body = response.read()
-      response_dict = dict(x.split("=")
-                           for x in response_body.split("\n") if x)
-      return response_dict["Auth"]
-    except urllib2.HTTPError, e:
-      if e.code == 403:
-        body = e.read()
-        response_dict = dict(x.split("=", 1) for x in body.split("\n") if x)
-        raise ClientLoginError(req.get_full_url(), e.code, e.msg,
-                               e.headers, response_dict)
-      else:
-        raise
-
-  def _GetAuthCookie(self, auth_token):
-    """Fetches authentication cookies for an authentication token.
-
-    Args:
-      auth_token: The authentication token returned by ClientLogin.
-
-    Raises:
-      HTTPError: If there was an error fetching the authentication cookies.
-    """
-    # This is a dummy value to allow us to identify when we're successful.
-    continue_location = "http://localhost/"
-    args = {"continue": continue_location, "auth": auth_token}
-    req = self._CreateRequest("http://%s/_ah/login?%s" %
-                              (self.host, urllib.urlencode(args)))
-    try:
-      response = self.opener.open(req)
-    except urllib2.HTTPError, e:
-      response = e
-    if (response.code != 302 or
-        response.info()["location"] != continue_location):
-      raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg,
-                              response.headers, response.fp)
-    self.authenticated = True
-
-  def _Authenticate(self):
-    """Authenticates the user.
-
-    The authentication process works as follows:
-     1) We get a username and password from the user
-     2) We use ClientLogin to obtain an AUTH token for the user
-        (see https://developers.google.com/identity/protocols/AuthForInstalledApps).
-     3) We pass the auth token to /_ah/login on the server to obtain an
-        authentication cookie. If login was successful, it tries to redirect
-        us to the URL we provided.
-
-    If we attempt to access the upload API without first obtaining an
-    authentication cookie, it returns a 401 response and directs us to
-    authenticate ourselves with ClientLogin.
-    """
-    for i in range(3):
-      credentials = self.auth_function()
-      try:
-        auth_token = self._GetAuthToken(credentials[0], credentials[1])
-      except ClientLoginError, e:
-        if e.reason == "BadAuthentication":
-          print >>sys.stderr, "Invalid username or password."
-          continue
-        if e.reason == "CaptchaRequired":
-          print >>sys.stderr, (
-              "Please go to\n"
-              "https://www.google.com/accounts/DisplayUnlockCaptcha\n"
-              "and verify you are a human.  Then try again.")
-          break
-        if e.reason == "NotVerified":
-          print >>sys.stderr, "Account not verified."
-          break
-        if e.reason == "TermsNotAgreed":
-          print >>sys.stderr, "User has not agreed to TOS."
-          break
-        if e.reason == "AccountDeleted":
-          print >>sys.stderr, "The user account has been deleted."
-          break
-        if e.reason == "AccountDisabled":
-          print >>sys.stderr, "The user account has been disabled."
-          break
-        if e.reason == "ServiceDisabled":
-          print >>sys.stderr, ("The user's access to the service has been "
-                               "disabled.")
-          break
-        if e.reason == "ServiceUnavailable":
-          print >>sys.stderr, "The service is not available; try again later."
-          break
-        raise
-      self._GetAuthCookie(auth_token)
-      return
-
-  def Send(self, request_path, payload=None,
-           content_type="application/octet-stream",
-           timeout=None,
-           **kwargs):
-    """Sends an RPC and returns the response.
-
-    Args:
-      request_path: The path to send the request to, eg /api/appversion/create.
-      payload: The body of the request, or None to send an empty request.
-      content_type: The Content-Type header to use.
-      timeout: timeout in seconds; default None i.e. no timeout.
-        (Note: for large requests on OS X, the timeout doesn't work right.)
-      kwargs: Any keyword arguments are converted into query string parameters.
-
-    Returns:
-      The response body, as a string.
-    """
-    # TODO: Don't require authentication.  Let the server say
-    # whether it is necessary.
-    if not self.authenticated:
-      self._Authenticate()
-
-    old_timeout = socket.getdefaulttimeout()
-    socket.setdefaulttimeout(timeout)
-    try:
-      tries = 0
-      while True:
-        tries += 1
-        args = dict(kwargs)
-        url = "http://%s%s" % (self.host, request_path)
-        if args:
-          url += "?" + urllib.urlencode(args)
-        req = self._CreateRequest(url=url, data=payload)
-        req.add_header("Content-Type", content_type)
-        try:
-          f = self.opener.open(req)
-          response = f.read()
-          f.close()
-          return response
-        except urllib2.HTTPError, e:
-          if tries > 3:
-            raise
-          elif e.code == 401:
-            self._Authenticate()
-##           elif e.code >= 500 and e.code < 600:
-##             # Server Error - try again.
-##             continue
-          else:
-            raise
-    finally:
-      socket.setdefaulttimeout(old_timeout)
-
-
-class HttpRpcServer(AbstractRpcServer):
-  """Provides a simplified RPC-style interface for HTTP requests."""
-
-  def _Authenticate(self):
-    """Save the cookie jar after authentication."""
-    super(HttpRpcServer, self)._Authenticate()
-    if self.save_cookies:
-      StatusUpdate("Saving authentication cookies to %s" % self.cookie_file)
-      self.cookie_jar.save()
-
-  def _GetOpener(self):
-    """Returns an OpenerDirector that supports cookies and ignores redirects.
-
-    Returns:
-      A urllib2.OpenerDirector object.
-    """
-    opener = urllib2.OpenerDirector()
-    opener.add_handler(urllib2.ProxyHandler())
-    opener.add_handler(urllib2.UnknownHandler())
-    opener.add_handler(urllib2.HTTPHandler())
-    opener.add_handler(urllib2.HTTPDefaultErrorHandler())
-    opener.add_handler(urllib2.HTTPSHandler())
-    opener.add_handler(urllib2.HTTPErrorProcessor())
-    if self.save_cookies:
-      self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies")
-      self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file)
-      if os.path.exists(self.cookie_file):
-        try:
-          self.cookie_jar.load()
-          self.authenticated = True
-          StatusUpdate("Loaded authentication cookies from %s" %
-                       self.cookie_file)
-        except (cookielib.LoadError, IOError):
-          # Failed to load cookies - just ignore them.
-          pass
-      else:
-        # Create an empty cookie file with mode 600
-        fd = os.open(self.cookie_file, os.O_CREAT, 0600)
-        os.close(fd)
-      # Always chmod the cookie file
-      os.chmod(self.cookie_file, 0600)
-    else:
-      # Don't save cookies across runs of update.py.
-      self.cookie_jar = cookielib.CookieJar()
-    opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar))
-    return opener
-
-
-parser = optparse.OptionParser(usage="%prog [options] [-- diff_options]")
-parser.add_option("-y", "--assume_yes", action="store_true",
-                  dest="assume_yes", default=False,
-                  help="Assume that the answer to yes/no questions is 'yes'.")
-# Logging
-group = parser.add_option_group("Logging options")
-group.add_option("-q", "--quiet", action="store_const", const=0,
-                 dest="verbose", help="Print errors only.")
-group.add_option("-v", "--verbose", action="store_const", const=2,
-                 dest="verbose", default=1,
-                 help="Print info level logs (default).")
-group.add_option("--noisy", action="store_const", const=3,
-                 dest="verbose", help="Print all logs.")
-# Review server
-group = parser.add_option_group("Review server options")
-group.add_option("-s", "--server", action="store", dest="server",
-                 default="codereview.appspot.com",
-                 metavar="SERVER",
-                 help=("The server to upload to. The format is host[:port]. "
-                       "Defaults to 'codereview.appspot.com'."))
-group.add_option("-e", "--email", action="store", dest="email",
-                 metavar="EMAIL", default=None,
-                 help="The username to use. Will prompt if omitted.")
-group.add_option("-H", "--host", action="store", dest="host",
-                 metavar="HOST", default=None,
-                 help="Overrides the Host header sent with all RPCs.")
-group.add_option("--no_cookies", action="store_false",
-                 dest="save_cookies", default=True,
-                 help="Do not save authentication cookies to local disk.")
-# Issue
-group = parser.add_option_group("Issue options")
-group.add_option("-d", "--description", action="store", dest="description",
-                 metavar="DESCRIPTION", default=None,
-                 help="Optional description when creating an issue.")
-group.add_option("-f", "--description_file", action="store",
-                 dest="description_file", metavar="DESCRIPTION_FILE",
-                 default=None,
-                 help="Optional path of a file that contains "
-                      "the description when creating an issue.")
-group.add_option("-r", "--reviewers", action="store", dest="reviewers",
-                 metavar="REVIEWERS", default=None,
-                 help="Add reviewers (comma separated email addresses).")
-group.add_option("--cc", action="store", dest="cc",
-                 metavar="CC", default=None,
-                 help="Add CC (comma separated email addresses).")
-# Upload options
-group = parser.add_option_group("Patch options")
-group.add_option("-m", "--message", action="store", dest="message",
-                 metavar="MESSAGE", default=None,
-                 help="A message to identify the patch. "
-                      "Will prompt if omitted.")
-group.add_option("-i", "--issue", type="int", action="store",
-                 metavar="ISSUE", default=None,
-                 help="Issue number to which to add. Defaults to new issue.")
-group.add_option("--download_base", action="store_true",
-                 dest="download_base", default=False,
-                 help="Base files will be downloaded by the server "
-                 "(side-by-side diffs may not work on files with CRs).")
-group.add_option("--rev", action="store", dest="revision",
-                 metavar="REV", default=None,
-                 help="Branch/tree/revision to diff against (used by DVCS).")
-group.add_option("--send_mail", action="store_true",
-                 dest="send_mail", default=False,
-                 help="Send notification email to reviewers.")
-
-
-def GetRpcServer(options):
-  """Returns an instance of an AbstractRpcServer.
-
-  Returns:
-    A new AbstractRpcServer, on which RPC calls can be made.
-  """
-
-  rpc_server_class = HttpRpcServer
-
-  def GetUserCredentials():
-    """Prompts the user for a username and password."""
-    email = options.email
-    if email is None:
-      email = GetEmail("Email (login for uploading to %s)" % options.server)
-    password = getpass.getpass("Password for %s: " % email)
-    return (email, password)
-
-  # If this is the dev_appserver, use fake authentication.
-  host = (options.host or options.server).lower()
-  if host == "localhost" or host.startswith("localhost:"):
-    email = options.email
-    if email is None:
-      email = "test@example.com"
-      logging.info("Using debug user %s.  Override with --email" % email)
-    server = rpc_server_class(
-        options.server,
-        lambda: (email, "password"),
-        host_override=options.host,
-        extra_headers={"Cookie":
-                       'dev_appserver_login="%s:False"' % email},
-        save_cookies=options.save_cookies)
-    # Don't try to talk to ClientLogin.
-    server.authenticated = True
-    return server
-
-  return rpc_server_class(options.server, GetUserCredentials,
-                          host_override=options.host,
-                          save_cookies=options.save_cookies)
-
-
-def EncodeMultipartFormData(fields, files):
-  """Encode form fields for multipart/form-data.
-
-  Args:
-    fields: A sequence of (name, value) elements for regular form fields.
-    files: A sequence of (name, filename, value) elements for data to be
-           uploaded as files.
-  Returns:
-    (content_type, body) ready for httplib.HTTP instance.
-
-  Source:
-    https://web.archive.org/web/20160116052001/code.activestate.com/recipes/146306
-  """
-  BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-'
-  CRLF = '\r\n'
-  lines = []
-  for (key, value) in fields:
-    lines.append('--' + BOUNDARY)
-    lines.append('Content-Disposition: form-data; name="%s"' % key)
-    lines.append('')
-    lines.append(value)
-  for (key, filename, value) in files:
-    lines.append('--' + BOUNDARY)
-    lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' %
-             (key, filename))
-    lines.append('Content-Type: %s' % GetContentType(filename))
-    lines.append('')
-    lines.append(value)
-  lines.append('--' + BOUNDARY + '--')
-  lines.append('')
-  body = CRLF.join(lines)
-  content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
-  return content_type, body
-
-
-def GetContentType(filename):
-  """Helper to guess the content-type from the filename."""
-  return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
-
-
-# Use a shell for subcommands on Windows to get a PATH search.
-use_shell = sys.platform.startswith("win")
-
-def RunShellWithReturnCode(command, print_output=False,
-                           universal_newlines=True):
-  """Executes a command and returns the output from stdout and the return code.
-
-  Args:
-    command: Command to execute.
-    print_output: If True, the output is printed to stdout.
-                  If False, both stdout and stderr are ignored.
-    universal_newlines: Use universal_newlines flag (default: True).
-
-  Returns:
-    Tuple (output, return code)
-  """
-  logging.info("Running %s", command)
-  p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                       shell=use_shell, universal_newlines=universal_newlines)
-  if print_output:
-    output_array = []
-    while True:
-      line = p.stdout.readline()
-      if not line:
-        break
-      print line.strip("\n")
-      output_array.append(line)
-    output = "".join(output_array)
-  else:
-    output = p.stdout.read()
-  p.wait()
-  errout = p.stderr.read()
-  if print_output and errout:
-    print >>sys.stderr, errout
-  p.stdout.close()
-  p.stderr.close()
-  return output, p.returncode
-
-
-def RunShell(command, silent_ok=False, universal_newlines=True,
-             print_output=False):
-  data, retcode = RunShellWithReturnCode(command, print_output,
-                                         universal_newlines)
-  if retcode:
-    ErrorExit("Got error status from %s:\n%s" % (command, data))
-  if not silent_ok and not data:
-    ErrorExit("No output from %s" % command)
-  return data
-
-
-class VersionControlSystem(object):
-  """Abstract base class providing an interface to the VCS."""
-
-  def __init__(self, options):
-    """Constructor.
-
-    Args:
-      options: Command line options.
-    """
-    self.options = options
-
-  def GenerateDiff(self, args):
-    """Return the current diff as a string.
-
-    Args:
-      args: Extra arguments to pass to the diff command.
-    """
-    raise NotImplementedError(
-        "abstract method -- subclass %s must override" % self.__class__)
-
-  def GetUnknownFiles(self):
-    """Return a list of files unknown to the VCS."""
-    raise NotImplementedError(
-        "abstract method -- subclass %s must override" % self.__class__)
-
-  def CheckForUnknownFiles(self):
-    """Show an "are you sure?" prompt if there are unknown files."""
-    unknown_files = self.GetUnknownFiles()
-    if unknown_files:
-      print "The following files are not added to version control:"
-      for line in unknown_files:
-        print line
-      prompt = "Are you sure to continue?(y/N) "
-      answer = raw_input(prompt).strip()
-      if answer != "y":
-        ErrorExit("User aborted")
-
-  def GetBaseFile(self, filename):
-    """Get the content of the upstream version of a file.
-
-    Returns:
-      A tuple (base_content, new_content, is_binary, status)
-        base_content: The contents of the base file.
-        new_content: For text files, this is empty.  For binary files, this is
-          the contents of the new file, since the diff output won't contain
-          information to reconstruct the current file.
-        is_binary: True iff the file is binary.
-        status: The status of the file.
-    """
-
-    raise NotImplementedError(
-        "abstract method -- subclass %s must override" % self.__class__)
-
-
-  def GetBaseFiles(self, diff):
-    """Helper that calls GetBase file for each file in the patch.
-
-    Returns:
-      A dictionary that maps from filename to GetBaseFile's tuple.  Filenames
-      are retrieved based on lines that start with "Index:" or
-      "Property changes on:".
-    """
-    files = {}
-    for line in diff.splitlines(True):
-      if line.startswith('Index:') or line.startswith('Property changes on:'):
-        unused, filename = line.split(':', 1)
-        # On Windows if a file has property changes its filename uses '\'
-        # instead of '/'.
-        filename = filename.strip().replace('\\', '/')
-        files[filename] = self.GetBaseFile(filename)
-    return files
-
-
-  def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options,
-                      files):
-    """Uploads the base files (and if necessary, the current ones as well)."""
-
-    def UploadFile(filename, file_id, content, is_binary, status, is_base):
-      """Uploads a file to the server."""
-      file_too_large = False
-      if is_base:
-        type = "base"
-      else:
-        type = "current"
-      if len(content) > MAX_UPLOAD_SIZE:
-        print ("Not uploading the %s file for %s because it's too large." %
-               (type, filename))
-        file_too_large = True
-        content = ""
-      checksum = md5.new(content).hexdigest()
-      if options.verbose > 0 and not file_too_large:
-        print "Uploading %s file for %s" % (type, filename)
-      url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id)
-      form_fields = [("filename", filename),
-                     ("status", status),
-                     ("checksum", checksum),
-                     ("is_binary", str(is_binary)),
-                     ("is_current", str(not is_base)),
-                    ]
-      if file_too_large:
-        form_fields.append(("file_too_large", "1"))
-      if options.email:
-        form_fields.append(("user", options.email))
-      ctype, body = EncodeMultipartFormData(form_fields,
-                                            [("data", filename, content)])
-      response_body = rpc_server.Send(url, body,
-                                      content_type=ctype)
-      if not response_body.startswith("OK"):
-        StatusUpdate("  --> %s" % response_body)
-        sys.exit(1)
-
-    patches = dict()
-    [patches.setdefault(v, k) for k, v in patch_list]
-    for filename in patches.keys():
-      base_content, new_content, is_binary, status = files[filename]
-      file_id_str = patches.get(filename)
-      if file_id_str.find("nobase") != -1:
-        base_content = None
-        file_id_str = file_id_str[file_id_str.rfind("_") + 1:]
-      file_id = int(file_id_str)
-      if base_content != None:
-        UploadFile(filename, file_id, base_content, is_binary, status, True)
-      if new_content != None:
-        UploadFile(filename, file_id, new_content, is_binary, status, False)
-
-  def IsImage(self, filename):
-    """Returns true if the filename has an image extension."""
-    mimetype =  mimetypes.guess_type(filename)[0]
-    if not mimetype:
-      return False
-    return mimetype.startswith("image/")
-
-
-class SubversionVCS(VersionControlSystem):
-  """Implementation of the VersionControlSystem interface for Subversion."""
-
-  def __init__(self, options):
-    super(SubversionVCS, self).__init__(options)
-    if self.options.revision:
-      match = re.match(r"(\d+)(:(\d+))?", self.options.revision)
-      if not match:
-        ErrorExit("Invalid Subversion revision %s." % self.options.revision)
-      self.rev_start = match.group(1)
-      self.rev_end = match.group(3)
-    else:
-      self.rev_start = self.rev_end = None
-    # Cache output from "svn list -r REVNO dirname".
-    # Keys: dirname, Values: 2-tuple (ouput for start rev and end rev).
-    self.svnls_cache = {}
-    # SVN base URL is required to fetch files deleted in an older revision.
-    # Result is cached to not guess it over and over again in GetBaseFile().
-    required = self.options.download_base or self.options.revision is not None
-    self.svn_base = self._GuessBase(required)
-
-  def GuessBase(self, required):
-    """Wrapper for _GuessBase."""
-    return self.svn_base
-
-  def _GuessBase(self, required):
-    """Returns the SVN base URL.
-
-    Args:
-      required: If true, exits if the url can't be guessed, otherwise None is
-        returned.
-    """
-    info = RunShell(["svn", "info"])
-    for line in info.splitlines():
-      words = line.split()
-      if len(words) == 2 and words[0] == "URL:":
-        url = words[1]
-        scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
-        username, netloc = urllib.splituser(netloc)
-        if username:
-          logging.info("Removed username from base URL")
-        if netloc.endswith("svn.python.org"):
-          if netloc == "svn.python.org":
-            if path.startswith("/projects/"):
-              path = path[9:]
-          elif netloc != "pythondev@svn.python.org":
-            ErrorExit("Unrecognized Python URL: %s" % url)
-          base = "http://svn.python.org/view/*checkout*%s/" % path
-          logging.info("Guessed Python base = %s", base)
-        elif netloc.endswith("svn.collab.net"):
-          if path.startswith("/repos/"):
-            path = path[6:]
-          base = "http://svn.collab.net/viewvc/*checkout*%s/" % path
-          logging.info("Guessed CollabNet base = %s", base)
-        elif netloc.endswith(".googlecode.com"):
-          path = path + "/"
-          base = urlparse.urlunparse(("http", netloc, path, params,
-                                      query, fragment))
-          logging.info("Guessed Google Code base = %s", base)
-        else:
-          path = path + "/"
-          base = urlparse.urlunparse((scheme, netloc, path, params,
-                                      query, fragment))
-          logging.info("Guessed base = %s", base)
-        return base
-    if required:
-      ErrorExit("Can't find URL in output from svn info")
-    return None
-
-  def GenerateDiff(self, args):
-    cmd = ["svn", "diff"]
-    if self.options.revision:
-      cmd += ["-r", self.options.revision]
-    cmd.extend(args)
-    data = RunShell(cmd)
-    count = 0
-    for line in data.splitlines():
-      if line.startswith("Index:") or line.startswith("Property changes on:"):
-        count += 1
-        logging.info(line)
-    if not count:
-      ErrorExit("No valid patches found in output from svn diff")
-    return data
-
-  def _CollapseKeywords(self, content, keyword_str):
-    """Collapses SVN keywords."""
-    # svn cat translates keywords but svn diff doesn't. As a result of this
-    # behavior patching.PatchChunks() fails with a chunk mismatch error.
-    # This part was originally written by the Review Board development team
-    # who had the same problem (https://reviews.reviewboard.org/r/276/).
-    # Mapping of keywords to known aliases
-    svn_keywords = {
-      # Standard keywords
-      'Date':                ['Date', 'LastChangedDate'],
-      'Revision':            ['Revision', 'LastChangedRevision', 'Rev'],
-      'Author':              ['Author', 'LastChangedBy'],
-      'HeadURL':             ['HeadURL', 'URL'],
-      'Id':                  ['Id'],
-
-      # Aliases
-      'LastChangedDate':     ['LastChangedDate', 'Date'],
-      'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'],
-      'LastChangedBy':       ['LastChangedBy', 'Author'],
-      'URL':                 ['URL', 'HeadURL'],
-    }
-
-    def repl(m):
-       if m.group(2):
-         return "$%s::%s$" % (m.group(1), " " * len(m.group(3)))
-       return "$%s$" % m.group(1)
-    keywords = [keyword
-                for name in keyword_str.split(" ")
-                for keyword in svn_keywords.get(name, [])]
-    return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content)
-
-  def GetUnknownFiles(self):
-    status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True)
-    unknown_files = []
-    for line in status.split("\n"):
-      if line and line[0] == "?":
-        unknown_files.append(line)
-    return unknown_files
-
-  def ReadFile(self, filename):
-    """Returns the contents of a file."""
-    file = open(filename, 'rb')
-    result = ""
-    try:
-      result = file.read()
-    finally:
-      file.close()
-    return result
-
-  def GetStatus(self, filename):
-    """Returns the status of a file."""
-    if not self.options.revision:
-      status = RunShell(["svn", "status", "--ignore-externals", filename])
-      if not status:
-        ErrorExit("svn status returned no output for %s" % filename)
-      status_lines = status.splitlines()
-      # If file is in a cl, the output will begin with
-      # "\n--- Changelist 'cl_name':\n".  See
-      # https://web.archive.org/web/20090918234815/svn.collab.net/repos/svn/trunk/notes/changelist-design.txt
-      if (len(status_lines) == 3 and
-          not status_lines[0] and
-          status_lines[1].startswith("--- Changelist")):
-        status = status_lines[2]
-      else:
-        status = status_lines[0]
-    # If we have a revision to diff against we need to run "svn list"
-    # for the old and the new revision and compare the results to get
-    # the correct status for a file.
-    else:
-      dirname, relfilename = os.path.split(filename)
-      if dirname not in self.svnls_cache:
-        cmd = ["svn", "list", "-r", self.rev_start, dirname or "."]
-        out, returncode = RunShellWithReturnCode(cmd)
-        if returncode:
-          ErrorExit("Failed to get status for %s." % filename)
-        old_files = out.splitlines()
-        args = ["svn", "list"]
-        if self.rev_end:
-          args += ["-r", self.rev_end]
-        cmd = args + [dirname or "."]
-        out, returncode = RunShellWithReturnCode(cmd)
-        if returncode:
-          ErrorExit("Failed to run command %s" % cmd)
-        self.svnls_cache[dirname] = (old_files, out.splitlines())
-      old_files, new_files = self.svnls_cache[dirname]
-      if relfilename in old_files and relfilename not in new_files:
-        status = "D   "
-      elif relfilename in old_files and relfilename in new_files:
-        status = "M   "
-      else:
-        status = "A   "
-    return status
-
-  def GetBaseFile(self, filename):
-    status = self.GetStatus(filename)
-    base_content = None
-    new_content = None
-
-    # If a file is copied its status will be "A  +", which signifies
-    # "addition-with-history".  See "svn st" for more information.  We need to
-    # upload the original file or else diff parsing will fail if the file was
-    # edited.
-    if status[0] == "A" and status[3] != "+":
-      # We'll need to upload the new content if we're adding a binary file
-      # since diff's output won't contain it.
-      mimetype = RunShell(["svn", "propget", "svn:mime-type", filename],
-                          silent_ok=True)
-      base_content = ""
-      is_binary = mimetype and not mimetype.startswith("text/")
-      if is_binary and self.IsImage(filename):
-        new_content = self.ReadFile(filename)
-    elif (status[0] in ("M", "D", "R") or
-          (status[0] == "A" and status[3] == "+") or  # Copied file.
-          (status[0] == " " and status[1] == "M")):  # Property change.
-      args = []
-      if self.options.revision:
-        url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start)
-      else:
-        # Don't change filename, it's needed later.
-        url = filename
-        args += ["-r", "BASE"]
-      cmd = ["svn"] + args + ["propget", "svn:mime-type", url]
-      mimetype, returncode = RunShellWithReturnCode(cmd)
-      if returncode:
-        # File does not exist in the requested revision.
-        # Reset mimetype, it contains an error message.
-        mimetype = ""
-      get_base = False
-      is_binary = mimetype and not mimetype.startswith("text/")
-      if status[0] == " ":
-        # Empty base content just to force an upload.
-        base_content = ""
-      elif is_binary:
-        if self.IsImage(filename):
-          get_base = True
-          if status[0] == "M":
-            if not self.rev_end:
-              new_content = self.ReadFile(filename)
-            else:
-              url = "%s/%s@%s" % (self.svn_base, filename, self.rev_end)
-              new_content = RunShell(["svn", "cat", url],
-                                     universal_newlines=True, silent_ok=True)
-        else:
-          base_content = ""
-      else:
-        get_base = True
-
-      if get_base:
-        if is_binary:
-          universal_newlines = False
-        else:
-          universal_newlines = True
-        if self.rev_start:
-          # "svn cat -r REV delete_file.txt" doesn't work. cat requires
-          # the full URL with "@REV" appended instead of using "-r" option.
-          url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start)
-          base_content = RunShell(["svn", "cat", url],
-                                  universal_newlines=universal_newlines,
-                                  silent_ok=True)
-        else:
-          base_content = RunShell(["svn", "cat", filename],
-                                  universal_newlines=universal_newlines,
-                                  silent_ok=True)
-        if not is_binary:
-          args = []
-          if self.rev_start:
-            url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start)
-          else:
-            url = filename
-            args += ["-r", "BASE"]
-          cmd = ["svn"] + args + ["propget", "svn:keywords", url]
-          keywords, returncode = RunShellWithReturnCode(cmd)
-          if keywords and not returncode:
-            base_content = self._CollapseKeywords(base_content, keywords)
-    else:
-      StatusUpdate("svn status returned unexpected output: %s" % status)
-      sys.exit(1)
-    return base_content, new_content, is_binary, status[0:5]
-
-
-class GitVCS(VersionControlSystem):
-  """Implementation of the VersionControlSystem interface for Git."""
-
-  def __init__(self, options):
-    super(GitVCS, self).__init__(options)
-    # Map of filename -> hash of base file.
-    self.base_hashes = {}
-
-  def GenerateDiff(self, extra_args):
-    # This is more complicated than svn's GenerateDiff because we must convert
-    # the diff output to include an svn-style "Index:" line as well as record
-    # the hashes of the base files, so we can upload them along with our diff.
-    if self.options.revision:
-      extra_args = [self.options.revision] + extra_args
-    gitdiff = RunShell(["git", "diff", "--full-index"] + extra_args)
-    svndiff = []
-    filecount = 0
-    filename = None
-    for line in gitdiff.splitlines():
-      match = re.match(r"diff --git a/(.*) b/.*$", line)
-      if match:
-        filecount += 1
-        filename = match.group(1)
-        svndiff.append("Index: %s\n" % filename)
-      else:
-        # The "index" line in a git diff looks like this (long hashes elided):
-        #   index 82c0d44..b2cee3f 100755
-        # We want to save the left hash, as that identifies the base file.
-        match = re.match(r"index (\w+)\.\.", line)
-        if match:
-          self.base_hashes[filename] = match.group(1)
-      svndiff.append(line + "\n")
-    if not filecount:
-      ErrorExit("No valid patches found in output from git diff")
-    return "".join(svndiff)
-
-  def GetUnknownFiles(self):
-    status = RunShell(["git", "ls-files", "--exclude-standard", "--others"],
-                      silent_ok=True)
-    return status.splitlines()
-
-  def GetBaseFile(self, filename):
-    hash = self.base_hashes[filename]
-    base_content = None
-    new_content = None
-    is_binary = False
-    if hash == "0" * 40:  # All-zero hash indicates no base file.
-      status = "A"
-      base_content = ""
-    else:
-      status = "M"
-      base_content, returncode = RunShellWithReturnCode(["git", "show", hash])
-      if returncode:
-        ErrorExit("Got error status from 'git show %s'" % hash)
-    return (base_content, new_content, is_binary, status)
-
-
-class MercurialVCS(VersionControlSystem):
-  """Implementation of the VersionControlSystem interface for Mercurial."""
-
-  def __init__(self, options, repo_dir):
-    super(MercurialVCS, self).__init__(options)
-    # Absolute path to repository (we can be in a subdir)
-    self.repo_dir = os.path.normpath(repo_dir)
-    # Compute the subdir
-    cwd = os.path.normpath(os.getcwd())
-    assert cwd.startswith(self.repo_dir)
-    self.subdir = cwd[len(self.repo_dir):].lstrip(r"\/")
-    if self.options.revision:
-      self.base_rev = self.options.revision
-    else:
-      self.base_rev = RunShell(["hg", "parent", "-q"]).split(':')[1].strip()
-
-  def _GetRelPath(self, filename):
-    """Get relative path of a file according to the current directory,
-    given its logical path in the repo."""
-    assert filename.startswith(self.subdir), filename
-    return filename[len(self.subdir):].lstrip(r"\/")
-
-  def GenerateDiff(self, extra_args):
-    # If no file specified, restrict to the current subdir
-    extra_args = extra_args or ["."]
-    cmd = ["hg", "diff", "--git", "-r", self.base_rev] + extra_args
-    data = RunShell(cmd, silent_ok=True)
-    svndiff = []
-    filecount = 0
-    for line in data.splitlines():
-      m = re.match("diff --git a/(\S+) b/(\S+)", line)
-      if m:
-        # Modify line to make it look like as it comes from svn diff.
-        # With this modification no changes on the server side are required
-        # to make upload.py work with Mercurial repos.
-        # NOTE: for proper handling of moved/copied files, we have to use
-        # the second filename.
-        filename = m.group(2)
-        svndiff.append("Index: %s" % filename)
-        svndiff.append("=" * 67)
-        filecount += 1
-        logging.info(line)
-      else:
-        svndiff.append(line)
-    if not filecount:
-      ErrorExit("No valid patches found in output from hg diff")
-    return "\n".join(svndiff) + "\n"
-
-  def GetUnknownFiles(self):
-    """Return a list of files unknown to the VCS."""
-    args = []
-    status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."],
-        silent_ok=True)
-    unknown_files = []
-    for line in status.splitlines():
-      st, fn = line.split(" ", 1)
-      if st == "?":
-        unknown_files.append(fn)
-    return unknown_files
-
-  def GetBaseFile(self, filename):
-    # "hg status" and "hg cat" both take a path relative to the current subdir
-    # rather than to the repo root, but "hg diff" has given us the full path
-    # to the repo root.
-    base_content = ""
-    new_content = None
-    is_binary = False
-    oldrelpath = relpath = self._GetRelPath(filename)
-    # "hg status -C" returns two lines for moved/copied files, one otherwise
-    out = RunShell(["hg", "status", "-C", "--rev", self.base_rev, relpath])
-    out = out.splitlines()
-    # HACK: strip error message about missing file/directory if it isn't in
-    # the working copy
-    if out[0].startswith('%s: ' % relpath):
-      out = out[1:]
-    if len(out) > 1:
-      # Moved/copied => considered as modified, use old filename to
-      # retrieve base contents
-      oldrelpath = out[1].strip()
-      status = "M"
-    else:
-      status, _ = out[0].split(' ', 1)
-    if status != "A":
-      base_content = RunShell(["hg", "cat", "-r", self.base_rev, oldrelpath],
-        silent_ok=True)
-      is_binary = "\0" in base_content  # Mercurial's heuristic
-    if status != "R":
-      new_content = open(relpath, "rb").read()
-      is_binary = is_binary or "\0" in new_content
-    if is_binary and base_content:
-      # Fetch again without converting newlines
-      base_content = RunShell(["hg", "cat", "-r", self.base_rev, oldrelpath],
-        silent_ok=True, universal_newlines=False)
-    if not is_binary or not self.IsImage(relpath):
-      new_content = None
-    return base_content, new_content, is_binary, status
-
-
-# NOTE: The SplitPatch function is duplicated in engine.py, keep them in sync.
-def SplitPatch(data):
-  """Splits a patch into separate pieces for each file.
-
-  Args:
-    data: A string containing the output of svn diff.
-
-  Returns:
-    A list of 2-tuple (filename, text) where text is the svn diff output
-      pertaining to filename.
-  """
-  patches = []
-  filename = None
-  diff = []
-  for line in data.splitlines(True):
-    new_filename = None
-    if line.startswith('Index:'):
-      unused, new_filename = line.split(':', 1)
-      new_filename = new_filename.strip()
-    elif line.startswith('Property changes on:'):
-      unused, temp_filename = line.split(':', 1)
-      # When a file is modified, paths use '/' between directories, however
-      # when a property is modified '\' is used on Windows.  Make them the same
-      # otherwise the file shows up twice.
-      temp_filename = temp_filename.strip().replace('\\', '/')
-      if temp_filename != filename:
-        # File has property changes but no modifications, create a new diff.
-        new_filename = temp_filename
-    if new_filename:
-      if filename and diff:
-        patches.append((filename, ''.join(diff)))
-      filename = new_filename
-      diff = [line]
-      continue
-    if diff is not None:
-      diff.append(line)
-  if filename and diff:
-    patches.append((filename, ''.join(diff)))
-  return patches
-
-
-def UploadSeparatePatches(issue, rpc_server, patchset, data, options):
-  """Uploads a separate patch for each file in the diff output.
-
-  Returns a list of [patch_key, filename] for each file.
-  """
-  patches = SplitPatch(data)
-  rv = []
-  for patch in patches:
-    if len(patch[1]) > MAX_UPLOAD_SIZE:
-      print ("Not uploading the patch for " + patch[0] +
-             " because the file is too large.")
-      continue
-    form_fields = [("filename", patch[0])]
-    if not options.download_base:
-      form_fields.append(("content_upload", "1"))
-    files = [("data", "data.diff", patch[1])]
-    ctype, body = EncodeMultipartFormData(form_fields, files)
-    url = "/%d/upload_patch/%d" % (int(issue), int(patchset))
-    print "Uploading patch for " + patch[0]
-    response_body = rpc_server.Send(url, body, content_type=ctype)
-    lines = response_body.splitlines()
-    if not lines or lines[0] != "OK":
-      StatusUpdate("  --> %s" % response_body)
-      sys.exit(1)
-    rv.append([lines[1], patch[0]])
-  return rv
-
-
-def GuessVCS(options):
-  """Helper to guess the version control system.
-
-  This examines the current directory, guesses which VersionControlSystem
-  we're using, and returns an instance of the appropriate class.  Exit with an
-  error if we can't figure it out.
-
-  Returns:
-    A VersionControlSystem instance. Exits if the VCS can't be guessed.
-  """
-  # Mercurial has a command to get the base directory of a repository
-  # Try running it, but don't die if we don't have hg installed.
-  # NOTE: we try Mercurial first as it can sit on top of an SVN working copy.
-  try:
-    out, returncode = RunShellWithReturnCode(["hg", "root"])
-    if returncode == 0:
-      return MercurialVCS(options, out.strip())
-  except OSError, (errno, message):
-    if errno != 2:  # ENOENT -- they don't have hg installed.
-      raise
-
-  # Subversion has a .svn in all working directories.
-  if os.path.isdir('.svn'):
-    logging.info("Guessed VCS = Subversion")
-    return SubversionVCS(options)
-
-  # Git has a command to test if you're in a git tree.
-  # Try running it, but don't die if we don't have git installed.
-  try:
-    out, returncode = RunShellWithReturnCode(["git", "rev-parse",
-                                              "--is-inside-work-tree"])
-    if returncode == 0:
-      return GitVCS(options)
-  except OSError, (errno, message):
-    if errno != 2:  # ENOENT -- they don't have git installed.
-      raise
-
-  ErrorExit(("Could not guess version control system. "
-             "Are you in a working copy directory?"))
-
-
-def RealMain(argv, data=None):
-  """The real main function.
-
-  Args:
-    argv: Command line arguments.
-    data: Diff contents. If None (default) the diff is generated by
-      the VersionControlSystem implementation returned by GuessVCS().
-
-  Returns:
-    A 2-tuple (issue id, patchset id).
-    The patchset id is None if the base files are not uploaded by this
-    script (applies only to SVN checkouts).
-  """
-  logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:"
-                              "%(lineno)s %(message)s "))
-  os.environ['LC_ALL'] = 'C'
-  options, args = parser.parse_args(argv[1:])
-  global verbosity
-  verbosity = options.verbose
-  if verbosity >= 3:
-    logging.getLogger().setLevel(logging.DEBUG)
-  elif verbosity >= 2:
-    logging.getLogger().setLevel(logging.INFO)
-  vcs = GuessVCS(options)
-  if isinstance(vcs, SubversionVCS):
-    # base field is only allowed for Subversion.
-    # Note: Fetching base files may become deprecated in future releases.
-    base = vcs.GuessBase(options.download_base)
-  else:
-    base = None
-  if not base and options.download_base:
-    options.download_base = True
-    logging.info("Enabled upload of base file")
-  if not options.assume_yes:
-    vcs.CheckForUnknownFiles()
-  if data is None:
-    data = vcs.GenerateDiff(args)
-  files = vcs.GetBaseFiles(data)
-  if verbosity >= 1:
-    print "Upload server:", options.server, "(change with -s/--server)"
-  if options.issue:
-    prompt = "Message describing this patch set: "
-  else:
-    prompt = "New issue subject: "
-  message = options.message or raw_input(prompt).strip()
-  if not message:
-    ErrorExit("A non-empty message is required")
-  rpc_server = GetRpcServer(options)
-  form_fields = [("subject", message)]
-  if base:
-    form_fields.append(("base", base))
-  if options.issue:
-    form_fields.append(("issue", str(options.issue)))
-  if options.email:
-    form_fields.append(("user", options.email))
-  if options.reviewers:
-    for reviewer in options.reviewers.split(','):
-      if "@" in reviewer and not reviewer.split("@")[1].count(".") == 1:
-        ErrorExit("Invalid email address: %s" % reviewer)
-    form_fields.append(("reviewers", options.reviewers))
-  if options.cc:
-    for cc in options.cc.split(','):
-      if "@" in cc and not cc.split("@")[1].count(".") == 1:
-        ErrorExit("Invalid email address: %s" % cc)
-    form_fields.append(("cc", options.cc))
-  description = options.description
-  if options.description_file:
-    if options.description:
-      ErrorExit("Can't specify description and description_file")
-    file = open(options.description_file, 'r')
-    description = file.read()
-    file.close()
-  if description:
-    form_fields.append(("description", description))
-  # Send a hash of all the base file so the server can determine if a copy
-  # already exists in an earlier patchset.
-  base_hashes = ""
-  for file, info in files.iteritems():
-    if not info[0] is None:
-      checksum = md5.new(info[0]).hexdigest()
-      if base_hashes:
-        base_hashes += "|"
-      base_hashes += checksum + ":" + file
-  form_fields.append(("base_hashes", base_hashes))
-  # If we're uploading base files, don't send the email before the uploads, so
-  # that it contains the file status.
-  if options.send_mail and options.download_base:
-    form_fields.append(("send_mail", "1"))
-  if not options.download_base:
-    form_fields.append(("content_upload", "1"))
-  if len(data) > MAX_UPLOAD_SIZE:
-    print "Patch is large, so uploading file patches separately."
-    uploaded_diff_file = []
-    form_fields.append(("separate_patches", "1"))
-  else:
-    uploaded_diff_file = [("data", "data.diff", data)]
-  ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file)
-  response_body = rpc_server.Send("/upload", body, content_type=ctype)
-  patchset = None
-  if not options.download_base or not uploaded_diff_file:
-    lines = response_body.splitlines()
-    if len(lines) >= 2:
-      msg = lines[0]
-      patchset = lines[1].strip()
-      patches = [x.split(" ", 1) for x in lines[2:]]
-    else:
-      msg = response_body
-  else:
-    msg = response_body
-  StatusUpdate(msg)
-  if not response_body.startswith("Issue created.") and \
-  not response_body.startswith("Issue updated."):
-    sys.exit(0)
-  issue = msg[msg.rfind("/")+1:]
-
-  if not uploaded_diff_file:
-    result = UploadSeparatePatches(issue, rpc_server, patchset, data, options)
-    if not options.download_base:
-      patches = result
-
-  if not options.download_base:
-    vcs.UploadBaseFiles(issue, rpc_server, patches, patchset, options, files)
-    if options.send_mail:
-      rpc_server.Send("/" + issue + "/mail", payload="")
-  return issue, patchset
-
-
-def main():
-  try:
-    RealMain(sys.argv)
-  except KeyboardInterrupt:
-    print
-    StatusUpdate("Interrupted.")
-    sys.exit(1)
-
-
-if __name__ == "__main__":
-  main()
diff --git a/ext/googletest/googlemock/scripts/upload_gmock.py b/ext/googletest/googlemock/scripts/upload_gmock.py
deleted file mode 100755
index 5dc484b..0000000
--- a/ext/googletest/googlemock/scripts/upload_gmock.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2009, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""upload_gmock.py v0.1.0 -- uploads a Google Mock patch for review.
-
-This simple wrapper passes all command line flags and
---cc=googlemock@googlegroups.com to upload.py.
-
-USAGE: upload_gmock.py [options for upload.py]
-"""
-
-__author__ = 'wan@google.com (Zhanyong Wan)'
-
-import os
-import sys
-
-CC_FLAG = '--cc='
-GMOCK_GROUP = 'googlemock@googlegroups.com'
-
-
-def main():
-  # Finds the path to upload.py, assuming it is in the same directory
-  # as this file.
-  my_dir = os.path.dirname(os.path.abspath(__file__))
-  upload_py_path = os.path.join(my_dir, 'upload.py')
-
-  # Adds Google Mock discussion group to the cc line if it's not there
-  # already.
-  upload_py_argv = [upload_py_path]
-  found_cc_flag = False
-  for arg in sys.argv[1:]:
-    if arg.startswith(CC_FLAG):
-      found_cc_flag = True
-      cc_line = arg[len(CC_FLAG):]
-      cc_list = [addr for addr in cc_line.split(',') if addr]
-      if GMOCK_GROUP not in cc_list:
-        cc_list.append(GMOCK_GROUP)
-      upload_py_argv.append(CC_FLAG + ','.join(cc_list))
-    else:
-      upload_py_argv.append(arg)
-
-  if not found_cc_flag:
-    upload_py_argv.append(CC_FLAG + GMOCK_GROUP)
-
-  # Invokes upload.py with the modified command line flags.
-  os.execv(upload_py_path, upload_py_argv)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/ext/googletest/googlemock/src/gmock-matchers.cc b/ext/googletest/googlemock/src/gmock-matchers.cc
index 4a3f7af..dded437 100644
--- a/ext/googletest/googlemock/src/gmock-matchers.cc
+++ b/ext/googletest/googlemock/src/gmock-matchers.cc
@@ -34,7 +34,6 @@
 // utilities for defining matchers.
 
 #include "gmock/gmock-matchers.h"
-#include "gmock/gmock-generated-matchers.h"
 
 #include <string.h>
 #include <iostream>
@@ -219,8 +218,6 @@
   // right_[left_[i]] = i.
   ::std::vector<size_t> left_;
   ::std::vector<size_t> right_;
-
-  GTEST_DISALLOW_ASSIGN_(MaxBipartiteMatchState);
 };
 
 const size_t MaxBipartiteMatchState::kUnused;
diff --git a/ext/googletest/googlemock/src/gmock-spec-builders.cc b/ext/googletest/googlemock/src/gmock-spec-builders.cc
index f9d3434..c7266a3 100644
--- a/ext/googletest/googlemock/src/gmock-spec-builders.cc
+++ b/ext/googletest/googlemock/src/gmock-spec-builders.cc
@@ -36,14 +36,17 @@
 #include "gmock/gmock-spec-builders.h"
 
 #include <stdlib.h>
+
 #include <iostream>  // NOLINT
 #include <map>
 #include <memory>
 #include <set>
 #include <string>
 #include <vector>
+
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "gtest/internal/gtest-port.h"
 
 #if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC
 # include <unistd.h>  // NOLINT
@@ -70,7 +73,8 @@
                                 const char* file, int line,
                                 const std::string& message) {
   ::std::ostringstream s;
-  s << file << ":" << line << ": " << message << ::std::endl;
+  s << internal::FormatFileLocation(file, line) << " " << message
+    << ::std::endl;
   Log(severity, s.str(), 0);
 }
 
@@ -291,8 +295,8 @@
               "call should not happen.  Do not suppress it by blindly adding "
               "an EXPECT_CALL() if you don't mean to enforce the call.  "
               "See "
-              "https://github.com/google/googletest/blob/master/googlemock/"
-              "docs/cook_book.md#"
+              "https://github.com/google/googletest/blob/master/docs/"
+              "gmock_cook_book.md#"
               "knowing-when-to-expect for details.\n",
           stack_frames_to_skip);
       break;
@@ -429,10 +433,10 @@
 
   // The UntypedFindMatchingExpectation() function acquires and
   // releases g_gmock_mutex.
+
   const ExpectationBase* const untyped_expectation =
-      this->UntypedFindMatchingExpectation(
-          untyped_args, &untyped_action, &is_excessive,
-          &ss, &why);
+      this->UntypedFindMatchingExpectation(untyped_args, &untyped_action,
+                                           &is_excessive, &ss, &why);
   const bool found = untyped_expectation != nullptr;
 
   // True if and only if we need to print the call's arguments
@@ -457,26 +461,42 @@
     untyped_expectation->DescribeLocationTo(&loc);
   }
 
-  UntypedActionResultHolderBase* const result =
-      untyped_action == nullptr
-          ? this->UntypedPerformDefaultAction(untyped_args, ss.str())
-          : this->UntypedPerformAction(untyped_action, untyped_args);
-  if (result != nullptr) result->PrintAsActionResult(&ss);
-  ss << "\n" << why.str();
+  UntypedActionResultHolderBase* result = nullptr;
 
-  if (!found) {
-    // No expectation matches this call - reports a failure.
-    Expect(false, nullptr, -1, ss.str());
-  } else if (is_excessive) {
-    // We had an upper-bound violation and the failure message is in ss.
-    Expect(false, untyped_expectation->file(),
-           untyped_expectation->line(), ss.str());
-  } else {
-    // We had an expected call and the matching expectation is
-    // described in ss.
-    Log(kInfo, loc.str() + ss.str(), 2);
+  auto perform_action = [&] {
+    return untyped_action == nullptr
+               ? this->UntypedPerformDefaultAction(untyped_args, ss.str())
+               : this->UntypedPerformAction(untyped_action, untyped_args);
+  };
+  auto handle_failures = [&] {
+    ss << "\n" << why.str();
+
+    if (!found) {
+      // No expectation matches this call - reports a failure.
+      Expect(false, nullptr, -1, ss.str());
+    } else if (is_excessive) {
+      // We had an upper-bound violation and the failure message is in ss.
+      Expect(false, untyped_expectation->file(), untyped_expectation->line(),
+             ss.str());
+    } else {
+      // We had an expected call and the matching expectation is
+      // described in ss.
+      Log(kInfo, loc.str() + ss.str(), 2);
+    }
+  };
+#if GTEST_HAS_EXCEPTIONS
+  try {
+    result = perform_action();
+  } catch (...) {
+    handle_failures();
+    throw;
   }
+#else
+  result = perform_action();
+#endif
 
+  if (result != nullptr) result->PrintAsActionResult(&ss);
+  handle_failures();
   return result;
 }
 
@@ -620,7 +640,7 @@
     if (leaked_count > 0) {
       std::cout << "\nERROR: " << leaked_count << " leaked mock "
                 << (leaked_count == 1 ? "object" : "objects")
-                << " found at program exit. Expectations on a mock object is "
+                << " found at program exit. Expectations on a mock object are "
                    "verified when the object is destructed. Leaking a mock "
                    "means that its expectations aren't verified, which is "
                    "usually a test bug. If you really intend to leak a mock, "
diff --git a/ext/googletest/googlemock/src/gmock.cc b/ext/googletest/googlemock/src/gmock.cc
index 32b2a73..7bcdb0b 100644
--- a/ext/googletest/googlemock/src/gmock.cc
+++ b/ext/googletest/googlemock/src/gmock.cc
@@ -124,7 +124,7 @@
 }
 
 static bool ParseGoogleMockIntFlag(const char* str, const char* flag,
-                                   int* value) {
+                                   int32_t* value) {
   // Gets the value of the flag as a string.
   const char* const value_str = ParseGoogleMockFlagValue(str, flag, true);
 
diff --git a/ext/googletest/googlemock/src/gmock_main.cc b/ext/googletest/googlemock/src/gmock_main.cc
index 98611b9..18c500f 100644
--- a/ext/googletest/googlemock/src/gmock_main.cc
+++ b/ext/googletest/googlemock/src/gmock_main.cc
@@ -32,7 +32,10 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
-#ifdef ARDUINO
+#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
+#if GTEST_OS_ESP8266
+extern "C" {
+#endif
 void setup() {
   // Since Google Mock depends on Google Test, InitGoogleMock() is
   // also responsible for initializing Google Test.  Therefore there's
@@ -40,6 +43,10 @@
   testing::InitGoogleMock();
 }
 void loop() { RUN_ALL_TESTS(); }
+#if GTEST_OS_ESP8266
+}
+#endif
+
 #else
 
 // MS C++ compiler/linker has a bug on Windows (not on Windows CE), which
diff --git a/ext/googletest/googlemock/test/BUILD.bazel b/ext/googletest/googlemock/test/BUILD.bazel
index da95ed5..6193ed4 100644
--- a/ext/googletest/googlemock/test/BUILD.bazel
+++ b/ext/googletest/googlemock/test/BUILD.bazel
@@ -28,8 +28,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #
-# Author: misterg@google.com (Gennadiy Civil)
-#
 #   Bazel Build for Google C++ Testing Framework(Google Test)-googlemock
 
 load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_test")
@@ -43,6 +41,7 @@
     size = "small",
     srcs = glob(include = ["gmock-*.cc"]),
     linkopts = select({
+        "//:qnx": [],
         "//:windows": [],
         "//conditions:default": ["-pthread"],
     }),
@@ -54,6 +53,9 @@
     name = "gmock_test_utils",
     testonly = 1,
     srcs = ["gmock_test_utils.py"],
+    deps = [
+        "//googletest/test:gtest_test_utils",
+    ],
 )
 
 cc_binary(
@@ -71,6 +73,10 @@
         ":gmock_leak_test_",
         ":gmock_test_utils",
     ],
+    tags = [
+        "no_test_msvc2015",
+        "no_test_msvc2017",
+    ],
 )
 
 cc_test(
@@ -98,7 +104,10 @@
         ":gmock_output_test_",
         ":gmock_output_test_golden.txt",
     ],
-    python_version = "PY2",
+    tags = [
+        "no_test_msvc2015",
+        "no_test_msvc2017",
+    ],
     deps = [":gmock_test_utils"],
 )
 
diff --git a/ext/googletest/googlemock/test/gmock-actions_test.cc b/ext/googletest/googlemock/test/gmock-actions_test.cc
index f63c8c5..e1ca7fe 100644
--- a/ext/googletest/googlemock/test/gmock-actions_test.cc
+++ b/ext/googletest/googlemock/test/gmock-actions_test.cc
@@ -32,11 +32,13 @@
 //
 // This file tests the built-in actions.
 
-// Silence C4800 (C4800: 'int *const ': forcing value
-// to bool 'true' or 'false') for MSVC 15
+// Silence C4100 (unreferenced formal parameter) for MSVC
 #ifdef _MSC_VER
-#if _MSC_VER == 1900
 #  pragma warning(push)
+#  pragma warning(disable:4100)
+#if _MSC_VER == 1900
+// and silence C4800 (C4800: 'int *const ': forcing value
+// to bool 'true' or 'false') for MSVC 15
 #  pragma warning(disable:4800)
 #endif
 #endif
@@ -46,6 +48,7 @@
 #include <iterator>
 #include <memory>
 #include <string>
+#include <type_traits>
 #include "gmock/gmock.h"
 #include "gmock/internal/gmock-port.h"
 #include "gtest/gtest.h"
@@ -53,36 +56,34 @@
 
 namespace {
 
-// This list should be kept sorted.
-using testing::_;
-using testing::Action;
-using testing::ActionInterface;
-using testing::Assign;
-using testing::ByMove;
-using testing::ByRef;
-using testing::DefaultValue;
-using testing::DoAll;
-using testing::DoDefault;
-using testing::IgnoreResult;
-using testing::Invoke;
-using testing::InvokeWithoutArgs;
-using testing::MakePolymorphicAction;
-using testing::Ne;
-using testing::PolymorphicAction;
-using testing::Return;
-using testing::ReturnNull;
-using testing::ReturnRef;
-using testing::ReturnRefOfCopy;
-using testing::SetArgPointee;
-using testing::SetArgumentPointee;
-using testing::Unused;
-using testing::WithArgs;
-using testing::internal::BuiltInDefaultValue;
-using testing::internal::Int64;
-using testing::internal::UInt64;
+using ::testing::_;
+using ::testing::Action;
+using ::testing::ActionInterface;
+using ::testing::Assign;
+using ::testing::ByMove;
+using ::testing::ByRef;
+using ::testing::DefaultValue;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::IgnoreResult;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::MakePolymorphicAction;
+using ::testing::PolymorphicAction;
+using ::testing::Return;
+using ::testing::ReturnNew;
+using ::testing::ReturnNull;
+using ::testing::ReturnRef;
+using ::testing::ReturnRefOfCopy;
+using ::testing::ReturnRoundRobin;
+using ::testing::SetArgPointee;
+using ::testing::SetArgumentPointee;
+using ::testing::Unused;
+using ::testing::WithArgs;
+using ::testing::internal::BuiltInDefaultValue;
 
 #if !GTEST_OS_WINDOWS_MOBILE
-using testing::SetErrnoAndReturn;
+using ::testing::SetErrnoAndReturn;
 #endif
 
 // Tests that BuiltInDefaultValue<T*>::Get() returns NULL.
@@ -121,8 +122,9 @@
   EXPECT_EQ(0U, BuiltInDefaultValue<unsigned long>::Get());  // NOLINT
   EXPECT_EQ(0, BuiltInDefaultValue<signed long>::Get());  // NOLINT
   EXPECT_EQ(0, BuiltInDefaultValue<long>::Get());  // NOLINT
-  EXPECT_EQ(0U, BuiltInDefaultValue<UInt64>::Get());
-  EXPECT_EQ(0, BuiltInDefaultValue<Int64>::Get());
+  EXPECT_EQ(0U, BuiltInDefaultValue<unsigned long long>::Get());  // NOLINT
+  EXPECT_EQ(0, BuiltInDefaultValue<signed long long>::Get());  // NOLINT
+  EXPECT_EQ(0, BuiltInDefaultValue<long long>::Get());  // NOLINT
   EXPECT_EQ(0, BuiltInDefaultValue<float>::Get());
   EXPECT_EQ(0, BuiltInDefaultValue<double>::Get());
 }
@@ -145,8 +147,9 @@
   EXPECT_TRUE(BuiltInDefaultValue<unsigned long>::Exists());  // NOLINT
   EXPECT_TRUE(BuiltInDefaultValue<signed long>::Exists());  // NOLINT
   EXPECT_TRUE(BuiltInDefaultValue<long>::Exists());  // NOLINT
-  EXPECT_TRUE(BuiltInDefaultValue<UInt64>::Exists());
-  EXPECT_TRUE(BuiltInDefaultValue<Int64>::Exists());
+  EXPECT_TRUE(BuiltInDefaultValue<unsigned long long>::Exists());  // NOLINT
+  EXPECT_TRUE(BuiltInDefaultValue<signed long long>::Exists());  // NOLINT
+  EXPECT_TRUE(BuiltInDefaultValue<long long>::Exists());  // NOLINT
   EXPECT_TRUE(BuiltInDefaultValue<float>::Exists());
   EXPECT_TRUE(BuiltInDefaultValue<double>::Exists());
 }
@@ -573,8 +576,6 @@
 
  private:
   bool* const converted_;
-
-  GTEST_DISALLOW_ASSIGN_(FromType);
 };
 
 class ToType {
@@ -646,6 +647,41 @@
   EXPECT_EQ(&derived, &a.Perform(std::make_tuple()));
 }
 
+template <typename T, typename = decltype(ReturnRef(std::declval<T&&>()))>
+bool CanCallReturnRef(T&&) { return true; }
+bool CanCallReturnRef(Unused) { return false; }
+
+// Tests that ReturnRef(v) is working with non-temporaries (T&)
+TEST(ReturnRefTest, WorksForNonTemporary) {
+  int scalar_value = 123;
+  EXPECT_TRUE(CanCallReturnRef(scalar_value));
+
+  std::string non_scalar_value("ABC");
+  EXPECT_TRUE(CanCallReturnRef(non_scalar_value));
+
+  const int const_scalar_value{321};
+  EXPECT_TRUE(CanCallReturnRef(const_scalar_value));
+
+  const std::string const_non_scalar_value("CBA");
+  EXPECT_TRUE(CanCallReturnRef(const_non_scalar_value));
+}
+
+// Tests that ReturnRef(v) is not working with temporaries (T&&)
+TEST(ReturnRefTest, DoesNotWorkForTemporary) {
+  auto scalar_value = []()  -> int { return 123; };
+  EXPECT_FALSE(CanCallReturnRef(scalar_value()));
+
+  auto non_scalar_value = []() -> std::string { return "ABC"; };
+  EXPECT_FALSE(CanCallReturnRef(non_scalar_value()));
+
+  // cannot use here callable returning "const scalar type",
+  // because such const for scalar return type is ignored
+  EXPECT_FALSE(CanCallReturnRef(static_cast<const int>(321)));
+
+  auto const_non_scalar_value = []() -> const std::string { return "CBA"; };
+  EXPECT_FALSE(CanCallReturnRef(const_non_scalar_value()));
+}
+
 // Tests that ReturnRefOfCopy(v) works for reference types.
 TEST(ReturnRefOfCopyTest, WorksForReference) {
   int n = 42;
@@ -670,6 +706,31 @@
   EXPECT_NE(&derived, &a.Perform(std::make_tuple()));
 }
 
+// Tests that ReturnRoundRobin(v) works with initializer lists
+TEST(ReturnRoundRobinTest, WorksForInitList) {
+  Action<int()> ret = ReturnRoundRobin({1, 2, 3});
+
+  EXPECT_EQ(1, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(2, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(3, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(1, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(2, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(3, ret.Perform(std::make_tuple()));
+}
+
+// Tests that ReturnRoundRobin(v) works with vectors
+TEST(ReturnRoundRobinTest, WorksForVector) {
+  std::vector<double> v = {4.4, 5.5, 6.6};
+  Action<double()> ret = ReturnRoundRobin(v);
+
+  EXPECT_EQ(4.4, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(5.5, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(6.6, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(4.4, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(5.5, ret.Perform(std::make_tuple()));
+  EXPECT_EQ(6.6, ret.Perform(std::make_tuple()));
+}
+
 // Tests that DoDefault() does the default action for the mock method.
 
 class MockClass {
@@ -734,7 +795,7 @@
 }
 
 // Tests that DoDefault() returns the default value set by
-// DefaultValue<T>::Set() when it's not overriden by an ON_CALL().
+// DefaultValue<T>::Set() when it's not overridden by an ON_CALL().
 TEST(DoDefaultTest, ReturnsUserSpecifiedPerTypeDefaultValueWhenThereIsOne) {
   DefaultValue<int>::Set(1);
   MockClass mock;
@@ -1230,6 +1291,52 @@
   EXPECT_EQ(expected.str(), actual.str());
 }
 
+struct UnaryConstructorClass {
+  explicit UnaryConstructorClass(int v) : value(v) {}
+  int value;
+};
+
+// Tests using ReturnNew() with a unary constructor.
+TEST(ReturnNewTest, Unary) {
+  Action<UnaryConstructorClass*()> a = ReturnNew<UnaryConstructorClass>(4000);
+  UnaryConstructorClass* c = a.Perform(std::make_tuple());
+  EXPECT_EQ(4000, c->value);
+  delete c;
+}
+
+TEST(ReturnNewTest, UnaryWorksWhenMockMethodHasArgs) {
+  Action<UnaryConstructorClass*(bool, int)> a =
+      ReturnNew<UnaryConstructorClass>(4000);
+  UnaryConstructorClass* c = a.Perform(std::make_tuple(false, 5));
+  EXPECT_EQ(4000, c->value);
+  delete c;
+}
+
+TEST(ReturnNewTest, UnaryWorksWhenMockMethodReturnsPointerToConst) {
+  Action<const UnaryConstructorClass*()> a =
+      ReturnNew<UnaryConstructorClass>(4000);
+  const UnaryConstructorClass* c = a.Perform(std::make_tuple());
+  EXPECT_EQ(4000, c->value);
+  delete c;
+}
+
+class TenArgConstructorClass {
+ public:
+  TenArgConstructorClass(int a1, int a2, int a3, int a4, int a5, int a6, int a7,
+                         int a8, int a9, int a10)
+      : value_(a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10) {}
+  int value_;
+};
+
+// Tests using ReturnNew() with a 10-argument constructor.
+TEST(ReturnNewTest, ConstructorThatTakes10Arguments) {
+  Action<TenArgConstructorClass*()> a = ReturnNew<TenArgConstructorClass>(
+      1000000000, 200000000, 30000000, 4000000, 500000, 60000, 7000, 800, 90,
+      0);
+  TenArgConstructorClass* c = a.Perform(std::make_tuple());
+  EXPECT_EQ(1234567890, c->value_);
+  delete c;
+}
 
 std::unique_ptr<int> UniquePtrSource() {
   return std::unique_ptr<int>(new int(19));
@@ -1408,8 +1515,19 @@
   EXPECT_EQ(1, s2.Perform(std::make_tuple("hello")));
 
   // Also between the lambda and the action itself.
-  const Action<bool(std::string)> x = [](Unused) { return 42; };
-  EXPECT_TRUE(x.Perform(std::make_tuple("hello")));
+  const Action<bool(std::string)> x1 = [](Unused) { return 42; };
+  const Action<bool(std::string)> x2 = [] { return 42; };
+  EXPECT_TRUE(x1.Perform(std::make_tuple("hello")));
+  EXPECT_TRUE(x2.Perform(std::make_tuple("hello")));
+
+  // Ensure decay occurs where required.
+  std::function<int()> f = [] { return 7; };
+  Action<int(int)> d = f;
+  f = nullptr;
+  EXPECT_EQ(7, d.Perform(std::make_tuple(1)));
+
+  // Ensure creation of an empty action succeeds.
+  Action<void(int)>(nullptr);
 }
 
 TEST(FunctorActionTest, UnusedArguments) {
@@ -1434,6 +1552,26 @@
   EXPECT_EQ(x, 3);
 }
 
+ACTION(ReturnArity) {
+  return std::tuple_size<args_type>::value;
+}
+
+TEST(ActionMacro, LargeArity) {
+  EXPECT_EQ(
+      1, testing::Action<int(int)>(ReturnArity()).Perform(std::make_tuple(0)));
+  EXPECT_EQ(
+      10,
+      testing::Action<int(int, int, int, int, int, int, int, int, int, int)>(
+          ReturnArity())
+          .Perform(std::make_tuple(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)));
+  EXPECT_EQ(
+      20,
+      testing::Action<int(int, int, int, int, int, int, int, int, int, int, int,
+                          int, int, int, int, int, int, int, int, int)>(
+          ReturnArity())
+          .Perform(std::make_tuple(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+                                   14, 15, 16, 17, 18, 19)));
+}
 
 }  // Unnamed namespace
 
diff --git a/ext/googletest/googlemock/test/gmock-function-mocker_nc.cc b/ext/googletest/googlemock/test/gmock-function-mocker_nc.cc
deleted file mode 100644
index d38fe85..0000000
--- a/ext/googletest/googlemock/test/gmock-function-mocker_nc.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "gmock/gmock.h"
-
-#include <memory>
-#include <string>
-
-#if defined(TEST_MOCK_METHOD_INVALID_CONST_SPEC)
-
-struct Base {
-  MOCK_METHOD(int, F, (), (onst));
-};
-
-#else
-
-// Sanity check - this should compile.
-
-#endif
diff --git a/ext/googletest/googlemock/test/gmock-function-mocker_nc_test.py b/ext/googletest/googlemock/test/gmock-function-mocker_nc_test.py
deleted file mode 100644
index 8ef6e09..0000000
--- a/ext/googletest/googlemock/test/gmock-function-mocker_nc_test.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""Negative compilation tests for Google Mock macro MOCK_METHOD."""
-
-import os
-import sys
-
-IS_LINUX = os.name == "posix" and os.uname()[0] == "Linux"
-if not IS_LINUX:
-  sys.stderr.write(
-      "WARNING: Negative compilation tests are not supported on this platform")
-  sys.exit(0)
-
-# Suppresses the 'Import not at the top of the file' lint complaint.
-# pylint: disable-msg=C6204
-from google3.testing.pybase import fake_target_util
-from google3.testing.pybase import googletest
-
-# pylint: enable-msg=C6204
-
-
-class GMockMethodNCTest(googletest.TestCase):
-  """Negative compilation tests for MOCK_METHOD."""
-
-  # The class body is intentionally empty.  The actual test*() methods
-  # will be defined at run time by a call to
-  # DefineNegativeCompilationTests() later.
-  pass
-
-
-# Defines a list of test specs, where each element is a tuple
-# (test name, list of regexes for matching the compiler errors).
-TEST_SPECS = [
-    ("MOCK_METHOD_INVALID_CONST_SPEC",
-     [r"onst cannot be recognized as a valid specification modifier"]),
-]
-
-# Define a test method in GMockNCTest for each element in TEST_SPECS.
-fake_target_util.DefineNegativeCompilationTests(
-    GMockMethodNCTest,
-    "google3/third_party/googletest/googlemock/test/gmock-function-mocker_nc",
-    "gmock-function-mocker_nc.o", TEST_SPECS)
-
-if __name__ == "__main__":
-  googletest.main()
diff --git a/ext/googletest/googlemock/test/gmock-function-mocker_test.cc b/ext/googletest/googlemock/test/gmock-function-mocker_test.cc
index fbc5d5b..cf76fa9 100644
--- a/ext/googletest/googlemock/test/gmock-function-mocker_test.cc
+++ b/ext/googletest/googlemock/test/gmock-function-mocker_test.cc
@@ -31,7 +31,7 @@
 // Google Mock - a framework for writing C++ mock classes.
 //
 // This file tests the function mocker classes.
-#include "gmock/gmock-generated-function-mockers.h"
+#include "gmock/gmock-function-mocker.h"
 
 #if GTEST_OS_WINDOWS
 // MSDN says the header file to be included for STDMETHOD is BaseTyps.h but
@@ -40,8 +40,11 @@
 # include <objbase.h>
 #endif  // GTEST_OS_WINDOWS
 
+#include <functional>
 #include <map>
 #include <string>
+#include <type_traits>
+
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -101,6 +104,20 @@
   virtual int TypeWithComma(const std::map<int, std::string>& a_map) = 0;
   virtual int TypeWithTemplatedCopyCtor(const TemplatedCopyable<int>&) = 0;
 
+  virtual int (*ReturnsFunctionPointer1(int))(bool) = 0;
+  using fn_ptr = int (*)(bool);
+  virtual fn_ptr ReturnsFunctionPointer2(int) = 0;
+
+  virtual int RefQualifiedConstRef() const& = 0;
+  virtual int RefQualifiedConstRefRef() const&& = 0;
+  virtual int RefQualifiedRef() & = 0;
+  virtual int RefQualifiedRefRef() && = 0;
+
+  virtual int RefQualifiedOverloaded() const& = 0;
+  virtual int RefQualifiedOverloaded() const&& = 0;
+  virtual int RefQualifiedOverloaded() & = 0;
+  virtual int RefQualifiedOverloaded() && = 0;
+
 #if GTEST_OS_WINDOWS
   STDMETHOD_(int, CTNullary)() = 0;
   STDMETHOD_(bool, CTUnary)(int x) = 0;
@@ -159,6 +176,9 @@
   MOCK_METHOD(int, TypeWithTemplatedCopyCtor,
               (const TemplatedCopyable<int>&));  // NOLINT
 
+  MOCK_METHOD(int (*)(bool), ReturnsFunctionPointer1, (int), ());
+  MOCK_METHOD(fn_ptr, ReturnsFunctionPointer2, (int), ());
+
 #if GTEST_OS_WINDOWS
   MOCK_METHOD(int, CTNullary, (), (Calltype(STDMETHODCALLTYPE)));
   MOCK_METHOD(bool, CTUnary, (int), (Calltype(STDMETHODCALLTYPE)));
@@ -171,189 +191,301 @@
               (Calltype(STDMETHODCALLTYPE)));
 #endif  // GTEST_OS_WINDOWS
 
+  // Test reference qualified functions.
+  MOCK_METHOD(int, RefQualifiedConstRef, (), (const, ref(&), override));
+  MOCK_METHOD(int, RefQualifiedConstRefRef, (), (const, ref(&&), override));
+  MOCK_METHOD(int, RefQualifiedRef, (), (ref(&), override));
+  MOCK_METHOD(int, RefQualifiedRefRef, (), (ref(&&), override));
+
+  MOCK_METHOD(int, RefQualifiedOverloaded, (), (const, ref(&), override));
+  MOCK_METHOD(int, RefQualifiedOverloaded, (), (const, ref(&&), override));
+  MOCK_METHOD(int, RefQualifiedOverloaded, (), (ref(&), override));
+  MOCK_METHOD(int, RefQualifiedOverloaded, (), (ref(&&), override));
+
  private:
   GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFoo);
 };
+
+class LegacyMockFoo : public FooInterface {
+ public:
+  LegacyMockFoo() {}
+
+  // Makes sure that a mock function parameter can be named.
+  MOCK_METHOD1(VoidReturning, void(int n));  // NOLINT
+
+  MOCK_METHOD0(Nullary, int());  // NOLINT
+
+  // Makes sure that a mock function parameter can be unnamed.
+  MOCK_METHOD1(Unary, bool(int));                                  // NOLINT
+  MOCK_METHOD2(Binary, long(short, int));                          // NOLINT
+  MOCK_METHOD10(Decimal, int(bool, char, short, int, long, float,  // NOLINT
+                             double, unsigned, char*, const std::string& str));
+
+  MOCK_METHOD1(TakesNonConstReference, bool(int&));  // NOLINT
+  MOCK_METHOD1(TakesConstReference, std::string(const int&));
+  MOCK_METHOD1(TakesConst, bool(const int));  // NOLINT
+
+  // Tests that the function return type can contain unprotected comma.
+  MOCK_METHOD0(ReturnTypeWithComma, std::map<int, std::string>());
+  MOCK_CONST_METHOD1(ReturnTypeWithComma,
+                     std::map<int, std::string>(int));  // NOLINT
+
+  MOCK_METHOD0(OverloadedOnArgumentNumber, int());     // NOLINT
+  MOCK_METHOD1(OverloadedOnArgumentNumber, int(int));  // NOLINT
+
+  MOCK_METHOD1(OverloadedOnArgumentType, int(int));    // NOLINT
+  MOCK_METHOD1(OverloadedOnArgumentType, char(char));  // NOLINT
+
+  MOCK_METHOD0(OverloadedOnConstness, int());         // NOLINT
+  MOCK_CONST_METHOD0(OverloadedOnConstness, char());  // NOLINT
+
+  MOCK_METHOD1(TypeWithHole, int(int (*)()));  // NOLINT
+  MOCK_METHOD1(TypeWithComma,
+               int(const std::map<int, std::string>&));  // NOLINT
+  MOCK_METHOD1(TypeWithTemplatedCopyCtor,
+               int(const TemplatedCopyable<int>&));  // NOLINT
+
+  MOCK_METHOD1(ReturnsFunctionPointer1, int (*(int))(bool));
+  MOCK_METHOD1(ReturnsFunctionPointer2, fn_ptr(int));
+
+#if GTEST_OS_WINDOWS
+  MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, CTNullary, int());
+  MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, CTUnary, bool(int));  // NOLINT
+  MOCK_METHOD10_WITH_CALLTYPE(STDMETHODCALLTYPE, CTDecimal,
+                              int(bool b, char c, short d, int e,  // NOLINT
+                                  long f, float g, double h,       // NOLINT
+                                  unsigned i, char* j, const std::string& k));
+  MOCK_CONST_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, CTConst,
+                                   char(int));  // NOLINT
+
+  // Tests that the function return type can contain unprotected comma.
+  MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, CTReturnTypeWithComma,
+                             std::map<int, std::string>());
+#endif  // GTEST_OS_WINDOWS
+
+  // We can't mock these with the old macros, but we need to define them to make
+  // it concrete.
+  int RefQualifiedConstRef() const& override { return 0; }
+  int RefQualifiedConstRefRef() const&& override { return 0; }
+  int RefQualifiedRef() & override { return 0; }
+  int RefQualifiedRefRef() && override { return 0; }
+  int RefQualifiedOverloaded() const& override { return 0; }
+  int RefQualifiedOverloaded() const&& override { return 0; }
+  int RefQualifiedOverloaded() & override { return 0; }
+  int RefQualifiedOverloaded() && override { return 0; }
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(LegacyMockFoo);
+};
+
 #ifdef _MSC_VER
 # pragma warning(pop)
 #endif
 
-class MockMethodFunctionMockerTest : public testing::Test {
+template <class T>
+class FunctionMockerTest : public testing::Test {
  protected:
-  MockMethodFunctionMockerTest() : foo_(&mock_foo_) {}
+  FunctionMockerTest() : foo_(&mock_foo_) {}
 
   FooInterface* const foo_;
-  MockFoo mock_foo_;
+  T mock_foo_;
 };
+using FunctionMockerTestTypes = ::testing::Types<MockFoo, LegacyMockFoo>;
+TYPED_TEST_SUITE(FunctionMockerTest, FunctionMockerTestTypes);
 
 // Tests mocking a void-returning function.
-TEST_F(MockMethodFunctionMockerTest, MocksVoidFunction) {
-  EXPECT_CALL(mock_foo_, VoidReturning(Lt(100)));
-  foo_->VoidReturning(0);
+TYPED_TEST(FunctionMockerTest, MocksVoidFunction) {
+  EXPECT_CALL(this->mock_foo_, VoidReturning(Lt(100)));
+  this->foo_->VoidReturning(0);
 }
 
 // Tests mocking a nullary function.
-TEST_F(MockMethodFunctionMockerTest, MocksNullaryFunction) {
-  EXPECT_CALL(mock_foo_, Nullary())
+TYPED_TEST(FunctionMockerTest, MocksNullaryFunction) {
+  EXPECT_CALL(this->mock_foo_, Nullary())
       .WillOnce(DoDefault())
       .WillOnce(Return(1));
 
-  EXPECT_EQ(0, foo_->Nullary());
-  EXPECT_EQ(1, foo_->Nullary());
+  EXPECT_EQ(0, this->foo_->Nullary());
+  EXPECT_EQ(1, this->foo_->Nullary());
 }
 
 // Tests mocking a unary function.
-TEST_F(MockMethodFunctionMockerTest, MocksUnaryFunction) {
-  EXPECT_CALL(mock_foo_, Unary(Eq(2)))
-      .Times(2)
-      .WillOnce(Return(true));
+TYPED_TEST(FunctionMockerTest, MocksUnaryFunction) {
+  EXPECT_CALL(this->mock_foo_, Unary(Eq(2))).Times(2).WillOnce(Return(true));
 
-  EXPECT_TRUE(foo_->Unary(2));
-  EXPECT_FALSE(foo_->Unary(2));
+  EXPECT_TRUE(this->foo_->Unary(2));
+  EXPECT_FALSE(this->foo_->Unary(2));
 }
 
 // Tests mocking a binary function.
-TEST_F(MockMethodFunctionMockerTest, MocksBinaryFunction) {
-  EXPECT_CALL(mock_foo_, Binary(2, _))
-      .WillOnce(Return(3));
+TYPED_TEST(FunctionMockerTest, MocksBinaryFunction) {
+  EXPECT_CALL(this->mock_foo_, Binary(2, _)).WillOnce(Return(3));
 
-  EXPECT_EQ(3, foo_->Binary(2, 1));
+  EXPECT_EQ(3, this->foo_->Binary(2, 1));
 }
 
 // Tests mocking a decimal function.
-TEST_F(MockMethodFunctionMockerTest, MocksDecimalFunction) {
-  EXPECT_CALL(mock_foo_, Decimal(true, 'a', 0, 0, 1L, A<float>(),
-                                 Lt(100), 5U, NULL, "hi"))
+TYPED_TEST(FunctionMockerTest, MocksDecimalFunction) {
+  EXPECT_CALL(this->mock_foo_,
+              Decimal(true, 'a', 0, 0, 1L, A<float>(), Lt(100), 5U, NULL, "hi"))
       .WillOnce(Return(5));
 
-  EXPECT_EQ(5, foo_->Decimal(true, 'a', 0, 0, 1, 0, 0, 5, nullptr, "hi"));
+  EXPECT_EQ(5, this->foo_->Decimal(true, 'a', 0, 0, 1, 0, 0, 5, nullptr, "hi"));
 }
 
 // Tests mocking a function that takes a non-const reference.
-TEST_F(MockMethodFunctionMockerTest,
-       MocksFunctionWithNonConstReferenceArgument) {
+TYPED_TEST(FunctionMockerTest, MocksFunctionWithNonConstReferenceArgument) {
   int a = 0;
-  EXPECT_CALL(mock_foo_, TakesNonConstReference(Ref(a)))
+  EXPECT_CALL(this->mock_foo_, TakesNonConstReference(Ref(a)))
       .WillOnce(Return(true));
 
-  EXPECT_TRUE(foo_->TakesNonConstReference(a));
+  EXPECT_TRUE(this->foo_->TakesNonConstReference(a));
 }
 
 // Tests mocking a function that takes a const reference.
-TEST_F(MockMethodFunctionMockerTest, MocksFunctionWithConstReferenceArgument) {
+TYPED_TEST(FunctionMockerTest, MocksFunctionWithConstReferenceArgument) {
   int a = 0;
-  EXPECT_CALL(mock_foo_, TakesConstReference(Ref(a)))
+  EXPECT_CALL(this->mock_foo_, TakesConstReference(Ref(a)))
       .WillOnce(Return("Hello"));
 
-  EXPECT_EQ("Hello", foo_->TakesConstReference(a));
+  EXPECT_EQ("Hello", this->foo_->TakesConstReference(a));
 }
 
 // Tests mocking a function that takes a const variable.
-TEST_F(MockMethodFunctionMockerTest, MocksFunctionWithConstArgument) {
-  EXPECT_CALL(mock_foo_, TakesConst(Lt(10)))
-      .WillOnce(DoDefault());
+TYPED_TEST(FunctionMockerTest, MocksFunctionWithConstArgument) {
+  EXPECT_CALL(this->mock_foo_, TakesConst(Lt(10))).WillOnce(DoDefault());
 
-  EXPECT_FALSE(foo_->TakesConst(5));
+  EXPECT_FALSE(this->foo_->TakesConst(5));
 }
 
 // Tests mocking functions overloaded on the number of arguments.
-TEST_F(MockMethodFunctionMockerTest, MocksFunctionsOverloadedOnArgumentNumber) {
-  EXPECT_CALL(mock_foo_, OverloadedOnArgumentNumber())
+TYPED_TEST(FunctionMockerTest, MocksFunctionsOverloadedOnArgumentNumber) {
+  EXPECT_CALL(this->mock_foo_, OverloadedOnArgumentNumber())
       .WillOnce(Return(1));
-  EXPECT_CALL(mock_foo_, OverloadedOnArgumentNumber(_))
+  EXPECT_CALL(this->mock_foo_, OverloadedOnArgumentNumber(_))
       .WillOnce(Return(2));
 
-  EXPECT_EQ(2, foo_->OverloadedOnArgumentNumber(1));
-  EXPECT_EQ(1, foo_->OverloadedOnArgumentNumber());
+  EXPECT_EQ(2, this->foo_->OverloadedOnArgumentNumber(1));
+  EXPECT_EQ(1, this->foo_->OverloadedOnArgumentNumber());
 }
 
 // Tests mocking functions overloaded on the types of argument.
-TEST_F(MockMethodFunctionMockerTest, MocksFunctionsOverloadedOnArgumentType) {
-  EXPECT_CALL(mock_foo_, OverloadedOnArgumentType(An<int>()))
+TYPED_TEST(FunctionMockerTest, MocksFunctionsOverloadedOnArgumentType) {
+  EXPECT_CALL(this->mock_foo_, OverloadedOnArgumentType(An<int>()))
       .WillOnce(Return(1));
-  EXPECT_CALL(mock_foo_, OverloadedOnArgumentType(TypedEq<char>('a')))
+  EXPECT_CALL(this->mock_foo_, OverloadedOnArgumentType(TypedEq<char>('a')))
       .WillOnce(Return('b'));
 
-  EXPECT_EQ(1, foo_->OverloadedOnArgumentType(0));
-  EXPECT_EQ('b', foo_->OverloadedOnArgumentType('a'));
+  EXPECT_EQ(1, this->foo_->OverloadedOnArgumentType(0));
+  EXPECT_EQ('b', this->foo_->OverloadedOnArgumentType('a'));
 }
 
 // Tests mocking functions overloaded on the const-ness of this object.
-TEST_F(MockMethodFunctionMockerTest,
-       MocksFunctionsOverloadedOnConstnessOfThis) {
-  EXPECT_CALL(mock_foo_, OverloadedOnConstness());
-  EXPECT_CALL(Const(mock_foo_), OverloadedOnConstness())
+TYPED_TEST(FunctionMockerTest, MocksFunctionsOverloadedOnConstnessOfThis) {
+  EXPECT_CALL(this->mock_foo_, OverloadedOnConstness());
+  EXPECT_CALL(Const(this->mock_foo_), OverloadedOnConstness())
       .WillOnce(Return('a'));
 
-  EXPECT_EQ(0, foo_->OverloadedOnConstness());
-  EXPECT_EQ('a', Const(*foo_).OverloadedOnConstness());
+  EXPECT_EQ(0, this->foo_->OverloadedOnConstness());
+  EXPECT_EQ('a', Const(*this->foo_).OverloadedOnConstness());
 }
 
-TEST_F(MockMethodFunctionMockerTest, MocksReturnTypeWithComma) {
+TYPED_TEST(FunctionMockerTest, MocksReturnTypeWithComma) {
   const std::map<int, std::string> a_map;
-  EXPECT_CALL(mock_foo_, ReturnTypeWithComma())
-      .WillOnce(Return(a_map));
-  EXPECT_CALL(mock_foo_, ReturnTypeWithComma(42))
-      .WillOnce(Return(a_map));
+  EXPECT_CALL(this->mock_foo_, ReturnTypeWithComma()).WillOnce(Return(a_map));
+  EXPECT_CALL(this->mock_foo_, ReturnTypeWithComma(42)).WillOnce(Return(a_map));
 
-  EXPECT_EQ(a_map, mock_foo_.ReturnTypeWithComma());
-  EXPECT_EQ(a_map, mock_foo_.ReturnTypeWithComma(42));
+  EXPECT_EQ(a_map, this->mock_foo_.ReturnTypeWithComma());
+  EXPECT_EQ(a_map, this->mock_foo_.ReturnTypeWithComma(42));
 }
 
-TEST_F(MockMethodFunctionMockerTest, MocksTypeWithTemplatedCopyCtor) {
-  EXPECT_CALL(mock_foo_, TypeWithTemplatedCopyCtor(_)).WillOnce(Return(true));
-  EXPECT_TRUE(foo_->TypeWithTemplatedCopyCtor(TemplatedCopyable<int>()));
+TYPED_TEST(FunctionMockerTest, MocksTypeWithTemplatedCopyCtor) {
+  EXPECT_CALL(this->mock_foo_, TypeWithTemplatedCopyCtor(_))
+      .WillOnce(Return(true));
+  EXPECT_TRUE(this->foo_->TypeWithTemplatedCopyCtor(TemplatedCopyable<int>()));
 }
 
 #if GTEST_OS_WINDOWS
 // Tests mocking a nullary function with calltype.
-TEST_F(MockMethodFunctionMockerTest, MocksNullaryFunctionWithCallType) {
-  EXPECT_CALL(mock_foo_, CTNullary())
+TYPED_TEST(FunctionMockerTest, MocksNullaryFunctionWithCallType) {
+  EXPECT_CALL(this->mock_foo_, CTNullary())
       .WillOnce(Return(-1))
       .WillOnce(Return(0));
 
-  EXPECT_EQ(-1, foo_->CTNullary());
-  EXPECT_EQ(0, foo_->CTNullary());
+  EXPECT_EQ(-1, this->foo_->CTNullary());
+  EXPECT_EQ(0, this->foo_->CTNullary());
 }
 
 // Tests mocking a unary function with calltype.
-TEST_F(MockMethodFunctionMockerTest, MocksUnaryFunctionWithCallType) {
-  EXPECT_CALL(mock_foo_, CTUnary(Eq(2)))
+TYPED_TEST(FunctionMockerTest, MocksUnaryFunctionWithCallType) {
+  EXPECT_CALL(this->mock_foo_, CTUnary(Eq(2)))
       .Times(2)
       .WillOnce(Return(true))
       .WillOnce(Return(false));
 
-  EXPECT_TRUE(foo_->CTUnary(2));
-  EXPECT_FALSE(foo_->CTUnary(2));
+  EXPECT_TRUE(this->foo_->CTUnary(2));
+  EXPECT_FALSE(this->foo_->CTUnary(2));
 }
 
 // Tests mocking a decimal function with calltype.
-TEST_F(MockMethodFunctionMockerTest, MocksDecimalFunctionWithCallType) {
-  EXPECT_CALL(mock_foo_, CTDecimal(true, 'a', 0, 0, 1L, A<float>(),
-                                   Lt(100), 5U, NULL, "hi"))
+TYPED_TEST(FunctionMockerTest, MocksDecimalFunctionWithCallType) {
+  EXPECT_CALL(this->mock_foo_, CTDecimal(true, 'a', 0, 0, 1L, A<float>(),
+                                         Lt(100), 5U, NULL, "hi"))
       .WillOnce(Return(10));
 
-  EXPECT_EQ(10, foo_->CTDecimal(true, 'a', 0, 0, 1, 0, 0, 5, NULL, "hi"));
+  EXPECT_EQ(10, this->foo_->CTDecimal(true, 'a', 0, 0, 1, 0, 0, 5, NULL, "hi"));
 }
 
 // Tests mocking functions overloaded on the const-ness of this object.
-TEST_F(MockMethodFunctionMockerTest, MocksFunctionsConstFunctionWithCallType) {
-  EXPECT_CALL(Const(mock_foo_), CTConst(_))
-      .WillOnce(Return('a'));
+TYPED_TEST(FunctionMockerTest, MocksFunctionsConstFunctionWithCallType) {
+  EXPECT_CALL(Const(this->mock_foo_), CTConst(_)).WillOnce(Return('a'));
 
-  EXPECT_EQ('a', Const(*foo_).CTConst(0));
+  EXPECT_EQ('a', Const(*this->foo_).CTConst(0));
 }
 
-TEST_F(MockMethodFunctionMockerTest, MocksReturnTypeWithCommaAndCallType) {
+TYPED_TEST(FunctionMockerTest, MocksReturnTypeWithCommaAndCallType) {
   const std::map<int, std::string> a_map;
-  EXPECT_CALL(mock_foo_, CTReturnTypeWithComma())
-      .WillOnce(Return(a_map));
+  EXPECT_CALL(this->mock_foo_, CTReturnTypeWithComma()).WillOnce(Return(a_map));
 
-  EXPECT_EQ(a_map, mock_foo_.CTReturnTypeWithComma());
+  EXPECT_EQ(a_map, this->mock_foo_.CTReturnTypeWithComma());
 }
 
 #endif  // GTEST_OS_WINDOWS
 
+TEST(FunctionMockerTest, RefQualified) {
+  MockFoo mock_foo;
+
+  EXPECT_CALL(mock_foo, RefQualifiedConstRef).WillOnce(Return(1));
+  EXPECT_CALL(std::move(mock_foo),  // NOLINT
+              RefQualifiedConstRefRef)
+      .WillOnce(Return(2));
+  EXPECT_CALL(mock_foo, RefQualifiedRef).WillOnce(Return(3));
+  EXPECT_CALL(std::move(mock_foo),  // NOLINT
+              RefQualifiedRefRef)
+      .WillOnce(Return(4));
+
+  EXPECT_CALL(static_cast<const MockFoo&>(mock_foo), RefQualifiedOverloaded())
+      .WillOnce(Return(5));
+  EXPECT_CALL(static_cast<const MockFoo&&>(mock_foo), RefQualifiedOverloaded())
+      .WillOnce(Return(6));
+  EXPECT_CALL(static_cast<MockFoo&>(mock_foo), RefQualifiedOverloaded())
+      .WillOnce(Return(7));
+  EXPECT_CALL(static_cast<MockFoo&&>(mock_foo), RefQualifiedOverloaded())
+      .WillOnce(Return(8));
+
+  EXPECT_EQ(mock_foo.RefQualifiedConstRef(), 1);
+  EXPECT_EQ(std::move(mock_foo).RefQualifiedConstRefRef(), 2);  // NOLINT
+  EXPECT_EQ(mock_foo.RefQualifiedRef(), 3);
+  EXPECT_EQ(std::move(mock_foo).RefQualifiedRefRef(), 4);  // NOLINT
+
+  EXPECT_EQ(std::cref(mock_foo).get().RefQualifiedOverloaded(), 5);
+  EXPECT_EQ(std::move(std::cref(mock_foo).get())  // NOLINT
+                .RefQualifiedOverloaded(),
+            6);
+  EXPECT_EQ(mock_foo.RefQualifiedOverloaded(), 7);
+  EXPECT_EQ(std::move(mock_foo).RefQualifiedOverloaded(), 8);  // NOLINT
+}
+
 class MockB {
  public:
   MockB() {}
@@ -364,20 +496,33 @@
   GTEST_DISALLOW_COPY_AND_ASSIGN_(MockB);
 };
 
+class LegacyMockB {
+ public:
+  LegacyMockB() {}
+
+  MOCK_METHOD0(DoB, void());
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(LegacyMockB);
+};
+
+template <typename T>
+class ExpectCallTest : public ::testing::Test {};
+using ExpectCallTestTypes = ::testing::Types<MockB, LegacyMockB>;
+TYPED_TEST_SUITE(ExpectCallTest, ExpectCallTestTypes);
+
 // Tests that functions with no EXPECT_CALL() rules can be called any
 // number of times.
-TEST(MockMethodExpectCallTest, UnmentionedFunctionCanBeCalledAnyNumberOfTimes) {
-  {
-    MockB b;
-  }
+TYPED_TEST(ExpectCallTest, UnmentionedFunctionCanBeCalledAnyNumberOfTimes) {
+  { TypeParam b; }
 
   {
-    MockB b;
+    TypeParam b;
     b.DoB();
   }
 
   {
-    MockB b;
+    TypeParam b;
     b.DoB();
     b.DoB();
   }
@@ -416,9 +561,33 @@
   GTEST_DISALLOW_COPY_AND_ASSIGN_(MockStack);
 };
 
+template <typename T>
+class LegacyMockStack : public StackInterface<T> {
+ public:
+  LegacyMockStack() {}
+
+  MOCK_METHOD1_T(Push, void(const T& elem));
+  MOCK_METHOD0_T(Pop, void());
+  MOCK_CONST_METHOD0_T(GetSize, int());  // NOLINT
+  MOCK_CONST_METHOD0_T(GetTop, const T&());
+
+  // Tests that the function return type can contain unprotected comma.
+  MOCK_METHOD0_T(ReturnTypeWithComma, std::map<int, int>());
+  MOCK_CONST_METHOD1_T(ReturnTypeWithComma, std::map<int, int>(int));  // NOLINT
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(LegacyMockStack);
+};
+
+template <typename T>
+class TemplateMockTest : public ::testing::Test {};
+using TemplateMockTestTypes =
+    ::testing::Types<MockStack<int>, LegacyMockStack<int>>;
+TYPED_TEST_SUITE(TemplateMockTest, TemplateMockTestTypes);
+
 // Tests that template mock works.
-TEST(MockMethodTemplateMockTest, Works) {
-  MockStack<int> mock;
+TYPED_TEST(TemplateMockTest, Works) {
+  TypeParam mock;
 
   EXPECT_CALL(mock, GetSize())
       .WillOnce(Return(0))
@@ -439,8 +608,8 @@
   EXPECT_EQ(0, mock.GetSize());
 }
 
-TEST(MockMethodTemplateMockTest, MethodWithCommaInReturnTypeWorks) {
-  MockStack<int> mock;
+TYPED_TEST(TemplateMockTest, MethodWithCommaInReturnTypeWorks) {
+  TypeParam mock;
 
   const std::map<int, int> a_map;
   EXPECT_CALL(mock, ReturnTypeWithComma())
@@ -484,9 +653,31 @@
   GTEST_DISALLOW_COPY_AND_ASSIGN_(MockStackWithCallType);
 };
 
+template <typename T>
+class LegacyMockStackWithCallType : public StackInterfaceWithCallType<T> {
+ public:
+  LegacyMockStackWithCallType() {}
+
+  MOCK_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Push, void(const T& elem));
+  MOCK_METHOD0_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Pop, void());
+  MOCK_CONST_METHOD0_T_WITH_CALLTYPE(STDMETHODCALLTYPE, GetSize, int());
+  MOCK_CONST_METHOD0_T_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTop, const T&());
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(LegacyMockStackWithCallType);
+};
+
+template <typename T>
+class TemplateMockTestWithCallType : public ::testing::Test {};
+using TemplateMockTestWithCallTypeTypes =
+    ::testing::Types<MockStackWithCallType<int>,
+                     LegacyMockStackWithCallType<int>>;
+TYPED_TEST_SUITE(TemplateMockTestWithCallType,
+                 TemplateMockTestWithCallTypeTypes);
+
 // Tests that template mock with calltype works.
-TEST(MockMethodTemplateMockTestWithCallType, Works) {
-  MockStackWithCallType<int> mock;
+TYPED_TEST(TemplateMockTestWithCallType, Works) {
+  TypeParam mock;
 
   EXPECT_CALL(mock, GetSize())
       .WillOnce(Return(0))
@@ -513,6 +704,11 @@
   MOCK_METHOD(int, Overloaded, (int), (const)); \
   MOCK_METHOD(bool, Overloaded, (bool f, int n))
 
+#define LEGACY_MY_MOCK_METHODS1_              \
+  MOCK_METHOD0(Overloaded, void());           \
+  MOCK_CONST_METHOD1(Overloaded, int(int n)); \
+  MOCK_METHOD2(Overloaded, bool(bool f, int n))
+
 class MockOverloadedOnArgNumber {
  public:
   MockOverloadedOnArgNumber() {}
@@ -523,8 +719,25 @@
   GTEST_DISALLOW_COPY_AND_ASSIGN_(MockOverloadedOnArgNumber);
 };
 
-TEST(MockMethodOverloadedMockMethodTest, CanOverloadOnArgNumberInMacroBody) {
-  MockOverloadedOnArgNumber mock;
+class LegacyMockOverloadedOnArgNumber {
+ public:
+  LegacyMockOverloadedOnArgNumber() {}
+
+  LEGACY_MY_MOCK_METHODS1_;
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(LegacyMockOverloadedOnArgNumber);
+};
+
+template <typename T>
+class OverloadedMockMethodTest : public ::testing::Test {};
+using OverloadedMockMethodTestTypes =
+    ::testing::Types<MockOverloadedOnArgNumber,
+                     LegacyMockOverloadedOnArgNumber>;
+TYPED_TEST_SUITE(OverloadedMockMethodTest, OverloadedMockMethodTestTypes);
+
+TYPED_TEST(OverloadedMockMethodTest, CanOverloadOnArgNumberInMacroBody) {
+  TypeParam mock;
   EXPECT_CALL(mock, Overloaded());
   EXPECT_CALL(mock, Overloaded(1)).WillOnce(Return(2));
   EXPECT_CALL(mock, Overloaded(true, 1)).WillOnce(Return(true));
@@ -632,6 +845,68 @@
   EXPECT_EQ(-1, call(foo.AsStdFunction(), i));
 }
 
+namespace {
+
+template <typename Expected, typename F>
+static constexpr bool IsMockFunctionTemplateArgumentDeducedTo(
+    const internal::MockFunction<F>&) {
+  return std::is_same<F, Expected>::value;
+}
+
+}  // namespace
+
+template <typename F>
+class MockMethodMockFunctionSignatureTest : public Test {};
+
+using MockMethodMockFunctionSignatureTypes =
+    Types<void(), int(), void(int), int(int), int(bool, int),
+          int(bool, char, int, int, int, int, int, char, int, bool)>;
+TYPED_TEST_SUITE(MockMethodMockFunctionSignatureTest,
+                 MockMethodMockFunctionSignatureTypes);
+
+TYPED_TEST(MockMethodMockFunctionSignatureTest,
+           IsMockFunctionTemplateArgumentDeducedForRawSignature) {
+  using Argument = TypeParam;
+  MockFunction<Argument> foo;
+  EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo));
+}
+
+TYPED_TEST(MockMethodMockFunctionSignatureTest,
+           IsMockFunctionTemplateArgumentDeducedForStdFunction) {
+  using Argument = std::function<TypeParam>;
+  MockFunction<Argument> foo;
+  EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo));
+}
+
+TYPED_TEST(
+    MockMethodMockFunctionSignatureTest,
+    IsMockFunctionCallMethodSignatureTheSameForRawSignatureAndStdFunction) {
+  using ForRawSignature = decltype(&MockFunction<TypeParam>::Call);
+  using ForStdFunction =
+      decltype(&MockFunction<std::function<TypeParam>>::Call);
+  EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value));
+}
+
+template <typename F>
+struct AlternateCallable {
+};
+
+TYPED_TEST(MockMethodMockFunctionSignatureTest,
+           IsMockFunctionTemplateArgumentDeducedForAlternateCallable) {
+  using Argument = AlternateCallable<TypeParam>;
+  MockFunction<Argument> foo;
+  EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<TypeParam>(foo));
+}
+
+TYPED_TEST(
+    MockMethodMockFunctionSignatureTest,
+    IsMockFunctionCallMethodSignatureTheSameForAlternateCallable) {
+  using ForRawSignature = decltype(&MockFunction<TypeParam>::Call);
+  using ForStdFunction =
+      decltype(&MockFunction<std::function<TypeParam>>::Call);
+  EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value));
+}
+
 
 struct MockMethodSizes0 {
   MOCK_METHOD(void, func, ());
@@ -649,11 +924,62 @@
   MOCK_METHOD(void, func, (int, int, int, int));
 };
 
+struct LegacyMockMethodSizes0 {
+    MOCK_METHOD0(func, void());
+};
+struct LegacyMockMethodSizes1 {
+    MOCK_METHOD1(func, void(int));
+};
+struct LegacyMockMethodSizes2 {
+    MOCK_METHOD2(func, void(int, int));
+};
+struct LegacyMockMethodSizes3 {
+    MOCK_METHOD3(func, void(int, int, int));
+};
+struct LegacyMockMethodSizes4 {
+    MOCK_METHOD4(func, void(int, int, int, int));
+};
+
+
 TEST(MockMethodMockFunctionTest, MockMethodSizeOverhead) {
   EXPECT_EQ(sizeof(MockMethodSizes0), sizeof(MockMethodSizes1));
   EXPECT_EQ(sizeof(MockMethodSizes0), sizeof(MockMethodSizes2));
   EXPECT_EQ(sizeof(MockMethodSizes0), sizeof(MockMethodSizes3));
   EXPECT_EQ(sizeof(MockMethodSizes0), sizeof(MockMethodSizes4));
+
+  EXPECT_EQ(sizeof(LegacyMockMethodSizes0), sizeof(LegacyMockMethodSizes1));
+  EXPECT_EQ(sizeof(LegacyMockMethodSizes0), sizeof(LegacyMockMethodSizes2));
+  EXPECT_EQ(sizeof(LegacyMockMethodSizes0), sizeof(LegacyMockMethodSizes3));
+  EXPECT_EQ(sizeof(LegacyMockMethodSizes0), sizeof(LegacyMockMethodSizes4));
+
+  EXPECT_EQ(sizeof(LegacyMockMethodSizes0), sizeof(MockMethodSizes0));
+}
+
+void hasTwoParams(int, int);
+void MaybeThrows();
+void DoesntThrow() noexcept;
+struct MockMethodNoexceptSpecifier {
+  MOCK_METHOD(void, func1, (), (noexcept));
+  MOCK_METHOD(void, func2, (), (noexcept(true)));
+  MOCK_METHOD(void, func3, (), (noexcept(false)));
+  MOCK_METHOD(void, func4, (), (noexcept(noexcept(MaybeThrows()))));
+  MOCK_METHOD(void, func5, (), (noexcept(noexcept(DoesntThrow()))));
+  MOCK_METHOD(void, func6, (), (noexcept(noexcept(DoesntThrow())), const));
+  MOCK_METHOD(void, func7, (), (const, noexcept(noexcept(DoesntThrow()))));
+  // Put commas in the noexcept expression
+  MOCK_METHOD(void, func8, (), (noexcept(noexcept(hasTwoParams(1, 2))), const));
+};
+
+TEST(MockMethodMockFunctionTest, NoexceptSpecifierPreserved) {
+  EXPECT_TRUE(noexcept(std::declval<MockMethodNoexceptSpecifier>().func1()));
+  EXPECT_TRUE(noexcept(std::declval<MockMethodNoexceptSpecifier>().func2()));
+  EXPECT_FALSE(noexcept(std::declval<MockMethodNoexceptSpecifier>().func3()));
+  EXPECT_FALSE(noexcept(std::declval<MockMethodNoexceptSpecifier>().func4()));
+  EXPECT_TRUE(noexcept(std::declval<MockMethodNoexceptSpecifier>().func5()));
+  EXPECT_TRUE(noexcept(std::declval<MockMethodNoexceptSpecifier>().func6()));
+  EXPECT_TRUE(noexcept(std::declval<MockMethodNoexceptSpecifier>().func7()));
+  EXPECT_EQ(noexcept(std::declval<MockMethodNoexceptSpecifier>().func8()),
+            noexcept(hasTwoParams(1, 2)));
 }
 
 }  // namespace gmock_function_mocker_test
diff --git a/ext/googletest/googlemock/test/gmock-generated-actions_test.cc b/ext/googletest/googlemock/test/gmock-generated-actions_test.cc
deleted file mode 100644
index 4c649a7..0000000
--- a/ext/googletest/googlemock/test/gmock-generated-actions_test.cc
+++ /dev/null
@@ -1,1064 +0,0 @@
-// Copyright 2007, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file tests the built-in actions generated by a script.
-
-#include "gmock/gmock-generated-actions.h"
-
-#include <functional>
-#include <memory>
-#include <sstream>
-#include <string>
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace testing {
-namespace gmock_generated_actions_test {
-
-using ::std::plus;
-using ::std::string;
-using testing::_;
-using testing::Action;
-using testing::ActionInterface;
-using testing::ByRef;
-using testing::DoAll;
-using testing::Invoke;
-using testing::Return;
-using testing::ReturnNew;
-using testing::SetArgPointee;
-using testing::StaticAssertTypeEq;
-using testing::Unused;
-
-// For suppressing compiler warnings on conversion possibly losing precision.
-inline short Short(short n) { return n; }  // NOLINT
-inline char Char(char ch) { return ch; }
-
-// Sample functions and functors for testing various actions.
-int Nullary() { return 1; }
-
-bool g_done = false;
-
-bool ByConstRef(const std::string& s) { return s == "Hi"; }
-
-const double g_double = 0;
-bool ReferencesGlobalDouble(const double& x) { return &x == &g_double; }
-
-struct UnaryFunctor {
-  int operator()(bool x) { return x ? 1 : -1; }
-};
-
-const char* Binary(const char* input, short n) { return input + n; }  // NOLINT
-
-int SumOf5(int a, int b, int c, int d, int e) { return a + b + c + d + e; }
-
-struct SumOf5Functor {
-  int operator()(int a, int b, int c, int d, int e) {
-    return a + b + c + d + e;
-  }
-};
-
-std::string Concat5(const char* s1, const char* s2, const char* s3,
-                    const char* s4, const char* s5) {
-  return std::string(s1) + s2 + s3 + s4 + s5;
-}
-
-int SumOf6(int a, int b, int c, int d, int e, int f) {
-  return a + b + c + d + e + f;
-}
-
-struct SumOf6Functor {
-  int operator()(int a, int b, int c, int d, int e, int f) {
-    return a + b + c + d + e + f;
-  }
-};
-
-std::string Concat6(const char* s1, const char* s2, const char* s3,
-                    const char* s4, const char* s5, const char* s6) {
-  return std::string(s1) + s2 + s3 + s4 + s5 + s6;
-}
-
-std::string Concat7(const char* s1, const char* s2, const char* s3,
-                    const char* s4, const char* s5, const char* s6,
-                    const char* s7) {
-  return std::string(s1) + s2 + s3 + s4 + s5 + s6 + s7;
-}
-
-std::string Concat8(const char* s1, const char* s2, const char* s3,
-                    const char* s4, const char* s5, const char* s6,
-                    const char* s7, const char* s8) {
-  return std::string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8;
-}
-
-std::string Concat9(const char* s1, const char* s2, const char* s3,
-                    const char* s4, const char* s5, const char* s6,
-                    const char* s7, const char* s8, const char* s9) {
-  return std::string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9;
-}
-
-std::string Concat10(const char* s1, const char* s2, const char* s3,
-                     const char* s4, const char* s5, const char* s6,
-                     const char* s7, const char* s8, const char* s9,
-                     const char* s10) {
-  return std::string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10;
-}
-
-// A helper that turns the type of a C-string literal from const
-// char[N] to const char*.
-inline const char* CharPtr(const char* s) { return s; }
-
-// Tests InvokeArgument<N>(...).
-
-// Tests using InvokeArgument with a nullary function.
-TEST(InvokeArgumentTest, Function0) {
-  Action<int(int, int(*)())> a = InvokeArgument<1>();  // NOLINT
-  EXPECT_EQ(1, a.Perform(std::make_tuple(2, &Nullary)));
-}
-
-// Tests using InvokeArgument with a unary function.
-TEST(InvokeArgumentTest, Functor1) {
-  Action<int(UnaryFunctor)> a = InvokeArgument<0>(true);  // NOLINT
-  EXPECT_EQ(1, a.Perform(std::make_tuple(UnaryFunctor())));
-}
-
-// Tests using InvokeArgument with a 5-ary function.
-TEST(InvokeArgumentTest, Function5) {
-  Action<int(int(*)(int, int, int, int, int))> a =  // NOLINT
-      InvokeArgument<0>(10000, 2000, 300, 40, 5);
-  EXPECT_EQ(12345, a.Perform(std::make_tuple(&SumOf5)));
-}
-
-// Tests using InvokeArgument with a 5-ary functor.
-TEST(InvokeArgumentTest, Functor5) {
-  Action<int(SumOf5Functor)> a =  // NOLINT
-      InvokeArgument<0>(10000, 2000, 300, 40, 5);
-  EXPECT_EQ(12345, a.Perform(std::make_tuple(SumOf5Functor())));
-}
-
-// Tests using InvokeArgument with a 6-ary function.
-TEST(InvokeArgumentTest, Function6) {
-  Action<int(int(*)(int, int, int, int, int, int))> a =  // NOLINT
-      InvokeArgument<0>(100000, 20000, 3000, 400, 50, 6);
-  EXPECT_EQ(123456, a.Perform(std::make_tuple(&SumOf6)));
-}
-
-// Tests using InvokeArgument with a 6-ary functor.
-TEST(InvokeArgumentTest, Functor6) {
-  Action<int(SumOf6Functor)> a =  // NOLINT
-      InvokeArgument<0>(100000, 20000, 3000, 400, 50, 6);
-  EXPECT_EQ(123456, a.Perform(std::make_tuple(SumOf6Functor())));
-}
-
-// Tests using InvokeArgument with a 7-ary function.
-TEST(InvokeArgumentTest, Function7) {
-  Action<std::string(std::string(*)(const char*, const char*, const char*,
-                                    const char*, const char*, const char*,
-                                    const char*))>
-      a = InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7");
-  EXPECT_EQ("1234567", a.Perform(std::make_tuple(&Concat7)));
-}
-
-// Tests using InvokeArgument with a 8-ary function.
-TEST(InvokeArgumentTest, Function8) {
-  Action<std::string(std::string(*)(const char*, const char*, const char*,
-                                    const char*, const char*, const char*,
-                                    const char*, const char*))>
-      a = InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7", "8");
-  EXPECT_EQ("12345678", a.Perform(std::make_tuple(&Concat8)));
-}
-
-// Tests using InvokeArgument with a 9-ary function.
-TEST(InvokeArgumentTest, Function9) {
-  Action<std::string(std::string(*)(const char*, const char*, const char*,
-                                    const char*, const char*, const char*,
-                                    const char*, const char*, const char*))>
-      a = InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7", "8", "9");
-  EXPECT_EQ("123456789", a.Perform(std::make_tuple(&Concat9)));
-}
-
-// Tests using InvokeArgument with a 10-ary function.
-TEST(InvokeArgumentTest, Function10) {
-  Action<std::string(std::string(*)(
-      const char*, const char*, const char*, const char*, const char*,
-      const char*, const char*, const char*, const char*, const char*))>
-      a = InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7", "8", "9", "0");
-  EXPECT_EQ("1234567890", a.Perform(std::make_tuple(&Concat10)));
-}
-
-// Tests using InvokeArgument with a function that takes a pointer argument.
-TEST(InvokeArgumentTest, ByPointerFunction) {
-  Action<const char*(const char*(*)(const char* input, short n))> a =  // NOLINT
-      InvokeArgument<0>(static_cast<const char*>("Hi"), Short(1));
-  EXPECT_STREQ("i", a.Perform(std::make_tuple(&Binary)));
-}
-
-// Tests using InvokeArgument with a function that takes a const char*
-// by passing it a C-string literal.
-TEST(InvokeArgumentTest, FunctionWithCStringLiteral) {
-  Action<const char*(const char*(*)(const char* input, short n))> a =  // NOLINT
-      InvokeArgument<0>("Hi", Short(1));
-  EXPECT_STREQ("i", a.Perform(std::make_tuple(&Binary)));
-}
-
-// Tests using InvokeArgument with a function that takes a const reference.
-TEST(InvokeArgumentTest, ByConstReferenceFunction) {
-  Action<bool(bool (*function)(const std::string& s))> a =  // NOLINT
-      InvokeArgument<0>(std::string("Hi"));
-  // When action 'a' is constructed, it makes a copy of the temporary
-  // string object passed to it, so it's OK to use 'a' later, when the
-  // temporary object has already died.
-  EXPECT_TRUE(a.Perform(std::make_tuple(&ByConstRef)));
-}
-
-// Tests using InvokeArgument with ByRef() and a function that takes a
-// const reference.
-TEST(InvokeArgumentTest, ByExplicitConstReferenceFunction) {
-  Action<bool(bool(*)(const double& x))> a =  // NOLINT
-      InvokeArgument<0>(ByRef(g_double));
-  // The above line calls ByRef() on a const value.
-  EXPECT_TRUE(a.Perform(std::make_tuple(&ReferencesGlobalDouble)));
-
-  double x = 0;
-  a = InvokeArgument<0>(ByRef(x));  // This calls ByRef() on a non-const.
-  EXPECT_FALSE(a.Perform(std::make_tuple(&ReferencesGlobalDouble)));
-}
-
-// Tests DoAll(a1, a2).
-TEST(DoAllTest, TwoActions) {
-  int n = 0;
-  Action<int(int*)> a = DoAll(SetArgPointee<0>(1),  // NOLINT
-                              Return(2));
-  EXPECT_EQ(2, a.Perform(std::make_tuple(&n)));
-  EXPECT_EQ(1, n);
-}
-
-// Tests DoAll(a1, a2, a3).
-TEST(DoAllTest, ThreeActions) {
-  int m = 0, n = 0;
-  Action<int(int*, int*)> a = DoAll(SetArgPointee<0>(1),  // NOLINT
-                                    SetArgPointee<1>(2),
-                                    Return(3));
-  EXPECT_EQ(3, a.Perform(std::make_tuple(&m, &n)));
-  EXPECT_EQ(1, m);
-  EXPECT_EQ(2, n);
-}
-
-// Tests DoAll(a1, a2, a3, a4).
-TEST(DoAllTest, FourActions) {
-  int m = 0, n = 0;
-  char ch = '\0';
-  Action<int(int*, int*, char*)> a =  // NOLINT
-      DoAll(SetArgPointee<0>(1),
-            SetArgPointee<1>(2),
-            SetArgPointee<2>('a'),
-            Return(3));
-  EXPECT_EQ(3, a.Perform(std::make_tuple(&m, &n, &ch)));
-  EXPECT_EQ(1, m);
-  EXPECT_EQ(2, n);
-  EXPECT_EQ('a', ch);
-}
-
-// Tests DoAll(a1, a2, a3, a4, a5).
-TEST(DoAllTest, FiveActions) {
-  int m = 0, n = 0;
-  char a = '\0', b = '\0';
-  Action<int(int*, int*, char*, char*)> action =  // NOLINT
-      DoAll(SetArgPointee<0>(1),
-            SetArgPointee<1>(2),
-            SetArgPointee<2>('a'),
-            SetArgPointee<3>('b'),
-            Return(3));
-  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b)));
-  EXPECT_EQ(1, m);
-  EXPECT_EQ(2, n);
-  EXPECT_EQ('a', a);
-  EXPECT_EQ('b', b);
-}
-
-// Tests DoAll(a1, a2, ..., a6).
-TEST(DoAllTest, SixActions) {
-  int m = 0, n = 0;
-  char a = '\0', b = '\0', c = '\0';
-  Action<int(int*, int*, char*, char*, char*)> action =  // NOLINT
-      DoAll(SetArgPointee<0>(1),
-            SetArgPointee<1>(2),
-            SetArgPointee<2>('a'),
-            SetArgPointee<3>('b'),
-            SetArgPointee<4>('c'),
-            Return(3));
-  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c)));
-  EXPECT_EQ(1, m);
-  EXPECT_EQ(2, n);
-  EXPECT_EQ('a', a);
-  EXPECT_EQ('b', b);
-  EXPECT_EQ('c', c);
-}
-
-// Tests DoAll(a1, a2, ..., a7).
-TEST(DoAllTest, SevenActions) {
-  int m = 0, n = 0;
-  char a = '\0', b = '\0', c = '\0', d = '\0';
-  Action<int(int*, int*, char*, char*, char*, char*)> action =  // NOLINT
-      DoAll(SetArgPointee<0>(1),
-            SetArgPointee<1>(2),
-            SetArgPointee<2>('a'),
-            SetArgPointee<3>('b'),
-            SetArgPointee<4>('c'),
-            SetArgPointee<5>('d'),
-            Return(3));
-  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c, &d)));
-  EXPECT_EQ(1, m);
-  EXPECT_EQ(2, n);
-  EXPECT_EQ('a', a);
-  EXPECT_EQ('b', b);
-  EXPECT_EQ('c', c);
-  EXPECT_EQ('d', d);
-}
-
-// Tests DoAll(a1, a2, ..., a8).
-TEST(DoAllTest, EightActions) {
-  int m = 0, n = 0;
-  char a = '\0', b = '\0', c = '\0', d = '\0', e = '\0';
-  Action<int(int*, int*, char*, char*, char*, char*,  // NOLINT
-             char*)> action =
-      DoAll(SetArgPointee<0>(1),
-            SetArgPointee<1>(2),
-            SetArgPointee<2>('a'),
-            SetArgPointee<3>('b'),
-            SetArgPointee<4>('c'),
-            SetArgPointee<5>('d'),
-            SetArgPointee<6>('e'),
-            Return(3));
-  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c, &d, &e)));
-  EXPECT_EQ(1, m);
-  EXPECT_EQ(2, n);
-  EXPECT_EQ('a', a);
-  EXPECT_EQ('b', b);
-  EXPECT_EQ('c', c);
-  EXPECT_EQ('d', d);
-  EXPECT_EQ('e', e);
-}
-
-// Tests DoAll(a1, a2, ..., a9).
-TEST(DoAllTest, NineActions) {
-  int m = 0, n = 0;
-  char a = '\0', b = '\0', c = '\0', d = '\0', e = '\0', f = '\0';
-  Action<int(int*, int*, char*, char*, char*, char*,  // NOLINT
-             char*, char*)> action =
-      DoAll(SetArgPointee<0>(1),
-            SetArgPointee<1>(2),
-            SetArgPointee<2>('a'),
-            SetArgPointee<3>('b'),
-            SetArgPointee<4>('c'),
-            SetArgPointee<5>('d'),
-            SetArgPointee<6>('e'),
-            SetArgPointee<7>('f'),
-            Return(3));
-  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c, &d, &e, &f)));
-  EXPECT_EQ(1, m);
-  EXPECT_EQ(2, n);
-  EXPECT_EQ('a', a);
-  EXPECT_EQ('b', b);
-  EXPECT_EQ('c', c);
-  EXPECT_EQ('d', d);
-  EXPECT_EQ('e', e);
-  EXPECT_EQ('f', f);
-}
-
-// Tests DoAll(a1, a2, ..., a10).
-TEST(DoAllTest, TenActions) {
-  int m = 0, n = 0;
-  char a = '\0', b = '\0', c = '\0', d = '\0';
-  char e = '\0', f = '\0', g = '\0';
-  Action<int(int*, int*, char*, char*, char*, char*,  // NOLINT
-             char*, char*, char*)> action =
-      DoAll(SetArgPointee<0>(1),
-            SetArgPointee<1>(2),
-            SetArgPointee<2>('a'),
-            SetArgPointee<3>('b'),
-            SetArgPointee<4>('c'),
-            SetArgPointee<5>('d'),
-            SetArgPointee<6>('e'),
-            SetArgPointee<7>('f'),
-            SetArgPointee<8>('g'),
-            Return(3));
-  EXPECT_EQ(
-      3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c, &d, &e, &f, &g)));
-  EXPECT_EQ(1, m);
-  EXPECT_EQ(2, n);
-  EXPECT_EQ('a', a);
-  EXPECT_EQ('b', b);
-  EXPECT_EQ('c', c);
-  EXPECT_EQ('d', d);
-  EXPECT_EQ('e', e);
-  EXPECT_EQ('f', f);
-  EXPECT_EQ('g', g);
-}
-
-// The ACTION*() macros trigger warning C4100 (unreferenced formal
-// parameter) in MSVC with -W4.  Unfortunately they cannot be fixed in
-// the macro definition, as the warnings are generated when the macro
-// is expanded and macro expansion cannot contain #pragma.  Therefore
-// we suppress them here.
-// Also suppress C4503 decorated name length exceeded, name was truncated
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable:4100)
-# pragma warning(disable:4503)
-#endif
-// Tests the ACTION*() macro family.
-
-// Tests that ACTION() can define an action that doesn't reference the
-// mock function arguments.
-ACTION(Return5) { return 5; }
-
-TEST(ActionMacroTest, WorksWhenNotReferencingArguments) {
-  Action<double()> a1 = Return5();
-  EXPECT_DOUBLE_EQ(5, a1.Perform(std::make_tuple()));
-
-  Action<int(double, bool)> a2 = Return5();
-  EXPECT_EQ(5, a2.Perform(std::make_tuple(1, true)));
-}
-
-// Tests that ACTION() can define an action that returns void.
-ACTION(IncrementArg1) { (*arg1)++; }
-
-TEST(ActionMacroTest, WorksWhenReturningVoid) {
-  Action<void(int, int*)> a1 = IncrementArg1();
-  int n = 0;
-  a1.Perform(std::make_tuple(5, &n));
-  EXPECT_EQ(1, n);
-}
-
-// Tests that the body of ACTION() can reference the type of the
-// argument.
-ACTION(IncrementArg2) {
-  StaticAssertTypeEq<int*, arg2_type>();
-  arg2_type temp = arg2;
-  (*temp)++;
-}
-
-TEST(ActionMacroTest, CanReferenceArgumentType) {
-  Action<void(int, bool, int*)> a1 = IncrementArg2();
-  int n = 0;
-  a1.Perform(std::make_tuple(5, false, &n));
-  EXPECT_EQ(1, n);
-}
-
-// Tests that the body of ACTION() can reference the argument tuple
-// via args_type and args.
-ACTION(Sum2) {
-  StaticAssertTypeEq<std::tuple<int, char, int*>, args_type>();
-  args_type args_copy = args;
-  return std::get<0>(args_copy) + std::get<1>(args_copy);
-}
-
-TEST(ActionMacroTest, CanReferenceArgumentTuple) {
-  Action<int(int, char, int*)> a1 = Sum2();
-  int dummy = 0;
-  EXPECT_EQ(11, a1.Perform(std::make_tuple(5, Char(6), &dummy)));
-}
-
-// Tests that the body of ACTION() can reference the mock function
-// type.
-int Dummy(bool flag) { return flag? 1 : 0; }
-
-ACTION(InvokeDummy) {
-  StaticAssertTypeEq<int(bool), function_type>();
-  function_type* fp = &Dummy;
-  return (*fp)(true);
-}
-
-TEST(ActionMacroTest, CanReferenceMockFunctionType) {
-  Action<int(bool)> a1 = InvokeDummy();
-  EXPECT_EQ(1, a1.Perform(std::make_tuple(true)));
-  EXPECT_EQ(1, a1.Perform(std::make_tuple(false)));
-}
-
-// Tests that the body of ACTION() can reference the mock function's
-// return type.
-ACTION(InvokeDummy2) {
-  StaticAssertTypeEq<int, return_type>();
-  return_type result = Dummy(true);
-  return result;
-}
-
-TEST(ActionMacroTest, CanReferenceMockFunctionReturnType) {
-  Action<int(bool)> a1 = InvokeDummy2();
-  EXPECT_EQ(1, a1.Perform(std::make_tuple(true)));
-  EXPECT_EQ(1, a1.Perform(std::make_tuple(false)));
-}
-
-// Tests that ACTION() works for arguments passed by const reference.
-ACTION(ReturnAddrOfConstBoolReferenceArg) {
-  StaticAssertTypeEq<const bool&, arg1_type>();
-  return &arg1;
-}
-
-TEST(ActionMacroTest, WorksForConstReferenceArg) {
-  Action<const bool*(int, const bool&)> a = ReturnAddrOfConstBoolReferenceArg();
-  const bool b = false;
-  EXPECT_EQ(&b, a.Perform(std::tuple<int, const bool&>(0, b)));
-}
-
-// Tests that ACTION() works for arguments passed by non-const reference.
-ACTION(ReturnAddrOfIntReferenceArg) {
-  StaticAssertTypeEq<int&, arg0_type>();
-  return &arg0;
-}
-
-TEST(ActionMacroTest, WorksForNonConstReferenceArg) {
-  Action<int*(int&, bool, int)> a = ReturnAddrOfIntReferenceArg();
-  int n = 0;
-  EXPECT_EQ(&n, a.Perform(std::tuple<int&, bool, int>(n, true, 1)));
-}
-
-// Tests that ACTION() can be used in a namespace.
-namespace action_test {
-ACTION(Sum) { return arg0 + arg1; }
-}  // namespace action_test
-
-TEST(ActionMacroTest, WorksInNamespace) {
-  Action<int(int, int)> a1 = action_test::Sum();
-  EXPECT_EQ(3, a1.Perform(std::make_tuple(1, 2)));
-}
-
-// Tests that the same ACTION definition works for mock functions with
-// different argument numbers.
-ACTION(PlusTwo) { return arg0 + 2; }
-
-TEST(ActionMacroTest, WorksForDifferentArgumentNumbers) {
-  Action<int(int)> a1 = PlusTwo();
-  EXPECT_EQ(4, a1.Perform(std::make_tuple(2)));
-
-  Action<double(float, void*)> a2 = PlusTwo();
-  int dummy;
-  EXPECT_DOUBLE_EQ(6, a2.Perform(std::make_tuple(4.0f, &dummy)));
-}
-
-// Tests that ACTION_P can define a parameterized action.
-ACTION_P(Plus, n) { return arg0 + n; }
-
-TEST(ActionPMacroTest, DefinesParameterizedAction) {
-  Action<int(int m, bool t)> a1 = Plus(9);
-  EXPECT_EQ(10, a1.Perform(std::make_tuple(1, true)));
-}
-
-// Tests that the body of ACTION_P can reference the argument types
-// and the parameter type.
-ACTION_P(TypedPlus, n) {
-  arg0_type t1 = arg0;
-  n_type t2 = n;
-  return t1 + t2;
-}
-
-TEST(ActionPMacroTest, CanReferenceArgumentAndParameterTypes) {
-  Action<int(char m, bool t)> a1 = TypedPlus(9);
-  EXPECT_EQ(10, a1.Perform(std::make_tuple(Char(1), true)));
-}
-
-// Tests that a parameterized action can be used in any mock function
-// whose type is compatible.
-TEST(ActionPMacroTest, WorksInCompatibleMockFunction) {
-  Action<std::string(const std::string& s)> a1 = Plus("tail");
-  const std::string re = "re";
-  std::tuple<const std::string> dummy = std::make_tuple(re);
-  EXPECT_EQ("retail", a1.Perform(dummy));
-}
-
-// Tests that we can use ACTION*() to define actions overloaded on the
-// number of parameters.
-
-ACTION(OverloadedAction) { return arg0 ? arg1 : "hello"; }
-
-ACTION_P(OverloadedAction, default_value) {
-  return arg0 ? arg1 : default_value;
-}
-
-ACTION_P2(OverloadedAction, true_value, false_value) {
-  return arg0 ? true_value : false_value;
-}
-
-TEST(ActionMacroTest, CanDefineOverloadedActions) {
-  typedef Action<const char*(bool, const char*)> MyAction;
-
-  const MyAction a1 = OverloadedAction();
-  EXPECT_STREQ("hello", a1.Perform(std::make_tuple(false, CharPtr("world"))));
-  EXPECT_STREQ("world", a1.Perform(std::make_tuple(true, CharPtr("world"))));
-
-  const MyAction a2 = OverloadedAction("hi");
-  EXPECT_STREQ("hi", a2.Perform(std::make_tuple(false, CharPtr("world"))));
-  EXPECT_STREQ("world", a2.Perform(std::make_tuple(true, CharPtr("world"))));
-
-  const MyAction a3 = OverloadedAction("hi", "you");
-  EXPECT_STREQ("hi", a3.Perform(std::make_tuple(true, CharPtr("world"))));
-  EXPECT_STREQ("you", a3.Perform(std::make_tuple(false, CharPtr("world"))));
-}
-
-// Tests ACTION_Pn where n >= 3.
-
-ACTION_P3(Plus, m, n, k) { return arg0 + m + n + k; }
-
-TEST(ActionPnMacroTest, WorksFor3Parameters) {
-  Action<double(int m, bool t)> a1 = Plus(100, 20, 3.4);
-  EXPECT_DOUBLE_EQ(3123.4, a1.Perform(std::make_tuple(3000, true)));
-
-  Action<std::string(const std::string& s)> a2 = Plus("tail", "-", ">");
-  const std::string re = "re";
-  std::tuple<const std::string> dummy = std::make_tuple(re);
-  EXPECT_EQ("retail->", a2.Perform(dummy));
-}
-
-ACTION_P4(Plus, p0, p1, p2, p3) { return arg0 + p0 + p1 + p2 + p3; }
-
-TEST(ActionPnMacroTest, WorksFor4Parameters) {
-  Action<int(int)> a1 = Plus(1, 2, 3, 4);
-  EXPECT_EQ(10 + 1 + 2 + 3 + 4, a1.Perform(std::make_tuple(10)));
-}
-
-ACTION_P5(Plus, p0, p1, p2, p3, p4) { return arg0 + p0 + p1 + p2 + p3 + p4; }
-
-TEST(ActionPnMacroTest, WorksFor5Parameters) {
-  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5);
-  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5, a1.Perform(std::make_tuple(10)));
-}
-
-ACTION_P6(Plus, p0, p1, p2, p3, p4, p5) {
-  return arg0 + p0 + p1 + p2 + p3 + p4 + p5;
-}
-
-TEST(ActionPnMacroTest, WorksFor6Parameters) {
-  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6);
-  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6, a1.Perform(std::make_tuple(10)));
-}
-
-ACTION_P7(Plus, p0, p1, p2, p3, p4, p5, p6) {
-  return arg0 + p0 + p1 + p2 + p3 + p4 + p5 + p6;
-}
-
-TEST(ActionPnMacroTest, WorksFor7Parameters) {
-  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6, 7);
-  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7, a1.Perform(std::make_tuple(10)));
-}
-
-ACTION_P8(Plus, p0, p1, p2, p3, p4, p5, p6, p7) {
-  return arg0 + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7;
-}
-
-TEST(ActionPnMacroTest, WorksFor8Parameters) {
-  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6, 7, 8);
-  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8,
-            a1.Perform(std::make_tuple(10)));
-}
-
-ACTION_P9(Plus, p0, p1, p2, p3, p4, p5, p6, p7, p8) {
-  return arg0 + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8;
-}
-
-TEST(ActionPnMacroTest, WorksFor9Parameters) {
-  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6, 7, 8, 9);
-  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9,
-            a1.Perform(std::make_tuple(10)));
-}
-
-ACTION_P10(Plus, p0, p1, p2, p3, p4, p5, p6, p7, p8, last_param) {
-  arg0_type t0 = arg0;
-  last_param_type t9 = last_param;
-  return t0 + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + t9;
-}
-
-TEST(ActionPnMacroTest, WorksFor10Parameters) {
-  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
-  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10,
-            a1.Perform(std::make_tuple(10)));
-}
-
-// Tests that the action body can promote the parameter types.
-
-ACTION_P2(PadArgument, prefix, suffix) {
-  // The following lines promote the two parameters to desired types.
-  std::string prefix_str(prefix);
-  char suffix_char = static_cast<char>(suffix);
-  return prefix_str + arg0 + suffix_char;
-}
-
-TEST(ActionPnMacroTest, SimpleTypePromotion) {
-  Action<std::string(const char*)> no_promo =
-      PadArgument(std::string("foo"), 'r');
-  Action<std::string(const char*)> promo =
-      PadArgument("foo", static_cast<int>('r'));
-  EXPECT_EQ("foobar", no_promo.Perform(std::make_tuple(CharPtr("ba"))));
-  EXPECT_EQ("foobar", promo.Perform(std::make_tuple(CharPtr("ba"))));
-}
-
-// Tests that we can partially restrict parameter types using a
-// straight-forward pattern.
-
-// Defines a generic action that doesn't restrict the types of its
-// parameters.
-ACTION_P3(ConcatImpl, a, b, c) {
-  std::stringstream ss;
-  ss << a << b << c;
-  return ss.str();
-}
-
-// Next, we try to restrict that either the first parameter is a
-// string, or the second parameter is an int.
-
-// Defines a partially specialized wrapper that restricts the first
-// parameter to std::string.
-template <typename T1, typename T2>
-// ConcatImplActionP3 is the class template ACTION_P3 uses to
-// implement ConcatImpl.  We shouldn't change the name as this
-// pattern requires the user to use it directly.
-ConcatImplActionP3<std::string, T1, T2>
-Concat(const std::string& a, T1 b, T2 c) {
-  GTEST_INTENTIONAL_CONST_COND_PUSH_()
-  if (true) {
-  GTEST_INTENTIONAL_CONST_COND_POP_()
-    // This branch verifies that ConcatImpl() can be invoked without
-    // explicit template arguments.
-    return ConcatImpl(a, b, c);
-  } else {
-    // This branch verifies that ConcatImpl() can also be invoked with
-    // explicit template arguments.  It doesn't really need to be
-    // executed as this is a compile-time verification.
-    return ConcatImpl<std::string, T1, T2>(a, b, c);
-  }
-}
-
-// Defines another partially specialized wrapper that restricts the
-// second parameter to int.
-template <typename T1, typename T2>
-ConcatImplActionP3<T1, int, T2>
-Concat(T1 a, int b, T2 c) {
-  return ConcatImpl(a, b, c);
-}
-
-TEST(ActionPnMacroTest, CanPartiallyRestrictParameterTypes) {
-  Action<const std::string()> a1 = Concat("Hello", "1", 2);
-  EXPECT_EQ("Hello12", a1.Perform(std::make_tuple()));
-
-  a1 = Concat(1, 2, 3);
-  EXPECT_EQ("123", a1.Perform(std::make_tuple()));
-}
-
-// Verifies the type of an ACTION*.
-
-ACTION(DoFoo) {}
-ACTION_P(DoFoo, p) {}
-ACTION_P2(DoFoo, p0, p1) {}
-
-TEST(ActionPnMacroTest, TypesAreCorrect) {
-  // DoFoo() must be assignable to a DoFooAction variable.
-  DoFooAction a0 = DoFoo();
-
-  // DoFoo(1) must be assignable to a DoFooActionP variable.
-  DoFooActionP<int> a1 = DoFoo(1);
-
-  // DoFoo(p1, ..., pk) must be assignable to a DoFooActionPk
-  // variable, and so on.
-  DoFooActionP2<int, char> a2 = DoFoo(1, '2');
-  PlusActionP3<int, int, char> a3 = Plus(1, 2, '3');
-  PlusActionP4<int, int, int, char> a4 = Plus(1, 2, 3, '4');
-  PlusActionP5<int, int, int, int, char> a5 = Plus(1, 2, 3, 4, '5');
-  PlusActionP6<int, int, int, int, int, char> a6 = Plus(1, 2, 3, 4, 5, '6');
-  PlusActionP7<int, int, int, int, int, int, char> a7 =
-      Plus(1, 2, 3, 4, 5, 6, '7');
-  PlusActionP8<int, int, int, int, int, int, int, char> a8 =
-      Plus(1, 2, 3, 4, 5, 6, 7, '8');
-  PlusActionP9<int, int, int, int, int, int, int, int, char> a9 =
-      Plus(1, 2, 3, 4, 5, 6, 7, 8, '9');
-  PlusActionP10<int, int, int, int, int, int, int, int, int, char> a10 =
-      Plus(1, 2, 3, 4, 5, 6, 7, 8, 9, '0');
-
-  // Avoid "unused variable" warnings.
-  (void)a0;
-  (void)a1;
-  (void)a2;
-  (void)a3;
-  (void)a4;
-  (void)a5;
-  (void)a6;
-  (void)a7;
-  (void)a8;
-  (void)a9;
-  (void)a10;
-}
-
-// Tests that an ACTION_P*() action can be explicitly instantiated
-// with reference-typed parameters.
-
-ACTION_P(Plus1, x) { return x; }
-ACTION_P2(Plus2, x, y) { return x + y; }
-ACTION_P3(Plus3, x, y, z) { return x + y + z; }
-ACTION_P10(Plus10, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) {
-  return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9;
-}
-
-TEST(ActionPnMacroTest, CanExplicitlyInstantiateWithReferenceTypes) {
-  int x = 1, y = 2, z = 3;
-  const std::tuple<> empty = std::make_tuple();
-
-  Action<int()> a = Plus1<int&>(x);
-  EXPECT_EQ(1, a.Perform(empty));
-
-  a = Plus2<const int&, int&>(x, y);
-  EXPECT_EQ(3, a.Perform(empty));
-
-  a = Plus3<int&, const int&, int&>(x, y, z);
-  EXPECT_EQ(6, a.Perform(empty));
-
-  int n[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
-  a = Plus10<const int&, int&, const int&, int&, const int&, int&, const int&,
-      int&, const int&, int&>(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7],
-                              n[8], n[9]);
-  EXPECT_EQ(55, a.Perform(empty));
-}
-
-class NullaryConstructorClass {
- public:
-  NullaryConstructorClass() : value_(123) {}
-  int value_;
-};
-
-// Tests using ReturnNew() with a nullary constructor.
-TEST(ReturnNewTest, NoArgs) {
-  Action<NullaryConstructorClass*()> a = ReturnNew<NullaryConstructorClass>();
-  NullaryConstructorClass* c = a.Perform(std::make_tuple());
-  EXPECT_EQ(123, c->value_);
-  delete c;
-}
-
-class UnaryConstructorClass {
- public:
-  explicit UnaryConstructorClass(int value) : value_(value) {}
-  int value_;
-};
-
-// Tests using ReturnNew() with a unary constructor.
-TEST(ReturnNewTest, Unary) {
-  Action<UnaryConstructorClass*()> a = ReturnNew<UnaryConstructorClass>(4000);
-  UnaryConstructorClass* c = a.Perform(std::make_tuple());
-  EXPECT_EQ(4000, c->value_);
-  delete c;
-}
-
-TEST(ReturnNewTest, UnaryWorksWhenMockMethodHasArgs) {
-  Action<UnaryConstructorClass*(bool, int)> a =
-      ReturnNew<UnaryConstructorClass>(4000);
-  UnaryConstructorClass* c = a.Perform(std::make_tuple(false, 5));
-  EXPECT_EQ(4000, c->value_);
-  delete c;
-}
-
-TEST(ReturnNewTest, UnaryWorksWhenMockMethodReturnsPointerToConst) {
-  Action<const UnaryConstructorClass*()> a =
-      ReturnNew<UnaryConstructorClass>(4000);
-  const UnaryConstructorClass* c = a.Perform(std::make_tuple());
-  EXPECT_EQ(4000, c->value_);
-  delete c;
-}
-
-class TenArgConstructorClass {
- public:
-  TenArgConstructorClass(int a1, int a2, int a3, int a4, int a5,
-                         int a6, int a7, int a8, int a9, int a10)
-    : value_(a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10) {
-  }
-  int value_;
-};
-
-// Tests using ReturnNew() with a 10-argument constructor.
-TEST(ReturnNewTest, ConstructorThatTakes10Arguments) {
-  Action<TenArgConstructorClass*()> a =
-      ReturnNew<TenArgConstructorClass>(1000000000, 200000000, 30000000,
-                                        4000000, 500000, 60000,
-                                        7000, 800, 90, 0);
-  TenArgConstructorClass* c = a.Perform(std::make_tuple());
-  EXPECT_EQ(1234567890, c->value_);
-  delete c;
-}
-
-// Tests that ACTION_TEMPLATE works when there is no value parameter.
-ACTION_TEMPLATE(CreateNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_0_VALUE_PARAMS()) {
-  return new T;
-}
-
-TEST(ActionTemplateTest, WorksWithoutValueParam) {
-  const Action<int*()> a = CreateNew<int>();
-  int* p = a.Perform(std::make_tuple());
-  delete p;
-}
-
-// Tests that ACTION_TEMPLATE works when there are value parameters.
-ACTION_TEMPLATE(CreateNew,
-                HAS_1_TEMPLATE_PARAMS(typename, T),
-                AND_1_VALUE_PARAMS(a0)) {
-  return new T(a0);
-}
-
-TEST(ActionTemplateTest, WorksWithValueParams) {
-  const Action<int*()> a = CreateNew<int>(42);
-  int* p = a.Perform(std::make_tuple());
-  EXPECT_EQ(42, *p);
-  delete p;
-}
-
-// Tests that ACTION_TEMPLATE works for integral template parameters.
-ACTION_TEMPLATE(MyDeleteArg,
-                HAS_1_TEMPLATE_PARAMS(int, k),
-                AND_0_VALUE_PARAMS()) {
-  delete std::get<k>(args);
-}
-
-// Resets a bool variable in the destructor.
-class BoolResetter {
- public:
-  explicit BoolResetter(bool* value) : value_(value) {}
-  ~BoolResetter() { *value_ = false; }
- private:
-  bool* value_;
-};
-
-TEST(ActionTemplateTest, WorksForIntegralTemplateParams) {
-  const Action<void(int*, BoolResetter*)> a = MyDeleteArg<1>();
-  int n = 0;
-  bool b = true;
-  BoolResetter* resetter = new BoolResetter(&b);
-  a.Perform(std::make_tuple(&n, resetter));
-  EXPECT_FALSE(b);  // Verifies that resetter is deleted.
-}
-
-// Tests that ACTION_TEMPLATES works for template template parameters.
-ACTION_TEMPLATE(ReturnSmartPointer,
-                HAS_1_TEMPLATE_PARAMS(template <typename Pointee> class,
-                                      Pointer),
-                AND_1_VALUE_PARAMS(pointee)) {
-  return Pointer<pointee_type>(new pointee_type(pointee));
-}
-
-TEST(ActionTemplateTest, WorksForTemplateTemplateParameters) {
-  const Action<std::shared_ptr<int>()> a =
-      ReturnSmartPointer<std::shared_ptr>(42);
-  std::shared_ptr<int> p = a.Perform(std::make_tuple());
-  EXPECT_EQ(42, *p);
-}
-
-// Tests that ACTION_TEMPLATE works for 10 template parameters.
-template <typename T1, typename T2, typename T3, int k4, bool k5,
-          unsigned int k6, typename T7, typename T8, typename T9>
-struct GiantTemplate {
- public:
-  explicit GiantTemplate(int a_value) : value(a_value) {}
-  int value;
-};
-
-ACTION_TEMPLATE(ReturnGiant,
-                HAS_10_TEMPLATE_PARAMS(
-                    typename, T1,
-                    typename, T2,
-                    typename, T3,
-                    int, k4,
-                    bool, k5,
-                    unsigned int, k6,
-                    class, T7,
-                    class, T8,
-                    class, T9,
-                    template <typename T> class, T10),
-                AND_1_VALUE_PARAMS(value)) {
-  return GiantTemplate<T10<T1>, T2, T3, k4, k5, k6, T7, T8, T9>(value);
-}
-
-TEST(ActionTemplateTest, WorksFor10TemplateParameters) {
-  using Giant = GiantTemplate<std::shared_ptr<int>, bool, double, 5, true, 6,
-                              char, unsigned, int>;
-  const Action<Giant()> a = ReturnGiant<int, bool, double, 5, true, 6, char,
-                                        unsigned, int, std::shared_ptr>(42);
-  Giant giant = a.Perform(std::make_tuple());
-  EXPECT_EQ(42, giant.value);
-}
-
-// Tests that ACTION_TEMPLATE works for 10 value parameters.
-ACTION_TEMPLATE(ReturnSum,
-                HAS_1_TEMPLATE_PARAMS(typename, Number),
-                AND_10_VALUE_PARAMS(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10)) {
-  return static_cast<Number>(v1) + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10;
-}
-
-TEST(ActionTemplateTest, WorksFor10ValueParameters) {
-  const Action<int()> a = ReturnSum<int>(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
-  EXPECT_EQ(55, a.Perform(std::make_tuple()));
-}
-
-// Tests that ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded
-// on the number of value parameters.
-
-ACTION(ReturnSum) { return 0; }
-
-ACTION_P(ReturnSum, x) { return x; }
-
-ACTION_TEMPLATE(ReturnSum,
-                HAS_1_TEMPLATE_PARAMS(typename, Number),
-                AND_2_VALUE_PARAMS(v1, v2)) {
-  return static_cast<Number>(v1) + v2;
-}
-
-ACTION_TEMPLATE(ReturnSum,
-                HAS_1_TEMPLATE_PARAMS(typename, Number),
-                AND_3_VALUE_PARAMS(v1, v2, v3)) {
-  return static_cast<Number>(v1) + v2 + v3;
-}
-
-ACTION_TEMPLATE(ReturnSum,
-                HAS_2_TEMPLATE_PARAMS(typename, Number, int, k),
-                AND_4_VALUE_PARAMS(v1, v2, v3, v4)) {
-  return static_cast<Number>(v1) + v2 + v3 + v4 + k;
-}
-
-TEST(ActionTemplateTest, CanBeOverloadedOnNumberOfValueParameters) {
-  const Action<int()> a0 = ReturnSum();
-  const Action<int()> a1 = ReturnSum(1);
-  const Action<int()> a2 = ReturnSum<int>(1, 2);
-  const Action<int()> a3 = ReturnSum<int>(1, 2, 3);
-  const Action<int()> a4 = ReturnSum<int, 10000>(2000, 300, 40, 5);
-  EXPECT_EQ(0, a0.Perform(std::make_tuple()));
-  EXPECT_EQ(1, a1.Perform(std::make_tuple()));
-  EXPECT_EQ(3, a2.Perform(std::make_tuple()));
-  EXPECT_EQ(6, a3.Perform(std::make_tuple()));
-  EXPECT_EQ(12345, a4.Perform(std::make_tuple()));
-}
-
-
-}  // namespace gmock_generated_actions_test
-}  // namespace testing
diff --git a/ext/googletest/googlemock/test/gmock-generated-function-mockers_test.cc b/ext/googletest/googlemock/test/gmock-generated-function-mockers_test.cc
deleted file mode 100644
index dff3a9f..0000000
--- a/ext/googletest/googlemock/test/gmock-generated-function-mockers_test.cc
+++ /dev/null
@@ -1,659 +0,0 @@
-// Copyright 2007, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file tests the function mocker classes.
-
-#include "gmock/gmock-generated-function-mockers.h"
-
-#if GTEST_OS_WINDOWS
-// MSDN says the header file to be included for STDMETHOD is BaseTyps.h but
-// we are getting compiler errors if we use basetyps.h, hence including
-// objbase.h for definition of STDMETHOD.
-# include <objbase.h>
-#endif  // GTEST_OS_WINDOWS
-
-#include <map>
-#include <string>
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace testing {
-namespace gmock_generated_function_mockers_test {
-
-using testing::_;
-using testing::A;
-using testing::An;
-using testing::AnyNumber;
-using testing::Const;
-using testing::DoDefault;
-using testing::Eq;
-using testing::Lt;
-using testing::MockFunction;
-using testing::Ref;
-using testing::Return;
-using testing::ReturnRef;
-using testing::TypedEq;
-
-template<typename T>
-class TemplatedCopyable {
- public:
-  TemplatedCopyable() {}
-
-  template <typename U>
-  TemplatedCopyable(const U& other) {}  // NOLINT
-};
-
-class FooInterface {
- public:
-  virtual ~FooInterface() {}
-
-  virtual void VoidReturning(int x) = 0;
-
-  virtual int Nullary() = 0;
-  virtual bool Unary(int x) = 0;
-  virtual long Binary(short x, int y) = 0;  // NOLINT
-  virtual int Decimal(bool b, char c, short d, int e, long f,  // NOLINT
-                      float g, double h, unsigned i, char* j,
-                      const std::string& k) = 0;
-
-  virtual bool TakesNonConstReference(int& n) = 0;  // NOLINT
-  virtual std::string TakesConstReference(const int& n) = 0;
-  virtual bool TakesConst(const int x) = 0;
-
-  virtual int OverloadedOnArgumentNumber() = 0;
-  virtual int OverloadedOnArgumentNumber(int n) = 0;
-
-  virtual int OverloadedOnArgumentType(int n) = 0;
-  virtual char OverloadedOnArgumentType(char c) = 0;
-
-  virtual int OverloadedOnConstness() = 0;
-  virtual char OverloadedOnConstness() const = 0;
-
-  virtual int TypeWithHole(int (*func)()) = 0;
-  virtual int TypeWithComma(const std::map<int, std::string>& a_map) = 0;
-  virtual int TypeWithTemplatedCopyCtor(
-      const TemplatedCopyable<int>& a_vector) = 0;
-
-#if GTEST_OS_WINDOWS
-  STDMETHOD_(int, CTNullary)() = 0;
-  STDMETHOD_(bool, CTUnary)(int x) = 0;
-  STDMETHOD_(int, CTDecimal)
-  (bool b, char c, short d, int e, long f,  // NOLINT
-   float g, double h, unsigned i, char* j, const std::string& k) = 0;
-  STDMETHOD_(char, CTConst)(int x) const = 0;
-#endif  // GTEST_OS_WINDOWS
-};
-
-// Const qualifiers on arguments were once (incorrectly) considered
-// significant in determining whether two virtual functions had the same
-// signature. This was fixed in Visual Studio 2008. However, the compiler
-// still emits a warning that alerts about this change in behavior.
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable : 4373)
-#endif
-class MockFoo : public FooInterface {
- public:
-  MockFoo() {}
-
-  // Makes sure that a mock function parameter can be named.
-  MOCK_METHOD1(VoidReturning, void(int n));  // NOLINT
-
-  MOCK_METHOD0(Nullary, int());  // NOLINT
-
-  // Makes sure that a mock function parameter can be unnamed.
-  MOCK_METHOD1(Unary, bool(int));  // NOLINT
-  MOCK_METHOD2(Binary, long(short, int));  // NOLINT
-  MOCK_METHOD10(Decimal, int(bool, char, short, int, long, float,  // NOLINT
-                             double, unsigned, char*, const std::string& str));
-
-  MOCK_METHOD1(TakesNonConstReference, bool(int&));  // NOLINT
-  MOCK_METHOD1(TakesConstReference, std::string(const int&));
-  MOCK_METHOD1(TakesConst, bool(const int));  // NOLINT
-
-  // Tests that the function return type can contain unprotected comma.
-  MOCK_METHOD0(ReturnTypeWithComma, std::map<int, std::string>());
-  MOCK_CONST_METHOD1(ReturnTypeWithComma,
-                     std::map<int, std::string>(int));  // NOLINT
-
-  MOCK_METHOD0(OverloadedOnArgumentNumber, int());  // NOLINT
-  MOCK_METHOD1(OverloadedOnArgumentNumber, int(int));  // NOLINT
-
-  MOCK_METHOD1(OverloadedOnArgumentType, int(int));  // NOLINT
-  MOCK_METHOD1(OverloadedOnArgumentType, char(char));  // NOLINT
-
-  MOCK_METHOD0(OverloadedOnConstness, int());  // NOLINT
-  MOCK_CONST_METHOD0(OverloadedOnConstness, char());  // NOLINT
-
-  MOCK_METHOD1(TypeWithHole, int(int (*)()));  // NOLINT
-  MOCK_METHOD1(TypeWithComma,
-               int(const std::map<int, std::string>&));  // NOLINT
-  MOCK_METHOD1(TypeWithTemplatedCopyCtor,
-               int(const TemplatedCopyable<int>&));  // NOLINT
-
-#if GTEST_OS_WINDOWS
-  MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, CTNullary, int());
-  MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, CTUnary, bool(int));
-  MOCK_METHOD10_WITH_CALLTYPE(STDMETHODCALLTYPE, CTDecimal,
-                              int(bool b, char c, short d, int e, long f,
-                                  float g, double h, unsigned i, char* j,
-                                  const std::string& k));
-  MOCK_CONST_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, CTConst, char(int));
-
-  // Tests that the function return type can contain unprotected comma.
-  MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, CTReturnTypeWithComma,
-                             std::map<int, std::string>());
-#endif  // GTEST_OS_WINDOWS
-
- private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFoo);
-};
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
-
-class FunctionMockerTest : public testing::Test {
- protected:
-  FunctionMockerTest() : foo_(&mock_foo_) {}
-
-  FooInterface* const foo_;
-  MockFoo mock_foo_;
-};
-
-// Tests mocking a void-returning function.
-TEST_F(FunctionMockerTest, MocksVoidFunction) {
-  EXPECT_CALL(mock_foo_, VoidReturning(Lt(100)));
-  foo_->VoidReturning(0);
-}
-
-// Tests mocking a nullary function.
-TEST_F(FunctionMockerTest, MocksNullaryFunction) {
-  EXPECT_CALL(mock_foo_, Nullary())
-      .WillOnce(DoDefault())
-      .WillOnce(Return(1));
-
-  EXPECT_EQ(0, foo_->Nullary());
-  EXPECT_EQ(1, foo_->Nullary());
-}
-
-// Tests mocking a unary function.
-TEST_F(FunctionMockerTest, MocksUnaryFunction) {
-  EXPECT_CALL(mock_foo_, Unary(Eq(2)))
-      .Times(2)
-      .WillOnce(Return(true));
-
-  EXPECT_TRUE(foo_->Unary(2));
-  EXPECT_FALSE(foo_->Unary(2));
-}
-
-// Tests mocking a binary function.
-TEST_F(FunctionMockerTest, MocksBinaryFunction) {
-  EXPECT_CALL(mock_foo_, Binary(2, _))
-      .WillOnce(Return(3));
-
-  EXPECT_EQ(3, foo_->Binary(2, 1));
-}
-
-// Tests mocking a decimal function.
-TEST_F(FunctionMockerTest, MocksDecimalFunction) {
-  EXPECT_CALL(mock_foo_, Decimal(true, 'a', 0, 0, 1L, A<float>(), Lt(100), 5U,
-                                 nullptr, "hi"))
-      .WillOnce(Return(5));
-
-  EXPECT_EQ(5, foo_->Decimal(true, 'a', 0, 0, 1, 0, 0, 5, nullptr, "hi"));
-}
-
-// Tests mocking a function that takes a non-const reference.
-TEST_F(FunctionMockerTest, MocksFunctionWithNonConstReferenceArgument) {
-  int a = 0;
-  EXPECT_CALL(mock_foo_, TakesNonConstReference(Ref(a)))
-      .WillOnce(Return(true));
-
-  EXPECT_TRUE(foo_->TakesNonConstReference(a));
-}
-
-// Tests mocking a function that takes a const reference.
-TEST_F(FunctionMockerTest, MocksFunctionWithConstReferenceArgument) {
-  int a = 0;
-  EXPECT_CALL(mock_foo_, TakesConstReference(Ref(a)))
-      .WillOnce(Return("Hello"));
-
-  EXPECT_EQ("Hello", foo_->TakesConstReference(a));
-}
-
-// Tests mocking a function that takes a const variable.
-TEST_F(FunctionMockerTest, MocksFunctionWithConstArgument) {
-  EXPECT_CALL(mock_foo_, TakesConst(Lt(10)))
-      .WillOnce(DoDefault());
-
-  EXPECT_FALSE(foo_->TakesConst(5));
-}
-
-// Tests mocking functions overloaded on the number of arguments.
-TEST_F(FunctionMockerTest, MocksFunctionsOverloadedOnArgumentNumber) {
-  EXPECT_CALL(mock_foo_, OverloadedOnArgumentNumber())
-      .WillOnce(Return(1));
-  EXPECT_CALL(mock_foo_, OverloadedOnArgumentNumber(_))
-      .WillOnce(Return(2));
-
-  EXPECT_EQ(2, foo_->OverloadedOnArgumentNumber(1));
-  EXPECT_EQ(1, foo_->OverloadedOnArgumentNumber());
-}
-
-// Tests mocking functions overloaded on the types of argument.
-TEST_F(FunctionMockerTest, MocksFunctionsOverloadedOnArgumentType) {
-  EXPECT_CALL(mock_foo_, OverloadedOnArgumentType(An<int>()))
-      .WillOnce(Return(1));
-  EXPECT_CALL(mock_foo_, OverloadedOnArgumentType(TypedEq<char>('a')))
-      .WillOnce(Return('b'));
-
-  EXPECT_EQ(1, foo_->OverloadedOnArgumentType(0));
-  EXPECT_EQ('b', foo_->OverloadedOnArgumentType('a'));
-}
-
-// Tests mocking functions overloaded on the const-ness of this object.
-TEST_F(FunctionMockerTest, MocksFunctionsOverloadedOnConstnessOfThis) {
-  EXPECT_CALL(mock_foo_, OverloadedOnConstness());
-  EXPECT_CALL(Const(mock_foo_), OverloadedOnConstness())
-      .WillOnce(Return('a'));
-
-  EXPECT_EQ(0, foo_->OverloadedOnConstness());
-  EXPECT_EQ('a', Const(*foo_).OverloadedOnConstness());
-}
-
-TEST_F(FunctionMockerTest, MocksReturnTypeWithComma) {
-  const std::map<int, std::string> a_map;
-  EXPECT_CALL(mock_foo_, ReturnTypeWithComma())
-      .WillOnce(Return(a_map));
-  EXPECT_CALL(mock_foo_, ReturnTypeWithComma(42))
-      .WillOnce(Return(a_map));
-
-  EXPECT_EQ(a_map, mock_foo_.ReturnTypeWithComma());
-  EXPECT_EQ(a_map, mock_foo_.ReturnTypeWithComma(42));
-}
-
-TEST_F(FunctionMockerTest, MocksTypeWithTemplatedCopyCtor) {
-  EXPECT_CALL(mock_foo_, TypeWithTemplatedCopyCtor(_)).WillOnce(Return(true));
-  EXPECT_TRUE(foo_->TypeWithTemplatedCopyCtor(TemplatedCopyable<int>()));
-}
-
-#if GTEST_OS_WINDOWS
-// Tests mocking a nullary function with calltype.
-TEST_F(FunctionMockerTest, MocksNullaryFunctionWithCallType) {
-  EXPECT_CALL(mock_foo_, CTNullary())
-      .WillOnce(Return(-1))
-      .WillOnce(Return(0));
-
-  EXPECT_EQ(-1, foo_->CTNullary());
-  EXPECT_EQ(0, foo_->CTNullary());
-}
-
-// Tests mocking a unary function with calltype.
-TEST_F(FunctionMockerTest, MocksUnaryFunctionWithCallType) {
-  EXPECT_CALL(mock_foo_, CTUnary(Eq(2)))
-      .Times(2)
-      .WillOnce(Return(true))
-      .WillOnce(Return(false));
-
-  EXPECT_TRUE(foo_->CTUnary(2));
-  EXPECT_FALSE(foo_->CTUnary(2));
-}
-
-// Tests mocking a decimal function with calltype.
-TEST_F(FunctionMockerTest, MocksDecimalFunctionWithCallType) {
-  EXPECT_CALL(mock_foo_, CTDecimal(true, 'a', 0, 0, 1L, A<float>(), Lt(100), 5U,
-                                   nullptr, "hi"))
-      .WillOnce(Return(10));
-
-  EXPECT_EQ(10, foo_->CTDecimal(true, 'a', 0, 0, 1, 0, 0, 5, nullptr, "hi"));
-}
-
-// Tests mocking functions overloaded on the const-ness of this object.
-TEST_F(FunctionMockerTest, MocksFunctionsConstFunctionWithCallType) {
-  EXPECT_CALL(Const(mock_foo_), CTConst(_))
-      .WillOnce(Return('a'));
-
-  EXPECT_EQ('a', Const(*foo_).CTConst(0));
-}
-
-TEST_F(FunctionMockerTest, MocksReturnTypeWithCommaAndCallType) {
-  const std::map<int, std::string> a_map;
-  EXPECT_CALL(mock_foo_, CTReturnTypeWithComma())
-      .WillOnce(Return(a_map));
-
-  EXPECT_EQ(a_map, mock_foo_.CTReturnTypeWithComma());
-}
-
-#endif  // GTEST_OS_WINDOWS
-
-class MockB {
- public:
-  MockB() {}
-
-  MOCK_METHOD0(DoB, void());
-
- private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(MockB);
-};
-
-// Tests that functions with no EXPECT_CALL() ruls can be called any
-// number of times.
-TEST(ExpectCallTest, UnmentionedFunctionCanBeCalledAnyNumberOfTimes) {
-  {
-    MockB b;
-  }
-
-  {
-    MockB b;
-    b.DoB();
-  }
-
-  {
-    MockB b;
-    b.DoB();
-    b.DoB();
-  }
-}
-
-// Tests mocking template interfaces.
-
-template <typename T>
-class StackInterface {
- public:
-  virtual ~StackInterface() {}
-
-  // Template parameter appears in function parameter.
-  virtual void Push(const T& value) = 0;
-  virtual void Pop() = 0;
-  virtual int GetSize() const = 0;
-  // Template parameter appears in function return type.
-  virtual const T& GetTop() const = 0;
-};
-
-template <typename T>
-class MockStack : public StackInterface<T> {
- public:
-  MockStack() {}
-
-  MOCK_METHOD1_T(Push, void(const T& elem));
-  MOCK_METHOD0_T(Pop, void());
-  MOCK_CONST_METHOD0_T(GetSize, int());  // NOLINT
-  MOCK_CONST_METHOD0_T(GetTop, const T&());
-
-  // Tests that the function return type can contain unprotected comma.
-  MOCK_METHOD0_T(ReturnTypeWithComma, std::map<int, int>());
-  MOCK_CONST_METHOD1_T(ReturnTypeWithComma, std::map<int, int>(int));  // NOLINT
-
- private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(MockStack);
-};
-
-// Tests that template mock works.
-TEST(TemplateMockTest, Works) {
-  MockStack<int> mock;
-
-  EXPECT_CALL(mock, GetSize())
-      .WillOnce(Return(0))
-      .WillOnce(Return(1))
-      .WillOnce(Return(0));
-  EXPECT_CALL(mock, Push(_));
-  int n = 5;
-  EXPECT_CALL(mock, GetTop())
-      .WillOnce(ReturnRef(n));
-  EXPECT_CALL(mock, Pop())
-      .Times(AnyNumber());
-
-  EXPECT_EQ(0, mock.GetSize());
-  mock.Push(5);
-  EXPECT_EQ(1, mock.GetSize());
-  EXPECT_EQ(5, mock.GetTop());
-  mock.Pop();
-  EXPECT_EQ(0, mock.GetSize());
-}
-
-TEST(TemplateMockTest, MethodWithCommaInReturnTypeWorks) {
-  MockStack<int> mock;
-
-  const std::map<int, int> a_map;
-  EXPECT_CALL(mock, ReturnTypeWithComma())
-      .WillOnce(Return(a_map));
-  EXPECT_CALL(mock, ReturnTypeWithComma(1))
-      .WillOnce(Return(a_map));
-
-  EXPECT_EQ(a_map, mock.ReturnTypeWithComma());
-  EXPECT_EQ(a_map, mock.ReturnTypeWithComma(1));
-}
-
-#if GTEST_OS_WINDOWS
-// Tests mocking template interfaces with calltype.
-
-template <typename T>
-class StackInterfaceWithCallType {
- public:
-  virtual ~StackInterfaceWithCallType() {}
-
-  // Template parameter appears in function parameter.
-  STDMETHOD_(void, Push)(const T& value) = 0;
-  STDMETHOD_(void, Pop)() = 0;
-  STDMETHOD_(int, GetSize)() const = 0;
-  // Template parameter appears in function return type.
-  STDMETHOD_(const T&, GetTop)() const = 0;
-};
-
-template <typename T>
-class MockStackWithCallType : public StackInterfaceWithCallType<T> {
- public:
-  MockStackWithCallType() {}
-
-  MOCK_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Push, void(const T& elem));
-  MOCK_METHOD0_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Pop, void());
-  MOCK_CONST_METHOD0_T_WITH_CALLTYPE(STDMETHODCALLTYPE, GetSize, int());
-  MOCK_CONST_METHOD0_T_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTop, const T&());
-
- private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(MockStackWithCallType);
-};
-
-// Tests that template mock with calltype works.
-TEST(TemplateMockTestWithCallType, Works) {
-  MockStackWithCallType<int> mock;
-
-  EXPECT_CALL(mock, GetSize())
-      .WillOnce(Return(0))
-      .WillOnce(Return(1))
-      .WillOnce(Return(0));
-  EXPECT_CALL(mock, Push(_));
-  int n = 5;
-  EXPECT_CALL(mock, GetTop())
-      .WillOnce(ReturnRef(n));
-  EXPECT_CALL(mock, Pop())
-      .Times(AnyNumber());
-
-  EXPECT_EQ(0, mock.GetSize());
-  mock.Push(5);
-  EXPECT_EQ(1, mock.GetSize());
-  EXPECT_EQ(5, mock.GetTop());
-  mock.Pop();
-  EXPECT_EQ(0, mock.GetSize());
-}
-#endif  // GTEST_OS_WINDOWS
-
-#define MY_MOCK_METHODS1_ \
-    MOCK_METHOD0(Overloaded, void()); \
-    MOCK_CONST_METHOD1(Overloaded, int(int n)); \
-    MOCK_METHOD2(Overloaded, bool(bool f, int n))
-
-class MockOverloadedOnArgNumber {
- public:
-  MockOverloadedOnArgNumber() {}
-
-  MY_MOCK_METHODS1_;
-
- private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(MockOverloadedOnArgNumber);
-};
-
-TEST(OverloadedMockMethodTest, CanOverloadOnArgNumberInMacroBody) {
-  MockOverloadedOnArgNumber mock;
-  EXPECT_CALL(mock, Overloaded());
-  EXPECT_CALL(mock, Overloaded(1)).WillOnce(Return(2));
-  EXPECT_CALL(mock, Overloaded(true, 1)).WillOnce(Return(true));
-
-  mock.Overloaded();
-  EXPECT_EQ(2, mock.Overloaded(1));
-  EXPECT_TRUE(mock.Overloaded(true, 1));
-}
-
-#define MY_MOCK_METHODS2_ \
-    MOCK_CONST_METHOD1(Overloaded, int(int n)); \
-    MOCK_METHOD1(Overloaded, int(int n))
-
-class MockOverloadedOnConstness {
- public:
-  MockOverloadedOnConstness() {}
-
-  MY_MOCK_METHODS2_;
-
- private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(MockOverloadedOnConstness);
-};
-
-TEST(OverloadedMockMethodTest, CanOverloadOnConstnessInMacroBody) {
-  MockOverloadedOnConstness mock;
-  const MockOverloadedOnConstness* const_mock = &mock;
-  EXPECT_CALL(mock, Overloaded(1)).WillOnce(Return(2));
-  EXPECT_CALL(*const_mock, Overloaded(1)).WillOnce(Return(3));
-
-  EXPECT_EQ(2, mock.Overloaded(1));
-  EXPECT_EQ(3, const_mock->Overloaded(1));
-}
-
-TEST(MockFunctionTest, WorksForVoidNullary) {
-  MockFunction<void()> foo;
-  EXPECT_CALL(foo, Call());
-  foo.Call();
-}
-
-TEST(MockFunctionTest, WorksForNonVoidNullary) {
-  MockFunction<int()> foo;
-  EXPECT_CALL(foo, Call())
-      .WillOnce(Return(1))
-      .WillOnce(Return(2));
-  EXPECT_EQ(1, foo.Call());
-  EXPECT_EQ(2, foo.Call());
-}
-
-TEST(MockFunctionTest, WorksForVoidUnary) {
-  MockFunction<void(int)> foo;
-  EXPECT_CALL(foo, Call(1));
-  foo.Call(1);
-}
-
-TEST(MockFunctionTest, WorksForNonVoidBinary) {
-  MockFunction<int(bool, int)> foo;
-  EXPECT_CALL(foo, Call(false, 42))
-      .WillOnce(Return(1))
-      .WillOnce(Return(2));
-  EXPECT_CALL(foo, Call(true, Ge(100)))
-      .WillOnce(Return(3));
-  EXPECT_EQ(1, foo.Call(false, 42));
-  EXPECT_EQ(2, foo.Call(false, 42));
-  EXPECT_EQ(3, foo.Call(true, 120));
-}
-
-TEST(MockFunctionTest, WorksFor10Arguments) {
-  MockFunction<int(bool a0, char a1, int a2, int a3, int a4,
-                   int a5, int a6, char a7, int a8, bool a9)> foo;
-  EXPECT_CALL(foo, Call(_, 'a', _, _, _, _, _, _, _, _))
-      .WillOnce(Return(1))
-      .WillOnce(Return(2));
-  EXPECT_EQ(1, foo.Call(false, 'a', 0, 0, 0, 0, 0, 'b', 0, true));
-  EXPECT_EQ(2, foo.Call(true, 'a', 0, 0, 0, 0, 0, 'b', 1, false));
-}
-
-TEST(MockFunctionTest, AsStdFunction) {
-  MockFunction<int(int)> foo;
-  auto call = [](const std::function<int(int)> &f, int i) {
-    return f(i);
-  };
-  EXPECT_CALL(foo, Call(1)).WillOnce(Return(-1));
-  EXPECT_CALL(foo, Call(2)).WillOnce(Return(-2));
-  EXPECT_EQ(-1, call(foo.AsStdFunction(), 1));
-  EXPECT_EQ(-2, call(foo.AsStdFunction(), 2));
-}
-
-TEST(MockFunctionTest, AsStdFunctionReturnsReference) {
-  MockFunction<int&()> foo;
-  int value = 1;
-  EXPECT_CALL(foo, Call()).WillOnce(ReturnRef(value));
-  int& ref = foo.AsStdFunction()();
-  EXPECT_EQ(1, ref);
-  value = 2;
-  EXPECT_EQ(2, ref);
-}
-
-TEST(MockFunctionTest, AsStdFunctionWithReferenceParameter) {
-  MockFunction<int(int &)> foo;
-  auto call = [](const std::function<int(int& )> &f, int &i) {
-    return f(i);
-  };
-  int i = 42;
-  EXPECT_CALL(foo, Call(i)).WillOnce(Return(-1));
-  EXPECT_EQ(-1, call(foo.AsStdFunction(), i));
-}
-
-
-struct MockMethodSizes0 {
-  MOCK_METHOD0(func, void());
-};
-struct MockMethodSizes1 {
-  MOCK_METHOD1(func, void(int));
-};
-struct MockMethodSizes2 {
-  MOCK_METHOD2(func, void(int, int));
-};
-struct MockMethodSizes3 {
-  MOCK_METHOD3(func, void(int, int, int));
-};
-struct MockMethodSizes4 {
-  MOCK_METHOD4(func, void(int, int, int, int));
-};
-
-TEST(MockFunctionTest, MockMethodSizeOverhead) {
-  EXPECT_EQ(sizeof(MockMethodSizes0), sizeof(MockMethodSizes1));
-  EXPECT_EQ(sizeof(MockMethodSizes0), sizeof(MockMethodSizes2));
-  EXPECT_EQ(sizeof(MockMethodSizes0), sizeof(MockMethodSizes3));
-  EXPECT_EQ(sizeof(MockMethodSizes0), sizeof(MockMethodSizes4));
-}
-
-}  // namespace gmock_generated_function_mockers_test
-}  // namespace testing
diff --git a/ext/googletest/googlemock/test/gmock-generated-matchers_test.cc b/ext/googletest/googlemock/test/gmock-generated-matchers_test.cc
deleted file mode 100644
index 6c4b300..0000000
--- a/ext/googletest/googlemock/test/gmock-generated-matchers_test.cc
+++ /dev/null
@@ -1,1324 +0,0 @@
-// Copyright 2008, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Google Mock - a framework for writing C++ mock classes.
-//
-// This file tests the built-in matchers generated by a script.
-
-// Silence warning C4244: 'initializing': conversion from 'int' to 'short',
-// possible loss of data and C4100, unreferenced local parameter
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable:4244)
-# pragma warning(disable:4100)
-#endif
-
-#include "gmock/gmock-generated-matchers.h"
-
-#include <list>
-#include <map>
-#include <memory>
-#include <set>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "gtest/gtest-spi.h"
-
-namespace {
-
-using std::list;
-using std::map;
-using std::pair;
-using std::set;
-using std::stringstream;
-using std::vector;
-using testing::_;
-using testing::AllOf;
-using testing::AllOfArray;
-using testing::AnyOf;
-using testing::AnyOfArray;
-using testing::Args;
-using testing::Contains;
-using testing::ElementsAre;
-using testing::ElementsAreArray;
-using testing::Eq;
-using testing::Ge;
-using testing::Gt;
-using testing::Le;
-using testing::Lt;
-using testing::MakeMatcher;
-using testing::Matcher;
-using testing::MatcherInterface;
-using testing::MatchResultListener;
-using testing::Ne;
-using testing::Not;
-using testing::Pointee;
-using testing::PrintToString;
-using testing::Ref;
-using testing::StaticAssertTypeEq;
-using testing::StrEq;
-using testing::Value;
-using testing::internal::ElementsAreArrayMatcher;
-
-// Returns the description of the given matcher.
-template <typename T>
-std::string Describe(const Matcher<T>& m) {
-  stringstream ss;
-  m.DescribeTo(&ss);
-  return ss.str();
-}
-
-// Returns the description of the negation of the given matcher.
-template <typename T>
-std::string DescribeNegation(const Matcher<T>& m) {
-  stringstream ss;
-  m.DescribeNegationTo(&ss);
-  return ss.str();
-}
-
-// Returns the reason why x matches, or doesn't match, m.
-template <typename MatcherType, typename Value>
-std::string Explain(const MatcherType& m, const Value& x) {
-  stringstream ss;
-  m.ExplainMatchResultTo(x, &ss);
-  return ss.str();
-}
-
-// For testing ExplainMatchResultTo().
-class GreaterThanMatcher : public MatcherInterface<int> {
- public:
-  explicit GreaterThanMatcher(int rhs) : rhs_(rhs) {}
-
-  void DescribeTo(::std::ostream* os) const override {
-    *os << "is greater than " << rhs_;
-  }
-
-  bool MatchAndExplain(int lhs, MatchResultListener* listener) const override {
-    const int diff = lhs - rhs_;
-    if (diff > 0) {
-      *listener << "which is " << diff << " more than " << rhs_;
-    } else if (diff == 0) {
-      *listener << "which is the same as " << rhs_;
-    } else {
-      *listener << "which is " << -diff << " less than " << rhs_;
-    }
-
-    return lhs > rhs_;
-  }
-
- private:
-  int rhs_;
-};
-
-Matcher<int> GreaterThan(int n) {
-  return MakeMatcher(new GreaterThanMatcher(n));
-}
-
-// Tests for ElementsAre().
-
-TEST(ElementsAreTest, CanDescribeExpectingNoElement) {
-  Matcher<const vector<int>&> m = ElementsAre();
-  EXPECT_EQ("is empty", Describe(m));
-}
-
-TEST(ElementsAreTest, CanDescribeExpectingOneElement) {
-  Matcher<vector<int> > m = ElementsAre(Gt(5));
-  EXPECT_EQ("has 1 element that is > 5", Describe(m));
-}
-
-TEST(ElementsAreTest, CanDescribeExpectingManyElements) {
-  Matcher<list<std::string> > m = ElementsAre(StrEq("one"), "two");
-  EXPECT_EQ("has 2 elements where\n"
-            "element #0 is equal to \"one\",\n"
-            "element #1 is equal to \"two\"", Describe(m));
-}
-
-TEST(ElementsAreTest, CanDescribeNegationOfExpectingNoElement) {
-  Matcher<vector<int> > m = ElementsAre();
-  EXPECT_EQ("isn't empty", DescribeNegation(m));
-}
-
-TEST(ElementsAreTest, CanDescribeNegationOfExpectingOneElment) {
-  Matcher<const list<int>& > m = ElementsAre(Gt(5));
-  EXPECT_EQ("doesn't have 1 element, or\n"
-            "element #0 isn't > 5", DescribeNegation(m));
-}
-
-TEST(ElementsAreTest, CanDescribeNegationOfExpectingManyElements) {
-  Matcher<const list<std::string>&> m = ElementsAre("one", "two");
-  EXPECT_EQ("doesn't have 2 elements, or\n"
-            "element #0 isn't equal to \"one\", or\n"
-            "element #1 isn't equal to \"two\"", DescribeNegation(m));
-}
-
-TEST(ElementsAreTest, DoesNotExplainTrivialMatch) {
-  Matcher<const list<int>& > m = ElementsAre(1, Ne(2));
-
-  list<int> test_list;
-  test_list.push_back(1);
-  test_list.push_back(3);
-  EXPECT_EQ("", Explain(m, test_list));  // No need to explain anything.
-}
-
-TEST(ElementsAreTest, ExplainsNonTrivialMatch) {
-  Matcher<const vector<int>& > m =
-      ElementsAre(GreaterThan(1), 0, GreaterThan(2));
-
-  const int a[] = { 10, 0, 100 };
-  vector<int> test_vector(a, a + GTEST_ARRAY_SIZE_(a));
-  EXPECT_EQ("whose element #0 matches, which is 9 more than 1,\n"
-            "and whose element #2 matches, which is 98 more than 2",
-            Explain(m, test_vector));
-}
-
-TEST(ElementsAreTest, CanExplainMismatchWrongSize) {
-  Matcher<const list<int>& > m = ElementsAre(1, 3);
-
-  list<int> test_list;
-  // No need to explain when the container is empty.
-  EXPECT_EQ("", Explain(m, test_list));
-
-  test_list.push_back(1);
-  EXPECT_EQ("which has 1 element", Explain(m, test_list));
-}
-
-TEST(ElementsAreTest, CanExplainMismatchRightSize) {
-  Matcher<const vector<int>& > m = ElementsAre(1, GreaterThan(5));
-
-  vector<int> v;
-  v.push_back(2);
-  v.push_back(1);
-  EXPECT_EQ("whose element #0 doesn't match", Explain(m, v));
-
-  v[0] = 1;
-  EXPECT_EQ("whose element #1 doesn't match, which is 4 less than 5",
-            Explain(m, v));
-}
-
-TEST(ElementsAreTest, MatchesOneElementVector) {
-  vector<std::string> test_vector;
-  test_vector.push_back("test string");
-
-  EXPECT_THAT(test_vector, ElementsAre(StrEq("test string")));
-}
-
-TEST(ElementsAreTest, MatchesOneElementList) {
-  list<std::string> test_list;
-  test_list.push_back("test string");
-
-  EXPECT_THAT(test_list, ElementsAre("test string"));
-}
-
-TEST(ElementsAreTest, MatchesThreeElementVector) {
-  vector<std::string> test_vector;
-  test_vector.push_back("one");
-  test_vector.push_back("two");
-  test_vector.push_back("three");
-
-  EXPECT_THAT(test_vector, ElementsAre("one", StrEq("two"), _));
-}
-
-TEST(ElementsAreTest, MatchesOneElementEqMatcher) {
-  vector<int> test_vector;
-  test_vector.push_back(4);
-
-  EXPECT_THAT(test_vector, ElementsAre(Eq(4)));
-}
-
-TEST(ElementsAreTest, MatchesOneElementAnyMatcher) {
-  vector<int> test_vector;
-  test_vector.push_back(4);
-
-  EXPECT_THAT(test_vector, ElementsAre(_));
-}
-
-TEST(ElementsAreTest, MatchesOneElementValue) {
-  vector<int> test_vector;
-  test_vector.push_back(4);
-
-  EXPECT_THAT(test_vector, ElementsAre(4));
-}
-
-TEST(ElementsAreTest, MatchesThreeElementsMixedMatchers) {
-  vector<int> test_vector;
-  test_vector.push_back(1);
-  test_vector.push_back(2);
-  test_vector.push_back(3);
-
-  EXPECT_THAT(test_vector, ElementsAre(1, Eq(2), _));
-}
-
-TEST(ElementsAreTest, MatchesTenElementVector) {
-  const int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
-  vector<int> test_vector(a, a + GTEST_ARRAY_SIZE_(a));
-
-  EXPECT_THAT(test_vector,
-              // The element list can contain values and/or matchers
-              // of different types.
-              ElementsAre(0, Ge(0), _, 3, 4, Ne(2), Eq(6), 7, 8, _));
-}
-
-TEST(ElementsAreTest, DoesNotMatchWrongSize) {
-  vector<std::string> test_vector;
-  test_vector.push_back("test string");
-  test_vector.push_back("test string");
-
-  Matcher<vector<std::string> > m = ElementsAre(StrEq("test string"));
-  EXPECT_FALSE(m.Matches(test_vector));
-}
-
-TEST(ElementsAreTest, DoesNotMatchWrongValue) {
-  vector<std::string> test_vector;
-  test_vector.push_back("other string");
-
-  Matcher<vector<std::string> > m = ElementsAre(StrEq("test string"));
-  EXPECT_FALSE(m.Matches(test_vector));
-}
-
-TEST(ElementsAreTest, DoesNotMatchWrongOrder) {
-  vector<std::string> test_vector;
-  test_vector.push_back("one");
-  test_vector.push_back("three");
-  test_vector.push_back("two");
-
-  Matcher<vector<std::string> > m =
-      ElementsAre(StrEq("one"), StrEq("two"), StrEq("three"));
-  EXPECT_FALSE(m.Matches(test_vector));
-}
-
-TEST(ElementsAreTest, WorksForNestedContainer) {
-  const char* strings[] = {
-    "Hi",
-    "world"
-  };
-
-  vector<list<char> > nested;
-  for (size_t i = 0; i < GTEST_ARRAY_SIZE_(strings); i++) {
-    nested.push_back(list<char>(strings[i], strings[i] + strlen(strings[i])));
-  }
-
-  EXPECT_THAT(nested, ElementsAre(ElementsAre('H', Ne('e')),
-                                  ElementsAre('w', 'o', _, _, 'd')));
-  EXPECT_THAT(nested, Not(ElementsAre(ElementsAre('H', 'e'),
-                                      ElementsAre('w', 'o', _, _, 'd'))));
-}
-
-TEST(ElementsAreTest, WorksWithByRefElementMatchers) {
-  int a[] = { 0, 1, 2 };
-  vector<int> v(a, a + GTEST_ARRAY_SIZE_(a));
-
-  EXPECT_THAT(v, ElementsAre(Ref(v[0]), Ref(v[1]), Ref(v[2])));
-  EXPECT_THAT(v, Not(ElementsAre(Ref(v[0]), Ref(v[1]), Ref(a[2]))));
-}
-
-TEST(ElementsAreTest, WorksWithContainerPointerUsingPointee) {
-  int a[] = { 0, 1, 2 };
-  vector<int> v(a, a + GTEST_ARRAY_SIZE_(a));
-
-  EXPECT_THAT(&v, Pointee(ElementsAre(0, 1, _)));
-  EXPECT_THAT(&v, Not(Pointee(ElementsAre(0, _, 3))));
-}
-
-TEST(ElementsAreTest, WorksWithNativeArrayPassedByReference) {
-  int array[] = { 0, 1, 2 };
-  EXPECT_THAT(array, ElementsAre(0, 1, _));
-  EXPECT_THAT(array, Not(ElementsAre(1, _, _)));
-  EXPECT_THAT(array, Not(ElementsAre(0, _)));
-}
-
-class NativeArrayPassedAsPointerAndSize {
- public:
-  NativeArrayPassedAsPointerAndSize() {}
-
-  MOCK_METHOD2(Helper, void(int* array, int size));
-
- private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(NativeArrayPassedAsPointerAndSize);
-};
-
-TEST(ElementsAreTest, WorksWithNativeArrayPassedAsPointerAndSize) {
-  int array[] = { 0, 1 };
-  ::std::tuple<int*, size_t> array_as_tuple(array, 2);
-  EXPECT_THAT(array_as_tuple, ElementsAre(0, 1));
-  EXPECT_THAT(array_as_tuple, Not(ElementsAre(0)));
-
-  NativeArrayPassedAsPointerAndSize helper;
-  EXPECT_CALL(helper, Helper(_, _))
-      .With(ElementsAre(0, 1));
-  helper.Helper(array, 2);
-}
-
-TEST(ElementsAreTest, WorksWithTwoDimensionalNativeArray) {
-  const char a2[][3] = { "hi", "lo" };
-  EXPECT_THAT(a2, ElementsAre(ElementsAre('h', 'i', '\0'),
-                              ElementsAre('l', 'o', '\0')));
-  EXPECT_THAT(a2, ElementsAre(StrEq("hi"), StrEq("lo")));
-  EXPECT_THAT(a2, ElementsAre(Not(ElementsAre('h', 'o', '\0')),
-                              ElementsAre('l', 'o', '\0')));
-}
-
-TEST(ElementsAreTest, AcceptsStringLiteral) {
-  std::string array[] = {"hi", "one", "two"};
-  EXPECT_THAT(array, ElementsAre("hi", "one", "two"));
-  EXPECT_THAT(array, Not(ElementsAre("hi", "one", "too")));
-}
-
-#ifndef _MSC_VER
-
-// The following test passes a value of type const char[] to a
-// function template that expects const T&.  Some versions of MSVC
-// generates a compiler error C2665 for that.  We believe it's a bug
-// in MSVC.  Therefore this test is #if-ed out for MSVC.
-
-// Declared here with the size unknown.  Defined AFTER the following test.
-extern const char kHi[];
-
-TEST(ElementsAreTest, AcceptsArrayWithUnknownSize) {
-  // The size of kHi is not known in this test, but ElementsAre() should
-  // still accept it.
-
-  std::string array1[] = {"hi"};
-  EXPECT_THAT(array1, ElementsAre(kHi));
-
-  std::string array2[] = {"ho"};
-  EXPECT_THAT(array2, Not(ElementsAre(kHi)));
-}
-
-const char kHi[] = "hi";
-
-#endif  // _MSC_VER
-
-TEST(ElementsAreTest, MakesCopyOfArguments) {
-  int x = 1;
-  int y = 2;
-  // This should make a copy of x and y.
-  ::testing::internal::ElementsAreMatcher<std::tuple<int, int> >
-      polymorphic_matcher = ElementsAre(x, y);
-  // Changing x and y now shouldn't affect the meaning of the above matcher.
-  x = y = 0;
-  const int array1[] = { 1, 2 };
-  EXPECT_THAT(array1, polymorphic_matcher);
-  const int array2[] = { 0, 0 };
-  EXPECT_THAT(array2, Not(polymorphic_matcher));
-}
-
-
-// Tests for ElementsAreArray().  Since ElementsAreArray() shares most
-// of the implementation with ElementsAre(), we don't test it as
-// thoroughly here.
-
-TEST(ElementsAreArrayTest, CanBeCreatedWithValueArray) {
-  const int a[] = { 1, 2, 3 };
-
-  vector<int> test_vector(a, a + GTEST_ARRAY_SIZE_(a));
-  EXPECT_THAT(test_vector, ElementsAreArray(a));
-
-  test_vector[2] = 0;
-  EXPECT_THAT(test_vector, Not(ElementsAreArray(a)));
-}
-
-TEST(ElementsAreArrayTest, CanBeCreatedWithArraySize) {
-  const char* a[] = { "one", "two", "three" };
-
-  vector<std::string> test_vector(a, a + GTEST_ARRAY_SIZE_(a));
-  EXPECT_THAT(test_vector, ElementsAreArray(a, GTEST_ARRAY_SIZE_(a)));
-
-  const char** p = a;
-  test_vector[0] = "1";
-  EXPECT_THAT(test_vector, Not(ElementsAreArray(p, GTEST_ARRAY_SIZE_(a))));
-}
-
-TEST(ElementsAreArrayTest, CanBeCreatedWithoutArraySize) {
-  const char* a[] = { "one", "two", "three" };
-
-  vector<std::string> test_vector(a, a + GTEST_ARRAY_SIZE_(a));
-  EXPECT_THAT(test_vector, ElementsAreArray(a));
-
-  test_vector[0] = "1";
-  EXPECT_THAT(test_vector, Not(ElementsAreArray(a)));
-}
-
-TEST(ElementsAreArrayTest, CanBeCreatedWithMatcherArray) {
-  const Matcher<std::string> kMatcherArray[] = {StrEq("one"), StrEq("two"),
-                                                StrEq("three")};
-
-  vector<std::string> test_vector;
-  test_vector.push_back("one");
-  test_vector.push_back("two");
-  test_vector.push_back("three");
-  EXPECT_THAT(test_vector, ElementsAreArray(kMatcherArray));
-
-  test_vector.push_back("three");
-  EXPECT_THAT(test_vector, Not(ElementsAreArray(kMatcherArray)));
-}
-
-TEST(ElementsAreArrayTest, CanBeCreatedWithVector) {
-  const int a[] = { 1, 2, 3 };
-  vector<int> test_vector(a, a + GTEST_ARRAY_SIZE_(a));
-  const vector<int> expected(a, a + GTEST_ARRAY_SIZE_(a));
-  EXPECT_THAT(test_vector, ElementsAreArray(expected));
-  test_vector.push_back(4);
-  EXPECT_THAT(test_vector, Not(ElementsAreArray(expected)));
-}
-
-
-TEST(ElementsAreArrayTest, TakesInitializerList) {
-  const int a[5] = { 1, 2, 3, 4, 5 };
-  EXPECT_THAT(a, ElementsAreArray({ 1, 2, 3, 4, 5 }));
-  EXPECT_THAT(a, Not(ElementsAreArray({ 1, 2, 3, 5, 4 })));
-  EXPECT_THAT(a, Not(ElementsAreArray({ 1, 2, 3, 4, 6 })));
-}
-
-TEST(ElementsAreArrayTest, TakesInitializerListOfCStrings) {
-  const std::string a[5] = {"a", "b", "c", "d", "e"};
-  EXPECT_THAT(a, ElementsAreArray({ "a", "b", "c", "d", "e" }));
-  EXPECT_THAT(a, Not(ElementsAreArray({ "a", "b", "c", "e", "d" })));
-  EXPECT_THAT(a, Not(ElementsAreArray({ "a", "b", "c", "d", "ef" })));
-}
-
-TEST(ElementsAreArrayTest, TakesInitializerListOfSameTypedMatchers) {
-  const int a[5] = { 1, 2, 3, 4, 5 };
-  EXPECT_THAT(a, ElementsAreArray(
-      { Eq(1), Eq(2), Eq(3), Eq(4), Eq(5) }));
-  EXPECT_THAT(a, Not(ElementsAreArray(
-      { Eq(1), Eq(2), Eq(3), Eq(4), Eq(6) })));
-}
-
-TEST(ElementsAreArrayTest,
-     TakesInitializerListOfDifferentTypedMatchers) {
-  const int a[5] = { 1, 2, 3, 4, 5 };
-  // The compiler cannot infer the type of the initializer list if its
-  // elements have different types.  We must explicitly specify the
-  // unified element type in this case.
-  EXPECT_THAT(a, ElementsAreArray<Matcher<int> >(
-      { Eq(1), Ne(-2), Ge(3), Le(4), Eq(5) }));
-  EXPECT_THAT(a, Not(ElementsAreArray<Matcher<int> >(
-      { Eq(1), Ne(-2), Ge(3), Le(4), Eq(6) })));
-}
-
-
-TEST(ElementsAreArrayTest, CanBeCreatedWithMatcherVector) {
-  const int a[] = { 1, 2, 3 };
-  const Matcher<int> kMatchers[] = { Eq(1), Eq(2), Eq(3) };
-  vector<int> test_vector(a, a + GTEST_ARRAY_SIZE_(a));
-  const vector<Matcher<int> > expected(
-      kMatchers, kMatchers + GTEST_ARRAY_SIZE_(kMatchers));
-  EXPECT_THAT(test_vector, ElementsAreArray(expected));
-  test_vector.push_back(4);
-  EXPECT_THAT(test_vector, Not(ElementsAreArray(expected)));
-}
-
-TEST(ElementsAreArrayTest, CanBeCreatedWithIteratorRange) {
-  const int a[] = { 1, 2, 3 };
-  const vector<int> test_vector(a, a + GTEST_ARRAY_SIZE_(a));
-  const vector<int> expected(a, a + GTEST_ARRAY_SIZE_(a));
-  EXPECT_THAT(test_vector, ElementsAreArray(expected.begin(), expected.end()));
-  // Pointers are iterators, too.
-  EXPECT_THAT(test_vector, ElementsAreArray(a, a + GTEST_ARRAY_SIZE_(a)));
-  // The empty range of NULL pointers should also be okay.
-  int* const null_int = nullptr;
-  EXPECT_THAT(test_vector, Not(ElementsAreArray(null_int, null_int)));
-  EXPECT_THAT((vector<int>()), ElementsAreArray(null_int, null_int));
-}
-
-// Since ElementsAre() and ElementsAreArray() share much of the
-// implementation, we only do a sanity test for native arrays here.
-TEST(ElementsAreArrayTest, WorksWithNativeArray) {
-  ::std::string a[] = { "hi", "ho" };
-  ::std::string b[] = { "hi", "ho" };
-
-  EXPECT_THAT(a, ElementsAreArray(b));
-  EXPECT_THAT(a, ElementsAreArray(b, 2));
-  EXPECT_THAT(a, Not(ElementsAreArray(b, 1)));
-}
-
-TEST(ElementsAreArrayTest, SourceLifeSpan) {
-  const int a[] = { 1, 2, 3 };
-  vector<int> test_vector(a, a + GTEST_ARRAY_SIZE_(a));
-  vector<int> expect(a, a + GTEST_ARRAY_SIZE_(a));
-  ElementsAreArrayMatcher<int> matcher_maker =
-      ElementsAreArray(expect.begin(), expect.end());
-  EXPECT_THAT(test_vector, matcher_maker);
-  // Changing in place the values that initialized matcher_maker should not
-  // affect matcher_maker anymore. It should have made its own copy of them.
-  typedef vector<int>::iterator Iter;
-  for (Iter it = expect.begin(); it != expect.end(); ++it) { *it += 10; }
-  EXPECT_THAT(test_vector, matcher_maker);
-  test_vector.push_back(3);
-  EXPECT_THAT(test_vector, Not(matcher_maker));
-}
-
-// Tests for the MATCHER*() macro family.
-
-// Tests that a simple MATCHER() definition works.
-
-MATCHER(IsEven, "") { return (arg % 2) == 0; }
-
-TEST(MatcherMacroTest, Works) {
-  const Matcher<int> m = IsEven();
-  EXPECT_TRUE(m.Matches(6));
-  EXPECT_FALSE(m.Matches(7));
-
-  EXPECT_EQ("is even", Describe(m));
-  EXPECT_EQ("not (is even)", DescribeNegation(m));
-  EXPECT_EQ("", Explain(m, 6));
-  EXPECT_EQ("", Explain(m, 7));
-}
-
-// This also tests that the description string can reference 'negation'.
-MATCHER(IsEven2, negation ? "is odd" : "is even") {
-  if ((arg % 2) == 0) {
-    // Verifies that we can stream to result_listener, a listener
-    // supplied by the MATCHER macro implicitly.
-    *result_listener << "OK";
-    return true;
-  } else {
-    *result_listener << "% 2 == " << (arg % 2);
-    return false;
-  }
-}
-
-// This also tests that the description string can reference matcher
-// parameters.
-MATCHER_P2(EqSumOf, x, y, std::string(negation ? "doesn't equal" : "equals") +
-                              " the sum of " + PrintToString(x) + " and " +
-                              PrintToString(y)) {
-  if (arg == (x + y)) {
-    *result_listener << "OK";
-    return true;
-  } else {
-    // Verifies that we can stream to the underlying stream of
-    // result_listener.
-    if (result_listener->stream() != nullptr) {
-      *result_listener->stream() << "diff == " << (x + y - arg);
-    }
-    return false;
-  }
-}
-
-// Tests that the matcher description can reference 'negation' and the
-// matcher parameters.
-TEST(MatcherMacroTest, DescriptionCanReferenceNegationAndParameters) {
-  const Matcher<int> m1 = IsEven2();
-  EXPECT_EQ("is even", Describe(m1));
-  EXPECT_EQ("is odd", DescribeNegation(m1));
-
-  const Matcher<int> m2 = EqSumOf(5, 9);
-  EXPECT_EQ("equals the sum of 5 and 9", Describe(m2));
-  EXPECT_EQ("doesn't equal the sum of 5 and 9", DescribeNegation(m2));
-}
-
-// Tests explaining match result in a MATCHER* macro.
-TEST(MatcherMacroTest, CanExplainMatchResult) {
-  const Matcher<int> m1 = IsEven2();
-  EXPECT_EQ("OK", Explain(m1, 4));
-  EXPECT_EQ("% 2 == 1", Explain(m1, 5));
-
-  const Matcher<int> m2 = EqSumOf(1, 2);
-  EXPECT_EQ("OK", Explain(m2, 3));
-  EXPECT_EQ("diff == -1", Explain(m2, 4));
-}
-
-// Tests that the body of MATCHER() can reference the type of the
-// value being matched.
-
-MATCHER(IsEmptyString, "") {
-  StaticAssertTypeEq< ::std::string, arg_type>();
-  return arg == "";
-}
-
-MATCHER(IsEmptyStringByRef, "") {
-  StaticAssertTypeEq<const ::std::string&, arg_type>();
-  return arg == "";
-}
-
-TEST(MatcherMacroTest, CanReferenceArgType) {
-  const Matcher< ::std::string> m1 = IsEmptyString();
-  EXPECT_TRUE(m1.Matches(""));
-
-  const Matcher<const ::std::string&> m2 = IsEmptyStringByRef();
-  EXPECT_TRUE(m2.Matches(""));
-}
-
-// Tests that MATCHER() can be used in a namespace.
-
-namespace matcher_test {
-MATCHER(IsOdd, "") { return (arg % 2) != 0; }
-}  // namespace matcher_test
-
-TEST(MatcherMacroTest, WorksInNamespace) {
-  Matcher<int> m = matcher_test::IsOdd();
-  EXPECT_FALSE(m.Matches(4));
-  EXPECT_TRUE(m.Matches(5));
-}
-
-// Tests that Value() can be used to compose matchers.
-MATCHER(IsPositiveOdd, "") {
-  return Value(arg, matcher_test::IsOdd()) && arg > 0;
-}
-
-TEST(MatcherMacroTest, CanBeComposedUsingValue) {
-  EXPECT_THAT(3, IsPositiveOdd());
-  EXPECT_THAT(4, Not(IsPositiveOdd()));
-  EXPECT_THAT(-1, Not(IsPositiveOdd()));
-}
-
-// Tests that a simple MATCHER_P() definition works.
-
-MATCHER_P(IsGreaterThan32And, n, "") { return arg > 32 && arg > n; }
-
-TEST(MatcherPMacroTest, Works) {
-  const Matcher<int> m = IsGreaterThan32And(5);
-  EXPECT_TRUE(m.Matches(36));
-  EXPECT_FALSE(m.Matches(5));
-
-  EXPECT_EQ("is greater than 32 and 5", Describe(m));
-  EXPECT_EQ("not (is greater than 32 and 5)", DescribeNegation(m));
-  EXPECT_EQ("", Explain(m, 36));
-  EXPECT_EQ("", Explain(m, 5));
-}
-
-// Tests that the description is calculated correctly from the matcher name.
-MATCHER_P(_is_Greater_Than32and_, n, "") { return arg > 32 && arg > n; }
-
-TEST(MatcherPMacroTest, GeneratesCorrectDescription) {
-  const Matcher<int> m = _is_Greater_Than32and_(5);
-
-  EXPECT_EQ("is greater than 32 and 5", Describe(m));
-  EXPECT_EQ("not (is greater than 32 and 5)", DescribeNegation(m));
-  EXPECT_EQ("", Explain(m, 36));
-  EXPECT_EQ("", Explain(m, 5));
-}
-
-// Tests that a MATCHER_P matcher can be explicitly instantiated with
-// a reference parameter type.
-
-class UncopyableFoo {
- public:
-  explicit UncopyableFoo(char value) : value_(value) {}
- private:
-  UncopyableFoo(const UncopyableFoo&);
-  void operator=(const UncopyableFoo&);
-
-  char value_;
-};
-
-MATCHER_P(ReferencesUncopyable, variable, "") { return &arg == &variable; }
-
-TEST(MatcherPMacroTest, WorksWhenExplicitlyInstantiatedWithReference) {
-  UncopyableFoo foo1('1'), foo2('2');
-  const Matcher<const UncopyableFoo&> m =
-      ReferencesUncopyable<const UncopyableFoo&>(foo1);
-
-  EXPECT_TRUE(m.Matches(foo1));
-  EXPECT_FALSE(m.Matches(foo2));
-
-  // We don't want the address of the parameter printed, as most
-  // likely it will just annoy the user.  If the address is
-  // interesting, the user should consider passing the parameter by
-  // pointer instead.
-  EXPECT_EQ("references uncopyable 1-byte object <31>", Describe(m));
-}
-
-
-// Tests that the body of MATCHER_Pn() can reference the parameter
-// types.
-
-MATCHER_P3(ParamTypesAreIntLongAndChar, foo, bar, baz, "") {
-  StaticAssertTypeEq<int, foo_type>();
-  StaticAssertTypeEq<long, bar_type>();  // NOLINT
-  StaticAssertTypeEq<char, baz_type>();
-  return arg == 0;
-}
-
-TEST(MatcherPnMacroTest, CanReferenceParamTypes) {
-  EXPECT_THAT(0, ParamTypesAreIntLongAndChar(10, 20L, 'a'));
-}
-
-// Tests that a MATCHER_Pn matcher can be explicitly instantiated with
-// reference parameter types.
-
-MATCHER_P2(ReferencesAnyOf, variable1, variable2, "") {
-  return &arg == &variable1 || &arg == &variable2;
-}
-
-TEST(MatcherPnMacroTest, WorksWhenExplicitlyInstantiatedWithReferences) {
-  UncopyableFoo foo1('1'), foo2('2'), foo3('3');
-  const Matcher<const UncopyableFoo&> m =
-      ReferencesAnyOf<const UncopyableFoo&, const UncopyableFoo&>(foo1, foo2);
-
-  EXPECT_TRUE(m.Matches(foo1));
-  EXPECT_TRUE(m.Matches(foo2));
-  EXPECT_FALSE(m.Matches(foo3));
-}
-
-TEST(MatcherPnMacroTest,
-     GeneratesCorretDescriptionWhenExplicitlyInstantiatedWithReferences) {
-  UncopyableFoo foo1('1'), foo2('2');
-  const Matcher<const UncopyableFoo&> m =
-      ReferencesAnyOf<const UncopyableFoo&, const UncopyableFoo&>(foo1, foo2);
-
-  // We don't want the addresses of the parameters printed, as most
-  // likely they will just annoy the user.  If the addresses are
-  // interesting, the user should consider passing the parameters by
-  // pointers instead.
-  EXPECT_EQ("references any of (1-byte object <31>, 1-byte object <32>)",
-            Describe(m));
-}
-
-// Tests that a simple MATCHER_P2() definition works.
-
-MATCHER_P2(IsNotInClosedRange, low, hi, "") { return arg < low || arg > hi; }
-
-TEST(MatcherPnMacroTest, Works) {
-  const Matcher<const long&> m = IsNotInClosedRange(10, 20);  // NOLINT
-  EXPECT_TRUE(m.Matches(36L));
-  EXPECT_FALSE(m.Matches(15L));
-
-  EXPECT_EQ("is not in closed range (10, 20)", Describe(m));
-  EXPECT_EQ("not (is not in closed range (10, 20))", DescribeNegation(m));
-  EXPECT_EQ("", Explain(m, 36L));
-  EXPECT_EQ("", Explain(m, 15L));
-}
-
-// Tests that MATCHER*() definitions can be overloaded on the number
-// of parameters; also tests MATCHER_Pn() where n >= 3.
-
-MATCHER(EqualsSumOf, "") { return arg == 0; }
-MATCHER_P(EqualsSumOf, a, "") { return arg == a; }
-MATCHER_P2(EqualsSumOf, a, b, "") { return arg == a + b; }
-MATCHER_P3(EqualsSumOf, a, b, c, "") { return arg == a + b + c; }
-MATCHER_P4(EqualsSumOf, a, b, c, d, "") { return arg == a + b + c + d; }
-MATCHER_P5(EqualsSumOf, a, b, c, d, e, "") { return arg == a + b + c + d + e; }
-MATCHER_P6(EqualsSumOf, a, b, c, d, e, f, "") {
-  return arg == a + b + c + d + e + f;
-}
-MATCHER_P7(EqualsSumOf, a, b, c, d, e, f, g, "") {
-  return arg == a + b + c + d + e + f + g;
-}
-MATCHER_P8(EqualsSumOf, a, b, c, d, e, f, g, h, "") {
-  return arg == a + b + c + d + e + f + g + h;
-}
-MATCHER_P9(EqualsSumOf, a, b, c, d, e, f, g, h, i, "") {
-  return arg == a + b + c + d + e + f + g + h + i;
-}
-MATCHER_P10(EqualsSumOf, a, b, c, d, e, f, g, h, i, j, "") {
-  return arg == a + b + c + d + e + f + g + h + i + j;
-}
-
-TEST(MatcherPnMacroTest, CanBeOverloadedOnNumberOfParameters) {
-  EXPECT_THAT(0, EqualsSumOf());
-  EXPECT_THAT(1, EqualsSumOf(1));
-  EXPECT_THAT(12, EqualsSumOf(10, 2));
-  EXPECT_THAT(123, EqualsSumOf(100, 20, 3));
-  EXPECT_THAT(1234, EqualsSumOf(1000, 200, 30, 4));
-  EXPECT_THAT(12345, EqualsSumOf(10000, 2000, 300, 40, 5));
-  EXPECT_THAT("abcdef",
-              EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f'));
-  EXPECT_THAT("abcdefg",
-              EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g'));
-  EXPECT_THAT("abcdefgh",
-              EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g',
-                          "h"));
-  EXPECT_THAT("abcdefghi",
-              EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g',
-                          "h", 'i'));
-  EXPECT_THAT("abcdefghij",
-              EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g',
-                          "h", 'i', ::std::string("j")));
-
-  EXPECT_THAT(1, Not(EqualsSumOf()));
-  EXPECT_THAT(-1, Not(EqualsSumOf(1)));
-  EXPECT_THAT(-12, Not(EqualsSumOf(10, 2)));
-  EXPECT_THAT(-123, Not(EqualsSumOf(100, 20, 3)));
-  EXPECT_THAT(-1234, Not(EqualsSumOf(1000, 200, 30, 4)));
-  EXPECT_THAT(-12345, Not(EqualsSumOf(10000, 2000, 300, 40, 5)));
-  EXPECT_THAT("abcdef ",
-              Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f')));
-  EXPECT_THAT("abcdefg ",
-              Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f',
-                              'g')));
-  EXPECT_THAT("abcdefgh ",
-              Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g',
-                              "h")));
-  EXPECT_THAT("abcdefghi ",
-              Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g',
-                              "h", 'i')));
-  EXPECT_THAT("abcdefghij ",
-              Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g',
-                              "h", 'i', ::std::string("j"))));
-}
-
-// Tests that a MATCHER_Pn() definition can be instantiated with any
-// compatible parameter types.
-TEST(MatcherPnMacroTest, WorksForDifferentParameterTypes) {
-  EXPECT_THAT(123, EqualsSumOf(100L, 20, static_cast<char>(3)));
-  EXPECT_THAT("abcd", EqualsSumOf(::std::string("a"), "b", 'c', "d"));
-
-  EXPECT_THAT(124, Not(EqualsSumOf(100L, 20, static_cast<char>(3))));
-  EXPECT_THAT("abcde", Not(EqualsSumOf(::std::string("a"), "b", 'c', "d")));
-}
-
-// Tests that the matcher body can promote the parameter types.
-
-MATCHER_P2(EqConcat, prefix, suffix, "") {
-  // The following lines promote the two parameters to desired types.
-  std::string prefix_str(prefix);
-  char suffix_char = static_cast<char>(suffix);
-  return arg == prefix_str + suffix_char;
-}
-
-TEST(MatcherPnMacroTest, SimpleTypePromotion) {
-  Matcher<std::string> no_promo =
-      EqConcat(std::string("foo"), 't');
-  Matcher<const std::string&> promo =
-      EqConcat("foo", static_cast<int>('t'));
-  EXPECT_FALSE(no_promo.Matches("fool"));
-  EXPECT_FALSE(promo.Matches("fool"));
-  EXPECT_TRUE(no_promo.Matches("foot"));
-  EXPECT_TRUE(promo.Matches("foot"));
-}
-
-// Verifies the type of a MATCHER*.
-
-TEST(MatcherPnMacroTest, TypesAreCorrect) {
-  // EqualsSumOf() must be assignable to a EqualsSumOfMatcher variable.
-  EqualsSumOfMatcher a0 = EqualsSumOf();
-
-  // EqualsSumOf(1) must be assignable to a EqualsSumOfMatcherP variable.
-  EqualsSumOfMatcherP<int> a1 = EqualsSumOf(1);
-
-  // EqualsSumOf(p1, ..., pk) must be assignable to a EqualsSumOfMatcherPk
-  // variable, and so on.
-  EqualsSumOfMatcherP2<int, char> a2 = EqualsSumOf(1, '2');
-  EqualsSumOfMatcherP3<int, int, char> a3 = EqualsSumOf(1, 2, '3');
-  EqualsSumOfMatcherP4<int, int, int, char> a4 = EqualsSumOf(1, 2, 3, '4');
-  EqualsSumOfMatcherP5<int, int, int, int, char> a5 =
-      EqualsSumOf(1, 2, 3, 4, '5');
-  EqualsSumOfMatcherP6<int, int, int, int, int, char> a6 =
-      EqualsSumOf(1, 2, 3, 4, 5, '6');
-  EqualsSumOfMatcherP7<int, int, int, int, int, int, char> a7 =
-      EqualsSumOf(1, 2, 3, 4, 5, 6, '7');
-  EqualsSumOfMatcherP8<int, int, int, int, int, int, int, char> a8 =
-      EqualsSumOf(1, 2, 3, 4, 5, 6, 7, '8');
-  EqualsSumOfMatcherP9<int, int, int, int, int, int, int, int, char> a9 =
-      EqualsSumOf(1, 2, 3, 4, 5, 6, 7, 8, '9');
-  EqualsSumOfMatcherP10<int, int, int, int, int, int, int, int, int, char> a10 =
-      EqualsSumOf(1, 2, 3, 4, 5, 6, 7, 8, 9, '0');
-
-  // Avoid "unused variable" warnings.
-  (void)a0;
-  (void)a1;
-  (void)a2;
-  (void)a3;
-  (void)a4;
-  (void)a5;
-  (void)a6;
-  (void)a7;
-  (void)a8;
-  (void)a9;
-  (void)a10;
-}
-
-// Tests that matcher-typed parameters can be used in Value() inside a
-// MATCHER_Pn definition.
-
-// Succeeds if arg matches exactly 2 of the 3 matchers.
-MATCHER_P3(TwoOf, m1, m2, m3, "") {
-  const int count = static_cast<int>(Value(arg, m1))
-      + static_cast<int>(Value(arg, m2)) + static_cast<int>(Value(arg, m3));
-  return count == 2;
-}
-
-TEST(MatcherPnMacroTest, CanUseMatcherTypedParameterInValue) {
-  EXPECT_THAT(42, TwoOf(Gt(0), Lt(50), Eq(10)));
-  EXPECT_THAT(0, Not(TwoOf(Gt(-1), Lt(1), Eq(0))));
-}
-
-// Tests Contains().
-
-TEST(ContainsTest, ListMatchesWhenElementIsInContainer) {
-  list<int> some_list;
-  some_list.push_back(3);
-  some_list.push_back(1);
-  some_list.push_back(2);
-  EXPECT_THAT(some_list, Contains(1));
-  EXPECT_THAT(some_list, Contains(Gt(2.5)));
-  EXPECT_THAT(some_list, Contains(Eq(2.0f)));
-
-  list<std::string> another_list;
-  another_list.push_back("fee");
-  another_list.push_back("fie");
-  another_list.push_back("foe");
-  another_list.push_back("fum");
-  EXPECT_THAT(another_list, Contains(std::string("fee")));
-}
-
-TEST(ContainsTest, ListDoesNotMatchWhenElementIsNotInContainer) {
-  list<int> some_list;
-  some_list.push_back(3);
-  some_list.push_back(1);
-  EXPECT_THAT(some_list, Not(Contains(4)));
-}
-
-TEST(ContainsTest, SetMatchesWhenElementIsInContainer) {
-  set<int> some_set;
-  some_set.insert(3);
-  some_set.insert(1);
-  some_set.insert(2);
-  EXPECT_THAT(some_set, Contains(Eq(1.0)));
-  EXPECT_THAT(some_set, Contains(Eq(3.0f)));
-  EXPECT_THAT(some_set, Contains(2));
-
-  set<const char*> another_set;
-  another_set.insert("fee");
-  another_set.insert("fie");
-  another_set.insert("foe");
-  another_set.insert("fum");
-  EXPECT_THAT(another_set, Contains(Eq(std::string("fum"))));
-}
-
-TEST(ContainsTest, SetDoesNotMatchWhenElementIsNotInContainer) {
-  set<int> some_set;
-  some_set.insert(3);
-  some_set.insert(1);
-  EXPECT_THAT(some_set, Not(Contains(4)));
-
-  set<const char*> c_string_set;
-  c_string_set.insert("hello");
-  EXPECT_THAT(c_string_set, Not(Contains(std::string("hello").c_str())));
-}
-
-TEST(ContainsTest, ExplainsMatchResultCorrectly) {
-  const int a[2] = { 1, 2 };
-  Matcher<const int (&)[2]> m = Contains(2);
-  EXPECT_EQ("whose element #1 matches", Explain(m, a));
-
-  m = Contains(3);
-  EXPECT_EQ("", Explain(m, a));
-
-  m = Contains(GreaterThan(0));
-  EXPECT_EQ("whose element #0 matches, which is 1 more than 0", Explain(m, a));
-
-  m = Contains(GreaterThan(10));
-  EXPECT_EQ("", Explain(m, a));
-}
-
-TEST(ContainsTest, DescribesItselfCorrectly) {
-  Matcher<vector<int> > m = Contains(1);
-  EXPECT_EQ("contains at least one element that is equal to 1", Describe(m));
-
-  Matcher<vector<int> > m2 = Not(m);
-  EXPECT_EQ("doesn't contain any element that is equal to 1", Describe(m2));
-}
-
-TEST(ContainsTest, MapMatchesWhenElementIsInContainer) {
-  map<const char*, int> my_map;
-  const char* bar = "a string";
-  my_map[bar] = 2;
-  EXPECT_THAT(my_map, Contains(pair<const char* const, int>(bar, 2)));
-
-  map<std::string, int> another_map;
-  another_map["fee"] = 1;
-  another_map["fie"] = 2;
-  another_map["foe"] = 3;
-  another_map["fum"] = 4;
-  EXPECT_THAT(another_map,
-              Contains(pair<const std::string, int>(std::string("fee"), 1)));
-  EXPECT_THAT(another_map, Contains(pair<const std::string, int>("fie", 2)));
-}
-
-TEST(ContainsTest, MapDoesNotMatchWhenElementIsNotInContainer) {
-  map<int, int> some_map;
-  some_map[1] = 11;
-  some_map[2] = 22;
-  EXPECT_THAT(some_map, Not(Contains(pair<const int, int>(2, 23))));
-}
-
-TEST(ContainsTest, ArrayMatchesWhenElementIsInContainer) {
-  const char* string_array[] = { "fee", "fie", "foe", "fum" };
-  EXPECT_THAT(string_array, Contains(Eq(std::string("fum"))));
-}
-
-TEST(ContainsTest, ArrayDoesNotMatchWhenElementIsNotInContainer) {
-  int int_array[] = { 1, 2, 3, 4 };
-  EXPECT_THAT(int_array, Not(Contains(5)));
-}
-
-TEST(ContainsTest, AcceptsMatcher) {
-  const int a[] = { 1, 2, 3 };
-  EXPECT_THAT(a, Contains(Gt(2)));
-  EXPECT_THAT(a, Not(Contains(Gt(4))));
-}
-
-TEST(ContainsTest, WorksForNativeArrayAsTuple) {
-  const int a[] = { 1, 2 };
-  const int* const pointer = a;
-  EXPECT_THAT(std::make_tuple(pointer, 2), Contains(1));
-  EXPECT_THAT(std::make_tuple(pointer, 2), Not(Contains(Gt(3))));
-}
-
-TEST(ContainsTest, WorksForTwoDimensionalNativeArray) {
-  int a[][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
-  EXPECT_THAT(a, Contains(ElementsAre(4, 5, 6)));
-  EXPECT_THAT(a, Contains(Contains(5)));
-  EXPECT_THAT(a, Not(Contains(ElementsAre(3, 4, 5))));
-  EXPECT_THAT(a, Contains(Not(Contains(5))));
-}
-
-TEST(AllOfArrayTest, BasicForms) {
-  // Iterator
-  std::vector<int> v0{};
-  std::vector<int> v1{1};
-  std::vector<int> v2{2, 3};
-  std::vector<int> v3{4, 4, 4};
-  EXPECT_THAT(0, AllOfArray(v0.begin(), v0.end()));
-  EXPECT_THAT(1, AllOfArray(v1.begin(), v1.end()));
-  EXPECT_THAT(2, Not(AllOfArray(v1.begin(), v1.end())));
-  EXPECT_THAT(3, Not(AllOfArray(v2.begin(), v2.end())));
-  EXPECT_THAT(4, AllOfArray(v3.begin(), v3.end()));
-  // Pointer +  size
-  int ar[6] = {1, 2, 3, 4, 4, 4};
-  EXPECT_THAT(0, AllOfArray(ar, 0));
-  EXPECT_THAT(1, AllOfArray(ar, 1));
-  EXPECT_THAT(2, Not(AllOfArray(ar, 1)));
-  EXPECT_THAT(3, Not(AllOfArray(ar + 1, 3)));
-  EXPECT_THAT(4, AllOfArray(ar + 3, 3));
-  // Array
-  // int ar0[0];  Not usable
-  int ar1[1] = {1};
-  int ar2[2] = {2, 3};
-  int ar3[3] = {4, 4, 4};
-  // EXPECT_THAT(0, Not(AllOfArray(ar0)));  // Cannot work
-  EXPECT_THAT(1, AllOfArray(ar1));
-  EXPECT_THAT(2, Not(AllOfArray(ar1)));
-  EXPECT_THAT(3, Not(AllOfArray(ar2)));
-  EXPECT_THAT(4, AllOfArray(ar3));
-  // Container
-  EXPECT_THAT(0, AllOfArray(v0));
-  EXPECT_THAT(1, AllOfArray(v1));
-  EXPECT_THAT(2, Not(AllOfArray(v1)));
-  EXPECT_THAT(3, Not(AllOfArray(v2)));
-  EXPECT_THAT(4, AllOfArray(v3));
-  // Initializer
-  EXPECT_THAT(0, AllOfArray<int>({}));  // Requires template arg.
-  EXPECT_THAT(1, AllOfArray({1}));
-  EXPECT_THAT(2, Not(AllOfArray({1})));
-  EXPECT_THAT(3, Not(AllOfArray({2, 3})));
-  EXPECT_THAT(4, AllOfArray({4, 4, 4}));
-}
-
-TEST(AllOfArrayTest, Matchers) {
-  // vector
-  std::vector<Matcher<int>> matchers{Ge(1), Lt(2)};
-  EXPECT_THAT(0, Not(AllOfArray(matchers)));
-  EXPECT_THAT(1, AllOfArray(matchers));
-  EXPECT_THAT(2, Not(AllOfArray(matchers)));
-  // initializer_list
-  EXPECT_THAT(0, Not(AllOfArray({Ge(0), Ge(1)})));
-  EXPECT_THAT(1, AllOfArray({Ge(0), Ge(1)}));
-}
-
-TEST(AnyOfArrayTest, BasicForms) {
-  // Iterator
-  std::vector<int> v0{};
-  std::vector<int> v1{1};
-  std::vector<int> v2{2, 3};
-  EXPECT_THAT(0, Not(AnyOfArray(v0.begin(), v0.end())));
-  EXPECT_THAT(1, AnyOfArray(v1.begin(), v1.end()));
-  EXPECT_THAT(2, Not(AnyOfArray(v1.begin(), v1.end())));
-  EXPECT_THAT(3, AnyOfArray(v2.begin(), v2.end()));
-  EXPECT_THAT(4, Not(AnyOfArray(v2.begin(), v2.end())));
-  // Pointer +  size
-  int ar[3] = {1, 2, 3};
-  EXPECT_THAT(0, Not(AnyOfArray(ar, 0)));
-  EXPECT_THAT(1, AnyOfArray(ar, 1));
-  EXPECT_THAT(2, Not(AnyOfArray(ar, 1)));
-  EXPECT_THAT(3, AnyOfArray(ar + 1, 2));
-  EXPECT_THAT(4, Not(AnyOfArray(ar + 1, 2)));
-  // Array
-  // int ar0[0];  Not usable
-  int ar1[1] = {1};
-  int ar2[2] = {2, 3};
-  // EXPECT_THAT(0, Not(AnyOfArray(ar0)));  // Cannot work
-  EXPECT_THAT(1, AnyOfArray(ar1));
-  EXPECT_THAT(2, Not(AnyOfArray(ar1)));
-  EXPECT_THAT(3, AnyOfArray(ar2));
-  EXPECT_THAT(4, Not(AnyOfArray(ar2)));
-  // Container
-  EXPECT_THAT(0, Not(AnyOfArray(v0)));
-  EXPECT_THAT(1, AnyOfArray(v1));
-  EXPECT_THAT(2, Not(AnyOfArray(v1)));
-  EXPECT_THAT(3, AnyOfArray(v2));
-  EXPECT_THAT(4, Not(AnyOfArray(v2)));
-  // Initializer
-  EXPECT_THAT(0, Not(AnyOfArray<int>({})));  // Requires template arg.
-  EXPECT_THAT(1, AnyOfArray({1}));
-  EXPECT_THAT(2, Not(AnyOfArray({1})));
-  EXPECT_THAT(3, AnyOfArray({2, 3}));
-  EXPECT_THAT(4, Not(AnyOfArray({2, 3})));
-}
-
-TEST(AnyOfArrayTest, Matchers) {
-  // We negate test AllOfArrayTest.Matchers.
-  // vector
-  std::vector<Matcher<int>> matchers{Lt(1), Ge(2)};
-  EXPECT_THAT(0, AnyOfArray(matchers));
-  EXPECT_THAT(1, Not(AnyOfArray(matchers)));
-  EXPECT_THAT(2, AnyOfArray(matchers));
-  // initializer_list
-  EXPECT_THAT(0, AnyOfArray({Lt(0), Lt(1)}));
-  EXPECT_THAT(1, Not(AllOfArray({Lt(0), Lt(1)})));
-}
-
-TEST(AnyOfArrayTest, ExplainsMatchResultCorrectly) {
-  // AnyOfArray and AllOfArry use the same underlying template-template,
-  // thus it is sufficient to test one here.
-  const std::vector<int> v0{};
-  const std::vector<int> v1{1};
-  const std::vector<int> v2{2, 3};
-  const Matcher<int> m0 = AnyOfArray(v0);
-  const Matcher<int> m1 = AnyOfArray(v1);
-  const Matcher<int> m2 = AnyOfArray(v2);
-  EXPECT_EQ("", Explain(m0, 0));
-  EXPECT_EQ("", Explain(m1, 1));
-  EXPECT_EQ("", Explain(m1, 2));
-  EXPECT_EQ("", Explain(m2, 3));
-  EXPECT_EQ("", Explain(m2, 4));
-  EXPECT_EQ("()", Describe(m0));
-  EXPECT_EQ("(is equal to 1)", Describe(m1));
-  EXPECT_EQ("(is equal to 2) or (is equal to 3)", Describe(m2));
-  EXPECT_EQ("()", DescribeNegation(m0));
-  EXPECT_EQ("(isn't equal to 1)", DescribeNegation(m1));
-  EXPECT_EQ("(isn't equal to 2) and (isn't equal to 3)", DescribeNegation(m2));
-  // Explain with matchers
-  const Matcher<int> g1 = AnyOfArray({GreaterThan(1)});
-  const Matcher<int> g2 = AnyOfArray({GreaterThan(1), GreaterThan(2)});
-  // Explains the first positiv match and all prior negative matches...
-  EXPECT_EQ("which is 1 less than 1", Explain(g1, 0));
-  EXPECT_EQ("which is the same as 1", Explain(g1, 1));
-  EXPECT_EQ("which is 1 more than 1", Explain(g1, 2));
-  EXPECT_EQ("which is 1 less than 1, and which is 2 less than 2",
-            Explain(g2, 0));
-  EXPECT_EQ("which is the same as 1, and which is 1 less than 2",
-            Explain(g2, 1));
-  EXPECT_EQ("which is 1 more than 1",  // Only the first
-            Explain(g2, 2));
-}
-
-TEST(AllOfTest, HugeMatcher) {
-  // Verify that using AllOf with many arguments doesn't cause
-  // the compiler to exceed template instantiation depth limit.
-  EXPECT_THAT(0, testing::AllOf(_, _, _, _, _, _, _, _, _,
-                                testing::AllOf(_, _, _, _, _, _, _, _, _, _)));
-}
-
-TEST(AnyOfTest, HugeMatcher) {
-  // Verify that using AnyOf with many arguments doesn't cause
-  // the compiler to exceed template instantiation depth limit.
-  EXPECT_THAT(0, testing::AnyOf(_, _, _, _, _, _, _, _, _,
-                                testing::AnyOf(_, _, _, _, _, _, _, _, _, _)));
-}
-
-namespace adl_test {
-
-// Verifies that the implementation of ::testing::AllOf and ::testing::AnyOf
-// don't issue unqualified recursive calls.  If they do, the argument dependent
-// name lookup will cause AllOf/AnyOf in the 'adl_test' namespace to be found
-// as a candidate and the compilation will break due to an ambiguous overload.
-
-// The matcher must be in the same namespace as AllOf/AnyOf to make argument
-// dependent lookup find those.
-MATCHER(M, "") { return true; }
-
-template <typename T1, typename T2>
-bool AllOf(const T1& /*t1*/, const T2& /*t2*/) { return true; }
-
-TEST(AllOfTest, DoesNotCallAllOfUnqualified) {
-  EXPECT_THAT(42, testing::AllOf(
-      M(), M(), M(), M(), M(), M(), M(), M(), M(), M()));
-}
-
-template <typename T1, typename T2> bool
-AnyOf(const T1& t1, const T2& t2) { return true; }
-
-TEST(AnyOfTest, DoesNotCallAnyOfUnqualified) {
-  EXPECT_THAT(42, testing::AnyOf(
-      M(), M(), M(), M(), M(), M(), M(), M(), M(), M()));
-}
-
-}  // namespace adl_test
-
-
-TEST(AllOfTest, WorksOnMoveOnlyType) {
-  std::unique_ptr<int> p(new int(3));
-  EXPECT_THAT(p, AllOf(Pointee(Eq(3)), Pointee(Gt(0)), Pointee(Lt(5))));
-  EXPECT_THAT(p, Not(AllOf(Pointee(Eq(3)), Pointee(Gt(0)), Pointee(Lt(3)))));
-}
-
-TEST(AnyOfTest, WorksOnMoveOnlyType) {
-  std::unique_ptr<int> p(new int(3));
-  EXPECT_THAT(p, AnyOf(Pointee(Eq(5)), Pointee(Lt(0)), Pointee(Lt(5))));
-  EXPECT_THAT(p, Not(AnyOf(Pointee(Eq(5)), Pointee(Lt(0)), Pointee(Gt(5)))));
-}
-
-MATCHER(IsNotNull, "") {
-  return arg != nullptr;
-}
-
-// Verifies that a matcher defined using MATCHER() can work on
-// move-only types.
-TEST(MatcherMacroTest, WorksOnMoveOnlyType) {
-  std::unique_ptr<int> p(new int(3));
-  EXPECT_THAT(p, IsNotNull());
-  EXPECT_THAT(std::unique_ptr<int>(), Not(IsNotNull()));
-}
-
-MATCHER_P(UniquePointee, pointee, "") {
-  return *arg == pointee;
-}
-
-// Verifies that a matcher defined using MATCHER_P*() can work on
-// move-only types.
-TEST(MatcherPMacroTest, WorksOnMoveOnlyType) {
-  std::unique_ptr<int> p(new int(3));
-  EXPECT_THAT(p, UniquePointee(3));
-  EXPECT_THAT(p, Not(UniquePointee(2)));
-}
-
-
-}  // namespace
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
diff --git a/ext/googletest/googlemock/test/gmock-internal-utils_test.cc b/ext/googletest/googlemock/test/gmock-internal-utils_test.cc
index d000e69..bd7e335 100644
--- a/ext/googletest/googlemock/test/gmock-internal-utils_test.cc
+++ b/ext/googletest/googlemock/test/gmock-internal-utils_test.cc
@@ -36,11 +36,11 @@
 
 #include <stdlib.h>
 
+#include <cstdint>
 #include <map>
 #include <memory>
 #include <sstream>
 #include <string>
-#include <type_traits>
 #include <vector>
 
 #include "gmock/gmock.h"
@@ -124,20 +124,6 @@
             ConvertIdentifierNameToWords("_Chapter11Section_1_"));
 }
 
-TEST(PointeeOfTest, WorksForSmartPointers) {
-  EXPECT_TRUE(
-      (std::is_same<int, PointeeOf<std::unique_ptr<int>>::type>::value));
-  EXPECT_TRUE(
-      (std::is_same<std::string,
-                    PointeeOf<std::shared_ptr<std::string>>::type>::value));
-}
-
-TEST(PointeeOfTest, WorksForRawPointers) {
-  EXPECT_TRUE((std::is_same<int, PointeeOf<int*>::type>::value));
-  EXPECT_TRUE((std::is_same<const char, PointeeOf<const char*>::type>::value));
-  EXPECT_TRUE((std::is_void<PointeeOf<void*>::type>::value));
-}
-
 TEST(GetRawPointerTest, WorksForSmartPointers) {
   const char* const raw_p1 = new const char('a');  // NOLINT
   const std::unique_ptr<const char> p1(raw_p1);
@@ -173,9 +159,9 @@
   EXPECT_EQ(kInteger, GMOCK_KIND_OF_(unsigned int));  // NOLINT
   EXPECT_EQ(kInteger, GMOCK_KIND_OF_(long));  // NOLINT
   EXPECT_EQ(kInteger, GMOCK_KIND_OF_(unsigned long));  // NOLINT
+  EXPECT_EQ(kInteger, GMOCK_KIND_OF_(long long));  // NOLINT
+  EXPECT_EQ(kInteger, GMOCK_KIND_OF_(unsigned long long));  // NOLINT
   EXPECT_EQ(kInteger, GMOCK_KIND_OF_(wchar_t));  // NOLINT
-  EXPECT_EQ(kInteger, GMOCK_KIND_OF_(Int64));  // NOLINT
-  EXPECT_EQ(kInteger, GMOCK_KIND_OF_(UInt64));  // NOLINT
   EXPECT_EQ(kInteger, GMOCK_KIND_OF_(size_t));  // NOLINT
 #if GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN
   // ssize_t is not defined on Windows and possibly some other OSes.
@@ -223,11 +209,12 @@
   EXPECT_TRUE((LosslessArithmeticConvertible<unsigned char, int>::value));
 
   // Unsigned => larger unsigned is fine.
-  EXPECT_TRUE(
-      (LosslessArithmeticConvertible<unsigned short, UInt64>::value)); // NOLINT
+  EXPECT_TRUE((LosslessArithmeticConvertible<
+               unsigned short, uint64_t>::value));  // NOLINT
 
   // Signed => unsigned is not fine.
-  EXPECT_FALSE((LosslessArithmeticConvertible<short, UInt64>::value)); // NOLINT
+  EXPECT_FALSE((LosslessArithmeticConvertible<
+                short, uint64_t>::value));  // NOLINT
   EXPECT_FALSE((LosslessArithmeticConvertible<
       signed char, unsigned int>::value));  // NOLINT
 
@@ -243,12 +230,12 @@
   EXPECT_FALSE((LosslessArithmeticConvertible<
                 unsigned char, signed char>::value));
   EXPECT_FALSE((LosslessArithmeticConvertible<int, unsigned int>::value));
-  EXPECT_FALSE((LosslessArithmeticConvertible<UInt64, Int64>::value));
+  EXPECT_FALSE((LosslessArithmeticConvertible<uint64_t, int64_t>::value));
 
   // Larger size => smaller size is not fine.
   EXPECT_FALSE((LosslessArithmeticConvertible<long, char>::value));  // NOLINT
   EXPECT_FALSE((LosslessArithmeticConvertible<int, signed char>::value));
-  EXPECT_FALSE((LosslessArithmeticConvertible<Int64, unsigned int>::value));
+  EXPECT_FALSE((LosslessArithmeticConvertible<int64_t, unsigned int>::value));
 }
 
 TEST(LosslessArithmeticConvertibleTest, IntegerToFloatingPoint) {
@@ -267,7 +254,7 @@
 
 TEST(LosslessArithmeticConvertibleTest, FloatingPointToInteger) {
   EXPECT_FALSE((LosslessArithmeticConvertible<float, long>::value));  // NOLINT
-  EXPECT_FALSE((LosslessArithmeticConvertible<double, Int64>::value));
+  EXPECT_FALSE((LosslessArithmeticConvertible<double, int64_t>::value));
   EXPECT_FALSE((LosslessArithmeticConvertible<long double, int>::value));
 }
 
@@ -453,7 +440,8 @@
   const std::string log = GetCapturedStdout();
 
   std::string expected_trace =
-      (testing::Message() << GTEST_FLAG(stack_trace_depth) << "::").GetString();
+      (testing::Message() << GTEST_FLAG_GET(stack_trace_depth) << "::")
+          .GetString();
   std::string expected_message =
       "\nGMOCK WARNING:\n"
       "Test log.\n"
diff --git a/ext/googletest/googlemock/test/gmock-matchers_test.cc b/ext/googletest/googlemock/test/gmock-matchers_test.cc
index 0373526..e6f280d 100644
--- a/ext/googletest/googlemock/test/gmock-matchers_test.cc
+++ b/ext/googletest/googlemock/test/gmock-matchers_test.cc
@@ -41,10 +41,12 @@
 #endif
 
 #include "gmock/gmock-matchers.h"
-#include "gmock/gmock-more-matchers.h"
 
 #include <string.h>
 #include <time.h>
+
+#include <array>
+#include <cstdint>
 #include <deque>
 #include <forward_list>
 #include <functional>
@@ -58,11 +60,15 @@
 #include <sstream>
 #include <string>
 #include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
 #include <utility>
 #include <vector>
+
+#include "gmock/gmock-more-matchers.h"
 #include "gmock/gmock.h"
-#include "gtest/gtest.h"
 #include "gtest/gtest-spi.h"
+#include "gtest/gtest.h"
 
 namespace testing {
 namespace gmock_matchers_test {
@@ -83,6 +89,7 @@
 using testing::internal::DummyMatchResultListener;
 using testing::internal::ElementMatcherPair;
 using testing::internal::ElementMatcherPairs;
+using testing::internal::ElementsAreArrayMatcher;
 using testing::internal::ExplainMatchFailureTupleTo;
 using testing::internal::FloatingEqMatcher;
 using testing::internal::FormatMatcherDescription;
@@ -107,36 +114,37 @@
 }
 
 // For testing ExplainMatchResultTo().
-class GreaterThanMatcher : public MatcherInterface<int> {
+template <typename T = int>
+class GreaterThanMatcher : public MatcherInterface<T> {
  public:
-  explicit GreaterThanMatcher(int rhs) : rhs_(rhs) {}
+  explicit GreaterThanMatcher(T rhs) : rhs_(rhs) {}
 
   void DescribeTo(ostream* os) const override { *os << "is > " << rhs_; }
 
-  bool MatchAndExplain(int lhs, MatchResultListener* listener) const override {
-    const int diff = lhs - rhs_;
-    if (diff > 0) {
-      *listener << "which is " << diff << " more than " << rhs_;
-    } else if (diff == 0) {
+  bool MatchAndExplain(T lhs, MatchResultListener* listener) const override {
+    if (lhs > rhs_) {
+      *listener << "which is " << (lhs - rhs_) << " more than " << rhs_;
+    } else if (lhs == rhs_) {
       *listener << "which is the same as " << rhs_;
     } else {
-      *listener << "which is " << -diff << " less than " << rhs_;
+      *listener << "which is " << (rhs_ - lhs) << " less than " << rhs_;
     }
 
     return lhs > rhs_;
   }
 
  private:
-  int rhs_;
+  const T rhs_;
 };
 
-Matcher<int> GreaterThan(int n) {
-  return MakeMatcher(new GreaterThanMatcher(n));
+template <typename T>
+Matcher<T> GreaterThan(T n) {
+  return MakeMatcher(new GreaterThanMatcher<T>(n));
 }
 
 std::string OfType(const std::string& type_name) {
 #if GTEST_HAS_RTTI
-  return " (of type " + type_name + ")";
+  return IsReadableTypeName(type_name) ? " (of type " + type_name + ")" : "";
 #else
   return "";
 #endif
@@ -347,43 +355,43 @@
   EXPECT_FALSE(m2.Matches("hello"));
 }
 
-#if GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_STRING_VIEW
 // Tests that a C-string literal can be implicitly converted to a
-// Matcher<absl::string_view> or Matcher<const absl::string_view&>.
+// Matcher<StringView> or Matcher<const StringView&>.
 TEST(StringViewMatcherTest, CanBeImplicitlyConstructedFromCStringLiteral) {
-  Matcher<absl::string_view> m1 = "cats";
+  Matcher<internal::StringView> m1 = "cats";
   EXPECT_TRUE(m1.Matches("cats"));
   EXPECT_FALSE(m1.Matches("dogs"));
 
-  Matcher<const absl::string_view&> m2 = "cats";
+  Matcher<const internal::StringView&> m2 = "cats";
   EXPECT_TRUE(m2.Matches("cats"));
   EXPECT_FALSE(m2.Matches("dogs"));
 }
 
 // Tests that a std::string object can be implicitly converted to a
-// Matcher<absl::string_view> or Matcher<const absl::string_view&>.
+// Matcher<StringView> or Matcher<const StringView&>.
 TEST(StringViewMatcherTest, CanBeImplicitlyConstructedFromString) {
-  Matcher<absl::string_view> m1 = std::string("cats");
+  Matcher<internal::StringView> m1 = std::string("cats");
   EXPECT_TRUE(m1.Matches("cats"));
   EXPECT_FALSE(m1.Matches("dogs"));
 
-  Matcher<const absl::string_view&> m2 = std::string("cats");
+  Matcher<const internal::StringView&> m2 = std::string("cats");
   EXPECT_TRUE(m2.Matches("cats"));
   EXPECT_FALSE(m2.Matches("dogs"));
 }
 
-// Tests that a absl::string_view object can be implicitly converted to a
-// Matcher<absl::string_view> or Matcher<const absl::string_view&>.
+// Tests that a StringView object can be implicitly converted to a
+// Matcher<StringView> or Matcher<const StringView&>.
 TEST(StringViewMatcherTest, CanBeImplicitlyConstructedFromStringView) {
-  Matcher<absl::string_view> m1 = absl::string_view("cats");
+  Matcher<internal::StringView> m1 = internal::StringView("cats");
   EXPECT_TRUE(m1.Matches("cats"));
   EXPECT_FALSE(m1.Matches("dogs"));
 
-  Matcher<const absl::string_view&> m2 = absl::string_view("cats");
+  Matcher<const internal::StringView&> m2 = internal::StringView("cats");
   EXPECT_TRUE(m2.Matches("cats"));
   EXPECT_FALSE(m2.Matches("dogs"));
 }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
 // Tests that a std::reference_wrapper<std::string> object can be implicitly
 // converted to a Matcher<std::string> or Matcher<const std::string&> via Eq().
@@ -403,7 +411,7 @@
 // MatcherInterface* without requiring the user to explicitly
 // write the type.
 TEST(MakeMatcherTest, ConstructsMatcherFromMatcherInterface) {
-  const MatcherInterface<int>* dummy_impl = nullptr;
+  const MatcherInterface<int>* dummy_impl = new EvenMatcherImpl;
   Matcher<int> m = MakeMatcher(dummy_impl);
 }
 
@@ -761,10 +769,11 @@
 
 // Tests that MatcherCast<const T&>(m) works when m is a Matcher<T>.
 TEST(SafeMatcherCastTest, FromNonReferenceToConstReference) {
-  Matcher<int> m1 = Eq(0);
-  Matcher<const int&> m2 = SafeMatcherCast<const int&>(m1);
-  EXPECT_TRUE(m2.Matches(0));
-  EXPECT_FALSE(m2.Matches(1));
+  Matcher<std::unique_ptr<int>> m1 = IsNull();
+  Matcher<const std::unique_ptr<int>&> m2 =
+      SafeMatcherCast<const std::unique_ptr<int>&>(m1);
+  EXPECT_TRUE(m2.Matches(std::unique_ptr<int>()));
+  EXPECT_FALSE(m2.Matches(std::unique_ptr<int>(new int)));
 }
 
 // Tests that SafeMatcherCast<T&>(m) works when m is a Matcher<T>.
@@ -1066,7 +1075,12 @@
   MOCK_METHOD1(Call, void(MoveOnly));
 };
 
+// Disable this test in VS 2015 (version 14), where it fails when SEH is enabled
+#if defined(_MSC_VER) && (_MSC_VER < 1910)
+TEST(ComparisonBaseTest, DISABLED_WorksWithMoveOnly) {
+#else
 TEST(ComparisonBaseTest, WorksWithMoveOnly) {
+#endif
   MoveOnly m{0};
   MoveHelper helper;
 
@@ -1221,6 +1235,25 @@
 
 // Tests string comparison matchers.
 
+template <typename T = std::string>
+std::string FromStringLike(internal::StringLike<T> str) {
+  return std::string(str);
+}
+
+TEST(StringLike, TestConversions) {
+  EXPECT_EQ("foo", FromStringLike("foo"));
+  EXPECT_EQ("foo", FromStringLike(std::string("foo")));
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  EXPECT_EQ("foo", FromStringLike(internal::StringView("foo")));
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
+
+  // Non deducible types.
+  EXPECT_EQ("", FromStringLike({}));
+  EXPECT_EQ("foo", FromStringLike({'f', 'o', 'o'}));
+  const char buf[] = "foo";
+  EXPECT_EQ("foo", FromStringLike({buf, buf + 3}));
+}
+
 TEST(StrEqTest, MatchesEqualString) {
   Matcher<const char*> m = StrEq(std::string("Hello"));
   EXPECT_TRUE(m.Matches("Hello"));
@@ -1231,17 +1264,18 @@
   EXPECT_TRUE(m2.Matches("Hello"));
   EXPECT_FALSE(m2.Matches("Hi"));
 
-#if GTEST_HAS_ABSL
-  Matcher<const absl::string_view&> m3 = StrEq("Hello");
-  EXPECT_TRUE(m3.Matches(absl::string_view("Hello")));
-  EXPECT_FALSE(m3.Matches(absl::string_view("hello")));
-  EXPECT_FALSE(m3.Matches(absl::string_view()));
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  Matcher<const internal::StringView&> m3 =
+      StrEq(internal::StringView("Hello"));
+  EXPECT_TRUE(m3.Matches(internal::StringView("Hello")));
+  EXPECT_FALSE(m3.Matches(internal::StringView("hello")));
+  EXPECT_FALSE(m3.Matches(internal::StringView()));
 
-  Matcher<const absl::string_view&> m_empty = StrEq("");
-  EXPECT_TRUE(m_empty.Matches(absl::string_view("")));
-  EXPECT_TRUE(m_empty.Matches(absl::string_view()));
-  EXPECT_FALSE(m_empty.Matches(absl::string_view("hello")));
-#endif  // GTEST_HAS_ABSL
+  Matcher<const internal::StringView&> m_empty = StrEq("");
+  EXPECT_TRUE(m_empty.Matches(internal::StringView("")));
+  EXPECT_TRUE(m_empty.Matches(internal::StringView()));
+  EXPECT_FALSE(m_empty.Matches(internal::StringView("hello")));
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 TEST(StrEqTest, CanDescribeSelf) {
@@ -1268,12 +1302,12 @@
   EXPECT_TRUE(m2.Matches("hello"));
   EXPECT_FALSE(m2.Matches("Hello"));
 
-#if GTEST_HAS_ABSL
-  Matcher<const absl::string_view> m3 = StrNe("Hello");
-  EXPECT_TRUE(m3.Matches(absl::string_view("")));
-  EXPECT_TRUE(m3.Matches(absl::string_view()));
-  EXPECT_FALSE(m3.Matches(absl::string_view("Hello")));
-#endif  // GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  Matcher<const internal::StringView> m3 = StrNe(internal::StringView("Hello"));
+  EXPECT_TRUE(m3.Matches(internal::StringView("")));
+  EXPECT_TRUE(m3.Matches(internal::StringView()));
+  EXPECT_FALSE(m3.Matches(internal::StringView("Hello")));
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 TEST(StrNeTest, CanDescribeSelf) {
@@ -1292,13 +1326,14 @@
   EXPECT_TRUE(m2.Matches("hello"));
   EXPECT_FALSE(m2.Matches("Hi"));
 
-#if GTEST_HAS_ABSL
-  Matcher<const absl::string_view&> m3 = StrCaseEq(std::string("Hello"));
-  EXPECT_TRUE(m3.Matches(absl::string_view("Hello")));
-  EXPECT_TRUE(m3.Matches(absl::string_view("hello")));
-  EXPECT_FALSE(m3.Matches(absl::string_view("Hi")));
-  EXPECT_FALSE(m3.Matches(absl::string_view()));
-#endif  // GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  Matcher<const internal::StringView&> m3 =
+      StrCaseEq(internal::StringView("Hello"));
+  EXPECT_TRUE(m3.Matches(internal::StringView("Hello")));
+  EXPECT_TRUE(m3.Matches(internal::StringView("hello")));
+  EXPECT_FALSE(m3.Matches(internal::StringView("Hi")));
+  EXPECT_FALSE(m3.Matches(internal::StringView()));
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 TEST(StrCaseEqTest, MatchesEqualStringWith0IgnoringCase) {
@@ -1342,13 +1377,14 @@
   EXPECT_TRUE(m2.Matches(""));
   EXPECT_FALSE(m2.Matches("Hello"));
 
-#if GTEST_HAS_ABSL
-  Matcher<const absl::string_view> m3 = StrCaseNe("Hello");
-  EXPECT_TRUE(m3.Matches(absl::string_view("Hi")));
-  EXPECT_TRUE(m3.Matches(absl::string_view()));
-  EXPECT_FALSE(m3.Matches(absl::string_view("Hello")));
-  EXPECT_FALSE(m3.Matches(absl::string_view("hello")));
-#endif  // GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  Matcher<const internal::StringView> m3 =
+      StrCaseNe(internal::StringView("Hello"));
+  EXPECT_TRUE(m3.Matches(internal::StringView("Hi")));
+  EXPECT_TRUE(m3.Matches(internal::StringView()));
+  EXPECT_FALSE(m3.Matches(internal::StringView("Hello")));
+  EXPECT_FALSE(m3.Matches(internal::StringView("hello")));
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 TEST(StrCaseNeTest, CanDescribeSelf) {
@@ -1389,25 +1425,26 @@
   EXPECT_FALSE(m_empty.Matches(nullptr));
 }
 
-#if GTEST_HAS_ABSL
-// Tests that HasSubstr() works for matching absl::string_view-typed values.
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+// Tests that HasSubstr() works for matching StringView-typed values.
 TEST(HasSubstrTest, WorksForStringViewClasses) {
-  const Matcher<absl::string_view> m1 = HasSubstr("foo");
-  EXPECT_TRUE(m1.Matches(absl::string_view("I love food.")));
-  EXPECT_FALSE(m1.Matches(absl::string_view("tofo")));
-  EXPECT_FALSE(m1.Matches(absl::string_view()));
+  const Matcher<internal::StringView> m1 =
+      HasSubstr(internal::StringView("foo"));
+  EXPECT_TRUE(m1.Matches(internal::StringView("I love food.")));
+  EXPECT_FALSE(m1.Matches(internal::StringView("tofo")));
+  EXPECT_FALSE(m1.Matches(internal::StringView()));
 
-  const Matcher<const absl::string_view&> m2 = HasSubstr("foo");
-  EXPECT_TRUE(m2.Matches(absl::string_view("I love food.")));
-  EXPECT_FALSE(m2.Matches(absl::string_view("tofo")));
-  EXPECT_FALSE(m2.Matches(absl::string_view()));
+  const Matcher<const internal::StringView&> m2 = HasSubstr("foo");
+  EXPECT_TRUE(m2.Matches(internal::StringView("I love food.")));
+  EXPECT_FALSE(m2.Matches(internal::StringView("tofo")));
+  EXPECT_FALSE(m2.Matches(internal::StringView()));
 
-  const Matcher<const absl::string_view&> m3 = HasSubstr("");
-  EXPECT_TRUE(m3.Matches(absl::string_view("foo")));
-  EXPECT_TRUE(m3.Matches(absl::string_view("")));
-  EXPECT_TRUE(m3.Matches(absl::string_view()));
+  const Matcher<const internal::StringView&> m3 = HasSubstr("");
+  EXPECT_TRUE(m3.Matches(internal::StringView("foo")));
+  EXPECT_TRUE(m3.Matches(internal::StringView("")));
+  EXPECT_TRUE(m3.Matches(internal::StringView()));
 }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
 // Tests that HasSubstr(s) describes itself properly.
 TEST(HasSubstrTest, CanDescribeSelf) {
@@ -1612,6 +1649,147 @@
   EXPECT_THAT(container, Not(Contains(Pair(3, _))));
 }
 
+TEST(FieldsAreTest, MatchesCorrectly) {
+  std::tuple<int, std::string, double> p(25, "foo", .5);
+
+  // All fields match.
+  EXPECT_THAT(p, FieldsAre(25, "foo", .5));
+  EXPECT_THAT(p, FieldsAre(Ge(20), HasSubstr("o"), DoubleEq(.5)));
+
+  // Some don't match.
+  EXPECT_THAT(p, Not(FieldsAre(26, "foo", .5)));
+  EXPECT_THAT(p, Not(FieldsAre(25, "fo", .5)));
+  EXPECT_THAT(p, Not(FieldsAre(25, "foo", .6)));
+}
+
+TEST(FieldsAreTest, CanDescribeSelf) {
+  Matcher<const pair<std::string, int>&> m1 = FieldsAre("foo", 42);
+  EXPECT_EQ(
+      "has field #0 that is equal to \"foo\""
+      ", and has field #1 that is equal to 42",
+      Describe(m1));
+  EXPECT_EQ(
+      "has field #0 that isn't equal to \"foo\""
+      ", or has field #1 that isn't equal to 42",
+      DescribeNegation(m1));
+}
+
+TEST(FieldsAreTest, CanExplainMatchResultTo) {
+  // The first one that fails is the one that gives the error.
+  Matcher<std::tuple<int, int, int>> m =
+      FieldsAre(GreaterThan(0), GreaterThan(0), GreaterThan(0));
+
+  EXPECT_EQ("whose field #0 does not match, which is 1 less than 0",
+            Explain(m, std::make_tuple(-1, -2, -3)));
+  EXPECT_EQ("whose field #1 does not match, which is 2 less than 0",
+            Explain(m, std::make_tuple(1, -2, -3)));
+  EXPECT_EQ("whose field #2 does not match, which is 3 less than 0",
+            Explain(m, std::make_tuple(1, 2, -3)));
+
+  // If they all match, we get a long explanation of success.
+  EXPECT_EQ(
+      "whose all elements match, "
+      "where field #0 is a value which is 1 more than 0"
+      ", and field #1 is a value which is 2 more than 0"
+      ", and field #2 is a value which is 3 more than 0",
+      Explain(m, std::make_tuple(1, 2, 3)));
+
+  // Only print those that have an explanation.
+  m = FieldsAre(GreaterThan(0), 0, GreaterThan(0));
+  EXPECT_EQ(
+      "whose all elements match, "
+      "where field #0 is a value which is 1 more than 0"
+      ", and field #2 is a value which is 3 more than 0",
+      Explain(m, std::make_tuple(1, 0, 3)));
+
+  // If only one has an explanation, then print that one.
+  m = FieldsAre(0, GreaterThan(0), 0);
+  EXPECT_EQ(
+      "whose all elements match, "
+      "where field #1 is a value which is 1 more than 0",
+      Explain(m, std::make_tuple(0, 1, 0)));
+}
+
+#if defined(__cpp_structured_bindings) && __cpp_structured_bindings >= 201606
+TEST(FieldsAreTest, StructuredBindings) {
+  // testing::FieldsAre can also match aggregates and such with C++17 and up.
+  struct MyType {
+    int i;
+    std::string str;
+  };
+  EXPECT_THAT((MyType{17, "foo"}), FieldsAre(Eq(17), HasSubstr("oo")));
+
+  // Test all the supported arities.
+  struct MyVarType1 {
+    int a;
+  };
+  EXPECT_THAT(MyVarType1{}, FieldsAre(0));
+  struct MyVarType2 {
+    int a, b;
+  };
+  EXPECT_THAT(MyVarType2{}, FieldsAre(0, 0));
+  struct MyVarType3 {
+    int a, b, c;
+  };
+  EXPECT_THAT(MyVarType3{}, FieldsAre(0, 0, 0));
+  struct MyVarType4 {
+    int a, b, c, d;
+  };
+  EXPECT_THAT(MyVarType4{}, FieldsAre(0, 0, 0, 0));
+  struct MyVarType5 {
+    int a, b, c, d, e;
+  };
+  EXPECT_THAT(MyVarType5{}, FieldsAre(0, 0, 0, 0, 0));
+  struct MyVarType6 {
+    int a, b, c, d, e, f;
+  };
+  EXPECT_THAT(MyVarType6{}, FieldsAre(0, 0, 0, 0, 0, 0));
+  struct MyVarType7 {
+    int a, b, c, d, e, f, g;
+  };
+  EXPECT_THAT(MyVarType7{}, FieldsAre(0, 0, 0, 0, 0, 0, 0));
+  struct MyVarType8 {
+    int a, b, c, d, e, f, g, h;
+  };
+  EXPECT_THAT(MyVarType8{}, FieldsAre(0, 0, 0, 0, 0, 0, 0, 0));
+  struct MyVarType9 {
+    int a, b, c, d, e, f, g, h, i;
+  };
+  EXPECT_THAT(MyVarType9{}, FieldsAre(0, 0, 0, 0, 0, 0, 0, 0, 0));
+  struct MyVarType10 {
+    int a, b, c, d, e, f, g, h, i, j;
+  };
+  EXPECT_THAT(MyVarType10{}, FieldsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+  struct MyVarType11 {
+    int a, b, c, d, e, f, g, h, i, j, k;
+  };
+  EXPECT_THAT(MyVarType11{}, FieldsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+  struct MyVarType12 {
+    int a, b, c, d, e, f, g, h, i, j, k, l;
+  };
+  EXPECT_THAT(MyVarType12{}, FieldsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+  struct MyVarType13 {
+    int a, b, c, d, e, f, g, h, i, j, k, l, m;
+  };
+  EXPECT_THAT(MyVarType13{}, FieldsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+  struct MyVarType14 {
+    int a, b, c, d, e, f, g, h, i, j, k, l, m, n;
+  };
+  EXPECT_THAT(MyVarType14{},
+              FieldsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+  struct MyVarType15 {
+    int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o;
+  };
+  EXPECT_THAT(MyVarType15{},
+              FieldsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+  struct MyVarType16 {
+    int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p;
+  };
+  EXPECT_THAT(MyVarType16{},
+              FieldsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+}
+#endif
+
 TEST(ContainsTest, WorksWithMoveOnly) {
   ContainerHelper helper;
   EXPECT_CALL(helper, Call(Contains(Pointee(2))));
@@ -1644,12 +1822,13 @@
   EXPECT_FALSE(m2.Matches("H"));
   EXPECT_FALSE(m2.Matches(" Hi"));
 
-#if GTEST_HAS_ABSL
-  const Matcher<absl::string_view> m_empty = StartsWith("");
-  EXPECT_TRUE(m_empty.Matches(absl::string_view()));
-  EXPECT_TRUE(m_empty.Matches(absl::string_view("")));
-  EXPECT_TRUE(m_empty.Matches(absl::string_view("not empty")));
-#endif  // GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  const Matcher<internal::StringView> m_empty =
+      StartsWith(internal::StringView(""));
+  EXPECT_TRUE(m_empty.Matches(internal::StringView()));
+  EXPECT_TRUE(m_empty.Matches(internal::StringView("")));
+  EXPECT_TRUE(m_empty.Matches(internal::StringView("not empty")));
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 TEST(StartsWithTest, CanDescribeSelf) {
@@ -1672,13 +1851,14 @@
   EXPECT_FALSE(m2.Matches("i"));
   EXPECT_FALSE(m2.Matches("Hi "));
 
-#if GTEST_HAS_ABSL
-  const Matcher<const absl::string_view&> m4 = EndsWith("");
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  const Matcher<const internal::StringView&> m4 =
+      EndsWith(internal::StringView(""));
   EXPECT_TRUE(m4.Matches("Hi"));
   EXPECT_TRUE(m4.Matches(""));
-  EXPECT_TRUE(m4.Matches(absl::string_view()));
-  EXPECT_TRUE(m4.Matches(absl::string_view("")));
-#endif  // GTEST_HAS_ABSL
+  EXPECT_TRUE(m4.Matches(internal::StringView()));
+  EXPECT_TRUE(m4.Matches(internal::StringView("")));
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 TEST(EndsWithTest, CanDescribeSelf) {
@@ -1699,16 +1879,17 @@
   EXPECT_FALSE(m2.Matches("az1"));
   EXPECT_FALSE(m2.Matches("1az"));
 
-#if GTEST_HAS_ABSL
-  const Matcher<const absl::string_view&> m3 = MatchesRegex("a.*z");
-  EXPECT_TRUE(m3.Matches(absl::string_view("az")));
-  EXPECT_TRUE(m3.Matches(absl::string_view("abcz")));
-  EXPECT_FALSE(m3.Matches(absl::string_view("1az")));
-  EXPECT_FALSE(m3.Matches(absl::string_view()));
-  const Matcher<const absl::string_view&> m4 = MatchesRegex("");
-  EXPECT_TRUE(m4.Matches(absl::string_view("")));
-  EXPECT_TRUE(m4.Matches(absl::string_view()));
-#endif  // GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  const Matcher<const internal::StringView&> m3 = MatchesRegex("a.*z");
+  EXPECT_TRUE(m3.Matches(internal::StringView("az")));
+  EXPECT_TRUE(m3.Matches(internal::StringView("abcz")));
+  EXPECT_FALSE(m3.Matches(internal::StringView("1az")));
+  EXPECT_FALSE(m3.Matches(internal::StringView()));
+  const Matcher<const internal::StringView&> m4 =
+      MatchesRegex(internal::StringView(""));
+  EXPECT_TRUE(m4.Matches(internal::StringView("")));
+  EXPECT_TRUE(m4.Matches(internal::StringView()));
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 TEST(MatchesRegexTest, CanDescribeSelf) {
@@ -1718,10 +1899,10 @@
   Matcher<const char*> m2 = MatchesRegex(new RE("a.*"));
   EXPECT_EQ("matches regular expression \"a.*\"", Describe(m2));
 
-#if GTEST_HAS_ABSL
-  Matcher<const absl::string_view> m3 = MatchesRegex(new RE("0.*"));
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  Matcher<const internal::StringView> m3 = MatchesRegex(new RE("0.*"));
   EXPECT_EQ("matches regular expression \"0.*\"", Describe(m3));
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 // Tests ContainsRegex().
@@ -1737,16 +1918,18 @@
   EXPECT_TRUE(m2.Matches("az1"));
   EXPECT_FALSE(m2.Matches("1a"));
 
-#if GTEST_HAS_ABSL
-  const Matcher<const absl::string_view&> m3 = ContainsRegex(new RE("a.*z"));
-  EXPECT_TRUE(m3.Matches(absl::string_view("azbz")));
-  EXPECT_TRUE(m3.Matches(absl::string_view("az1")));
-  EXPECT_FALSE(m3.Matches(absl::string_view("1a")));
-  EXPECT_FALSE(m3.Matches(absl::string_view()));
-  const Matcher<const absl::string_view&> m4 = ContainsRegex("");
-  EXPECT_TRUE(m4.Matches(absl::string_view("")));
-  EXPECT_TRUE(m4.Matches(absl::string_view()));
-#endif  // GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  const Matcher<const internal::StringView&> m3 =
+      ContainsRegex(new RE("a.*z"));
+  EXPECT_TRUE(m3.Matches(internal::StringView("azbz")));
+  EXPECT_TRUE(m3.Matches(internal::StringView("az1")));
+  EXPECT_FALSE(m3.Matches(internal::StringView("1a")));
+  EXPECT_FALSE(m3.Matches(internal::StringView()));
+  const Matcher<const internal::StringView&> m4 =
+      ContainsRegex(internal::StringView(""));
+  EXPECT_TRUE(m4.Matches(internal::StringView("")));
+  EXPECT_TRUE(m4.Matches(internal::StringView()));
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 TEST(ContainsRegexTest, CanDescribeSelf) {
@@ -1756,10 +1939,10 @@
   Matcher<const char*> m2 = ContainsRegex(new RE("a.*"));
   EXPECT_EQ("contains regular expression \"a.*\"", Describe(m2));
 
-#if GTEST_HAS_ABSL
-  Matcher<const absl::string_view> m3 = ContainsRegex(new RE("0.*"));
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  Matcher<const internal::StringView> m3 = ContainsRegex(new RE("0.*"));
   EXPECT_EQ("contains regular expression \"0.*\"", Describe(m3));
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 }
 
 // Tests for wide strings.
@@ -2054,6 +2237,114 @@
   EXPECT_TRUE(matcher.Matches(pointers));
 }
 
+// Tests that IsNan() matches a NaN, with float.
+TEST(IsNan, FloatMatchesNan) {
+  float quiet_nan = std::numeric_limits<float>::quiet_NaN();
+  float other_nan = std::nanf("1");
+  float real_value = 1.0f;
+
+  Matcher<float> m = IsNan();
+  EXPECT_TRUE(m.Matches(quiet_nan));
+  EXPECT_TRUE(m.Matches(other_nan));
+  EXPECT_FALSE(m.Matches(real_value));
+
+  Matcher<float&> m_ref = IsNan();
+  EXPECT_TRUE(m_ref.Matches(quiet_nan));
+  EXPECT_TRUE(m_ref.Matches(other_nan));
+  EXPECT_FALSE(m_ref.Matches(real_value));
+
+  Matcher<const float&> m_cref = IsNan();
+  EXPECT_TRUE(m_cref.Matches(quiet_nan));
+  EXPECT_TRUE(m_cref.Matches(other_nan));
+  EXPECT_FALSE(m_cref.Matches(real_value));
+}
+
+// Tests that IsNan() matches a NaN, with double.
+TEST(IsNan, DoubleMatchesNan) {
+  double quiet_nan = std::numeric_limits<double>::quiet_NaN();
+  double other_nan = std::nan("1");
+  double real_value = 1.0;
+
+  Matcher<double> m = IsNan();
+  EXPECT_TRUE(m.Matches(quiet_nan));
+  EXPECT_TRUE(m.Matches(other_nan));
+  EXPECT_FALSE(m.Matches(real_value));
+
+  Matcher<double&> m_ref = IsNan();
+  EXPECT_TRUE(m_ref.Matches(quiet_nan));
+  EXPECT_TRUE(m_ref.Matches(other_nan));
+  EXPECT_FALSE(m_ref.Matches(real_value));
+
+  Matcher<const double&> m_cref = IsNan();
+  EXPECT_TRUE(m_cref.Matches(quiet_nan));
+  EXPECT_TRUE(m_cref.Matches(other_nan));
+  EXPECT_FALSE(m_cref.Matches(real_value));
+}
+
+// Tests that IsNan() matches a NaN, with long double.
+TEST(IsNan, LongDoubleMatchesNan) {
+  long double quiet_nan = std::numeric_limits<long double>::quiet_NaN();
+  long double other_nan = std::nan("1");
+  long double real_value = 1.0;
+
+  Matcher<long double> m = IsNan();
+  EXPECT_TRUE(m.Matches(quiet_nan));
+  EXPECT_TRUE(m.Matches(other_nan));
+  EXPECT_FALSE(m.Matches(real_value));
+
+  Matcher<long double&> m_ref = IsNan();
+  EXPECT_TRUE(m_ref.Matches(quiet_nan));
+  EXPECT_TRUE(m_ref.Matches(other_nan));
+  EXPECT_FALSE(m_ref.Matches(real_value));
+
+  Matcher<const long double&> m_cref = IsNan();
+  EXPECT_TRUE(m_cref.Matches(quiet_nan));
+  EXPECT_TRUE(m_cref.Matches(other_nan));
+  EXPECT_FALSE(m_cref.Matches(real_value));
+}
+
+// Tests that IsNan() works with Not.
+TEST(IsNan, NotMatchesNan) {
+  Matcher<float> mf = Not(IsNan());
+  EXPECT_FALSE(mf.Matches(std::numeric_limits<float>::quiet_NaN()));
+  EXPECT_FALSE(mf.Matches(std::nanf("1")));
+  EXPECT_TRUE(mf.Matches(1.0));
+
+  Matcher<double> md = Not(IsNan());
+  EXPECT_FALSE(md.Matches(std::numeric_limits<double>::quiet_NaN()));
+  EXPECT_FALSE(md.Matches(std::nan("1")));
+  EXPECT_TRUE(md.Matches(1.0));
+
+  Matcher<long double> mld = Not(IsNan());
+  EXPECT_FALSE(mld.Matches(std::numeric_limits<long double>::quiet_NaN()));
+  EXPECT_FALSE(mld.Matches(std::nanl("1")));
+  EXPECT_TRUE(mld.Matches(1.0));
+}
+
+// Tests that IsNan() can describe itself.
+TEST(IsNan, CanDescribeSelf) {
+  Matcher<float> mf = IsNan();
+  EXPECT_EQ("is NaN", Describe(mf));
+
+  Matcher<double> md = IsNan();
+  EXPECT_EQ("is NaN", Describe(md));
+
+  Matcher<long double> mld = IsNan();
+  EXPECT_EQ("is NaN", Describe(mld));
+}
+
+// Tests that IsNan() can describe itself with Not.
+TEST(IsNan, CanDescribeSelfWithNot) {
+  Matcher<float> mf = Not(IsNan());
+  EXPECT_EQ("isn't NaN", Describe(mf));
+
+  Matcher<double> md = Not(IsNan());
+  EXPECT_EQ("isn't NaN", Describe(md));
+
+  Matcher<long double> mld = Not(IsNan());
+  EXPECT_EQ("isn't NaN", Describe(mld));
+}
+
 // Tests that FloatEq() matches a 2-tuple where
 // FloatEq(first field) matches the second field.
 TEST(FloatEq2Test, MatchesEqualArguments) {
@@ -2481,6 +2772,34 @@
                 "43", "44", "45", "46", "47", "48", "49", "50"));
 }
 
+TEST(ConditionalTest, MatchesFirstIfCondition) {
+  Matcher<std::string> eq_red = Eq("red");
+  Matcher<std::string> ne_red = Ne("red");
+  Matcher<std::string> m = Conditional(true, eq_red, ne_red);
+  EXPECT_TRUE(m.Matches("red"));
+  EXPECT_FALSE(m.Matches("green"));
+
+  StringMatchResultListener listener;
+  StringMatchResultListener expected;
+  EXPECT_FALSE(m.MatchAndExplain("green", &listener));
+  EXPECT_FALSE(eq_red.MatchAndExplain("green", &expected));
+  EXPECT_THAT(listener.str(), Eq(expected.str()));
+}
+
+TEST(ConditionalTest, MatchesSecondIfCondition) {
+  Matcher<std::string> eq_red = Eq("red");
+  Matcher<std::string> ne_red = Ne("red");
+  Matcher<std::string> m = Conditional(false, eq_red, ne_red);
+  EXPECT_FALSE(m.Matches("red"));
+  EXPECT_TRUE(m.Matches("green"));
+
+  StringMatchResultListener listener;
+  StringMatchResultListener expected;
+  EXPECT_FALSE(m.MatchAndExplain("red", &listener));
+  EXPECT_FALSE(ne_red.MatchAndExplain("red", &expected));
+  EXPECT_THAT(listener.str(), Eq(expected.str()));
+}
+
 // Tests the variadic version of the ElementsAreMatcher
 TEST(ElementsAreTest, HugeMatcher) {
   vector<int> test_vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
@@ -2699,6 +3018,13 @@
   EXPECT_FALSE(m.Matches(n));
 }
 
+// Tests that Truly(predicate) provides a helpful reason when it fails.
+TEST(TrulyTest, ExplainsFailures) {
+  StringMatchResultListener listener;
+  EXPECT_FALSE(ExplainMatchResult(Truly(IsPositive), -1, &listener));
+  EXPECT_EQ(listener.str(), "didn't satisfy the given predicate");
+}
+
 // Tests that Matches(m) is a predicate satisfied by whatever that
 // matches matcher m.
 TEST(MatchesTest, IsSatisfiedByWhatMatchesTheMatcher) {
@@ -2763,6 +3089,33 @@
   EXPECT_EQ("", listener2.str());
 }
 
+MATCHER(ConstructNoArg, "") { return true; }
+MATCHER_P(Construct1Arg, arg1, "") { return true; }
+MATCHER_P2(Construct2Args, arg1, arg2, "") { return true; }
+
+TEST(MatcherConstruct, ExplicitVsImplicit) {
+  {
+    // No arg constructor can be constructed with empty brace.
+    ConstructNoArgMatcher m = {};
+    (void)m;
+    // And with no args
+    ConstructNoArgMatcher m2;
+    (void)m2;
+  }
+  {
+    // The one arg constructor has an explicit constructor.
+    // This is to prevent the implicit conversion.
+    using M = Construct1ArgMatcherP<int>;
+    EXPECT_TRUE((std::is_constructible<M, int>::value));
+    EXPECT_FALSE((std::is_convertible<int, M>::value));
+  }
+  {
+    // Multiple arg matchers can be constructed with an implicit construction.
+    Construct2ArgsMatcherP2<int, double> m = {1, 2.2};
+    (void)m;
+  }
+}
+
 MATCHER_P(Really, inner_matcher, "") {
   return ExplainMatchResult(inner_matcher, arg, result_listener);
 }
@@ -2876,18 +3229,13 @@
   static unsigned short n;  // NOLINT
   n = 5;
 
-  // VC++ prior to version 8.0 SP1 has a bug where it will not see any
-  // functions declared in the namespace scope from within nested classes.
-  // EXPECT/ASSERT_(NON)FATAL_FAILURE macros use nested classes so that all
-  // namespace-level functions invoked inside them need to be explicitly
-  // resolved.
-  EXPECT_FATAL_FAILURE(ASSERT_THAT(n, ::testing::Gt(10)),
+  EXPECT_FATAL_FAILURE(ASSERT_THAT(n, Gt(10)),
                        "Value of: n\n"
                        "Expected: is > 10\n"
                        "  Actual: 5" + OfType("unsigned short"));
   n = 0;
   EXPECT_NONFATAL_FAILURE(
-      EXPECT_THAT(n, ::testing::AllOf(::testing::Le(7), ::testing::Ge(5))),
+      EXPECT_THAT(n, AllOf(Le(7), Ge(5))),
       "Value of: n\n"
       "Expected: (is <= 7) and (is >= 5)\n"
       "  Actual: 0" + OfType("unsigned short"));
@@ -2901,11 +3249,11 @@
   static int n;
   n = 0;
   EXPECT_THAT(n, AllOf(Le(7), Ref(n)));
-  EXPECT_FATAL_FAILURE(ASSERT_THAT(n, ::testing::Not(::testing::Ref(n))),
+  EXPECT_FATAL_FAILURE(ASSERT_THAT(n, Not(Ref(n))),
                        "Value of: n\n"
                        "Expected: does not reference the variable @");
   // Tests the "Actual" part.
-  EXPECT_FATAL_FAILURE(ASSERT_THAT(n, ::testing::Not(::testing::Ref(n))),
+  EXPECT_FATAL_FAILURE(ASSERT_THAT(n, Not(Ref(n))),
                        "Actual: 0" + OfType("int") + ", which is located @");
 }
 
@@ -3409,6 +3757,105 @@
   EXPECT_FALSE(m.Matches(p));
 }
 
+TEST(PointeeTest, SmartPointer) {
+  const Matcher<std::unique_ptr<int>> m = Pointee(Ge(0));
+
+  std::unique_ptr<int> n(new int(1));
+  EXPECT_TRUE(m.Matches(n));
+}
+
+TEST(PointeeTest, SmartPointerToConst) {
+  const Matcher<std::unique_ptr<const int>> m = Pointee(Ge(0));
+
+  // There's no implicit conversion from unique_ptr<int> to const
+  // unique_ptr<const int>, so we must pass a unique_ptr<const int> into the
+  // matcher.
+  std::unique_ptr<const int> n(new int(1));
+  EXPECT_TRUE(m.Matches(n));
+}
+
+TEST(PointerTest, RawPointer) {
+  int n = 1;
+  const Matcher<int*> m = Pointer(Eq(&n));
+
+  EXPECT_TRUE(m.Matches(&n));
+
+  int* p = nullptr;
+  EXPECT_FALSE(m.Matches(p));
+  EXPECT_FALSE(m.Matches(nullptr));
+}
+
+TEST(PointerTest, RawPointerToConst) {
+  int n = 1;
+  const Matcher<const int*> m = Pointer(Eq(&n));
+
+  EXPECT_TRUE(m.Matches(&n));
+
+  int* p = nullptr;
+  EXPECT_FALSE(m.Matches(p));
+  EXPECT_FALSE(m.Matches(nullptr));
+}
+
+TEST(PointerTest, SmartPointer) {
+  std::unique_ptr<int> n(new int(10));
+  int* raw_n = n.get();
+  const Matcher<std::unique_ptr<int>> m = Pointer(Eq(raw_n));
+
+  EXPECT_TRUE(m.Matches(n));
+}
+
+TEST(PointerTest, SmartPointerToConst) {
+  std::unique_ptr<const int> n(new int(10));
+  const int* raw_n = n.get();
+  const Matcher<std::unique_ptr<const int>> m = Pointer(Eq(raw_n));
+
+  // There's no implicit conversion from unique_ptr<int> to const
+  // unique_ptr<const int>, so we must pass a unique_ptr<const int> into the
+  // matcher.
+  std::unique_ptr<const int> p(new int(10));
+  EXPECT_FALSE(m.Matches(p));
+}
+
+TEST(AddressTest, NonConst) {
+  int n = 1;
+  const Matcher<int> m = Address(Eq(&n));
+
+  EXPECT_TRUE(m.Matches(n));
+
+  int other = 5;
+
+  EXPECT_FALSE(m.Matches(other));
+
+  int& n_ref = n;
+
+  EXPECT_TRUE(m.Matches(n_ref));
+}
+
+TEST(AddressTest, Const) {
+  const int n = 1;
+  const Matcher<int> m = Address(Eq(&n));
+
+  EXPECT_TRUE(m.Matches(n));
+
+  int other = 5;
+
+  EXPECT_FALSE(m.Matches(other));
+}
+
+TEST(AddressTest, MatcherDoesntCopy) {
+  std::unique_ptr<int> n(new int(1));
+  const Matcher<std::unique_ptr<int>> m = Address(Eq(&n));
+
+  EXPECT_TRUE(m.Matches(n));
+}
+
+TEST(AddressTest, Describe) {
+  Matcher<int> matcher = Address(_);
+  EXPECT_EQ("has address that is anything", Describe(matcher));
+  EXPECT_EQ("does not have address that is anything",
+            DescribeNegation(matcher));
+}
+
 MATCHER_P(FieldIIs, inner_matcher, "") {
   return ExplainMatchResult(inner_matcher, arg.i, result_listener);
 }
@@ -3610,17 +4057,11 @@
   const double y;  // A const field.
   Uncopyable z;    // An uncopyable field.
   const char* p;   // A pointer field.
-
- private:
-  GTEST_DISALLOW_ASSIGN_(AStruct);
 };
 
 // A derived struct for testing Field().
 struct DerivedStruct : public AStruct {
   char ch;
-
- private:
-  GTEST_DISALLOW_ASSIGN_(DerivedStruct);
 };
 
 // Tests that Field(&Foo::field, ...) works when field is non-const.
@@ -4590,20 +5031,18 @@
   Matcher<vector<int> > m1 = SizeIs(2);
   Matcher<vector<int> > m2 = SizeIs(Lt(2u));
   Matcher<vector<int> > m3 = SizeIs(AnyOf(0, 3));
-  Matcher<vector<int> > m4 = SizeIs(GreaterThan(1));
+  Matcher<vector<int> > m4 = SizeIs(Gt(1u));
   vector<int> container;
   EXPECT_EQ("whose size 0 doesn't match", Explain(m1, container));
   EXPECT_EQ("whose size 0 matches", Explain(m2, container));
   EXPECT_EQ("whose size 0 matches", Explain(m3, container));
-  EXPECT_EQ("whose size 0 doesn't match, which is 1 less than 1",
-            Explain(m4, container));
+  EXPECT_EQ("whose size 0 doesn't match", Explain(m4, container));
   container.push_back(0);
   container.push_back(0);
   EXPECT_EQ("whose size 2 matches", Explain(m1, container));
   EXPECT_EQ("whose size 2 doesn't match", Explain(m2, container));
   EXPECT_EQ("whose size 2 doesn't match", Explain(m3, container));
-  EXPECT_EQ("whose size 2 matches, which is 1 more than 1",
-            Explain(m4, container));
+  EXPECT_EQ("whose size 2 matches", Explain(m4, container));
 }
 
 #if GTEST_HAS_TYPED_TEST
@@ -5093,14 +5532,14 @@
   // Streamlike 'container' provides only minimal iterator support.
   // Its iterators are tagged with input_iterator_tag.
   const int a[5] = {2, 1, 4, 5, 3};
-  Streamlike<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  Streamlike<int> s(std::begin(a), std::end(a));
   EXPECT_THAT(s, WhenSorted(ElementsAre(1, 2, 3, 4, 5)));
   EXPECT_THAT(s, Not(WhenSorted(ElementsAre(2, 1, 4, 5, 3))));
 }
 
 TEST(WhenSortedTest, WorksForVectorConstRefMatcherOnStreamlike) {
   const int a[] = {2, 1, 4, 5, 3};
-  Streamlike<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  Streamlike<int> s(std::begin(a), std::end(a));
   Matcher<const std::vector<int>&> vector_match = ElementsAre(1, 2, 3, 4, 5);
   EXPECT_THAT(s, WhenSorted(vector_match));
   EXPECT_THAT(s, Not(WhenSorted(ElementsAre(2, 1, 4, 5, 3))));
@@ -5145,7 +5584,7 @@
 
 TEST(IsSupersetOfTest, WorksForStreamlike) {
   const int a[5] = {1, 2, 3, 4, 5};
-  Streamlike<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  Streamlike<int> s(std::begin(a), std::end(a));
 
   vector<int> expected;
   expected.push_back(1);
@@ -5273,7 +5712,7 @@
 
 TEST(IsSubsetOfTest, WorksForStreamlike) {
   const int a[5] = {1, 2};
-  Streamlike<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  Streamlike<int> s(std::begin(a), std::end(a));
 
   vector<int> expected;
   expected.push_back(1);
@@ -5367,14 +5806,14 @@
 
 TEST(ElemensAreStreamTest, WorksForStreamlike) {
   const int a[5] = {1, 2, 3, 4, 5};
-  Streamlike<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  Streamlike<int> s(std::begin(a), std::end(a));
   EXPECT_THAT(s, ElementsAre(1, 2, 3, 4, 5));
   EXPECT_THAT(s, Not(ElementsAre(2, 1, 4, 5, 3)));
 }
 
 TEST(ElemensAreArrayStreamTest, WorksForStreamlike) {
   const int a[5] = {1, 2, 3, 4, 5};
-  Streamlike<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  Streamlike<int> s(std::begin(a), std::end(a));
 
   vector<int> expected;
   expected.push_back(1);
@@ -5421,7 +5860,7 @@
 
 TEST(UnorderedElementsAreArrayTest, SucceedsWhenExpected) {
   const int a[] = {0, 1, 2, 3, 4};
-  std::vector<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  std::vector<int> s(std::begin(a), std::end(a));
   do {
     StringMatchResultListener listener;
     EXPECT_TRUE(ExplainMatchResult(UnorderedElementsAreArray(a),
@@ -5432,8 +5871,8 @@
 TEST(UnorderedElementsAreArrayTest, VectorBool) {
   const bool a[] = {0, 1, 0, 1, 1};
   const bool b[] = {1, 0, 1, 1, 0};
-  std::vector<bool> expected(a, a + GTEST_ARRAY_SIZE_(a));
-  std::vector<bool> actual(b, b + GTEST_ARRAY_SIZE_(b));
+  std::vector<bool> expected(std::begin(a), std::end(a));
+  std::vector<bool> actual(std::begin(b), std::end(b));
   StringMatchResultListener listener;
   EXPECT_TRUE(ExplainMatchResult(UnorderedElementsAreArray(expected),
                                  actual, &listener)) << listener.str();
@@ -5444,7 +5883,7 @@
   // Its iterators are tagged with input_iterator_tag, and it has no
   // size() or empty() methods.
   const int a[5] = {2, 1, 4, 5, 3};
-  Streamlike<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  Streamlike<int> s(std::begin(a), std::end(a));
 
   ::std::vector<int> expected;
   expected.push_back(1);
@@ -5527,7 +5966,7 @@
 
 TEST_F(UnorderedElementsAreTest, SucceedsWhenExpected) {
   const int a[] = {1, 2, 3};
-  std::vector<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  std::vector<int> s(std::begin(a), std::end(a));
   do {
     StringMatchResultListener listener;
     EXPECT_TRUE(ExplainMatchResult(UnorderedElementsAre(1, 2, 3),
@@ -5537,7 +5976,7 @@
 
 TEST_F(UnorderedElementsAreTest, FailsWhenAnElementMatchesNoMatcher) {
   const int a[] = {1, 2, 3};
-  std::vector<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  std::vector<int> s(std::begin(a), std::end(a));
   std::vector<Matcher<int> > mv;
   mv.push_back(1);
   mv.push_back(2);
@@ -5553,7 +5992,7 @@
   // Its iterators are tagged with input_iterator_tag, and it has no
   // size() or empty() methods.
   const int a[5] = {2, 1, 4, 5, 3};
-  Streamlike<int> s(a, a + GTEST_ARRAY_SIZE_(a));
+  Streamlike<int> s(std::begin(a), std::end(a));
 
   EXPECT_THAT(s, UnorderedElementsAre(1, 2, 3, 4, 5));
   EXPECT_THAT(s, Not(UnorderedElementsAre(2, 2, 3, 4, 5)));
@@ -5869,8 +6308,9 @@
   //  :.......:
   //    0 1 2
   MatchMatrix g(4, 3);
-  static const size_t kEdges[][2] = {{0, 2}, {1, 1}, {2, 1}, {3, 0}};
-  for (size_t i = 0; i < GTEST_ARRAY_SIZE_(kEdges); ++i) {
+  constexpr std::array<std::array<size_t, 2>, 4> kEdges = {
+      {{{0, 2}}, {{1, 1}}, {{2, 1}}, {{3, 0}}}};
+  for (size_t i = 0; i < kEdges.size(); ++i) {
     g.SetEdge(kEdges[i][0], kEdges[i][1], true);
   }
   EXPECT_THAT(FindBacktrackingMaxBPM(g),
@@ -5916,9 +6356,9 @@
   int iters = GetParam().second;
   MatchMatrix graph(static_cast<size_t>(nodes), static_cast<size_t>(nodes));
 
-  auto seed = static_cast<testing::internal::UInt32>(GTEST_FLAG(random_seed));
+  auto seed = static_cast<uint32_t>(GTEST_FLAG_GET(random_seed));
   if (seed == 0) {
-    seed = static_cast<testing::internal::UInt32>(time(nullptr));
+    seed = static_cast<uint32_t>(time(nullptr));
   }
 
   for (; iters > 0; --iters, ++seed) {
@@ -6777,7 +7217,8 @@
   EXPECT_FALSE(result);  // Implicit cast to bool.
   std::string expect =
       "Value of: dummy-name\nExpected: [DescribeTo]\n"
-      "  Actual: 1, [MatchAndExplain]";
+      "  Actual: 1" +
+      OfType(internal::GetTypeName<Behavior>()) + ", [MatchAndExplain]";
   EXPECT_EQ(expect, result.message());
 }
 
@@ -6788,10 +7229,1433 @@
       "Value of: dummy-name\nExpected: [DescribeTo]\n"
       "  The matcher failed on the initial attempt; but passed when rerun to "
       "generate the explanation.\n"
-      "  Actual: 2, [MatchAndExplain]";
+      "  Actual: 2" +
+      OfType(internal::GetTypeName<Behavior>()) + ", [MatchAndExplain]";
   EXPECT_EQ(expect, result.message());
 }
 
+// Tests for ElementsAre().
+
+TEST(ElementsAreTest, CanDescribeExpectingNoElement) {
+  Matcher<const vector<int>&> m = ElementsAre();
+  EXPECT_EQ("is empty", Describe(m));
+}
+
+TEST(ElementsAreTest, CanDescribeExpectingOneElement) {
+  Matcher<vector<int>> m = ElementsAre(Gt(5));
+  EXPECT_EQ("has 1 element that is > 5", Describe(m));
+}
+
+TEST(ElementsAreTest, CanDescribeExpectingManyElements) {
+  Matcher<list<std::string>> m = ElementsAre(StrEq("one"), "two");
+  EXPECT_EQ(
+      "has 2 elements where\n"
+      "element #0 is equal to \"one\",\n"
+      "element #1 is equal to \"two\"",
+      Describe(m));
+}
+
+TEST(ElementsAreTest, CanDescribeNegationOfExpectingNoElement) {
+  Matcher<vector<int>> m = ElementsAre();
+  EXPECT_EQ("isn't empty", DescribeNegation(m));
+}
+
+TEST(ElementsAreTest, CanDescribeNegationOfExpectingOneElment) {
+  Matcher<const list<int>&> m = ElementsAre(Gt(5));
+  EXPECT_EQ(
+      "doesn't have 1 element, or\n"
+      "element #0 isn't > 5",
+      DescribeNegation(m));
+}
+
+TEST(ElementsAreTest, CanDescribeNegationOfExpectingManyElements) {
+  Matcher<const list<std::string>&> m = ElementsAre("one", "two");
+  EXPECT_EQ(
+      "doesn't have 2 elements, or\n"
+      "element #0 isn't equal to \"one\", or\n"
+      "element #1 isn't equal to \"two\"",
+      DescribeNegation(m));
+}
+
+TEST(ElementsAreTest, DoesNotExplainTrivialMatch) {
+  Matcher<const list<int>&> m = ElementsAre(1, Ne(2));
+
+  list<int> test_list;
+  test_list.push_back(1);
+  test_list.push_back(3);
+  EXPECT_EQ("", Explain(m, test_list));  // No need to explain anything.
+}
+
+TEST(ElementsAreTest, ExplainsNonTrivialMatch) {
+  Matcher<const vector<int>&> m =
+      ElementsAre(GreaterThan(1), 0, GreaterThan(2));
+
+  const int a[] = {10, 0, 100};
+  vector<int> test_vector(std::begin(a), std::end(a));
+  EXPECT_EQ(
+      "whose element #0 matches, which is 9 more than 1,\n"
+      "and whose element #2 matches, which is 98 more than 2",
+      Explain(m, test_vector));
+}
+
+TEST(ElementsAreTest, CanExplainMismatchWrongSize) {
+  Matcher<const list<int>&> m = ElementsAre(1, 3);
+
+  list<int> test_list;
+  // No need to explain when the container is empty.
+  EXPECT_EQ("", Explain(m, test_list));
+
+  test_list.push_back(1);
+  EXPECT_EQ("which has 1 element", Explain(m, test_list));
+}
+
+TEST(ElementsAreTest, CanExplainMismatchRightSize) {
+  Matcher<const vector<int>&> m = ElementsAre(1, GreaterThan(5));
+
+  vector<int> v;
+  v.push_back(2);
+  v.push_back(1);
+  EXPECT_EQ("whose element #0 doesn't match", Explain(m, v));
+
+  v[0] = 1;
+  EXPECT_EQ("whose element #1 doesn't match, which is 4 less than 5",
+            Explain(m, v));
+}
+
+TEST(ElementsAreTest, MatchesOneElementVector) {
+  vector<std::string> test_vector;
+  test_vector.push_back("test string");
+
+  EXPECT_THAT(test_vector, ElementsAre(StrEq("test string")));
+}
+
+TEST(ElementsAreTest, MatchesOneElementList) {
+  list<std::string> test_list;
+  test_list.push_back("test string");
+
+  EXPECT_THAT(test_list, ElementsAre("test string"));
+}
+
+TEST(ElementsAreTest, MatchesThreeElementVector) {
+  vector<std::string> test_vector;
+  test_vector.push_back("one");
+  test_vector.push_back("two");
+  test_vector.push_back("three");
+
+  EXPECT_THAT(test_vector, ElementsAre("one", StrEq("two"), _));
+}
+
+TEST(ElementsAreTest, MatchesOneElementEqMatcher) {
+  vector<int> test_vector;
+  test_vector.push_back(4);
+
+  EXPECT_THAT(test_vector, ElementsAre(Eq(4)));
+}
+
+TEST(ElementsAreTest, MatchesOneElementAnyMatcher) {
+  vector<int> test_vector;
+  test_vector.push_back(4);
+
+  EXPECT_THAT(test_vector, ElementsAre(_));
+}
+
+TEST(ElementsAreTest, MatchesOneElementValue) {
+  vector<int> test_vector;
+  test_vector.push_back(4);
+
+  EXPECT_THAT(test_vector, ElementsAre(4));
+}
+
+TEST(ElementsAreTest, MatchesThreeElementsMixedMatchers) {
+  vector<int> test_vector;
+  test_vector.push_back(1);
+  test_vector.push_back(2);
+  test_vector.push_back(3);
+
+  EXPECT_THAT(test_vector, ElementsAre(1, Eq(2), _));
+}
+
+TEST(ElementsAreTest, MatchesTenElementVector) {
+  const int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  vector<int> test_vector(std::begin(a), std::end(a));
+
+  EXPECT_THAT(test_vector,
+              // The element list can contain values and/or matchers
+              // of different types.
+              ElementsAre(0, Ge(0), _, 3, 4, Ne(2), Eq(6), 7, 8, _));
+}
+
+TEST(ElementsAreTest, DoesNotMatchWrongSize) {
+  vector<std::string> test_vector;
+  test_vector.push_back("test string");
+  test_vector.push_back("test string");
+
+  Matcher<vector<std::string>> m = ElementsAre(StrEq("test string"));
+  EXPECT_FALSE(m.Matches(test_vector));
+}
+
+TEST(ElementsAreTest, DoesNotMatchWrongValue) {
+  vector<std::string> test_vector;
+  test_vector.push_back("other string");
+
+  Matcher<vector<std::string>> m = ElementsAre(StrEq("test string"));
+  EXPECT_FALSE(m.Matches(test_vector));
+}
+
+TEST(ElementsAreTest, DoesNotMatchWrongOrder) {
+  vector<std::string> test_vector;
+  test_vector.push_back("one");
+  test_vector.push_back("three");
+  test_vector.push_back("two");
+
+  Matcher<vector<std::string>> m =
+      ElementsAre(StrEq("one"), StrEq("two"), StrEq("three"));
+  EXPECT_FALSE(m.Matches(test_vector));
+}
+
+TEST(ElementsAreTest, WorksForNestedContainer) {
+  constexpr std::array<const char*, 2> strings = {{"Hi", "world"}};
+
+  vector<list<char>> nested;
+  for (const auto& s : strings) {
+    nested.emplace_back(s, s + strlen(s));
+  }
+
+  EXPECT_THAT(nested, ElementsAre(ElementsAre('H', Ne('e')),
+                                  ElementsAre('w', 'o', _, _, 'd')));
+  EXPECT_THAT(nested, Not(ElementsAre(ElementsAre('H', 'e'),
+                                      ElementsAre('w', 'o', _, _, 'd'))));
+}
+
+TEST(ElementsAreTest, WorksWithByRefElementMatchers) {
+  int a[] = {0, 1, 2};
+  vector<int> v(std::begin(a), std::end(a));
+
+  EXPECT_THAT(v, ElementsAre(Ref(v[0]), Ref(v[1]), Ref(v[2])));
+  EXPECT_THAT(v, Not(ElementsAre(Ref(v[0]), Ref(v[1]), Ref(a[2]))));
+}
+
+TEST(ElementsAreTest, WorksWithContainerPointerUsingPointee) {
+  int a[] = {0, 1, 2};
+  vector<int> v(std::begin(a), std::end(a));
+
+  EXPECT_THAT(&v, Pointee(ElementsAre(0, 1, _)));
+  EXPECT_THAT(&v, Not(Pointee(ElementsAre(0, _, 3))));
+}
+
+TEST(ElementsAreTest, WorksWithNativeArrayPassedByReference) {
+  int array[] = {0, 1, 2};
+  EXPECT_THAT(array, ElementsAre(0, 1, _));
+  EXPECT_THAT(array, Not(ElementsAre(1, _, _)));
+  EXPECT_THAT(array, Not(ElementsAre(0, _)));
+}
+
+class NativeArrayPassedAsPointerAndSize {
+ public:
+  NativeArrayPassedAsPointerAndSize() {}
+
+  MOCK_METHOD(void, Helper, (int* array, int size));
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(NativeArrayPassedAsPointerAndSize);
+};
+
+TEST(ElementsAreTest, WorksWithNativeArrayPassedAsPointerAndSize) {
+  int array[] = {0, 1};
+  ::std::tuple<int*, size_t> array_as_tuple(array, 2);
+  EXPECT_THAT(array_as_tuple, ElementsAre(0, 1));
+  EXPECT_THAT(array_as_tuple, Not(ElementsAre(0)));
+
+  NativeArrayPassedAsPointerAndSize helper;
+  EXPECT_CALL(helper, Helper(_, _)).With(ElementsAre(0, 1));
+  helper.Helper(array, 2);
+}
+
+TEST(ElementsAreTest, WorksWithTwoDimensionalNativeArray) {
+  const char a2[][3] = {"hi", "lo"};
+  EXPECT_THAT(a2, ElementsAre(ElementsAre('h', 'i', '\0'),
+                              ElementsAre('l', 'o', '\0')));
+  EXPECT_THAT(a2, ElementsAre(StrEq("hi"), StrEq("lo")));
+  EXPECT_THAT(a2, ElementsAre(Not(ElementsAre('h', 'o', '\0')),
+                              ElementsAre('l', 'o', '\0')));
+}
+
+TEST(ElementsAreTest, AcceptsStringLiteral) {
+  std::string array[] = {"hi", "one", "two"};
+  EXPECT_THAT(array, ElementsAre("hi", "one", "two"));
+  EXPECT_THAT(array, Not(ElementsAre("hi", "one", "too")));
+}
+
+// Declared here with the size unknown.  Defined AFTER the following test.
+extern const char kHi[];
+
+TEST(ElementsAreTest, AcceptsArrayWithUnknownSize) {
+  // The size of kHi is not known in this test, but ElementsAre() should
+  // still accept it.
+
+  std::string array1[] = {"hi"};
+  EXPECT_THAT(array1, ElementsAre(kHi));
+
+  std::string array2[] = {"ho"};
+  EXPECT_THAT(array2, Not(ElementsAre(kHi)));
+}
+
+const char kHi[] = "hi";
+
+TEST(ElementsAreTest, MakesCopyOfArguments) {
+  int x = 1;
+  int y = 2;
+  // This should make a copy of x and y.
+  ::testing::internal::ElementsAreMatcher<std::tuple<int, int>>
+      polymorphic_matcher = ElementsAre(x, y);
+  // Changing x and y now shouldn't affect the meaning of the above matcher.
+  x = y = 0;
+  const int array1[] = {1, 2};
+  EXPECT_THAT(array1, polymorphic_matcher);
+  const int array2[] = {0, 0};
+  EXPECT_THAT(array2, Not(polymorphic_matcher));
+}
+
+// Tests for ElementsAreArray().  Since ElementsAreArray() shares most
+// of the implementation with ElementsAre(), we don't test it as
+// thoroughly here.
+
+TEST(ElementsAreArrayTest, CanBeCreatedWithValueArray) {
+  const int a[] = {1, 2, 3};
+
+  vector<int> test_vector(std::begin(a), std::end(a));
+  EXPECT_THAT(test_vector, ElementsAreArray(a));
+
+  test_vector[2] = 0;
+  EXPECT_THAT(test_vector, Not(ElementsAreArray(a)));
+}
+
+TEST(ElementsAreArrayTest, CanBeCreatedWithArraySize) {
+  std::array<const char*, 3> a = {{"one", "two", "three"}};
+
+  vector<std::string> test_vector(std::begin(a), std::end(a));
+  EXPECT_THAT(test_vector, ElementsAreArray(a.data(), a.size()));
+
+  const char** p = a.data();
+  test_vector[0] = "1";
+  EXPECT_THAT(test_vector, Not(ElementsAreArray(p, a.size())));
+}
+
+TEST(ElementsAreArrayTest, CanBeCreatedWithoutArraySize) {
+  const char* a[] = {"one", "two", "three"};
+
+  vector<std::string> test_vector(std::begin(a), std::end(a));
+  EXPECT_THAT(test_vector, ElementsAreArray(a));
+
+  test_vector[0] = "1";
+  EXPECT_THAT(test_vector, Not(ElementsAreArray(a)));
+}
+
+TEST(ElementsAreArrayTest, CanBeCreatedWithMatcherArray) {
+  const Matcher<std::string> kMatcherArray[] = {StrEq("one"), StrEq("two"),
+                                                StrEq("three")};
+
+  vector<std::string> test_vector;
+  test_vector.push_back("one");
+  test_vector.push_back("two");
+  test_vector.push_back("three");
+  EXPECT_THAT(test_vector, ElementsAreArray(kMatcherArray));
+
+  test_vector.push_back("three");
+  EXPECT_THAT(test_vector, Not(ElementsAreArray(kMatcherArray)));
+}
+
+TEST(ElementsAreArrayTest, CanBeCreatedWithVector) {
+  const int a[] = {1, 2, 3};
+  vector<int> test_vector(std::begin(a), std::end(a));
+  const vector<int> expected(std::begin(a), std::end(a));
+  EXPECT_THAT(test_vector, ElementsAreArray(expected));
+  test_vector.push_back(4);
+  EXPECT_THAT(test_vector, Not(ElementsAreArray(expected)));
+}
+
+TEST(ElementsAreArrayTest, TakesInitializerList) {
+  const int a[5] = {1, 2, 3, 4, 5};
+  EXPECT_THAT(a, ElementsAreArray({1, 2, 3, 4, 5}));
+  EXPECT_THAT(a, Not(ElementsAreArray({1, 2, 3, 5, 4})));
+  EXPECT_THAT(a, Not(ElementsAreArray({1, 2, 3, 4, 6})));
+}
+
+TEST(ElementsAreArrayTest, TakesInitializerListOfCStrings) {
+  const std::string a[5] = {"a", "b", "c", "d", "e"};
+  EXPECT_THAT(a, ElementsAreArray({"a", "b", "c", "d", "e"}));
+  EXPECT_THAT(a, Not(ElementsAreArray({"a", "b", "c", "e", "d"})));
+  EXPECT_THAT(a, Not(ElementsAreArray({"a", "b", "c", "d", "ef"})));
+}
+
+TEST(ElementsAreArrayTest, TakesInitializerListOfSameTypedMatchers) {
+  const int a[5] = {1, 2, 3, 4, 5};
+  EXPECT_THAT(a, ElementsAreArray({Eq(1), Eq(2), Eq(3), Eq(4), Eq(5)}));
+  EXPECT_THAT(a, Not(ElementsAreArray({Eq(1), Eq(2), Eq(3), Eq(4), Eq(6)})));
+}
+
+TEST(ElementsAreArrayTest, TakesInitializerListOfDifferentTypedMatchers) {
+  const int a[5] = {1, 2, 3, 4, 5};
+  // The compiler cannot infer the type of the initializer list if its
+  // elements have different types.  We must explicitly specify the
+  // unified element type in this case.
+  EXPECT_THAT(
+      a, ElementsAreArray<Matcher<int>>({Eq(1), Ne(-2), Ge(3), Le(4), Eq(5)}));
+  EXPECT_THAT(a, Not(ElementsAreArray<Matcher<int>>(
+                     {Eq(1), Ne(-2), Ge(3), Le(4), Eq(6)})));
+}
+
+TEST(ElementsAreArrayTest, CanBeCreatedWithMatcherVector) {
+  const int a[] = {1, 2, 3};
+  const Matcher<int> kMatchers[] = {Eq(1), Eq(2), Eq(3)};
+  vector<int> test_vector(std::begin(a), std::end(a));
+  const vector<Matcher<int>> expected(std::begin(kMatchers),
+                                      std::end(kMatchers));
+  EXPECT_THAT(test_vector, ElementsAreArray(expected));
+  test_vector.push_back(4);
+  EXPECT_THAT(test_vector, Not(ElementsAreArray(expected)));
+}
+
+TEST(ElementsAreArrayTest, CanBeCreatedWithIteratorRange) {
+  const int a[] = {1, 2, 3};
+  const vector<int> test_vector(std::begin(a), std::end(a));
+  const vector<int> expected(std::begin(a), std::end(a));
+  EXPECT_THAT(test_vector, ElementsAreArray(expected.begin(), expected.end()));
+  // Pointers are iterators, too.
+  EXPECT_THAT(test_vector, ElementsAreArray(std::begin(a), std::end(a)));
+  // The empty range of NULL pointers should also be okay.
+  int* const null_int = nullptr;
+  EXPECT_THAT(test_vector, Not(ElementsAreArray(null_int, null_int)));
+  EXPECT_THAT((vector<int>()), ElementsAreArray(null_int, null_int));
+}
+
+// Since ElementsAre() and ElementsAreArray() share much of the
+// implementation, we only do a sanity test for native arrays here.
+TEST(ElementsAreArrayTest, WorksWithNativeArray) {
+  ::std::string a[] = {"hi", "ho"};
+  ::std::string b[] = {"hi", "ho"};
+
+  EXPECT_THAT(a, ElementsAreArray(b));
+  EXPECT_THAT(a, ElementsAreArray(b, 2));
+  EXPECT_THAT(a, Not(ElementsAreArray(b, 1)));
+}
+
+TEST(ElementsAreArrayTest, SourceLifeSpan) {
+  const int a[] = {1, 2, 3};
+  vector<int> test_vector(std::begin(a), std::end(a));
+  vector<int> expect(std::begin(a), std::end(a));
+  ElementsAreArrayMatcher<int> matcher_maker =
+      ElementsAreArray(expect.begin(), expect.end());
+  EXPECT_THAT(test_vector, matcher_maker);
+  // Changing in place the values that initialized matcher_maker should not
+  // affect matcher_maker anymore. It should have made its own copy of them.
+  for (int& i : expect) {
+    i += 10;
+  }
+  EXPECT_THAT(test_vector, matcher_maker);
+  test_vector.push_back(3);
+  EXPECT_THAT(test_vector, Not(matcher_maker));
+}
+
+// Tests for the MATCHER*() macro family.
+
+// Tests that a simple MATCHER() definition works.
+
+MATCHER(IsEven, "") { return (arg % 2) == 0; }
+
+TEST(MatcherMacroTest, Works) {
+  const Matcher<int> m = IsEven();
+  EXPECT_TRUE(m.Matches(6));
+  EXPECT_FALSE(m.Matches(7));
+
+  EXPECT_EQ("is even", Describe(m));
+  EXPECT_EQ("not (is even)", DescribeNegation(m));
+  EXPECT_EQ("", Explain(m, 6));
+  EXPECT_EQ("", Explain(m, 7));
+}
+
+// This also tests that the description string can reference 'negation'.
+MATCHER(IsEven2, negation ? "is odd" : "is even") {
+  if ((arg % 2) == 0) {
+    // Verifies that we can stream to result_listener, a listener
+    // supplied by the MATCHER macro implicitly.
+    *result_listener << "OK";
+    return true;
+  } else {
+    *result_listener << "% 2 == " << (arg % 2);
+    return false;
+  }
+}
+
+// This also tests that the description string can reference matcher
+// parameters.
+MATCHER_P2(EqSumOf, x, y,
+           std::string(negation ? "doesn't equal" : "equals") + " the sum of " +
+               PrintToString(x) + " and " + PrintToString(y)) {
+  if (arg == (x + y)) {
+    *result_listener << "OK";
+    return true;
+  } else {
+    // Verifies that we can stream to the underlying stream of
+    // result_listener.
+    if (result_listener->stream() != nullptr) {
+      *result_listener->stream() << "diff == " << (x + y - arg);
+    }
+    return false;
+  }
+}
+
+// Tests that the matcher description can reference 'negation' and the
+// matcher parameters.
+TEST(MatcherMacroTest, DescriptionCanReferenceNegationAndParameters) {
+  const Matcher<int> m1 = IsEven2();
+  EXPECT_EQ("is even", Describe(m1));
+  EXPECT_EQ("is odd", DescribeNegation(m1));
+
+  const Matcher<int> m2 = EqSumOf(5, 9);
+  EXPECT_EQ("equals the sum of 5 and 9", Describe(m2));
+  EXPECT_EQ("doesn't equal the sum of 5 and 9", DescribeNegation(m2));
+}
+
+// Tests explaining match result in a MATCHER* macro.
+TEST(MatcherMacroTest, CanExplainMatchResult) {
+  const Matcher<int> m1 = IsEven2();
+  EXPECT_EQ("OK", Explain(m1, 4));
+  EXPECT_EQ("% 2 == 1", Explain(m1, 5));
+
+  const Matcher<int> m2 = EqSumOf(1, 2);
+  EXPECT_EQ("OK", Explain(m2, 3));
+  EXPECT_EQ("diff == -1", Explain(m2, 4));
+}
+
+// Tests that the body of MATCHER() can reference the type of the
+// value being matched.
+
+MATCHER(IsEmptyString, "") {
+  StaticAssertTypeEq<::std::string, arg_type>();
+  return arg.empty();
+}
+
+MATCHER(IsEmptyStringByRef, "") {
+  StaticAssertTypeEq<const ::std::string&, arg_type>();
+  return arg.empty();
+}
+
+TEST(MatcherMacroTest, CanReferenceArgType) {
+  const Matcher<::std::string> m1 = IsEmptyString();
+  EXPECT_TRUE(m1.Matches(""));
+
+  const Matcher<const ::std::string&> m2 = IsEmptyStringByRef();
+  EXPECT_TRUE(m2.Matches(""));
+}
+
+// Tests that MATCHER() can be used in a namespace.
+
+namespace matcher_test {
+MATCHER(IsOdd, "") { return (arg % 2) != 0; }
+}  // namespace matcher_test
+
+TEST(MatcherMacroTest, WorksInNamespace) {
+  Matcher<int> m = matcher_test::IsOdd();
+  EXPECT_FALSE(m.Matches(4));
+  EXPECT_TRUE(m.Matches(5));
+}
+
+// Tests that Value() can be used to compose matchers.
+MATCHER(IsPositiveOdd, "") {
+  return Value(arg, matcher_test::IsOdd()) && arg > 0;
+}
+
+TEST(MatcherMacroTest, CanBeComposedUsingValue) {
+  EXPECT_THAT(3, IsPositiveOdd());
+  EXPECT_THAT(4, Not(IsPositiveOdd()));
+  EXPECT_THAT(-1, Not(IsPositiveOdd()));
+}
+
+// Tests that a simple MATCHER_P() definition works.
+
+MATCHER_P(IsGreaterThan32And, n, "") { return arg > 32 && arg > n; }
+
+TEST(MatcherPMacroTest, Works) {
+  const Matcher<int> m = IsGreaterThan32And(5);
+  EXPECT_TRUE(m.Matches(36));
+  EXPECT_FALSE(m.Matches(5));
+
+  EXPECT_EQ("is greater than 32 and 5", Describe(m));
+  EXPECT_EQ("not (is greater than 32 and 5)", DescribeNegation(m));
+  EXPECT_EQ("", Explain(m, 36));
+  EXPECT_EQ("", Explain(m, 5));
+}
+
+// Tests that the description is calculated correctly from the matcher name.
+MATCHER_P(_is_Greater_Than32and_, n, "") { return arg > 32 && arg > n; }
+
+TEST(MatcherPMacroTest, GeneratesCorrectDescription) {
+  const Matcher<int> m = _is_Greater_Than32and_(5);
+
+  EXPECT_EQ("is greater than 32 and 5", Describe(m));
+  EXPECT_EQ("not (is greater than 32 and 5)", DescribeNegation(m));
+  EXPECT_EQ("", Explain(m, 36));
+  EXPECT_EQ("", Explain(m, 5));
+}
+
+// Tests that a MATCHER_P matcher can be explicitly instantiated with
+// a reference parameter type.
+
+class UncopyableFoo {
+ public:
+  explicit UncopyableFoo(char value) : value_(value) { (void)value_; }
+
+  UncopyableFoo(const UncopyableFoo&) = delete;
+  void operator=(const UncopyableFoo&) = delete;
+
+ private:
+  char value_;
+};
+
+MATCHER_P(ReferencesUncopyable, variable, "") { return &arg == &variable; }
+
+TEST(MatcherPMacroTest, WorksWhenExplicitlyInstantiatedWithReference) {
+  UncopyableFoo foo1('1'), foo2('2');
+  const Matcher<const UncopyableFoo&> m =
+      ReferencesUncopyable<const UncopyableFoo&>(foo1);
+
+  EXPECT_TRUE(m.Matches(foo1));
+  EXPECT_FALSE(m.Matches(foo2));
+
+  // We don't want the address of the parameter printed, as most
+  // likely it will just annoy the user.  If the address is
+  // interesting, the user should consider passing the parameter by
+  // pointer instead.
+  EXPECT_EQ("references uncopyable 1-byte object <31>", Describe(m));
+}
+
+// Tests that the body of MATCHER_Pn() can reference the parameter
+// types.
+
+MATCHER_P3(ParamTypesAreIntLongAndChar, foo, bar, baz, "") {
+  StaticAssertTypeEq<int, foo_type>();
+  StaticAssertTypeEq<long, bar_type>();  // NOLINT
+  StaticAssertTypeEq<char, baz_type>();
+  return arg == 0;
+}
+
+TEST(MatcherPnMacroTest, CanReferenceParamTypes) {
+  EXPECT_THAT(0, ParamTypesAreIntLongAndChar(10, 20L, 'a'));
+}
+
+// Tests that a MATCHER_Pn matcher can be explicitly instantiated with
+// reference parameter types.
+
+MATCHER_P2(ReferencesAnyOf, variable1, variable2, "") {
+  return &arg == &variable1 || &arg == &variable2;
+}
+
+TEST(MatcherPnMacroTest, WorksWhenExplicitlyInstantiatedWithReferences) {
+  UncopyableFoo foo1('1'), foo2('2'), foo3('3');
+  const Matcher<const UncopyableFoo&> const_m =
+      ReferencesAnyOf<const UncopyableFoo&, const UncopyableFoo&>(foo1, foo2);
+
+  EXPECT_TRUE(const_m.Matches(foo1));
+  EXPECT_TRUE(const_m.Matches(foo2));
+  EXPECT_FALSE(const_m.Matches(foo3));
+
+  const Matcher<UncopyableFoo&> m =
+      ReferencesAnyOf<UncopyableFoo&, UncopyableFoo&>(foo1, foo2);
+
+  EXPECT_TRUE(m.Matches(foo1));
+  EXPECT_TRUE(m.Matches(foo2));
+  EXPECT_FALSE(m.Matches(foo3));
+}
+
+TEST(MatcherPnMacroTest,
+     GeneratesCorretDescriptionWhenExplicitlyInstantiatedWithReferences) {
+  UncopyableFoo foo1('1'), foo2('2');
+  const Matcher<const UncopyableFoo&> m =
+      ReferencesAnyOf<const UncopyableFoo&, const UncopyableFoo&>(foo1, foo2);
+
+  // We don't want the addresses of the parameters printed, as most
+  // likely they will just annoy the user.  If the addresses are
+  // interesting, the user should consider passing the parameters by
+  // pointers instead.
+  EXPECT_EQ("references any of (1-byte object <31>, 1-byte object <32>)",
+            Describe(m));
+}
+
+// Tests that a simple MATCHER_P2() definition works.
+
+MATCHER_P2(IsNotInClosedRange, low, hi, "") { return arg < low || arg > hi; }
+
+TEST(MatcherPnMacroTest, Works) {
+  const Matcher<const long&> m = IsNotInClosedRange(10, 20);  // NOLINT
+  EXPECT_TRUE(m.Matches(36L));
+  EXPECT_FALSE(m.Matches(15L));
+
+  EXPECT_EQ("is not in closed range (10, 20)", Describe(m));
+  EXPECT_EQ("not (is not in closed range (10, 20))", DescribeNegation(m));
+  EXPECT_EQ("", Explain(m, 36L));
+  EXPECT_EQ("", Explain(m, 15L));
+}
+
+// Tests that MATCHER*() definitions can be overloaded on the number
+// of parameters; also tests MATCHER_Pn() where n >= 3.
+
+MATCHER(EqualsSumOf, "") { return arg == 0; }
+MATCHER_P(EqualsSumOf, a, "") { return arg == a; }
+MATCHER_P2(EqualsSumOf, a, b, "") { return arg == a + b; }
+MATCHER_P3(EqualsSumOf, a, b, c, "") { return arg == a + b + c; }
+MATCHER_P4(EqualsSumOf, a, b, c, d, "") { return arg == a + b + c + d; }
+MATCHER_P5(EqualsSumOf, a, b, c, d, e, "") { return arg == a + b + c + d + e; }
+MATCHER_P6(EqualsSumOf, a, b, c, d, e, f, "") {
+  return arg == a + b + c + d + e + f;
+}
+MATCHER_P7(EqualsSumOf, a, b, c, d, e, f, g, "") {
+  return arg == a + b + c + d + e + f + g;
+}
+MATCHER_P8(EqualsSumOf, a, b, c, d, e, f, g, h, "") {
+  return arg == a + b + c + d + e + f + g + h;
+}
+MATCHER_P9(EqualsSumOf, a, b, c, d, e, f, g, h, i, "") {
+  return arg == a + b + c + d + e + f + g + h + i;
+}
+MATCHER_P10(EqualsSumOf, a, b, c, d, e, f, g, h, i, j, "") {
+  return arg == a + b + c + d + e + f + g + h + i + j;
+}
+
+TEST(MatcherPnMacroTest, CanBeOverloadedOnNumberOfParameters) {
+  EXPECT_THAT(0, EqualsSumOf());
+  EXPECT_THAT(1, EqualsSumOf(1));
+  EXPECT_THAT(12, EqualsSumOf(10, 2));
+  EXPECT_THAT(123, EqualsSumOf(100, 20, 3));
+  EXPECT_THAT(1234, EqualsSumOf(1000, 200, 30, 4));
+  EXPECT_THAT(12345, EqualsSumOf(10000, 2000, 300, 40, 5));
+  EXPECT_THAT("abcdef",
+              EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f'));
+  EXPECT_THAT("abcdefg",
+              EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g'));
+  EXPECT_THAT("abcdefgh", EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e",
+                                      'f', 'g', "h"));
+  EXPECT_THAT("abcdefghi", EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e",
+                                       'f', 'g', "h", 'i'));
+  EXPECT_THAT("abcdefghij",
+              EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g', "h",
+                          'i', ::std::string("j")));
+
+  EXPECT_THAT(1, Not(EqualsSumOf()));
+  EXPECT_THAT(-1, Not(EqualsSumOf(1)));
+  EXPECT_THAT(-12, Not(EqualsSumOf(10, 2)));
+  EXPECT_THAT(-123, Not(EqualsSumOf(100, 20, 3)));
+  EXPECT_THAT(-1234, Not(EqualsSumOf(1000, 200, 30, 4)));
+  EXPECT_THAT(-12345, Not(EqualsSumOf(10000, 2000, 300, 40, 5)));
+  EXPECT_THAT("abcdef ",
+              Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f')));
+  EXPECT_THAT("abcdefg ", Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d",
+                                          "e", 'f', 'g')));
+  EXPECT_THAT("abcdefgh ", Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d",
+                                           "e", 'f', 'g', "h")));
+  EXPECT_THAT("abcdefghi ", Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d",
+                                            "e", 'f', 'g', "h", 'i')));
+  EXPECT_THAT("abcdefghij ",
+              Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g',
+                              "h", 'i', ::std::string("j"))));
+}
+
+// Tests that a MATCHER_Pn() definition can be instantiated with any
+// compatible parameter types.
+TEST(MatcherPnMacroTest, WorksForDifferentParameterTypes) {
+  EXPECT_THAT(123, EqualsSumOf(100L, 20, static_cast<char>(3)));
+  EXPECT_THAT("abcd", EqualsSumOf(::std::string("a"), "b", 'c', "d"));
+
+  EXPECT_THAT(124, Not(EqualsSumOf(100L, 20, static_cast<char>(3))));
+  EXPECT_THAT("abcde", Not(EqualsSumOf(::std::string("a"), "b", 'c', "d")));
+}
+
+// Tests that the matcher body can promote the parameter types.
+
+MATCHER_P2(EqConcat, prefix, suffix, "") {
+  // The following lines promote the two parameters to desired types.
+  std::string prefix_str(prefix);
+  char suffix_char = static_cast<char>(suffix);
+  return arg == prefix_str + suffix_char;
+}
+
+TEST(MatcherPnMacroTest, SimpleTypePromotion) {
+  Matcher<std::string> no_promo = EqConcat(std::string("foo"), 't');
+  Matcher<const std::string&> promo = EqConcat("foo", static_cast<int>('t'));
+  EXPECT_FALSE(no_promo.Matches("fool"));
+  EXPECT_FALSE(promo.Matches("fool"));
+  EXPECT_TRUE(no_promo.Matches("foot"));
+  EXPECT_TRUE(promo.Matches("foot"));
+}
+
+// Verifies the type of a MATCHER*.
+
+TEST(MatcherPnMacroTest, TypesAreCorrect) {
+  // EqualsSumOf() must be assignable to a EqualsSumOfMatcher variable.
+  EqualsSumOfMatcher a0 = EqualsSumOf();
+
+  // EqualsSumOf(1) must be assignable to a EqualsSumOfMatcherP variable.
+  EqualsSumOfMatcherP<int> a1 = EqualsSumOf(1);
+
+  // EqualsSumOf(p1, ..., pk) must be assignable to a EqualsSumOfMatcherPk
+  // variable, and so on.
+  EqualsSumOfMatcherP2<int, char> a2 = EqualsSumOf(1, '2');
+  EqualsSumOfMatcherP3<int, int, char> a3 = EqualsSumOf(1, 2, '3');
+  EqualsSumOfMatcherP4<int, int, int, char> a4 = EqualsSumOf(1, 2, 3, '4');
+  EqualsSumOfMatcherP5<int, int, int, int, char> a5 =
+      EqualsSumOf(1, 2, 3, 4, '5');
+  EqualsSumOfMatcherP6<int, int, int, int, int, char> a6 =
+      EqualsSumOf(1, 2, 3, 4, 5, '6');
+  EqualsSumOfMatcherP7<int, int, int, int, int, int, char> a7 =
+      EqualsSumOf(1, 2, 3, 4, 5, 6, '7');
+  EqualsSumOfMatcherP8<int, int, int, int, int, int, int, char> a8 =
+      EqualsSumOf(1, 2, 3, 4, 5, 6, 7, '8');
+  EqualsSumOfMatcherP9<int, int, int, int, int, int, int, int, char> a9 =
+      EqualsSumOf(1, 2, 3, 4, 5, 6, 7, 8, '9');
+  EqualsSumOfMatcherP10<int, int, int, int, int, int, int, int, int, char> a10 =
+      EqualsSumOf(1, 2, 3, 4, 5, 6, 7, 8, 9, '0');
+
+  // Avoid "unused variable" warnings.
+  (void)a0;
+  (void)a1;
+  (void)a2;
+  (void)a3;
+  (void)a4;
+  (void)a5;
+  (void)a6;
+  (void)a7;
+  (void)a8;
+  (void)a9;
+  (void)a10;
+}
+
+// Tests that matcher-typed parameters can be used in Value() inside a
+// MATCHER_Pn definition.
+
+// Succeeds if arg matches exactly 2 of the 3 matchers.
+MATCHER_P3(TwoOf, m1, m2, m3, "") {
+  const int count = static_cast<int>(Value(arg, m1)) +
+                    static_cast<int>(Value(arg, m2)) +
+                    static_cast<int>(Value(arg, m3));
+  return count == 2;
+}
+
+TEST(MatcherPnMacroTest, CanUseMatcherTypedParameterInValue) {
+  EXPECT_THAT(42, TwoOf(Gt(0), Lt(50), Eq(10)));
+  EXPECT_THAT(0, Not(TwoOf(Gt(-1), Lt(1), Eq(0))));
+}
+
+// Tests Contains().
+
+TEST(ContainsTest, ListMatchesWhenElementIsInContainer) {
+  list<int> some_list;
+  some_list.push_back(3);
+  some_list.push_back(1);
+  some_list.push_back(2);
+  some_list.push_back(3);
+  EXPECT_THAT(some_list, Contains(1));
+  EXPECT_THAT(some_list, Contains(Gt(2.5)));
+  EXPECT_THAT(some_list, Contains(Eq(2.0f)));
+
+  list<std::string> another_list;
+  another_list.push_back("fee");
+  another_list.push_back("fie");
+  another_list.push_back("foe");
+  another_list.push_back("fum");
+  EXPECT_THAT(another_list, Contains(std::string("fee")));
+}
+
+TEST(ContainsTest, ListDoesNotMatchWhenElementIsNotInContainer) {
+  list<int> some_list;
+  some_list.push_back(3);
+  some_list.push_back(1);
+  EXPECT_THAT(some_list, Not(Contains(4)));
+}
+
+TEST(ContainsTest, SetMatchesWhenElementIsInContainer) {
+  set<int> some_set;
+  some_set.insert(3);
+  some_set.insert(1);
+  some_set.insert(2);
+  EXPECT_THAT(some_set, Contains(Eq(1.0)));
+  EXPECT_THAT(some_set, Contains(Eq(3.0f)));
+  EXPECT_THAT(some_set, Contains(2));
+
+  set<std::string> another_set;
+  another_set.insert("fee");
+  another_set.insert("fie");
+  another_set.insert("foe");
+  another_set.insert("fum");
+  EXPECT_THAT(another_set, Contains(Eq(std::string("fum"))));
+}
+
+TEST(ContainsTest, SetDoesNotMatchWhenElementIsNotInContainer) {
+  set<int> some_set;
+  some_set.insert(3);
+  some_set.insert(1);
+  EXPECT_THAT(some_set, Not(Contains(4)));
+
+  set<std::string> c_string_set;
+  c_string_set.insert("hello");
+  EXPECT_THAT(c_string_set, Not(Contains(std::string("goodbye"))));
+}
+
+TEST(ContainsTest, ExplainsMatchResultCorrectly) {
+  const int a[2] = {1, 2};
+  Matcher<const int(&)[2]> m = Contains(2);
+  EXPECT_EQ("whose element #1 matches", Explain(m, a));
+
+  m = Contains(3);
+  EXPECT_EQ("", Explain(m, a));
+
+  m = Contains(GreaterThan(0));
+  EXPECT_EQ("whose element #0 matches, which is 1 more than 0", Explain(m, a));
+
+  m = Contains(GreaterThan(10));
+  EXPECT_EQ("", Explain(m, a));
+}
+
+TEST(ContainsTest, DescribesItselfCorrectly) {
+  Matcher<vector<int>> m = Contains(1);
+  EXPECT_EQ("contains at least one element that is equal to 1", Describe(m));
+
+  Matcher<vector<int>> m2 = Not(m);
+  EXPECT_EQ("doesn't contain any element that is equal to 1", Describe(m2));
+}
+
+TEST(ContainsTest, MapMatchesWhenElementIsInContainer) {
+  map<std::string, int> my_map;
+  const char* bar = "a string";
+  my_map[bar] = 2;
+  EXPECT_THAT(my_map, Contains(pair<const char* const, int>(bar, 2)));
+
+  map<std::string, int> another_map;
+  another_map["fee"] = 1;
+  another_map["fie"] = 2;
+  another_map["foe"] = 3;
+  another_map["fum"] = 4;
+  EXPECT_THAT(another_map,
+              Contains(pair<const std::string, int>(std::string("fee"), 1)));
+  EXPECT_THAT(another_map, Contains(pair<const std::string, int>("fie", 2)));
+}
+
+TEST(ContainsTest, MapDoesNotMatchWhenElementIsNotInContainer) {
+  map<int, int> some_map;
+  some_map[1] = 11;
+  some_map[2] = 22;
+  EXPECT_THAT(some_map, Not(Contains(pair<const int, int>(2, 23))));
+}
+
+TEST(ContainsTest, ArrayMatchesWhenElementIsInContainer) {
+  const char* string_array[] = {"fee", "fie", "foe", "fum"};
+  EXPECT_THAT(string_array, Contains(Eq(std::string("fum"))));
+}
+
+TEST(ContainsTest, ArrayDoesNotMatchWhenElementIsNotInContainer) {
+  int int_array[] = {1, 2, 3, 4};
+  EXPECT_THAT(int_array, Not(Contains(5)));
+}
+
+TEST(ContainsTest, AcceptsMatcher) {
+  const int a[] = {1, 2, 3};
+  EXPECT_THAT(a, Contains(Gt(2)));
+  EXPECT_THAT(a, Not(Contains(Gt(4))));
+}
+
+TEST(ContainsTest, WorksForNativeArrayAsTuple) {
+  const int a[] = {1, 2};
+  const int* const pointer = a;
+  EXPECT_THAT(std::make_tuple(pointer, 2), Contains(1));
+  EXPECT_THAT(std::make_tuple(pointer, 2), Not(Contains(Gt(3))));
+}
+
+TEST(ContainsTest, WorksForTwoDimensionalNativeArray) {
+  int a[][3] = {{1, 2, 3}, {4, 5, 6}};
+  EXPECT_THAT(a, Contains(ElementsAre(4, 5, 6)));
+  EXPECT_THAT(a, Contains(Contains(5)));
+  EXPECT_THAT(a, Not(Contains(ElementsAre(3, 4, 5))));
+  EXPECT_THAT(a, Contains(Not(Contains(5))));
+}
+
+// Tests Contains().Times().
+
+TEST(ContainsTimes, ListMatchesWhenElementQuantityMatches) {
+  list<int> some_list;
+  some_list.push_back(3);
+  some_list.push_back(1);
+  some_list.push_back(2);
+  some_list.push_back(3);
+  EXPECT_THAT(some_list, Contains(3).Times(2));
+  EXPECT_THAT(some_list, Contains(2).Times(1));
+  EXPECT_THAT(some_list, Contains(Ge(2)).Times(3));
+  EXPECT_THAT(some_list, Contains(Ge(2)).Times(Gt(2)));
+  EXPECT_THAT(some_list, Contains(4).Times(0));
+  EXPECT_THAT(some_list, Contains(_).Times(4));
+  EXPECT_THAT(some_list, Not(Contains(5).Times(1)));
+  EXPECT_THAT(some_list, Contains(5).Times(_));  // Times(_) always matches
+  EXPECT_THAT(some_list, Not(Contains(3).Times(1)));
+  EXPECT_THAT(some_list, Contains(3).Times(Not(1)));
+  EXPECT_THAT(list<int>{}, Not(Contains(_)));
+}
+
+TEST(ContainsTimes, ExplainsMatchResultCorrectly) {
+  const int a[2] = {1, 2};
+  Matcher<const int(&)[2]> m = Contains(2).Times(3);
+  EXPECT_EQ(
+      "whose element #1 matches but whose match quantity of 1 does not match",
+      Explain(m, a));
+
+  m = Contains(3).Times(0);
+  EXPECT_EQ("has no element that matches and whose match quantity of 0 matches",
+            Explain(m, a));
+
+  m = Contains(3).Times(4);
+  EXPECT_EQ(
+      "has no element that matches and whose match quantity of 0 does not "
+      "match",
+      Explain(m, a));
+
+  m = Contains(2).Times(4);
+  EXPECT_EQ(
+      "whose element #1 matches but whose match quantity of 1 does not "
+      "match",
+      Explain(m, a));
+
+  m = Contains(GreaterThan(0)).Times(2);
+  EXPECT_EQ("whose elements (0, 1) match and whose match quantity of 2 matches",
+            Explain(m, a));
+
+  m = Contains(GreaterThan(10)).Times(Gt(1));
+  EXPECT_EQ(
+      "has no element that matches and whose match quantity of 0 does not "
+      "match",
+      Explain(m, a));
+
+  m = Contains(GreaterThan(0)).Times(GreaterThan<size_t>(5));
+  EXPECT_EQ(
+      "whose elements (0, 1) match but whose match quantity of 2 does not "
+      "match, which is 3 less than 5",
+      Explain(m, a));
+}
+
+TEST(ContainsTimes, DescribesItselfCorrectly) {
+  Matcher<vector<int>> m = Contains(1).Times(2);
+  EXPECT_EQ("quantity of elements that match is equal to 1 is equal to 2",
+            Describe(m));
+
+  Matcher<vector<int>> m2 = Not(m);
+  EXPECT_EQ("quantity of elements that match is equal to 1 isn't equal to 2",
+            Describe(m2));
+}
+
+// Tests AllOfArray()
+
+TEST(AllOfArrayTest, BasicForms) {
+  // Iterator
+  std::vector<int> v0{};
+  std::vector<int> v1{1};
+  std::vector<int> v2{2, 3};
+  std::vector<int> v3{4, 4, 4};
+  EXPECT_THAT(0, AllOfArray(v0.begin(), v0.end()));
+  EXPECT_THAT(1, AllOfArray(v1.begin(), v1.end()));
+  EXPECT_THAT(2, Not(AllOfArray(v1.begin(), v1.end())));
+  EXPECT_THAT(3, Not(AllOfArray(v2.begin(), v2.end())));
+  EXPECT_THAT(4, AllOfArray(v3.begin(), v3.end()));
+  // Pointer +  size
+  int ar[6] = {1, 2, 3, 4, 4, 4};
+  EXPECT_THAT(0, AllOfArray(ar, 0));
+  EXPECT_THAT(1, AllOfArray(ar, 1));
+  EXPECT_THAT(2, Not(AllOfArray(ar, 1)));
+  EXPECT_THAT(3, Not(AllOfArray(ar + 1, 3)));
+  EXPECT_THAT(4, AllOfArray(ar + 3, 3));
+  // Array
+  // int ar0[0];  Not usable
+  int ar1[1] = {1};
+  int ar2[2] = {2, 3};
+  int ar3[3] = {4, 4, 4};
+  // EXPECT_THAT(0, Not(AllOfArray(ar0)));  // Cannot work
+  EXPECT_THAT(1, AllOfArray(ar1));
+  EXPECT_THAT(2, Not(AllOfArray(ar1)));
+  EXPECT_THAT(3, Not(AllOfArray(ar2)));
+  EXPECT_THAT(4, AllOfArray(ar3));
+  // Container
+  EXPECT_THAT(0, AllOfArray(v0));
+  EXPECT_THAT(1, AllOfArray(v1));
+  EXPECT_THAT(2, Not(AllOfArray(v1)));
+  EXPECT_THAT(3, Not(AllOfArray(v2)));
+  EXPECT_THAT(4, AllOfArray(v3));
+  // Initializer
+  EXPECT_THAT(0, AllOfArray<int>({}));  // Requires template arg.
+  EXPECT_THAT(1, AllOfArray({1}));
+  EXPECT_THAT(2, Not(AllOfArray({1})));
+  EXPECT_THAT(3, Not(AllOfArray({2, 3})));
+  EXPECT_THAT(4, AllOfArray({4, 4, 4}));
+}
+
+TEST(AllOfArrayTest, Matchers) {
+  // vector
+  std::vector<Matcher<int>> matchers{Ge(1), Lt(2)};
+  EXPECT_THAT(0, Not(AllOfArray(matchers)));
+  EXPECT_THAT(1, AllOfArray(matchers));
+  EXPECT_THAT(2, Not(AllOfArray(matchers)));
+  // initializer_list
+  EXPECT_THAT(0, Not(AllOfArray({Ge(0), Ge(1)})));
+  EXPECT_THAT(1, AllOfArray({Ge(0), Ge(1)}));
+}
+
+TEST(AnyOfArrayTest, BasicForms) {
+  // Iterator
+  std::vector<int> v0{};
+  std::vector<int> v1{1};
+  std::vector<int> v2{2, 3};
+  EXPECT_THAT(0, Not(AnyOfArray(v0.begin(), v0.end())));
+  EXPECT_THAT(1, AnyOfArray(v1.begin(), v1.end()));
+  EXPECT_THAT(2, Not(AnyOfArray(v1.begin(), v1.end())));
+  EXPECT_THAT(3, AnyOfArray(v2.begin(), v2.end()));
+  EXPECT_THAT(4, Not(AnyOfArray(v2.begin(), v2.end())));
+  // Pointer +  size
+  int ar[3] = {1, 2, 3};
+  EXPECT_THAT(0, Not(AnyOfArray(ar, 0)));
+  EXPECT_THAT(1, AnyOfArray(ar, 1));
+  EXPECT_THAT(2, Not(AnyOfArray(ar, 1)));
+  EXPECT_THAT(3, AnyOfArray(ar + 1, 2));
+  EXPECT_THAT(4, Not(AnyOfArray(ar + 1, 2)));
+  // Array
+  // int ar0[0];  Not usable
+  int ar1[1] = {1};
+  int ar2[2] = {2, 3};
+  // EXPECT_THAT(0, Not(AnyOfArray(ar0)));  // Cannot work
+  EXPECT_THAT(1, AnyOfArray(ar1));
+  EXPECT_THAT(2, Not(AnyOfArray(ar1)));
+  EXPECT_THAT(3, AnyOfArray(ar2));
+  EXPECT_THAT(4, Not(AnyOfArray(ar2)));
+  // Container
+  EXPECT_THAT(0, Not(AnyOfArray(v0)));
+  EXPECT_THAT(1, AnyOfArray(v1));
+  EXPECT_THAT(2, Not(AnyOfArray(v1)));
+  EXPECT_THAT(3, AnyOfArray(v2));
+  EXPECT_THAT(4, Not(AnyOfArray(v2)));
+  // Initializer
+  EXPECT_THAT(0, Not(AnyOfArray<int>({})));  // Requires template arg.
+  EXPECT_THAT(1, AnyOfArray({1}));
+  EXPECT_THAT(2, Not(AnyOfArray({1})));
+  EXPECT_THAT(3, AnyOfArray({2, 3}));
+  EXPECT_THAT(4, Not(AnyOfArray({2, 3})));
+}
+
+TEST(AnyOfArrayTest, Matchers) {
+  // We negate test AllOfArrayTest.Matchers.
+  // vector
+  std::vector<Matcher<int>> matchers{Lt(1), Ge(2)};
+  EXPECT_THAT(0, AnyOfArray(matchers));
+  EXPECT_THAT(1, Not(AnyOfArray(matchers)));
+  EXPECT_THAT(2, AnyOfArray(matchers));
+  // initializer_list
+  EXPECT_THAT(0, AnyOfArray({Lt(0), Lt(1)}));
+  EXPECT_THAT(1, Not(AllOfArray({Lt(0), Lt(1)})));
+}
+
+TEST(AnyOfArrayTest, ExplainsMatchResultCorrectly) {
+  // AnyOfArray and AllOfArry use the same underlying template-template,
+  // thus it is sufficient to test one here.
+  const std::vector<int> v0{};
+  const std::vector<int> v1{1};
+  const std::vector<int> v2{2, 3};
+  const Matcher<int> m0 = AnyOfArray(v0);
+  const Matcher<int> m1 = AnyOfArray(v1);
+  const Matcher<int> m2 = AnyOfArray(v2);
+  EXPECT_EQ("", Explain(m0, 0));
+  EXPECT_EQ("", Explain(m1, 1));
+  EXPECT_EQ("", Explain(m1, 2));
+  EXPECT_EQ("", Explain(m2, 3));
+  EXPECT_EQ("", Explain(m2, 4));
+  EXPECT_EQ("()", Describe(m0));
+  EXPECT_EQ("(is equal to 1)", Describe(m1));
+  EXPECT_EQ("(is equal to 2) or (is equal to 3)", Describe(m2));
+  EXPECT_EQ("()", DescribeNegation(m0));
+  EXPECT_EQ("(isn't equal to 1)", DescribeNegation(m1));
+  EXPECT_EQ("(isn't equal to 2) and (isn't equal to 3)", DescribeNegation(m2));
+  // Explain with matchers
+  const Matcher<int> g1 = AnyOfArray({GreaterThan(1)});
+  const Matcher<int> g2 = AnyOfArray({GreaterThan(1), GreaterThan(2)});
+  // Explains the first positiv match and all prior negative matches...
+  EXPECT_EQ("which is 1 less than 1", Explain(g1, 0));
+  EXPECT_EQ("which is the same as 1", Explain(g1, 1));
+  EXPECT_EQ("which is 1 more than 1", Explain(g1, 2));
+  EXPECT_EQ("which is 1 less than 1, and which is 2 less than 2",
+            Explain(g2, 0));
+  EXPECT_EQ("which is the same as 1, and which is 1 less than 2",
+            Explain(g2, 1));
+  EXPECT_EQ("which is 1 more than 1",  // Only the first
+            Explain(g2, 2));
+}
+
+TEST(AllOfTest, HugeMatcher) {
+  // Verify that using AllOf with many arguments doesn't cause
+  // the compiler to exceed template instantiation depth limit.
+  EXPECT_THAT(0, testing::AllOf(_, _, _, _, _, _, _, _, _,
+                                testing::AllOf(_, _, _, _, _, _, _, _, _, _)));
+}
+
+TEST(AnyOfTest, HugeMatcher) {
+  // Verify that using AnyOf with many arguments doesn't cause
+  // the compiler to exceed template instantiation depth limit.
+  EXPECT_THAT(0, testing::AnyOf(_, _, _, _, _, _, _, _, _,
+                                testing::AnyOf(_, _, _, _, _, _, _, _, _, _)));
+}
+
+namespace adl_test {
+
+// Verifies that the implementation of ::testing::AllOf and ::testing::AnyOf
+// don't issue unqualified recursive calls.  If they do, the argument dependent
+// name lookup will cause AllOf/AnyOf in the 'adl_test' namespace to be found
+// as a candidate and the compilation will break due to an ambiguous overload.
+
+// The matcher must be in the same namespace as AllOf/AnyOf to make argument
+// dependent lookup find those.
+MATCHER(M, "") {
+  (void)arg;
+  return true;
+}
+
+template <typename T1, typename T2>
+bool AllOf(const T1& /*t1*/, const T2& /*t2*/) {
+  return true;
+}
+
+TEST(AllOfTest, DoesNotCallAllOfUnqualified) {
+  EXPECT_THAT(42,
+              testing::AllOf(M(), M(), M(), M(), M(), M(), M(), M(), M(), M()));
+}
+
+template <typename T1, typename T2>
+bool AnyOf(const T1&, const T2&) {
+  return true;
+}
+
+TEST(AnyOfTest, DoesNotCallAnyOfUnqualified) {
+  EXPECT_THAT(42,
+              testing::AnyOf(M(), M(), M(), M(), M(), M(), M(), M(), M(), M()));
+}
+
+}  // namespace adl_test
+
+TEST(AllOfTest, WorksOnMoveOnlyType) {
+  std::unique_ptr<int> p(new int(3));
+  EXPECT_THAT(p, AllOf(Pointee(Eq(3)), Pointee(Gt(0)), Pointee(Lt(5))));
+  EXPECT_THAT(p, Not(AllOf(Pointee(Eq(3)), Pointee(Gt(0)), Pointee(Lt(3)))));
+}
+
+TEST(AnyOfTest, WorksOnMoveOnlyType) {
+  std::unique_ptr<int> p(new int(3));
+  EXPECT_THAT(p, AnyOf(Pointee(Eq(5)), Pointee(Lt(0)), Pointee(Lt(5))));
+  EXPECT_THAT(p, Not(AnyOf(Pointee(Eq(5)), Pointee(Lt(0)), Pointee(Gt(5)))));
+}
+
+MATCHER(IsNotNull, "") { return arg != nullptr; }
+
+// Verifies that a matcher defined using MATCHER() can work on
+// move-only types.
+TEST(MatcherMacroTest, WorksOnMoveOnlyType) {
+  std::unique_ptr<int> p(new int(3));
+  EXPECT_THAT(p, IsNotNull());
+  EXPECT_THAT(std::unique_ptr<int>(), Not(IsNotNull()));
+}
+
+MATCHER_P(UniquePointee, pointee, "") { return *arg == pointee; }
+
+// Verifies that a matcher defined using MATCHER_P*() can work on
+// move-only types.
+TEST(MatcherPMacroTest, WorksOnMoveOnlyType) {
+  std::unique_ptr<int> p(new int(3));
+  EXPECT_THAT(p, UniquePointee(3));
+  EXPECT_THAT(p, Not(UniquePointee(2)));
+}
+
+#if GTEST_HAS_EXCEPTIONS
+
+// std::function<void()> is used below for compatibility with older copies of
+// GCC. Normally, a raw lambda is all that is needed.
+
+// Test that examples from documentation compile
+TEST(ThrowsTest, Examples) {
+  EXPECT_THAT(
+      std::function<void()>([]() { throw std::runtime_error("message"); }),
+      Throws<std::runtime_error>());
+
+  EXPECT_THAT(
+      std::function<void()>([]() { throw std::runtime_error("message"); }),
+      ThrowsMessage<std::runtime_error>(HasSubstr("message")));
+}
+
+TEST(ThrowsTest, DoesNotGenerateDuplicateCatchClauseWarning) {
+  EXPECT_THAT(std::function<void()>([]() { throw std::exception(); }),
+              Throws<std::exception>());
+}
+
+TEST(ThrowsTest, CallableExecutedExactlyOnce) {
+  size_t a = 0;
+
+  EXPECT_THAT(std::function<void()>([&a]() {
+                a++;
+                throw 10;
+              }),
+              Throws<int>());
+  EXPECT_EQ(a, 1u);
+
+  EXPECT_THAT(std::function<void()>([&a]() {
+                a++;
+                throw std::runtime_error("message");
+              }),
+              Throws<std::runtime_error>());
+  EXPECT_EQ(a, 2u);
+
+  EXPECT_THAT(std::function<void()>([&a]() {
+                a++;
+                throw std::runtime_error("message");
+              }),
+              ThrowsMessage<std::runtime_error>(HasSubstr("message")));
+  EXPECT_EQ(a, 3u);
+
+  EXPECT_THAT(std::function<void()>([&a]() {
+                a++;
+                throw std::runtime_error("message");
+              }),
+              Throws<std::runtime_error>(
+                  Property(&std::runtime_error::what, HasSubstr("message"))));
+  EXPECT_EQ(a, 4u);
+}
+
+TEST(ThrowsTest, Describe) {
+  Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
+  std::stringstream ss;
+  matcher.DescribeTo(&ss);
+  auto explanation = ss.str();
+  EXPECT_THAT(explanation, HasSubstr("std::runtime_error"));
+}
+
+TEST(ThrowsTest, Success) {
+  Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
+  StringMatchResultListener listener;
+  EXPECT_TRUE(matcher.MatchAndExplain(
+      []() { throw std::runtime_error("error message"); }, &listener));
+  EXPECT_THAT(listener.str(), HasSubstr("std::runtime_error"));
+}
+
+TEST(ThrowsTest, FailWrongType) {
+  Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
+  StringMatchResultListener listener;
+  EXPECT_FALSE(matcher.MatchAndExplain(
+      []() { throw std::logic_error("error message"); }, &listener));
+  EXPECT_THAT(listener.str(), HasSubstr("std::logic_error"));
+  EXPECT_THAT(listener.str(), HasSubstr("\"error message\""));
+}
+
+TEST(ThrowsTest, FailWrongTypeNonStd) {
+  Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
+  StringMatchResultListener listener;
+  EXPECT_FALSE(matcher.MatchAndExplain([]() { throw 10; }, &listener));
+  EXPECT_THAT(listener.str(),
+              HasSubstr("throws an exception of an unknown type"));
+}
+
+TEST(ThrowsTest, FailNoThrow) {
+  Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
+  StringMatchResultListener listener;
+  EXPECT_FALSE(matcher.MatchAndExplain([]() { (void)0; }, &listener));
+  EXPECT_THAT(listener.str(), HasSubstr("does not throw any exception"));
+}
+
+class ThrowsPredicateTest
+    : public TestWithParam<Matcher<std::function<void()>>> {};
+
+TEST_P(ThrowsPredicateTest, Describe) {
+  Matcher<std::function<void()>> matcher = GetParam();
+  std::stringstream ss;
+  matcher.DescribeTo(&ss);
+  auto explanation = ss.str();
+  EXPECT_THAT(explanation, HasSubstr("std::runtime_error"));
+  EXPECT_THAT(explanation, HasSubstr("error message"));
+}
+
+TEST_P(ThrowsPredicateTest, Success) {
+  Matcher<std::function<void()>> matcher = GetParam();
+  StringMatchResultListener listener;
+  EXPECT_TRUE(matcher.MatchAndExplain(
+      []() { throw std::runtime_error("error message"); }, &listener));
+  EXPECT_THAT(listener.str(), HasSubstr("std::runtime_error"));
+}
+
+TEST_P(ThrowsPredicateTest, FailWrongType) {
+  Matcher<std::function<void()>> matcher = GetParam();
+  StringMatchResultListener listener;
+  EXPECT_FALSE(matcher.MatchAndExplain(
+      []() { throw std::logic_error("error message"); }, &listener));
+  EXPECT_THAT(listener.str(), HasSubstr("std::logic_error"));
+  EXPECT_THAT(listener.str(), HasSubstr("\"error message\""));
+}
+
+TEST_P(ThrowsPredicateTest, FailWrongTypeNonStd) {
+  Matcher<std::function<void()>> matcher = GetParam();
+  StringMatchResultListener listener;
+  EXPECT_FALSE(matcher.MatchAndExplain([]() { throw 10; }, &listener));
+  EXPECT_THAT(listener.str(),
+              HasSubstr("throws an exception of an unknown type"));
+}
+
+TEST_P(ThrowsPredicateTest, FailWrongMessage) {
+  Matcher<std::function<void()>> matcher = GetParam();
+  StringMatchResultListener listener;
+  EXPECT_FALSE(matcher.MatchAndExplain(
+      []() { throw std::runtime_error("wrong message"); }, &listener));
+  EXPECT_THAT(listener.str(), HasSubstr("std::runtime_error"));
+  EXPECT_THAT(listener.str(), Not(HasSubstr("wrong message")));
+}
+
+TEST_P(ThrowsPredicateTest, FailNoThrow) {
+  Matcher<std::function<void()>> matcher = GetParam();
+  StringMatchResultListener listener;
+  EXPECT_FALSE(matcher.MatchAndExplain([]() {}, &listener));
+  EXPECT_THAT(listener.str(), HasSubstr("does not throw any exception"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllMessagePredicates, ThrowsPredicateTest,
+    Values(Matcher<std::function<void()>>(
+        ThrowsMessage<std::runtime_error>(HasSubstr("error message")))));
+
+// Tests that Throws<E1>(Matcher<E2>{}) compiles even when E2 != const E1&.
+TEST(ThrowsPredicateCompilesTest, ExceptionMatcherAcceptsBroadType) {
+  {
+    Matcher<std::function<void()>> matcher =
+        ThrowsMessage<std::runtime_error>(HasSubstr("error message"));
+    EXPECT_TRUE(
+        matcher.Matches([]() { throw std::runtime_error("error message"); }));
+    EXPECT_FALSE(
+        matcher.Matches([]() { throw std::runtime_error("wrong message"); }));
+  }
+
+  {
+    Matcher<uint64_t> inner = Eq(10);
+    Matcher<std::function<void()>> matcher = Throws<uint32_t>(inner);
+    EXPECT_TRUE(matcher.Matches([]() { throw(uint32_t) 10; }));
+    EXPECT_FALSE(matcher.Matches([]() { throw(uint32_t) 11; }));
+  }
+}
+
+// Tests that ThrowsMessage("message") is equivalent
+// to ThrowsMessage(Eq<std::string>("message")).
+TEST(ThrowsPredicateCompilesTest, MessageMatcherAcceptsNonMatcher) {
+  Matcher<std::function<void()>> matcher =
+      ThrowsMessage<std::runtime_error>("error message");
+  EXPECT_TRUE(
+      matcher.Matches([]() { throw std::runtime_error("error message"); }));
+  EXPECT_FALSE(matcher.Matches(
+      []() { throw std::runtime_error("wrong error message"); }));
+}
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
 }  // namespace
 }  // namespace gmock_matchers_test
 }  // namespace testing
diff --git a/ext/googletest/googlemock/test/gmock-more-actions_test.cc b/ext/googletest/googlemock/test/gmock-more-actions_test.cc
index 97ec5cf..53bb029 100644
--- a/ext/googletest/googlemock/test/gmock-more-actions_test.cc
+++ b/ext/googletest/googlemock/test/gmock-more-actions_test.cc
@@ -27,10 +27,14 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-
 // Google Mock - a framework for writing C++ mock classes.
 //
-// This file tests the built-in actions in gmock-more-actions.h.
+// This file tests the built-in actions in gmock-actions.h.
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4577)
+#endif
 
 #include "gmock/gmock-more-actions.h"
 
@@ -38,7 +42,9 @@
 #include <memory>
 #include <sstream>
 #include <string>
+
 #include "gmock/gmock.h"
+#include "gtest/gtest-spi.h"
 #include "gtest/gtest.h"
 
 namespace testing {
@@ -46,12 +52,9 @@
 
 using ::std::plus;
 using ::std::string;
-using testing::_;
 using testing::Action;
-using testing::ActionInterface;
 using testing::DeleteArg;
 using testing::Invoke;
-using testing::Return;
 using testing::ReturnArg;
 using testing::ReturnPointee;
 using testing::SaveArg;
@@ -68,55 +71,27 @@
 // Sample functions and functors for testing Invoke() and etc.
 int Nullary() { return 1; }
 
-class NullaryFunctor {
- public:
-  int operator()() { return 2; }
-};
-
 bool g_done = false;
-void VoidNullary() { g_done = true; }
-
-class VoidNullaryFunctor {
- public:
-  void operator()() { g_done = true; }
-};
 
 bool Unary(int x) { return x < 0; }
 
-const char* Plus1(const char* s) { return s + 1; }
-
-void VoidUnary(int /* n */) { g_done = true; }
-
 bool ByConstRef(const std::string& s) { return s == "Hi"; }
 
 const double g_double = 0;
 bool ReferencesGlobalDouble(const double& x) { return &x == &g_double; }
 
-std::string ByNonConstRef(std::string& s) { return s += "+"; }  // NOLINT
-
 struct UnaryFunctor {
   int operator()(bool x) { return x ? 1 : -1; }
 };
 
 const char* Binary(const char* input, short n) { return input + n; }  // NOLINT
 
-void VoidBinary(int, char) { g_done = true; }
-
 int Ternary(int x, char y, short z) { return x + y + z; }  // NOLINT
 
-void VoidTernary(int, char, bool) { g_done = true; }
-
 int SumOf4(int a, int b, int c, int d) { return a + b + c + d; }
 
 int SumOfFirst2(int a, int b, Unused, Unused) { return a + b; }
 
-void VoidFunctionWithFourArguments(char, int, float, double) { g_done = true; }
-
-std::string Concat4(const char* s1, const char* s2, const char* s3,
-                    const char* s4) {
-  return std::string(s1) + s2 + s3 + s4;
-}
-
 int SumOf5(int a, int b, int c, int d, int e) { return a + b + c + d + e; }
 
 struct SumOf5Functor {
@@ -125,11 +100,6 @@
   }
 };
 
-std::string Concat5(const char* s1, const char* s2, const char* s3,
-                    const char* s4, const char* s5) {
-  return std::string(s1) + s2 + s3 + s4 + s5;
-}
-
 int SumOf6(int a, int b, int c, int d, int e, int f) {
   return a + b + c + d + e + f;
 }
@@ -140,11 +110,6 @@
   }
 };
 
-std::string Concat6(const char* s1, const char* s2, const char* s3,
-                    const char* s4, const char* s5, const char* s6) {
-  return std::string(s1) + s2 + s3 + s4 + s5 + s6;
-}
-
 std::string Concat7(const char* s1, const char* s2, const char* s3,
                     const char* s4, const char* s5, const char* s6,
                     const char* s7) {
@@ -604,12 +569,39 @@
   EXPECT_THROW(a.Perform(std::make_tuple()), MyException);
 }
 
+class Object {
+ public:
+  virtual ~Object() {}
+  virtual void Func() {}
+};
+
+class MockObject : public Object {
+ public:
+  ~MockObject() override {}
+  MOCK_METHOD(void, Func, (), (override));
+};
+
+TEST(ThrowActionTest, Times0) {
+  EXPECT_NONFATAL_FAILURE(
+      [] {
+        try {
+          MockObject m;
+          ON_CALL(m, Func()).WillByDefault([] { throw "something"; });
+          EXPECT_CALL(m, Func()).Times(0);
+          m.Func();
+        } catch (...) {
+          // Exception is caught but Times(0) still triggers a failure.
+        }
+      }(),
+      "");
+}
+
 #endif  // GTEST_HAS_EXCEPTIONS
 
 // Tests that SetArrayArgument<N>(first, last) sets the elements of the array
 // pointed to by the N-th (0-based) argument to values in range [first, last).
 TEST(SetArrayArgumentTest, SetsTheNthArray) {
-  typedef void MyFunction(bool, int*, char*);
+  using MyFunction = void(bool, int*, char*);
   int numbers[] = { 1, 2, 3 };
   Action<MyFunction> a = SetArrayArgument<1>(numbers, numbers + 3);
 
@@ -645,7 +637,7 @@
 
 // Tests SetArrayArgument<N>(first, last) where first == last.
 TEST(SetArrayArgumentTest, SetsTheNthArrayWithEmptyRange) {
-  typedef void MyFunction(bool, int*);
+  using MyFunction = void(bool, int*);
   int numbers[] = { 1, 2, 3 };
   Action<MyFunction> a = SetArrayArgument<1>(numbers, numbers);
 
@@ -661,7 +653,7 @@
 // Tests SetArrayArgument<N>(first, last) where *first is convertible
 // (but not equal) to the argument type.
 TEST(SetArrayArgumentTest, SetsTheNthArrayWithConvertibleType) {
-  typedef void MyFunction(bool, int*);
+  using MyFunction = void(bool, int*);
   char chars[] = { 97, 98, 99 };
   Action<MyFunction> a = SetArrayArgument<1>(chars, chars + 3);
 
@@ -676,7 +668,7 @@
 
 // Test SetArrayArgument<N>(first, last) with iterator as argument.
 TEST(SetArrayArgumentTest, SetsTheNthArrayWithIteratorArgument) {
-  typedef void MyFunction(bool, std::back_insert_iterator<std::string>);
+  using MyFunction = void(bool, std::back_insert_iterator<std::string>);
   std::string letters = "abc";
   Action<MyFunction> a = SetArrayArgument<1>(letters.begin(), letters.end());
 
@@ -694,5 +686,862 @@
   EXPECT_EQ(43, a.Perform(std::make_tuple()));
 }
 
-}  // namespace gmock_generated_actions_test
+// Tests InvokeArgument<N>(...).
+
+// Tests using InvokeArgument with a nullary function.
+TEST(InvokeArgumentTest, Function0) {
+  Action<int(int, int (*)())> a = InvokeArgument<1>();  // NOLINT
+  EXPECT_EQ(1, a.Perform(std::make_tuple(2, &Nullary)));
+}
+
+// Tests using InvokeArgument with a unary function.
+TEST(InvokeArgumentTest, Functor1) {
+  Action<int(UnaryFunctor)> a = InvokeArgument<0>(true);  // NOLINT
+  EXPECT_EQ(1, a.Perform(std::make_tuple(UnaryFunctor())));
+}
+
+// Tests using InvokeArgument with a 5-ary function.
+TEST(InvokeArgumentTest, Function5) {
+  Action<int(int (*)(int, int, int, int, int))> a =  // NOLINT
+      InvokeArgument<0>(10000, 2000, 300, 40, 5);
+  EXPECT_EQ(12345, a.Perform(std::make_tuple(&SumOf5)));
+}
+
+// Tests using InvokeArgument with a 5-ary functor.
+TEST(InvokeArgumentTest, Functor5) {
+  Action<int(SumOf5Functor)> a =  // NOLINT
+      InvokeArgument<0>(10000, 2000, 300, 40, 5);
+  EXPECT_EQ(12345, a.Perform(std::make_tuple(SumOf5Functor())));
+}
+
+// Tests using InvokeArgument with a 6-ary function.
+TEST(InvokeArgumentTest, Function6) {
+  Action<int(int (*)(int, int, int, int, int, int))> a =  // NOLINT
+      InvokeArgument<0>(100000, 20000, 3000, 400, 50, 6);
+  EXPECT_EQ(123456, a.Perform(std::make_tuple(&SumOf6)));
+}
+
+// Tests using InvokeArgument with a 6-ary functor.
+TEST(InvokeArgumentTest, Functor6) {
+  Action<int(SumOf6Functor)> a =  // NOLINT
+      InvokeArgument<0>(100000, 20000, 3000, 400, 50, 6);
+  EXPECT_EQ(123456, a.Perform(std::make_tuple(SumOf6Functor())));
+}
+
+// Tests using InvokeArgument with a 7-ary function.
+TEST(InvokeArgumentTest, Function7) {
+  Action<std::string(std::string(*)(const char*, const char*, const char*,
+                                    const char*, const char*, const char*,
+                                    const char*))>
+      a = InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7");
+  EXPECT_EQ("1234567", a.Perform(std::make_tuple(&Concat7)));
+}
+
+// Tests using InvokeArgument with a 8-ary function.
+TEST(InvokeArgumentTest, Function8) {
+  Action<std::string(std::string(*)(const char*, const char*, const char*,
+                                    const char*, const char*, const char*,
+                                    const char*, const char*))>
+      a = InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7", "8");
+  EXPECT_EQ("12345678", a.Perform(std::make_tuple(&Concat8)));
+}
+
+// Tests using InvokeArgument with a 9-ary function.
+TEST(InvokeArgumentTest, Function9) {
+  Action<std::string(std::string(*)(const char*, const char*, const char*,
+                                    const char*, const char*, const char*,
+                                    const char*, const char*, const char*))>
+      a = InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7", "8", "9");
+  EXPECT_EQ("123456789", a.Perform(std::make_tuple(&Concat9)));
+}
+
+// Tests using InvokeArgument with a 10-ary function.
+TEST(InvokeArgumentTest, Function10) {
+  Action<std::string(std::string(*)(
+      const char*, const char*, const char*, const char*, const char*,
+      const char*, const char*, const char*, const char*, const char*))>
+      a = InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7", "8", "9", "0");
+  EXPECT_EQ("1234567890", a.Perform(std::make_tuple(&Concat10)));
+}
+
+// Tests using InvokeArgument with a function that takes a pointer argument.
+TEST(InvokeArgumentTest, ByPointerFunction) {
+  Action<const char*(const char* (*)(const char* input, short n))>  // NOLINT
+      a = InvokeArgument<0>(static_cast<const char*>("Hi"), Short(1));
+  EXPECT_STREQ("i", a.Perform(std::make_tuple(&Binary)));
+}
+
+// Tests using InvokeArgument with a function that takes a const char*
+// by passing it a C-string literal.
+TEST(InvokeArgumentTest, FunctionWithCStringLiteral) {
+  Action<const char*(const char* (*)(const char* input, short n))>  // NOLINT
+      a = InvokeArgument<0>("Hi", Short(1));
+  EXPECT_STREQ("i", a.Perform(std::make_tuple(&Binary)));
+}
+
+// Tests using InvokeArgument with a function that takes a const reference.
+TEST(InvokeArgumentTest, ByConstReferenceFunction) {
+  Action<bool(bool (*function)(const std::string& s))> a =  // NOLINT
+      InvokeArgument<0>(std::string("Hi"));
+  // When action 'a' is constructed, it makes a copy of the temporary
+  // string object passed to it, so it's OK to use 'a' later, when the
+  // temporary object has already died.
+  EXPECT_TRUE(a.Perform(std::make_tuple(&ByConstRef)));
+}
+
+// Tests using InvokeArgument with ByRef() and a function that takes a
+// const reference.
+TEST(InvokeArgumentTest, ByExplicitConstReferenceFunction) {
+  Action<bool(bool (*)(const double& x))> a =  // NOLINT
+      InvokeArgument<0>(ByRef(g_double));
+  // The above line calls ByRef() on a const value.
+  EXPECT_TRUE(a.Perform(std::make_tuple(&ReferencesGlobalDouble)));
+
+  double x = 0;
+  a = InvokeArgument<0>(ByRef(x));  // This calls ByRef() on a non-const.
+  EXPECT_FALSE(a.Perform(std::make_tuple(&ReferencesGlobalDouble)));
+}
+
+// Tests DoAll(a1, a2).
+TEST(DoAllTest, TwoActions) {
+  int n = 0;
+  Action<int(int*)> a = DoAll(SetArgPointee<0>(1),  // NOLINT
+                              Return(2));
+  EXPECT_EQ(2, a.Perform(std::make_tuple(&n)));
+  EXPECT_EQ(1, n);
+}
+
+// Tests DoAll(a1, a2, a3).
+TEST(DoAllTest, ThreeActions) {
+  int m = 0, n = 0;
+  Action<int(int*, int*)> a = DoAll(SetArgPointee<0>(1),  // NOLINT
+                                    SetArgPointee<1>(2), Return(3));
+  EXPECT_EQ(3, a.Perform(std::make_tuple(&m, &n)));
+  EXPECT_EQ(1, m);
+  EXPECT_EQ(2, n);
+}
+
+// Tests DoAll(a1, a2, a3, a4).
+TEST(DoAllTest, FourActions) {
+  int m = 0, n = 0;
+  char ch = '\0';
+  Action<int(int*, int*, char*)> a =  // NOLINT
+      DoAll(SetArgPointee<0>(1), SetArgPointee<1>(2), SetArgPointee<2>('a'),
+            Return(3));
+  EXPECT_EQ(3, a.Perform(std::make_tuple(&m, &n, &ch)));
+  EXPECT_EQ(1, m);
+  EXPECT_EQ(2, n);
+  EXPECT_EQ('a', ch);
+}
+
+// Tests DoAll(a1, a2, a3, a4, a5).
+TEST(DoAllTest, FiveActions) {
+  int m = 0, n = 0;
+  char a = '\0', b = '\0';
+  Action<int(int*, int*, char*, char*)> action =  // NOLINT
+      DoAll(SetArgPointee<0>(1), SetArgPointee<1>(2), SetArgPointee<2>('a'),
+            SetArgPointee<3>('b'), Return(3));
+  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b)));
+  EXPECT_EQ(1, m);
+  EXPECT_EQ(2, n);
+  EXPECT_EQ('a', a);
+  EXPECT_EQ('b', b);
+}
+
+// Tests DoAll(a1, a2, ..., a6).
+TEST(DoAllTest, SixActions) {
+  int m = 0, n = 0;
+  char a = '\0', b = '\0', c = '\0';
+  Action<int(int*, int*, char*, char*, char*)> action =  // NOLINT
+      DoAll(SetArgPointee<0>(1), SetArgPointee<1>(2), SetArgPointee<2>('a'),
+            SetArgPointee<3>('b'), SetArgPointee<4>('c'), Return(3));
+  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c)));
+  EXPECT_EQ(1, m);
+  EXPECT_EQ(2, n);
+  EXPECT_EQ('a', a);
+  EXPECT_EQ('b', b);
+  EXPECT_EQ('c', c);
+}
+
+// Tests DoAll(a1, a2, ..., a7).
+TEST(DoAllTest, SevenActions) {
+  int m = 0, n = 0;
+  char a = '\0', b = '\0', c = '\0', d = '\0';
+  Action<int(int*, int*, char*, char*, char*, char*)> action =  // NOLINT
+      DoAll(SetArgPointee<0>(1), SetArgPointee<1>(2), SetArgPointee<2>('a'),
+            SetArgPointee<3>('b'), SetArgPointee<4>('c'), SetArgPointee<5>('d'),
+            Return(3));
+  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c, &d)));
+  EXPECT_EQ(1, m);
+  EXPECT_EQ(2, n);
+  EXPECT_EQ('a', a);
+  EXPECT_EQ('b', b);
+  EXPECT_EQ('c', c);
+  EXPECT_EQ('d', d);
+}
+
+// Tests DoAll(a1, a2, ..., a8).
+TEST(DoAllTest, EightActions) {
+  int m = 0, n = 0;
+  char a = '\0', b = '\0', c = '\0', d = '\0', e = '\0';
+  Action<int(int*, int*, char*, char*, char*, char*,  // NOLINT
+             char*)>
+      action =
+          DoAll(SetArgPointee<0>(1), SetArgPointee<1>(2), SetArgPointee<2>('a'),
+                SetArgPointee<3>('b'), SetArgPointee<4>('c'),
+                SetArgPointee<5>('d'), SetArgPointee<6>('e'), Return(3));
+  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c, &d, &e)));
+  EXPECT_EQ(1, m);
+  EXPECT_EQ(2, n);
+  EXPECT_EQ('a', a);
+  EXPECT_EQ('b', b);
+  EXPECT_EQ('c', c);
+  EXPECT_EQ('d', d);
+  EXPECT_EQ('e', e);
+}
+
+// Tests DoAll(a1, a2, ..., a9).
+TEST(DoAllTest, NineActions) {
+  int m = 0, n = 0;
+  char a = '\0', b = '\0', c = '\0', d = '\0', e = '\0', f = '\0';
+  Action<int(int*, int*, char*, char*, char*, char*,  // NOLINT
+             char*, char*)>
+      action = DoAll(SetArgPointee<0>(1), SetArgPointee<1>(2),
+                     SetArgPointee<2>('a'), SetArgPointee<3>('b'),
+                     SetArgPointee<4>('c'), SetArgPointee<5>('d'),
+                     SetArgPointee<6>('e'), SetArgPointee<7>('f'), Return(3));
+  EXPECT_EQ(3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c, &d, &e, &f)));
+  EXPECT_EQ(1, m);
+  EXPECT_EQ(2, n);
+  EXPECT_EQ('a', a);
+  EXPECT_EQ('b', b);
+  EXPECT_EQ('c', c);
+  EXPECT_EQ('d', d);
+  EXPECT_EQ('e', e);
+  EXPECT_EQ('f', f);
+}
+
+// Tests DoAll(a1, a2, ..., a10).
+TEST(DoAllTest, TenActions) {
+  int m = 0, n = 0;
+  char a = '\0', b = '\0', c = '\0', d = '\0';
+  char e = '\0', f = '\0', g = '\0';
+  Action<int(int*, int*, char*, char*, char*, char*,  // NOLINT
+             char*, char*, char*)>
+      action =
+          DoAll(SetArgPointee<0>(1), SetArgPointee<1>(2), SetArgPointee<2>('a'),
+                SetArgPointee<3>('b'), SetArgPointee<4>('c'),
+                SetArgPointee<5>('d'), SetArgPointee<6>('e'),
+                SetArgPointee<7>('f'), SetArgPointee<8>('g'), Return(3));
+  EXPECT_EQ(
+      3, action.Perform(std::make_tuple(&m, &n, &a, &b, &c, &d, &e, &f, &g)));
+  EXPECT_EQ(1, m);
+  EXPECT_EQ(2, n);
+  EXPECT_EQ('a', a);
+  EXPECT_EQ('b', b);
+  EXPECT_EQ('c', c);
+  EXPECT_EQ('d', d);
+  EXPECT_EQ('e', e);
+  EXPECT_EQ('f', f);
+  EXPECT_EQ('g', g);
+}
+
+TEST(DoAllTest, NoArgs) {
+  bool ran_first = false;
+  Action<bool()> a =
+      DoAll([&] { ran_first = true; }, [&] { return ran_first; });
+  EXPECT_TRUE(a.Perform({}));
+}
+
+TEST(DoAllTest, MoveOnlyArgs) {
+  bool ran_first = false;
+  Action<int(std::unique_ptr<int>)> a =
+      DoAll(InvokeWithoutArgs([&] { ran_first = true; }),
+            [](std::unique_ptr<int> p) { return *p; });
+  EXPECT_EQ(7, a.Perform(std::make_tuple(std::unique_ptr<int>(new int(7)))));
+  EXPECT_TRUE(ran_first);
+}
+
+TEST(DoAllTest, ImplicitlyConvertsActionArguments) {
+  bool ran_first = false;
+  // Action<void(std::vector<int>)> isn't an
+  // Action<void(const std::vector<int>&) but can be converted.
+  Action<void(std::vector<int>)> first = [&] { ran_first = true; };
+  Action<int(std::vector<int>)> a =
+      DoAll(first, [](std::vector<int> arg) { return arg.front(); });
+  EXPECT_EQ(7, a.Perform(std::make_tuple(std::vector<int>{7})));
+  EXPECT_TRUE(ran_first);
+}
+
+// The ACTION*() macros trigger warning C4100 (unreferenced formal
+// parameter) in MSVC with -W4.  Unfortunately they cannot be fixed in
+// the macro definition, as the warnings are generated when the macro
+// is expanded and macro expansion cannot contain #pragma.  Therefore
+// we suppress them here.
+// Also suppress C4503 decorated name length exceeded, name was truncated
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#pragma warning(disable : 4503)
+#endif
+// Tests the ACTION*() macro family.
+
+// Tests that ACTION() can define an action that doesn't reference the
+// mock function arguments.
+ACTION(Return5) { return 5; }
+
+TEST(ActionMacroTest, WorksWhenNotReferencingArguments) {
+  Action<double()> a1 = Return5();
+  EXPECT_DOUBLE_EQ(5, a1.Perform(std::make_tuple()));
+
+  Action<int(double, bool)> a2 = Return5();
+  EXPECT_EQ(5, a2.Perform(std::make_tuple(1, true)));
+}
+
+// Tests that ACTION() can define an action that returns void.
+ACTION(IncrementArg1) { (*arg1)++; }
+
+TEST(ActionMacroTest, WorksWhenReturningVoid) {
+  Action<void(int, int*)> a1 = IncrementArg1();
+  int n = 0;
+  a1.Perform(std::make_tuple(5, &n));
+  EXPECT_EQ(1, n);
+}
+
+// Tests that the body of ACTION() can reference the type of the
+// argument.
+ACTION(IncrementArg2) {
+  StaticAssertTypeEq<int*, arg2_type>();
+  arg2_type temp = arg2;
+  (*temp)++;
+}
+
+TEST(ActionMacroTest, CanReferenceArgumentType) {
+  Action<void(int, bool, int*)> a1 = IncrementArg2();
+  int n = 0;
+  a1.Perform(std::make_tuple(5, false, &n));
+  EXPECT_EQ(1, n);
+}
+
+// Tests that the body of ACTION() can reference the argument tuple
+// via args_type and args.
+ACTION(Sum2) {
+  StaticAssertTypeEq<std::tuple<int, char, int*>, args_type>();
+  args_type args_copy = args;
+  return std::get<0>(args_copy) + std::get<1>(args_copy);
+}
+
+TEST(ActionMacroTest, CanReferenceArgumentTuple) {
+  Action<int(int, char, int*)> a1 = Sum2();
+  int dummy = 0;
+  EXPECT_EQ(11, a1.Perform(std::make_tuple(5, Char(6), &dummy)));
+}
+
+namespace {
+
+// Tests that the body of ACTION() can reference the mock function
+// type.
+int Dummy(bool flag) { return flag ? 1 : 0; }
+
+}  // namespace
+
+ACTION(InvokeDummy) {
+  StaticAssertTypeEq<int(bool), function_type>();
+  function_type* fp = &Dummy;
+  return (*fp)(true);
+}
+
+TEST(ActionMacroTest, CanReferenceMockFunctionType) {
+  Action<int(bool)> a1 = InvokeDummy();
+  EXPECT_EQ(1, a1.Perform(std::make_tuple(true)));
+  EXPECT_EQ(1, a1.Perform(std::make_tuple(false)));
+}
+
+// Tests that the body of ACTION() can reference the mock function's
+// return type.
+ACTION(InvokeDummy2) {
+  StaticAssertTypeEq<int, return_type>();
+  return_type result = Dummy(true);
+  return result;
+}
+
+TEST(ActionMacroTest, CanReferenceMockFunctionReturnType) {
+  Action<int(bool)> a1 = InvokeDummy2();
+  EXPECT_EQ(1, a1.Perform(std::make_tuple(true)));
+  EXPECT_EQ(1, a1.Perform(std::make_tuple(false)));
+}
+
+// Tests that ACTION() works for arguments passed by const reference.
+ACTION(ReturnAddrOfConstBoolReferenceArg) {
+  StaticAssertTypeEq<const bool&, arg1_type>();
+  return &arg1;
+}
+
+TEST(ActionMacroTest, WorksForConstReferenceArg) {
+  Action<const bool*(int, const bool&)> a = ReturnAddrOfConstBoolReferenceArg();
+  const bool b = false;
+  EXPECT_EQ(&b, a.Perform(std::tuple<int, const bool&>(0, b)));
+}
+
+// Tests that ACTION() works for arguments passed by non-const reference.
+ACTION(ReturnAddrOfIntReferenceArg) {
+  StaticAssertTypeEq<int&, arg0_type>();
+  return &arg0;
+}
+
+TEST(ActionMacroTest, WorksForNonConstReferenceArg) {
+  Action<int*(int&, bool, int)> a = ReturnAddrOfIntReferenceArg();
+  int n = 0;
+  EXPECT_EQ(&n, a.Perform(std::tuple<int&, bool, int>(n, true, 1)));
+}
+
+// Tests that ACTION() can be used in a namespace.
+namespace action_test {
+ACTION(Sum) { return arg0 + arg1; }
+}  // namespace action_test
+
+TEST(ActionMacroTest, WorksInNamespace) {
+  Action<int(int, int)> a1 = action_test::Sum();
+  EXPECT_EQ(3, a1.Perform(std::make_tuple(1, 2)));
+}
+
+// Tests that the same ACTION definition works for mock functions with
+// different argument numbers.
+ACTION(PlusTwo) { return arg0 + 2; }
+
+TEST(ActionMacroTest, WorksForDifferentArgumentNumbers) {
+  Action<int(int)> a1 = PlusTwo();
+  EXPECT_EQ(4, a1.Perform(std::make_tuple(2)));
+
+  Action<double(float, void*)> a2 = PlusTwo();
+  int dummy;
+  EXPECT_DOUBLE_EQ(6, a2.Perform(std::make_tuple(4.0f, &dummy)));
+}
+
+// Tests that ACTION_P can define a parameterized action.
+ACTION_P(Plus, n) { return arg0 + n; }
+
+TEST(ActionPMacroTest, DefinesParameterizedAction) {
+  Action<int(int m, bool t)> a1 = Plus(9);
+  EXPECT_EQ(10, a1.Perform(std::make_tuple(1, true)));
+}
+
+// Tests that the body of ACTION_P can reference the argument types
+// and the parameter type.
+ACTION_P(TypedPlus, n) {
+  arg0_type t1 = arg0;
+  n_type t2 = n;
+  return t1 + t2;
+}
+
+TEST(ActionPMacroTest, CanReferenceArgumentAndParameterTypes) {
+  Action<int(char m, bool t)> a1 = TypedPlus(9);
+  EXPECT_EQ(10, a1.Perform(std::make_tuple(Char(1), true)));
+}
+
+// Tests that a parameterized action can be used in any mock function
+// whose type is compatible.
+TEST(ActionPMacroTest, WorksInCompatibleMockFunction) {
+  Action<std::string(const std::string& s)> a1 = Plus("tail");
+  const std::string re = "re";
+  std::tuple<const std::string> dummy = std::make_tuple(re);
+  EXPECT_EQ("retail", a1.Perform(dummy));
+}
+
+// Tests that we can use ACTION*() to define actions overloaded on the
+// number of parameters.
+
+ACTION(OverloadedAction) { return arg0 ? arg1 : "hello"; }
+
+ACTION_P(OverloadedAction, default_value) {
+  return arg0 ? arg1 : default_value;
+}
+
+ACTION_P2(OverloadedAction, true_value, false_value) {
+  return arg0 ? true_value : false_value;
+}
+
+TEST(ActionMacroTest, CanDefineOverloadedActions) {
+  using MyAction = Action<const char*(bool, const char*)>;
+
+  const MyAction a1 = OverloadedAction();
+  EXPECT_STREQ("hello", a1.Perform(std::make_tuple(false, CharPtr("world"))));
+  EXPECT_STREQ("world", a1.Perform(std::make_tuple(true, CharPtr("world"))));
+
+  const MyAction a2 = OverloadedAction("hi");
+  EXPECT_STREQ("hi", a2.Perform(std::make_tuple(false, CharPtr("world"))));
+  EXPECT_STREQ("world", a2.Perform(std::make_tuple(true, CharPtr("world"))));
+
+  const MyAction a3 = OverloadedAction("hi", "you");
+  EXPECT_STREQ("hi", a3.Perform(std::make_tuple(true, CharPtr("world"))));
+  EXPECT_STREQ("you", a3.Perform(std::make_tuple(false, CharPtr("world"))));
+}
+
+// Tests ACTION_Pn where n >= 3.
+
+ACTION_P3(Plus, m, n, k) { return arg0 + m + n + k; }
+
+TEST(ActionPnMacroTest, WorksFor3Parameters) {
+  Action<double(int m, bool t)> a1 = Plus(100, 20, 3.4);
+  EXPECT_DOUBLE_EQ(3123.4, a1.Perform(std::make_tuple(3000, true)));
+
+  Action<std::string(const std::string& s)> a2 = Plus("tail", "-", ">");
+  const std::string re = "re";
+  std::tuple<const std::string> dummy = std::make_tuple(re);
+  EXPECT_EQ("retail->", a2.Perform(dummy));
+}
+
+ACTION_P4(Plus, p0, p1, p2, p3) { return arg0 + p0 + p1 + p2 + p3; }
+
+TEST(ActionPnMacroTest, WorksFor4Parameters) {
+  Action<int(int)> a1 = Plus(1, 2, 3, 4);
+  EXPECT_EQ(10 + 1 + 2 + 3 + 4, a1.Perform(std::make_tuple(10)));
+}
+
+ACTION_P5(Plus, p0, p1, p2, p3, p4) { return arg0 + p0 + p1 + p2 + p3 + p4; }
+
+TEST(ActionPnMacroTest, WorksFor5Parameters) {
+  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5);
+  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5, a1.Perform(std::make_tuple(10)));
+}
+
+ACTION_P6(Plus, p0, p1, p2, p3, p4, p5) {
+  return arg0 + p0 + p1 + p2 + p3 + p4 + p5;
+}
+
+TEST(ActionPnMacroTest, WorksFor6Parameters) {
+  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6);
+  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6, a1.Perform(std::make_tuple(10)));
+}
+
+ACTION_P7(Plus, p0, p1, p2, p3, p4, p5, p6) {
+  return arg0 + p0 + p1 + p2 + p3 + p4 + p5 + p6;
+}
+
+TEST(ActionPnMacroTest, WorksFor7Parameters) {
+  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6, 7);
+  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7, a1.Perform(std::make_tuple(10)));
+}
+
+ACTION_P8(Plus, p0, p1, p2, p3, p4, p5, p6, p7) {
+  return arg0 + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7;
+}
+
+TEST(ActionPnMacroTest, WorksFor8Parameters) {
+  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6, 7, 8);
+  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8,
+            a1.Perform(std::make_tuple(10)));
+}
+
+ACTION_P9(Plus, p0, p1, p2, p3, p4, p5, p6, p7, p8) {
+  return arg0 + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8;
+}
+
+TEST(ActionPnMacroTest, WorksFor9Parameters) {
+  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6, 7, 8, 9);
+  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9,
+            a1.Perform(std::make_tuple(10)));
+}
+
+ACTION_P10(Plus, p0, p1, p2, p3, p4, p5, p6, p7, p8, last_param) {
+  arg0_type t0 = arg0;
+  last_param_type t9 = last_param;
+  return t0 + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + t9;
+}
+
+TEST(ActionPnMacroTest, WorksFor10Parameters) {
+  Action<int(int)> a1 = Plus(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+  EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10,
+            a1.Perform(std::make_tuple(10)));
+}
+
+// Tests that the action body can promote the parameter types.
+
+ACTION_P2(PadArgument, prefix, suffix) {
+  // The following lines promote the two parameters to desired types.
+  std::string prefix_str(prefix);
+  char suffix_char = static_cast<char>(suffix);
+  return prefix_str + arg0 + suffix_char;
+}
+
+TEST(ActionPnMacroTest, SimpleTypePromotion) {
+  Action<std::string(const char*)> no_promo =
+      PadArgument(std::string("foo"), 'r');
+  Action<std::string(const char*)> promo =
+      PadArgument("foo", static_cast<int>('r'));
+  EXPECT_EQ("foobar", no_promo.Perform(std::make_tuple(CharPtr("ba"))));
+  EXPECT_EQ("foobar", promo.Perform(std::make_tuple(CharPtr("ba"))));
+}
+
+// Tests that we can partially restrict parameter types using a
+// straight-forward pattern.
+
+// Defines a generic action that doesn't restrict the types of its
+// parameters.
+ACTION_P3(ConcatImpl, a, b, c) {
+  std::stringstream ss;
+  ss << a << b << c;
+  return ss.str();
+}
+
+// Next, we try to restrict that either the first parameter is a
+// string, or the second parameter is an int.
+
+// Defines a partially specialized wrapper that restricts the first
+// parameter to std::string.
+template <typename T1, typename T2>
+// ConcatImplActionP3 is the class template ACTION_P3 uses to
+// implement ConcatImpl.  We shouldn't change the name as this
+// pattern requires the user to use it directly.
+ConcatImplActionP3<std::string, T1, T2> Concat(const std::string& a, T1 b,
+                                               T2 c) {
+  GTEST_INTENTIONAL_CONST_COND_PUSH_()
+  if (true) {
+    GTEST_INTENTIONAL_CONST_COND_POP_()
+    // This branch verifies that ConcatImpl() can be invoked without
+    // explicit template arguments.
+    return ConcatImpl(a, b, c);
+  } else {
+    // This branch verifies that ConcatImpl() can also be invoked with
+    // explicit template arguments.  It doesn't really need to be
+    // executed as this is a compile-time verification.
+    return ConcatImpl<std::string, T1, T2>(a, b, c);
+  }
+}
+
+// Defines another partially specialized wrapper that restricts the
+// second parameter to int.
+template <typename T1, typename T2>
+ConcatImplActionP3<T1, int, T2> Concat(T1 a, int b, T2 c) {
+  return ConcatImpl(a, b, c);
+}
+
+TEST(ActionPnMacroTest, CanPartiallyRestrictParameterTypes) {
+  Action<const std::string()> a1 = Concat("Hello", "1", 2);
+  EXPECT_EQ("Hello12", a1.Perform(std::make_tuple()));
+
+  a1 = Concat(1, 2, 3);
+  EXPECT_EQ("123", a1.Perform(std::make_tuple()));
+}
+
+// Verifies the type of an ACTION*.
+
+ACTION(DoFoo) {}
+ACTION_P(DoFoo, p) {}
+ACTION_P2(DoFoo, p0, p1) {}
+
+TEST(ActionPnMacroTest, TypesAreCorrect) {
+  // DoFoo() must be assignable to a DoFooAction variable.
+  DoFooAction a0 = DoFoo();
+
+  // DoFoo(1) must be assignable to a DoFooActionP variable.
+  DoFooActionP<int> a1 = DoFoo(1);
+
+  // DoFoo(p1, ..., pk) must be assignable to a DoFooActionPk
+  // variable, and so on.
+  DoFooActionP2<int, char> a2 = DoFoo(1, '2');
+  PlusActionP3<int, int, char> a3 = Plus(1, 2, '3');
+  PlusActionP4<int, int, int, char> a4 = Plus(1, 2, 3, '4');
+  PlusActionP5<int, int, int, int, char> a5 = Plus(1, 2, 3, 4, '5');
+  PlusActionP6<int, int, int, int, int, char> a6 = Plus(1, 2, 3, 4, 5, '6');
+  PlusActionP7<int, int, int, int, int, int, char> a7 =
+      Plus(1, 2, 3, 4, 5, 6, '7');
+  PlusActionP8<int, int, int, int, int, int, int, char> a8 =
+      Plus(1, 2, 3, 4, 5, 6, 7, '8');
+  PlusActionP9<int, int, int, int, int, int, int, int, char> a9 =
+      Plus(1, 2, 3, 4, 5, 6, 7, 8, '9');
+  PlusActionP10<int, int, int, int, int, int, int, int, int, char> a10 =
+      Plus(1, 2, 3, 4, 5, 6, 7, 8, 9, '0');
+
+  // Avoid "unused variable" warnings.
+  (void)a0;
+  (void)a1;
+  (void)a2;
+  (void)a3;
+  (void)a4;
+  (void)a5;
+  (void)a6;
+  (void)a7;
+  (void)a8;
+  (void)a9;
+  (void)a10;
+}
+
+// Tests that an ACTION_P*() action can be explicitly instantiated
+// with reference-typed parameters.
+
+ACTION_P(Plus1, x) { return x; }
+ACTION_P2(Plus2, x, y) { return x + y; }
+ACTION_P3(Plus3, x, y, z) { return x + y + z; }
+ACTION_P10(Plus10, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) {
+  return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9;
+}
+
+TEST(ActionPnMacroTest, CanExplicitlyInstantiateWithReferenceTypes) {
+  int x = 1, y = 2, z = 3;
+  const std::tuple<> empty = std::make_tuple();
+
+  Action<int()> a = Plus1<int&>(x);
+  EXPECT_EQ(1, a.Perform(empty));
+
+  a = Plus2<const int&, int&>(x, y);
+  EXPECT_EQ(3, a.Perform(empty));
+
+  a = Plus3<int&, const int&, int&>(x, y, z);
+  EXPECT_EQ(6, a.Perform(empty));
+
+  int n[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  a = Plus10<const int&, int&, const int&, int&, const int&, int&, const int&,
+             int&, const int&, int&>(n[0], n[1], n[2], n[3], n[4], n[5], n[6],
+                                     n[7], n[8], n[9]);
+  EXPECT_EQ(55, a.Perform(empty));
+}
+
+class TenArgConstructorClass {
+ public:
+  TenArgConstructorClass(int a1, int a2, int a3, int a4, int a5, int a6, int a7,
+                         int a8, int a9, int a10)
+      : value_(a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10) {}
+  int value_;
+};
+
+// Tests that ACTION_TEMPLATE works when there is no value parameter.
+ACTION_TEMPLATE(CreateNew, HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_0_VALUE_PARAMS()) {
+  return new T;
+}
+
+TEST(ActionTemplateTest, WorksWithoutValueParam) {
+  const Action<int*()> a = CreateNew<int>();
+  int* p = a.Perform(std::make_tuple());
+  delete p;
+}
+
+// Tests that ACTION_TEMPLATE works when there are value parameters.
+ACTION_TEMPLATE(CreateNew, HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_1_VALUE_PARAMS(a0)) {
+  return new T(a0);
+}
+
+TEST(ActionTemplateTest, WorksWithValueParams) {
+  const Action<int*()> a = CreateNew<int>(42);
+  int* p = a.Perform(std::make_tuple());
+  EXPECT_EQ(42, *p);
+  delete p;
+}
+
+// Tests that ACTION_TEMPLATE works for integral template parameters.
+ACTION_TEMPLATE(MyDeleteArg, HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_0_VALUE_PARAMS()) {
+  delete std::get<k>(args);
+}
+
+// Resets a bool variable in the destructor.
+class BoolResetter {
+ public:
+  explicit BoolResetter(bool* value) : value_(value) {}
+  ~BoolResetter() { *value_ = false; }
+
+ private:
+  bool* value_;
+};
+
+TEST(ActionTemplateTest, WorksForIntegralTemplateParams) {
+  const Action<void(int*, BoolResetter*)> a = MyDeleteArg<1>();
+  int n = 0;
+  bool b = true;
+  auto* resetter = new BoolResetter(&b);
+  a.Perform(std::make_tuple(&n, resetter));
+  EXPECT_FALSE(b);  // Verifies that resetter is deleted.
+}
+
+// Tests that ACTION_TEMPLATES works for template template parameters.
+ACTION_TEMPLATE(ReturnSmartPointer,
+                HAS_1_TEMPLATE_PARAMS(template <typename Pointee> class,
+                                      Pointer),
+                AND_1_VALUE_PARAMS(pointee)) {
+  return Pointer<pointee_type>(new pointee_type(pointee));
+}
+
+TEST(ActionTemplateTest, WorksForTemplateTemplateParameters) {
+  const Action<std::shared_ptr<int>()> a =
+      ReturnSmartPointer<std::shared_ptr>(42);
+  std::shared_ptr<int> p = a.Perform(std::make_tuple());
+  EXPECT_EQ(42, *p);
+}
+
+// Tests that ACTION_TEMPLATE works for 10 template parameters.
+template <typename T1, typename T2, typename T3, int k4, bool k5,
+          unsigned int k6, typename T7, typename T8, typename T9>
+struct GiantTemplate {
+ public:
+  explicit GiantTemplate(int a_value) : value(a_value) {}
+  int value;
+};
+
+ACTION_TEMPLATE(ReturnGiant,
+                HAS_10_TEMPLATE_PARAMS(typename, T1, typename, T2, typename, T3,
+                                       int, k4, bool, k5, unsigned int, k6,
+                                       class, T7, class, T8, class, T9,
+                                       template <typename T> class, T10),
+                AND_1_VALUE_PARAMS(value)) {
+  return GiantTemplate<T10<T1>, T2, T3, k4, k5, k6, T7, T8, T9>(value);
+}
+
+TEST(ActionTemplateTest, WorksFor10TemplateParameters) {
+  using Giant = GiantTemplate<std::shared_ptr<int>, bool, double, 5, true, 6,
+                              char, unsigned, int>;
+  const Action<Giant()> a = ReturnGiant<int, bool, double, 5, true, 6, char,
+                                        unsigned, int, std::shared_ptr>(42);
+  Giant giant = a.Perform(std::make_tuple());
+  EXPECT_EQ(42, giant.value);
+}
+
+// Tests that ACTION_TEMPLATE works for 10 value parameters.
+ACTION_TEMPLATE(ReturnSum, HAS_1_TEMPLATE_PARAMS(typename, Number),
+                AND_10_VALUE_PARAMS(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10)) {
+  return static_cast<Number>(v1) + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10;
+}
+
+TEST(ActionTemplateTest, WorksFor10ValueParameters) {
+  const Action<int()> a = ReturnSum<int>(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+  EXPECT_EQ(55, a.Perform(std::make_tuple()));
+}
+
+// Tests that ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded
+// on the number of value parameters.
+
+ACTION(ReturnSum) { return 0; }
+
+ACTION_P(ReturnSum, x) { return x; }
+
+ACTION_TEMPLATE(ReturnSum, HAS_1_TEMPLATE_PARAMS(typename, Number),
+                AND_2_VALUE_PARAMS(v1, v2)) {
+  return static_cast<Number>(v1) + v2;
+}
+
+ACTION_TEMPLATE(ReturnSum, HAS_1_TEMPLATE_PARAMS(typename, Number),
+                AND_3_VALUE_PARAMS(v1, v2, v3)) {
+  return static_cast<Number>(v1) + v2 + v3;
+}
+
+ACTION_TEMPLATE(ReturnSum, HAS_2_TEMPLATE_PARAMS(typename, Number, int, k),
+                AND_4_VALUE_PARAMS(v1, v2, v3, v4)) {
+  return static_cast<Number>(v1) + v2 + v3 + v4 + k;
+}
+
+TEST(ActionTemplateTest, CanBeOverloadedOnNumberOfValueParameters) {
+  const Action<int()> a0 = ReturnSum();
+  const Action<int()> a1 = ReturnSum(1);
+  const Action<int()> a2 = ReturnSum<int>(1, 2);
+  const Action<int()> a3 = ReturnSum<int>(1, 2, 3);
+  const Action<int()> a4 = ReturnSum<int, 10000>(2000, 300, 40, 5);
+  EXPECT_EQ(0, a0.Perform(std::make_tuple()));
+  EXPECT_EQ(1, a1.Perform(std::make_tuple()));
+  EXPECT_EQ(3, a2.Perform(std::make_tuple()));
+  EXPECT_EQ(6, a3.Perform(std::make_tuple()));
+  EXPECT_EQ(12345, a4.Perform(std::make_tuple()));
+}
+
+}  // namespace gmock_more_actions_test
 }  // namespace testing
diff --git a/ext/googletest/googlemock/test/gmock-nice-strict_test.cc b/ext/googletest/googlemock/test/gmock-nice-strict_test.cc
index 0a201ed..25558eb 100644
--- a/ext/googletest/googlemock/test/gmock-nice-strict_test.cc
+++ b/ext/googletest/googlemock/test/gmock-nice-strict_test.cc
@@ -67,6 +67,12 @@
   explicit NotDefaultConstructible(int) {}
 };
 
+class CallsMockMethodInDestructor {
+ public:
+  ~CallsMockMethodInDestructor() { OnDestroy(); }
+  MOCK_METHOD(void, OnDestroy, ());
+};
+
 // Defines some mock classes needed by the tests.
 
 class Foo {
@@ -302,6 +308,13 @@
   nice.DoThis();
 }
 
+TEST(NiceMockTest, IsNiceInDestructor) {
+  {
+    NiceMock<CallsMockMethodInDestructor> nice_on_destroy;
+    // Don't add an expectation for the call before the mock goes out of scope.
+  }
+}
+
 TEST(NiceMockTest, IsNaggy_IsNice_IsStrict) {
   NiceMock<MockFoo> nice_foo;
   EXPECT_FALSE(Mock::IsNaggy(&nice_foo));
@@ -405,6 +418,22 @@
   naggy.DoThis();
 }
 
+TEST(NaggyMockTest, IsNaggyInDestructor) {
+  const std::string saved_flag = GMOCK_FLAG(verbose);
+  GMOCK_FLAG(verbose) = "warning";
+  CaptureStdout();
+
+  {
+    NaggyMock<CallsMockMethodInDestructor> naggy_on_destroy;
+    // Don't add an expectation for the call before the mock goes out of scope.
+  }
+
+  EXPECT_THAT(GetCapturedStdout(),
+              HasSubstr("Uninteresting mock function call"));
+
+  GMOCK_FLAG(verbose) = saved_flag;
+}
+
 TEST(NaggyMockTest, IsNaggy_IsNice_IsStrict) {
   NaggyMock<MockFoo> naggy_foo;
   EXPECT_TRUE(Mock::IsNaggy(&naggy_foo));
@@ -489,6 +518,16 @@
   strict.DoThis();
 }
 
+TEST(StrictMockTest, IsStrictInDestructor) {
+  EXPECT_NONFATAL_FAILURE(
+      {
+        StrictMock<CallsMockMethodInDestructor> strict_on_destroy;
+        // Don't add an expectation for the call before the mock goes out of
+        // scope.
+      },
+      "Uninteresting mock function call");
+}
+
 TEST(StrictMockTest, IsNaggy_IsNice_IsStrict) {
   StrictMock<MockFoo> strict_foo;
   EXPECT_FALSE(Mock::IsNaggy(&strict_foo));
diff --git a/ext/googletest/googlemock/test/gmock-pp_test.cc b/ext/googletest/googlemock/test/gmock-pp_test.cc
index 7387d39..5d1566e 100644
--- a/ext/googletest/googlemock/test/gmock-pp_test.cc
+++ b/ext/googletest/googlemock/test/gmock-pp_test.cc
@@ -1,5 +1,10 @@
 #include "gmock/internal/gmock-pp.h"
 
+// Used to test MSVC treating __VA_ARGS__ with a comma in it as one value
+#define GMOCK_TEST_REPLACE_comma_WITH_COMMA_I_comma ,
+#define GMOCK_TEST_REPLACE_comma_WITH_COMMA(x) \
+  GMOCK_PP_CAT(GMOCK_TEST_REPLACE_comma_WITH_COMMA_I_, x)
+
 // Static assertions.
 namespace testing {
 namespace internal {
@@ -17,6 +22,11 @@
 static_assert(!GMOCK_PP_HAS_COMMA(), "");
 static_assert(GMOCK_PP_HAS_COMMA(b, ), "");
 static_assert(!GMOCK_PP_HAS_COMMA((, )), "");
+static_assert(GMOCK_PP_HAS_COMMA(GMOCK_TEST_REPLACE_comma_WITH_COMMA(comma)),
+              "");
+static_assert(
+    GMOCK_PP_HAS_COMMA(GMOCK_TEST_REPLACE_comma_WITH_COMMA(comma(unrelated))),
+    "");
 static_assert(!GMOCK_PP_IS_EMPTY(, ), "");
 static_assert(!GMOCK_PP_IS_EMPTY(a), "");
 static_assert(!GMOCK_PP_IS_EMPTY(()), "");
diff --git a/ext/googletest/googlemock/test/gmock-spec-builders_test.cc b/ext/googletest/googlemock/test/gmock-spec-builders_test.cc
index 7bf5a8a..fa97411 100644
--- a/ext/googletest/googlemock/test/gmock-spec-builders_test.cc
+++ b/ext/googletest/googlemock/test/gmock-spec-builders_test.cc
@@ -69,8 +69,8 @@
 using testing::Between;
 using testing::Cardinality;
 using testing::CardinalityInterface;
-using testing::ContainsRegex;
 using testing::Const;
+using testing::ContainsRegex;
 using testing::DoAll;
 using testing::DoDefault;
 using testing::Eq;
@@ -2179,8 +2179,8 @@
         "call should not happen.  Do not suppress it by blindly adding "
         "an EXPECT_CALL() if you don't mean to enforce the call.  "
         "See "
-        "https://github.com/google/googletest/blob/master/googlemock/docs/"
-        "cook_book.md#"
+        "https://github.com/google/googletest/blob/master/docs/"
+        "gmock_cook_book.md#"
         "knowing-when-to-expect for details.";
 
     // A void-returning function.
diff --git a/ext/googletest/googlemock/test/gmock_all_test.cc b/ext/googletest/googlemock/test/gmock_all_test.cc
index b2b2027..fffbb8b 100644
--- a/ext/googletest/googlemock/test/gmock_all_test.cc
+++ b/ext/googletest/googlemock/test/gmock_all_test.cc
@@ -37,9 +37,6 @@
 // below list of actual *_test.cc files might change).
 #include "test/gmock-actions_test.cc"
 #include "test/gmock-cardinalities_test.cc"
-#include "test/gmock-generated-actions_test.cc"
-#include "test/gmock-generated-function-mockers_test.cc"
-#include "test/gmock-generated-matchers_test.cc"
 #include "test/gmock-internal-utils_test.cc"
 #include "test/gmock-matchers_test.cc"
 #include "test/gmock-more-actions_test.cc"
diff --git a/ext/googletest/googlemock/test/gmock_link_test.h b/ext/googletest/googlemock/test/gmock_link_test.h
index 175d2bd..5734b2e 100644
--- a/ext/googletest/googlemock/test/gmock_link_test.h
+++ b/ext/googletest/googlemock/test/gmock_link_test.h
@@ -112,8 +112,8 @@
 // is defined as LinkTest1 in gmock_link_test.cc and as LinkTest2 in
 // gmock_link2_test.cc to avoid producing linker errors.
 
-#ifndef GMOCK_TEST_GMOCK_LINK_TEST_H_
-#define GMOCK_TEST_GMOCK_LINK_TEST_H_
+#ifndef GOOGLEMOCK_TEST_GMOCK_LINK_TEST_H_
+#define GOOGLEMOCK_TEST_GMOCK_LINK_TEST_H_
 
 #include "gmock/gmock.h"
 
@@ -687,4 +687,4 @@
   EXPECT_TRUE(m.Matches(nullptr));
 }
 
-#endif  // GMOCK_TEST_GMOCK_LINK_TEST_H_
+#endif  // GOOGLEMOCK_TEST_GMOCK_LINK_TEST_H_
diff --git a/ext/googletest/googlemock/test/gmock_output_test_golden.txt b/ext/googletest/googlemock/test/gmock_output_test_golden.txt
index 4c90b41..4846c12 100644
--- a/ext/googletest/googlemock/test/gmock_output_test_golden.txt
+++ b/ext/googletest/googlemock/test/gmock_output_test_golden.txt
@@ -75,14 +75,14 @@
 Uninteresting mock function call - returning default value.
     Function call: Bar2(0, 1)
           Returns: false
-NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  See https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#knowing-when-to-expect for details.
+NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  See https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md#knowing-when-to-expect for details.
 [       OK ] GMockOutputTest.UninterestingCall
 [ RUN      ] GMockOutputTest.UninterestingCallToVoidFunction
 
 GMOCK WARNING:
 Uninteresting mock function call - returning directly.
     Function call: Bar3(0, 1)
-NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  See https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#knowing-when-to-expect for details.
+NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  See https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md#knowing-when-to-expect for details.
 [       OK ] GMockOutputTest.UninterestingCallToVoidFunction
 [ RUN      ] GMockOutputTest.RetiredExpectation
 unknown file: Failure
@@ -266,14 +266,14 @@
 FILE:#:
     Function call: Bar2(2, 2)
           Returns: true
-NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  See https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#knowing-when-to-expect for details.
+NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  See https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md#knowing-when-to-expect for details.
 
 GMOCK WARNING:
 Uninteresting mock function call - taking default action specified at:
 FILE:#:
     Function call: Bar2(1, 1)
           Returns: false
-NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  See https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#knowing-when-to-expect for details.
+NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  See https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md#knowing-when-to-expect for details.
 [       OK ] GMockOutputTest.UninterestingCallWithDefaultAction
 [ RUN      ] GMockOutputTest.ExplicitActionsRunOutWithDefaultAction
 
@@ -314,4 +314,4 @@
 FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#.
 FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#.
 FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#.
-ERROR: 3 leaked mock objects found at program exit. Expectations on a mock object is verified when the object is destructed. Leaking a mock means that its expectations aren't verified, which is usually a test bug. If you really intend to leak a mock, you can suppress this error using testing::Mock::AllowLeak(mock_object), or you may use a fake or stub instead of a mock.
+ERROR: 3 leaked mock objects found at program exit. Expectations on a mock object are verified when the object is destructed. Leaking a mock means that its expectations aren't verified, which is usually a test bug. If you really intend to leak a mock, you can suppress this error using testing::Mock::AllowLeak(mock_object), or you may use a fake or stub instead of a mock.
diff --git a/ext/googletest/googletest/CMakeLists.txt b/ext/googletest/googletest/CMakeLists.txt
index db29294..abdd98b 100644
--- a/ext/googletest/googletest/CMakeLists.txt
+++ b/ext/googletest/googletest/CMakeLists.txt
@@ -53,7 +53,7 @@
   cmake_policy(SET CMP0048 NEW)
   project(gtest VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C)
 endif()
-cmake_minimum_required(VERSION 2.6.4)
+cmake_minimum_required(VERSION 2.8.12)
 
 if (POLICY CMP0063) # Visibility
   cmake_policy(SET CMP0063 NEW)
@@ -92,10 +92,13 @@
 
 config_compiler_and_linker()  # Defined in internal_utils.cmake.
 
+# Needed to set the namespace for both the export targets and the
+# alias libraries
+set(cmake_package_name GTest CACHE INTERNAL "")
+
 # Create the CMake package file descriptors.
 if (INSTALL_GTEST)
   include(CMakePackageConfigHelpers)
-  set(cmake_package_name GTest)
   set(targets_export_name ${cmake_package_name}Targets CACHE INTERNAL "")
   set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated" CACHE INTERNAL "")
   set(cmake_files_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${cmake_package_name}")
@@ -126,7 +129,9 @@
 # are used for other targets, to ensure that gtest can be compiled by a user
 # aggressive about warnings.
 cxx_library(gtest "${cxx_strict}" src/gtest-all.cc)
+set_target_properties(gtest PROPERTIES VERSION ${GOOGLETEST_VERSION})
 cxx_library(gtest_main "${cxx_strict}" src/gtest_main.cc)
+set_target_properties(gtest_main PROPERTIES VERSION ${GOOGLETEST_VERSION})
 # If the CMake version supports it, attach header directory information
 # to the targets for when we are part of a parent build (ie being pulled
 # in via add_subdirectory() rather than being a standalone build).
@@ -182,20 +187,6 @@
   # 'make test' or ctest.
   enable_testing()
 
-  if (WIN32)
-    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/RunTest.ps1"
-         CONTENT
-"$project_bin = \"${CMAKE_BINARY_DIR}/bin/$<CONFIG>\"
-$env:Path = \"$project_bin;$env:Path\"
-& $args")
-  elseif (MINGW OR CYGWIN)
-    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/RunTest.ps1"
-         CONTENT
-"$project_bin = (cygpath --windows ${CMAKE_BINARY_DIR}/bin)
-$env:Path = \"$project_bin;$env:Path\"
-& $args")
-  endif()
-
   ############################################################
   # C++ tests built with standard compiler flags.
 
@@ -266,6 +257,7 @@
   cxx_executable(googletest-break-on-failure-unittest_ test gtest)
   py_test(googletest-break-on-failure-unittest)
 
+  py_test(gtest_skip_check_output_test)
   py_test(gtest_skip_environment_check_output_test)
 
   # Visual Studio .NET 2003 does not support STL with exceptions disabled.
@@ -317,6 +309,9 @@
   cxx_executable(googletest-uninitialized-test_ test gtest)
   py_test(googletest-uninitialized-test)
 
+  cxx_executable(gtest_list_output_unittest_ test gtest)
+  py_test(gtest_list_output_unittest)
+
   cxx_executable(gtest_xml_outfile1_test_ test gtest_main)
   cxx_executable(gtest_xml_outfile2_test_ test gtest_main)
   py_test(gtest_xml_outfiles_test)
diff --git a/ext/googletest/googletest/LICENSE b/ext/googletest/googletest/LICENSE
deleted file mode 100644
index 1941a11..0000000
--- a/ext/googletest/googletest/LICENSE
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright 2008, Google Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-    * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-    * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ext/googletest/googletest/README.md b/ext/googletest/googletest/README.md
index 766ddc1..c8aedb7 100644
--- a/ext/googletest/googletest/README.md
+++ b/ext/googletest/googletest/README.md
@@ -2,39 +2,51 @@
 
 #### Setup
 
-To build Google Test and your tests that use it, you need to tell your build
+To build GoogleTest and your tests that use it, you need to tell your build
 system where to find its headers and source files. The exact way to do it
 depends on which build system you use, and is usually straightforward.
 
 ### Build with CMake
 
-Google Test comes with a CMake build script (
-[CMakeLists.txt](https://github.com/google/googletest/blob/master/CMakeLists.txt))
+GoogleTest comes with a CMake build script
+([CMakeLists.txt](https://github.com/google/googletest/blob/master/CMakeLists.txt))
 that can be used on a wide range of platforms ("C" stands for cross-platform.).
 If you don't have CMake installed already, you can download it for free from
 <http://www.cmake.org/>.
 
 CMake works by generating native makefiles or build projects that can be used in
-the compiler environment of your choice. You can either build Google Test as a
+the compiler environment of your choice. You can either build GoogleTest as a
 standalone project or it can be incorporated into an existing CMake build for
 another project.
 
 #### Standalone CMake Project
 
-When building Google Test as a standalone project, the typical workflow starts
-with:
-
-    mkdir mybuild       # Create a directory to hold the build output.
-    cd mybuild
-    cmake ${GTEST_DIR}  # Generate native build scripts.
-
-If you want to build Google Test's samples, you should replace the last command
+When building GoogleTest as a standalone project, the typical workflow starts
 with
 
-    cmake -Dgtest_build_samples=ON ${GTEST_DIR}
+```
+git clone https://github.com/google/googletest.git -b release-1.11.0
+cd googletest        # Main directory of the cloned repository.
+mkdir build          # Create a directory to hold the build output.
+cd build
+cmake ..             # Generate native build scripts for GoogleTest.
+```
+
+The above command also includes GoogleMock by default. And so, if you want to
+build only GoogleTest, you should replace the last command with
+
+```
+cmake .. -DBUILD_GMOCK=OFF
+```
 
 If you are on a \*nix system, you should now see a Makefile in the current
-directory. Just type 'make' to build gtest.
+directory. Just type `make` to build GoogleTest. And then you can simply install
+GoogleTest if you are a system administrator.
+
+```
+make
+sudo make install    # Install in /usr/local/ by default
+```
 
 If you use Windows and have Visual Studio installed, a `gtest.sln` file and
 several `.vcproj` files will be created. You can then build them using Visual
@@ -44,13 +56,19 @@
 
 #### Incorporating Into An Existing CMake Project
 
-If you want to use gtest in a project which already uses CMake, then a more
-robust and flexible approach is to build gtest as part of that project directly.
-This is done by making the GoogleTest source code available to the main build
-and adding it using CMake's `add_subdirectory()` command. This has the
-significant advantage that the same compiler and linker settings are used
-between gtest and the rest of your project, so issues associated with using
-incompatible libraries (eg debug/release), etc. are avoided. This is
+If you want to use GoogleTest in a project which already uses CMake, the easiest
+way is to get installed libraries and headers.
+
+*   Import GoogleTest by using `find_package` (or `pkg_check_modules`). For
+    example, if `find_package(GTest CONFIG REQUIRED)` succeeds, you can use the
+    libraries as `GTest::gtest`, `GTest::gmock`.
+
+And a more robust and flexible approach is to build GoogleTest as part of that
+project directly. This is done by making the GoogleTest source code available to
+the main build and adding it using CMake's `add_subdirectory()` command. This
+has the significant advantage that the same compiler and linker settings are
+used between GoogleTest and the rest of your project, so issues associated with
+using incompatible libraries (eg debug/release), etc. are avoided. This is
 particularly useful on Windows. Making GoogleTest's source code available to the
 main build can be done a few different ways:
 
@@ -64,68 +82,23 @@
     possible or appropriate. Git submodules, for example, have their own set of
     advantages and drawbacks.
 *   Use CMake to download GoogleTest as part of the build's configure step. This
-    is just a little more complex, but doesn't have the limitations of the other
-    methods.
+    approach doesn't have the limitations of the other methods.
 
-The last of the above methods is implemented with a small piece of CMake code in
-a separate file (e.g. `CMakeLists.txt.in`) which is copied to the build area and
-then invoked as a sub-build _during the CMake stage_. That directory is then
-pulled into the main build with `add_subdirectory()`. For example:
+The last of the above methods is implemented with a small piece of CMake code
+that downloads and pulls the GoogleTest code into the main build.
 
-New file `CMakeLists.txt.in`:
+Just add to your `CMakeLists.txt`:
 
 ```cmake
-cmake_minimum_required(VERSION 2.8.2)
-
-project(googletest-download NONE)
-
-include(ExternalProject)
-ExternalProject_Add(googletest
-  GIT_REPOSITORY    https://github.com/google/googletest.git
-  GIT_TAG           master
-  SOURCE_DIR        "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
-  BINARY_DIR        "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
-  CONFIGURE_COMMAND ""
-  BUILD_COMMAND     ""
-  INSTALL_COMMAND   ""
-  TEST_COMMAND      ""
+include(FetchContent)
+FetchContent_Declare(
+  googletest
+  # Specify the commit you depend on and update it regularly.
+  URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
 )
-```
-
-Existing build's `CMakeLists.txt`:
-
-```cmake
-# Download and unpack googletest at configure time
-configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
-execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
-  RESULT_VARIABLE result
-  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
-if(result)
-  message(FATAL_ERROR "CMake step for googletest failed: ${result}")
-endif()
-execute_process(COMMAND ${CMAKE_COMMAND} --build .
-  RESULT_VARIABLE result
-  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
-if(result)
-  message(FATAL_ERROR "Build step for googletest failed: ${result}")
-endif()
-
-# Prevent overriding the parent project's compiler/linker
-# settings on Windows
+# For Windows: Prevent overriding the parent project's compiler/linker settings
 set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
-
-# Add googletest directly to our build. This defines
-# the gtest and gtest_main targets.
-add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
-                 ${CMAKE_CURRENT_BINARY_DIR}/googletest-build
-                 EXCLUDE_FROM_ALL)
-
-# The gtest/gtest_main targets carry header search path
-# dependencies automatically when using CMake 2.8.11 or
-# later. Otherwise we have to add them here ourselves.
-if (CMAKE_VERSION VERSION_LESS 2.8.11)
-  include_directories("${gtest_SOURCE_DIR}/include")
-endif()
+FetchContent_MakeAvailable(googletest)
 
 # Now simply link against gtest or gtest_main as needed. Eg
 add_executable(example example.cpp)
@@ -133,20 +106,18 @@
 add_test(NAME example_test COMMAND example)
 ```
 
-Note that this approach requires CMake 2.8.2 or later due to its use of the
-`ExternalProject_Add()` command. The above technique is discussed in more detail
-in [this separate article](http://crascit.com/2015/07/25/cmake-gtest/) which
-also contains a link to a fully generalized implementation of the technique.
+Note that this approach requires CMake 3.14 or later due to its use of the
+`FetchContent_MakeAvailable()` command.
 
 ##### Visual Studio Dynamic vs Static Runtimes
 
 By default, new Visual Studio projects link the C runtimes dynamically but
-Google Test links them statically. This will generate an error that looks
+GoogleTest links them statically. This will generate an error that looks
 something like the following: gtest.lib(gtest-all.obj) : error LNK2038: mismatch
 detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value
 'MDd_DynamicDebug' in main.obj
 
-Google Test already has a CMake option for this: `gtest_force_shared_crt`
+GoogleTest already has a CMake option for this: `gtest_force_shared_crt`
 
 Enabling this option will make gtest link the runtimes dynamically too, and
 match the project in which it is included.
@@ -154,17 +125,17 @@
 #### C++ Standard Version
 
 An environment that supports C++11 is required in order to successfully build
-Google Test. One way to ensure this is to specify the standard in the top-level
+GoogleTest. One way to ensure this is to specify the standard in the top-level
 project, for example by using the `set(CMAKE_CXX_STANDARD 11)` command. If this
-is not feasible, for example in a C project using Google Test for validation,
+is not feasible, for example in a C project using GoogleTest for validation,
 then it can be specified by adding it to the options for cmake via the
 `DCMAKE_CXX_FLAGS` option.
 
-### Tweaking Google Test
+### Tweaking GoogleTest
 
-Google Test can be used in diverse environments. The default configuration may
+GoogleTest can be used in diverse environments. The default configuration may
 not work (or may not work well) out of the box in some environments. However,
-you can easily tweak Google Test by defining control macros on the compiler
+you can easily tweak GoogleTest by defining control macros on the compiler
 command line. Generally, these macros are named like `GTEST_XYZ` and you define
 them to either 1 or 0 to enable or disable a certain feature.
 
@@ -173,12 +144,12 @@
 
 ### Multi-threaded Tests
 
-Google Test is thread-safe where the pthread library is available. After
+GoogleTest is thread-safe where the pthread library is available. After
 `#include "gtest/gtest.h"`, you can check the
 `GTEST_IS_THREADSAFE` macro to see whether this is the case (yes if the macro is
 `#defined` to 1, no if it's undefined.).
 
-If Google Test doesn't correctly detect whether pthread is available in your
+If GoogleTest doesn't correctly detect whether pthread is available in your
 environment, you can force it with
 
     -DGTEST_HAS_PTHREAD=1
@@ -187,16 +158,16 @@
 
     -DGTEST_HAS_PTHREAD=0
 
-When Google Test uses pthread, you may need to add flags to your compiler and/or
+When GoogleTest uses pthread, you may need to add flags to your compiler and/or
 linker to select the pthread library, or you'll get link errors. If you use the
-CMake script or the deprecated Autotools script, this is taken care of for you.
-If you use your own build script, you'll need to read your compiler and linker's
-manual to figure out what flags to add.
+CMake script, this is taken care of for you. If you use your own build script,
+you'll need to read your compiler and linker's manual to figure out what flags
+to add.
 
 ### As a Shared Library (DLL)
 
-Google Test is compact, so most users can build and link it as a static library
-for the simplicity. You can choose to use Google Test as a shared library (known
+GoogleTest is compact, so most users can build and link it as a static library
+for the simplicity. You can choose to use GoogleTest as a shared library (known
 as a DLL on Windows) if you prefer.
 
 To compile *gtest* as a shared library, add
@@ -216,22 +187,22 @@
 compilers (e.g. GCC), they may become necessary in the future, if we decide to
 improve the speed of loading the library (see
 <http://gcc.gnu.org/wiki/Visibility> for details). Therefore you are recommended
-to always add the above flags when using Google Test as a shared library.
-Otherwise a future release of Google Test may break your build script.
+to always add the above flags when using GoogleTest as a shared library.
+Otherwise a future release of GoogleTest may break your build script.
 
 ### Avoiding Macro Name Clashes
 
 In C++, macros don't obey namespaces. Therefore two libraries that both define a
 macro of the same name will clash if you `#include` both definitions. In case a
-Google Test macro clashes with another library, you can force Google Test to
+GoogleTest macro clashes with another library, you can force GoogleTest to
 rename its macro to avoid the conflict.
 
-Specifically, if both Google Test and some other code define macro FOO, you can
+Specifically, if both GoogleTest and some other code define macro FOO, you can
 add
 
     -DGTEST_DONT_DEFINE_FOO=1
 
-to the compiler flags to tell Google Test to change the macro's name from `FOO`
+to the compiler flags to tell GoogleTest to change the macro's name from `FOO`
 to `GTEST_FOO`. Currently `FOO` can be `FAIL`, `SUCCEED`, or `TEST`. For
 example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll need to write
 
diff --git a/ext/googletest/googletest/cmake/gtest.pc.in b/ext/googletest/googletest/cmake/gtest.pc.in
index 9aae29e..b4148fa 100644
--- a/ext/googletest/googletest/cmake/gtest.pc.in
+++ b/ext/googletest/googletest/cmake/gtest.pc.in
@@ -1,10 +1,9 @@
-prefix=${pcfiledir}/../..
-libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
-includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
 
 Name: gtest
 Description: GoogleTest (without main() function)
 Version: @PROJECT_VERSION@
 URL: https://github.com/google/googletest
 Libs: -L${libdir} -lgtest @CMAKE_THREAD_LIBS_INIT@
-Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ @CMAKE_THREAD_LIBS_INIT@
+Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@
diff --git a/ext/googletest/googletest/cmake/gtest_main.pc.in b/ext/googletest/googletest/cmake/gtest_main.pc.in
index 915f297..38c88c5 100644
--- a/ext/googletest/googletest/cmake/gtest_main.pc.in
+++ b/ext/googletest/googletest/cmake/gtest_main.pc.in
@@ -1,11 +1,10 @@
-prefix=${pcfiledir}/../..
-libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
-includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
 
 Name: gtest_main
 Description: GoogleTest (with main() function)
 Version: @PROJECT_VERSION@
 URL: https://github.com/google/googletest
-Requires: gtest
+Requires: gtest = @PROJECT_VERSION@
 Libs: -L${libdir} -lgtest_main @CMAKE_THREAD_LIBS_INIT@
-Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ @CMAKE_THREAD_LIBS_INIT@
+Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@
diff --git a/ext/googletest/googletest/cmake/internal_utils.cmake b/ext/googletest/googletest/cmake/internal_utils.cmake
index 2f70f0b..58fc9bf 100644
--- a/ext/googletest/googletest/cmake/internal_utils.cmake
+++ b/ext/googletest/googletest/cmake/internal_utils.cmake
@@ -72,7 +72,7 @@
   if (MSVC)
     # Newlines inside flags variables break CMake's NMake generator.
     # TODO(vladl@google.com): Add -RTCs and -RTCu to debug builds.
-    set(cxx_base_flags "-GS -W4 -WX -wd4251 -wd4275 -nologo -J -Zi")
+    set(cxx_base_flags "-GS -W4 -WX -wd4251 -wd4275 -nologo -J")
     set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32")
     set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN")
     set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1")
@@ -81,14 +81,16 @@
     # Suppress "unreachable code" warning
     # http://stackoverflow.com/questions/3232669 explains the issue.
     set(cxx_base_flags "${cxx_base_flags} -wd4702")
+    # Ensure MSVC treats source files as UTF-8 encoded.
+    set(cxx_base_flags "${cxx_base_flags} -utf-8")
   elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
-    set(cxx_base_flags "-Wall -Wshadow -Werror -Wconversion")
+    set(cxx_base_flags "-Wall -Wshadow -Wconversion")
     set(cxx_exception_flags "-fexceptions")
     set(cxx_no_exception_flags "-fno-exceptions")
     set(cxx_strict_flags "-W -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wunused-parameter -Wcast-align -Wchar-subscripts -Winline -Wredundant-decls")
     set(cxx_no_rtti_flags "-fno-rtti")
   elseif (CMAKE_COMPILER_IS_GNUCXX)
-    set(cxx_base_flags "-Wall -Wshadow -Werror")
+    set(cxx_base_flags "-Wall -Wshadow")
     if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0)
       set(cxx_base_flags "${cxx_base_flags} -Wno-error=dangling-else")
     endif()
@@ -148,6 +150,7 @@
   # type can be either STATIC or SHARED to denote a static or shared library.
   # ARGN refers to additional arguments after 'cxx_flags'.
   add_library(${name} ${type} ${ARGN})
+  add_library(${cmake_package_name}::${name} ALIAS ${name})
   set_target_properties(${name}
     PROPERTIES
     COMPILE_FLAGS "${cxx_flags}")
@@ -188,6 +191,10 @@
     endif()
     target_link_libraries(${name} PUBLIC ${threads_spec})
   endif()
+
+  if (NOT "${CMAKE_VERSION}" VERSION_LESS "3.8")
+    target_compile_features(${name} PUBLIC cxx_std_11)
+  endif()
 endfunction()
 
 ########################################################################
@@ -240,7 +247,13 @@
 endfunction()
 
 # Sets PYTHONINTERP_FOUND and PYTHON_EXECUTABLE.
-find_package(PythonInterp)
+if ("${CMAKE_VERSION}" VERSION_LESS "3.12.0")
+  find_package(PythonInterp)
+else()
+  find_package(Python COMPONENTS Interpreter)
+  set(PYTHONINTERP_FOUND ${Python_Interpreter_FOUND})
+  set(PYTHON_EXECUTABLE ${Python_EXECUTABLE})
+endif()
 
 # cxx_test_with_flags(name cxx_flags libs srcs...)
 #
@@ -248,13 +261,7 @@
 # from the given source files with the given compiler flags.
 function(cxx_test_with_flags name cxx_flags libs)
   cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN})
-  if (WIN32 OR MINGW)
-    add_test(NAME ${name}
-      COMMAND "powershell" "-Command" "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/RunTest.ps1" "$<TARGET_FILE:${name}>")
-  else()
-    add_test(NAME ${name}
-      COMMAND "$<TARGET_FILE:${name}>")
-  endif()
+    add_test(NAME ${name} COMMAND "$<TARGET_FILE:${name}>")
 endfunction()
 
 # cxx_test(name libs srcs...)
@@ -278,45 +285,24 @@
         # Multi-configuration build generators as for Visual Studio save
         # output in a subdirectory of CMAKE_CURRENT_BINARY_DIR (Debug,
         # Release etc.), so we have to provide it here.
-        if (WIN32 OR MINGW)
-          add_test(NAME ${name}
-            COMMAND powershell -Command ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/RunTest.ps1
-              ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
+        add_test(NAME ${name}
+          COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
               --build_dir=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG> ${ARGN})
-        else()
-          add_test(NAME ${name}
-            COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
-              --build_dir=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG> ${ARGN})
-        endif()
       else (CMAKE_CONFIGURATION_TYPES)
         # Single-configuration build generators like Makefile generators
         # don't have subdirs below CMAKE_CURRENT_BINARY_DIR.
-        if (WIN32 OR MINGW)
-          add_test(NAME ${name}
-            COMMAND powershell -Command ${CMAKE_CURRENT_BINARY_DIR}/RunTest.ps1
-              ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
-              --build_dir=${CMAKE_CURRENT_BINARY_DIR} ${ARGN})
-        else()
-          add_test(NAME ${name}
-            COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
-              --build_dir=${CMAKE_CURRENT_BINARY_DIR} ${ARGN})
-        endif()
+        add_test(NAME ${name}
+          COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
+            --build_dir=${CMAKE_CURRENT_BINARY_DIR} ${ARGN})
       endif (CMAKE_CONFIGURATION_TYPES)
     else()
       # ${CMAKE_CURRENT_BINARY_DIR} is known at configuration time, so we can
       # directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known
       # only at ctest runtime (by calling ctest -c <Configuration>), so
       # we have to escape $ to delay variable substitution here.
-      if (WIN32 OR MINGW)
-        add_test(NAME ${name}
-          COMMAND powershell -Command ${CMAKE_CURRENT_BINARY_DIR}/RunTest.ps1
-            ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
-            --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE} ${ARGN})
-      else()
-        add_test(NAME ${name}
-          COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
-            --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE} ${ARGN})
-      endif()
+      add_test(NAME ${name}
+        COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
+          --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE} ${ARGN})
     endif()
   endif(PYTHONINTERP_FOUND)
 endfunction()
diff --git a/ext/googletest/googletest/docs/README.md b/ext/googletest/googletest/docs/README.md
new file mode 100644
index 0000000..1bc57b7
--- /dev/null
+++ b/ext/googletest/googletest/docs/README.md
@@ -0,0 +1,4 @@
+# Content Moved
+
+We are working on updates to the GoogleTest documentation, which has moved to
+the top-level [docs](../../docs) directory.
diff --git a/ext/googletest/googletest/docs/pkgconfig.md b/ext/googletest/googletest/docs/pkgconfig.md
deleted file mode 100644
index 6dc0673..0000000
--- a/ext/googletest/googletest/docs/pkgconfig.md
+++ /dev/null
@@ -1,141 +0,0 @@
-## Using GoogleTest from various build systems
-
-GoogleTest comes with pkg-config files that can be used to determine all
-necessary flags for compiling and linking to GoogleTest (and GoogleMock).
-Pkg-config is a standardised plain-text format containing
-
-*   the includedir (-I) path
-*   necessary macro (-D) definitions
-*   further required flags (-pthread)
-*   the library (-L) path
-*   the library (-l) to link to
-
-All current build systems support pkg-config in one way or another. For all
-examples here we assume you want to compile the sample
-`samples/sample3_unittest.cc`.
-
-### CMake
-
-Using `pkg-config` in CMake is fairly easy:
-
-```cmake
-cmake_minimum_required(VERSION 3.0)
-
-cmake_policy(SET CMP0048 NEW)
-project(my_gtest_pkgconfig VERSION 0.0.1 LANGUAGES CXX)
-
-find_package(PkgConfig)
-pkg_search_module(GTEST REQUIRED gtest_main)
-
-add_executable(testapp samples/sample3_unittest.cc)
-target_link_libraries(testapp ${GTEST_LDFLAGS})
-target_compile_options(testapp PUBLIC ${GTEST_CFLAGS})
-
-include(CTest)
-add_test(first_and_only_test testapp)
-```
-
-It is generally recommended that you use `target_compile_options` + `_CFLAGS`
-over `target_include_directories` + `_INCLUDE_DIRS` as the former includes not
-just -I flags (GoogleTest might require a macro indicating to internal headers
-that all libraries have been compiled with threading enabled. In addition,
-GoogleTest might also require `-pthread` in the compiling step, and as such
-splitting the pkg-config `Cflags` variable into include dirs and macros for
-`target_compile_definitions()` might still miss this). The same recommendation
-goes for using `_LDFLAGS` over the more commonplace `_LIBRARIES`, which happens
-to discard `-L` flags and `-pthread`.
-
-### Autotools
-
-Finding GoogleTest in Autoconf and using it from Automake is also fairly easy:
-
-In your `configure.ac`:
-
-```
-AC_PREREQ([2.69])
-AC_INIT([my_gtest_pkgconfig], [0.0.1])
-AC_CONFIG_SRCDIR([samples/sample3_unittest.cc])
-AC_PROG_CXX
-
-PKG_CHECK_MODULES([GTEST], [gtest_main])
-
-AM_INIT_AUTOMAKE([foreign subdir-objects])
-AC_CONFIG_FILES([Makefile])
-AC_OUTPUT
-```
-
-and in your `Makefile.am`:
-
-```
-check_PROGRAMS = testapp
-TESTS = $(check_PROGRAMS)
-
-testapp_SOURCES = samples/sample3_unittest.cc
-testapp_CXXFLAGS = $(GTEST_CFLAGS)
-testapp_LDADD = $(GTEST_LIBS)
-```
-
-### Meson
-
-Meson natively uses pkgconfig to query dependencies:
-
-```
-project('my_gtest_pkgconfig', 'cpp', version : '0.0.1')
-
-gtest_dep = dependency('gtest_main')
-
-testapp = executable(
-  'testapp',
-  files(['samples/sample3_unittest.cc']),
-  dependencies : gtest_dep,
-  install : false)
-
-test('first_and_only_test', testapp)
-```
-
-### Plain Makefiles
-
-Since `pkg-config` is a small Unix command-line utility, it can be used in
-handwritten `Makefile`s too:
-
-```makefile
-GTEST_CFLAGS = `pkg-config --cflags gtest_main`
-GTEST_LIBS = `pkg-config --libs gtest_main`
-
-.PHONY: tests all
-
-tests: all
-  ./testapp
-
-all: testapp
-
-testapp: testapp.o
-  $(CXX) $(CXXFLAGS) $(LDFLAGS) $< -o $@ $(GTEST_LIBS)
-
-testapp.o: samples/sample3_unittest.cc
-  $(CXX) $(CPPFLAGS) $(CXXFLAGS) $< -c -o $@ $(GTEST_CFLAGS)
-```
-
-### Help! pkg-config can't find GoogleTest!
-
-Let's say you have a `CMakeLists.txt` along the lines of the one in this
-tutorial and you try to run `cmake`. It is very possible that you get a failure
-along the lines of:
-
-```
--- Checking for one of the modules 'gtest_main'
-CMake Error at /usr/share/cmake/Modules/FindPkgConfig.cmake:640 (message):
-  None of the required 'gtest_main' found
-```
-
-These failures are common if you installed GoogleTest yourself and have not
-sourced it from a distro or other package manager. If so, you need to tell
-pkg-config where it can find the `.pc` files containing the information. Say you
-installed GoogleTest to `/usr/local`, then it might be that the `.pc` files are
-installed under `/usr/local/lib64/pkgconfig`. If you set
-
-```
-export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig
-```
-
-pkg-config will also try to look in `PKG_CONFIG_PATH` to find `gtest_main.pc`.
diff --git a/ext/googletest/googletest/docs/pump_manual.md b/ext/googletest/googletest/docs/pump_manual.md
deleted file mode 100644
index 10b3c5f..0000000
--- a/ext/googletest/googletest/docs/pump_manual.md
+++ /dev/null
@@ -1,190 +0,0 @@
-<b>P</b>ump is <b>U</b>seful for <b>M</b>eta <b>P</b>rogramming.
-
-# The Problem
-
-Template and macro libraries often need to define many classes, functions, or
-macros that vary only (or almost only) in the number of arguments they take.
-It's a lot of repetitive, mechanical, and error-prone work.
-
-Variadic templates and variadic macros can alleviate the problem. However, while
-both are being considered by the C++ committee, neither is in the standard yet
-or widely supported by compilers. Thus they are often not a good choice,
-especially when your code needs to be portable. And their capabilities are still
-limited.
-
-As a result, authors of such libraries often have to write scripts to generate
-their implementation. However, our experience is that it's tedious to write such
-scripts, which tend to reflect the structure of the generated code poorly and
-are often hard to read and edit. For example, a small change needed in the
-generated code may require some non-intuitive, non-trivial changes in the
-script. This is especially painful when experimenting with the code.
-
-# Our Solution
-
-Pump (for Pump is Useful for Meta Programming, Pretty Useful for Meta
-Programming, or Practical Utility for Meta Programming, whichever you prefer) is
-a simple meta-programming tool for C++. The idea is that a programmer writes a
-`foo.pump` file which contains C++ code plus meta code that manipulates the C++
-code. The meta code can handle iterations over a range, nested iterations, local
-meta variable definitions, simple arithmetic, and conditional expressions. You
-can view it as a small Domain-Specific Language. The meta language is designed
-to be non-intrusive (s.t. it won't confuse Emacs' C++ mode, for example) and
-concise, making Pump code intuitive and easy to maintain.
-
-## Highlights
-
-*   The implementation is in a single Python script and thus ultra portable: no
-    build or installation is needed and it works cross platforms.
-*   Pump tries to be smart with respect to
-    [Google's style guide](https://github.com/google/styleguide): it breaks long
-    lines (easy to have when they are generated) at acceptable places to fit
-    within 80 columns and indent the continuation lines correctly.
-*   The format is human-readable and more concise than XML.
-*   The format works relatively well with Emacs' C++ mode.
-
-## Examples
-
-The following Pump code (where meta keywords start with `$`, `[[` and `]]` are
-meta brackets, and `$$` starts a meta comment that ends with the line):
-
-```
-$var n = 3     $$ Defines a meta variable n.
-$range i 0..n  $$ Declares the range of meta iterator i (inclusive).
-$for i [[
-               $$ Meta loop.
-// Foo$i does blah for $i-ary predicates.
-$range j 1..i
-template <size_t N $for j [[, typename A$j]]>
-class Foo$i {
-$if i == 0 [[
-  blah a;
-]] $elif i <= 2 [[
-  blah b;
-]] $else [[
-  blah c;
-]]
-};
-
-]]
-```
-
-will be translated by the Pump compiler to:
-
-```cpp
-// Foo0 does blah for 0-ary predicates.
-template <size_t N>
-class Foo0 {
-  blah a;
-};
-
-// Foo1 does blah for 1-ary predicates.
-template <size_t N, typename A1>
-class Foo1 {
-  blah b;
-};
-
-// Foo2 does blah for 2-ary predicates.
-template <size_t N, typename A1, typename A2>
-class Foo2 {
-  blah b;
-};
-
-// Foo3 does blah for 3-ary predicates.
-template <size_t N, typename A1, typename A2, typename A3>
-class Foo3 {
-  blah c;
-};
-```
-
-In another example,
-
-```
-$range i 1..n
-Func($for i + [[a$i]]);
-$$ The text between i and [[ is the separator between iterations.
-```
-
-will generate one of the following lines (without the comments), depending on
-the value of `n`:
-
-```cpp
-Func();              // If n is 0.
-Func(a1);            // If n is 1.
-Func(a1 + a2);       // If n is 2.
-Func(a1 + a2 + a3);  // If n is 3.
-// And so on...
-```
-
-## Constructs
-
-We support the following meta programming constructs:
-
-| `$var id = exp`                  | Defines a named constant value. `$id` is |
-:                                  : valid util the end of the current meta   :
-:                                  : lexical block.                           :
-| :------------------------------- | :--------------------------------------- |
-| `$range id exp..exp`             | Sets the range of an iteration variable, |
-:                                  : which can be reused in multiple loops    :
-:                                  : later.                                   :
-| `$for id sep [[ code ]]`         | Iteration. The range of `id` must have   |
-:                                  : been defined earlier. `$id` is valid in  :
-:                                  : `code`.                                  :
-| `$($)`                           | Generates a single `$` character.        |
-| `$id`                            | Value of the named constant or iteration |
-:                                  : variable.                                :
-| `$(exp)`                         | Value of the expression.                 |
-| `$if exp [[ code ]] else_branch` | Conditional.                             |
-| `[[ code ]]`                     | Meta lexical block.                      |
-| `cpp_code`                       | Raw C++ code.                            |
-| `$$ comment`                     | Meta comment.                            |
-
-**Note:** To give the user some freedom in formatting the Pump source code, Pump
-ignores a new-line character if it's right after `$for foo` or next to `[[` or
-`]]`. Without this rule you'll often be forced to write very long lines to get
-the desired output. Therefore sometimes you may need to insert an extra new-line
-in such places for a new-line to show up in your output.
-
-## Grammar
-
-```ebnf
-code ::= atomic_code*
-atomic_code ::= $var id = exp
-    | $var id = [[ code ]]
-    | $range id exp..exp
-    | $for id sep [[ code ]]
-    | $($)
-    | $id
-    | $(exp)
-    | $if exp [[ code ]] else_branch
-    | [[ code ]]
-    | cpp_code
-sep ::= cpp_code | empty_string
-else_branch ::= $else [[ code ]]
-    | $elif exp [[ code ]] else_branch
-    | empty_string
-exp ::= simple_expression_in_Python_syntax
-```
-
-## Code
-
-You can find the source code of Pump in [scripts/pump.py](../scripts/pump.py).
-It is still very unpolished and lacks automated tests, although it has been
-successfully used many times. If you find a chance to use it in your project,
-please let us know what you think! We also welcome help on improving Pump.
-
-## Real Examples
-
-You can find real-world applications of Pump in
-[Google Test](https://github.com/google/googletest/tree/master/googletest) and
-[Google Mock](https://github.com/google/googletest/tree/master/googlemock). The
-source file `foo.h.pump` generates `foo.h`.
-
-## Tips
-
-*   If a meta variable is followed by a letter or digit, you can separate them
-    using `[[]]`, which inserts an empty string. For example `Foo$j[[]]Helper`
-    generate `Foo1Helper` when `j` is 1.
-*   To avoid extra-long Pump source lines, you can break a line anywhere you
-    want by inserting `[[]]` followed by a new line. Since any new-line
-    character next to `[[` or `]]` is ignored, the generated code won't contain
-    this new line.
diff --git a/ext/googletest/googletest/include/gtest/gtest-death-test.h b/ext/googletest/googletest/include/gtest/gtest-death-test.h
index dc878ff..4df53d9 100644
--- a/ext/googletest/googletest/include/gtest/gtest-death-test.h
+++ b/ext/googletest/googletest/include/gtest/gtest-death-test.h
@@ -35,13 +35,11 @@
 // directly.
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
-#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
 
 #include "gtest/internal/gtest-death-test-internal.h"
 
-namespace testing {
-
 // This flag controls the style of death tests.  Valid values are "threadsafe",
 // meaning that the death test child process will re-execute the test binary
 // from the start, running only a single death test, or "fast",
@@ -49,6 +47,8 @@
 // after forking.
 GTEST_DECLARE_string_(death_test_style);
 
+namespace testing {
+
 #if GTEST_HAS_DEATH_TEST
 
 namespace internal {
@@ -97,6 +97,10 @@
 //
 //   ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
 //
+// The final parameter to each of these macros is a matcher applied to any data
+// the sub-process wrote to stderr.  For compatibility with existing tests, a
+// bare string is interpreted as a regular expression matcher.
+//
 // On the regular expressions used in death tests:
 //
 //   GOOGLETEST_CM0005 DO NOT DELETE
@@ -162,27 +166,27 @@
 //   directory in PATH.
 //
 
-// Asserts that a given statement causes the program to exit, with an
-// integer exit status that satisfies predicate, and emitting error output
-// that matches regex.
-# define ASSERT_EXIT(statement, predicate, regex) \
-    GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_)
+// Asserts that a given `statement` causes the program to exit, with an
+// integer exit status that satisfies `predicate`, and emitting error output
+// that matches `matcher`.
+# define ASSERT_EXIT(statement, predicate, matcher) \
+    GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_)
 
-// Like ASSERT_EXIT, but continues on to successive tests in the
+// Like `ASSERT_EXIT`, but continues on to successive tests in the
 // test suite, if any:
-# define EXPECT_EXIT(statement, predicate, regex) \
-    GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_)
+# define EXPECT_EXIT(statement, predicate, matcher) \
+    GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_)
 
-// Asserts that a given statement causes the program to exit, either by
+// Asserts that a given `statement` causes the program to exit, either by
 // explicitly exiting with a nonzero exit code or being killed by a
-// signal, and emitting error output that matches regex.
-# define ASSERT_DEATH(statement, regex) \
-    ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+// signal, and emitting error output that matches `matcher`.
+# define ASSERT_DEATH(statement, matcher) \
+    ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
 
-// Like ASSERT_DEATH, but continues on to successive tests in the
+// Like `ASSERT_DEATH`, but continues on to successive tests in the
 // test suite, if any:
-# define EXPECT_DEATH(statement, regex) \
-    EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+# define EXPECT_DEATH(statement, matcher) \
+    EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
 
 // Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
 
@@ -190,11 +194,10 @@
 class GTEST_API_ ExitedWithCode {
  public:
   explicit ExitedWithCode(int exit_code);
+  ExitedWithCode(const ExitedWithCode&) = default;
+  void operator=(const ExitedWithCode& other) = delete;
   bool operator()(int exit_status) const;
  private:
-  // No implementation - assignment is unsupported.
-  void operator=(const ExitedWithCode& other);
-
   const int exit_code_;
 };
 
@@ -340,4 +343,4 @@
 
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest-matchers.h b/ext/googletest/googletest/include/gtest/gtest-matchers.h
index 9de6c2e..9fa34a0 100644
--- a/ext/googletest/googletest/include/gtest/gtest-matchers.h
+++ b/ext/googletest/googletest/include/gtest/gtest-matchers.h
@@ -32,13 +32,10 @@
 // This file implements just enough of the matcher interface to allow
 // EXPECT_DEATH and friends to accept a matcher argument.
 
-// IWYU pragma: private, include "testing/base/public/gunit.h"
-// IWYU pragma: friend third_party/googletest/googlemock/.*
-// IWYU pragma: friend third_party/googletest/googletest/.*
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
-#define GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
-
+#include <atomic>
 #include <memory>
 #include <ostream>
 #include <string>
@@ -63,20 +60,16 @@
 namespace testing {
 
 // To implement a matcher Foo for type T, define:
-//   1. a class FooMatcherImpl that implements the
-//      MatcherInterface<T> interface, and
+//   1. a class FooMatcherMatcher that implements the matcher interface:
+//     using is_gtest_matcher = void;
+//     bool MatchAndExplain(const T&, std::ostream*);
+//       (MatchResultListener* can also be used instead of std::ostream*)
+//     void DescribeTo(std::ostream*);
+//     void DescribeNegationTo(std::ostream*);
+//
 //   2. a factory function that creates a Matcher<T> object from a
-//      FooMatcherImpl*.
-//
-// The two-level delegation design makes it possible to allow a user
-// to write "v" instead of "Eq(v)" where a Matcher is expected, which
-// is impossible if we pass matchers by pointers.  It also eases
-// ownership management as Matcher objects can now be copied like
-// plain values.
+//      FooMatcherMatcher.
 
-// MatchResultListener is an abstract class.  Its << operator can be
-// used by a matcher to explain why a value matches or doesn't match.
-//
 class MatchResultListener {
  public:
   // Creates a listener object with the given underlying ostream.  The
@@ -113,7 +106,7 @@
 
 // An instance of a subclass of this knows how to describe itself as a
 // matcher.
-class MatcherDescriberInterface {
+class GTEST_API_ MatcherDescriberInterface {
  public:
   virtual ~MatcherDescriberInterface() {}
 
@@ -181,31 +174,6 @@
 
 namespace internal {
 
-// Converts a MatcherInterface<T> to a MatcherInterface<const T&>.
-template <typename T>
-class MatcherInterfaceAdapter : public MatcherInterface<const T&> {
- public:
-  explicit MatcherInterfaceAdapter(const MatcherInterface<T>* impl)
-      : impl_(impl) {}
-  ~MatcherInterfaceAdapter() override { delete impl_; }
-
-  void DescribeTo(::std::ostream* os) const override { impl_->DescribeTo(os); }
-
-  void DescribeNegationTo(::std::ostream* os) const override {
-    impl_->DescribeNegationTo(os);
-  }
-
-  bool MatchAndExplain(const T& x,
-                       MatchResultListener* listener) const override {
-    return impl_->MatchAndExplain(x, listener);
-  }
-
- private:
-  const MatcherInterface<T>* const impl_;
-
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(MatcherInterfaceAdapter);
-};
-
 struct AnyEq {
   template <typename A, typename B>
   bool operator()(const A& a, const B& b) const { return a == b; }
@@ -252,16 +220,35 @@
   GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener);
 };
 
+struct SharedPayloadBase {
+  std::atomic<int> ref{1};
+  void Ref() { ref.fetch_add(1, std::memory_order_relaxed); }
+  bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; }
+};
+
+template <typename T>
+struct SharedPayload : SharedPayloadBase {
+  explicit SharedPayload(const T& v) : value(v) {}
+  explicit SharedPayload(T&& v) : value(std::move(v)) {}
+
+  static void Destroy(SharedPayloadBase* shared) {
+    delete static_cast<SharedPayload*>(shared);
+  }
+
+  T value;
+};
+
 // An internal class for implementing Matcher<T>, which will derive
 // from it.  We put functionalities common to all Matcher<T>
 // specializations here to avoid code duplication.
 template <typename T>
-class MatcherBase {
+class MatcherBase : private MatcherDescriberInterface {
  public:
   // Returns true if and only if the matcher matches x; also explains the
   // match result to 'listener'.
   bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
-    return impl_->MatchAndExplain(x, listener);
+    GTEST_CHECK_(vtable_ != nullptr);
+    return vtable_->match_and_explain(*this, x, listener);
   }
 
   // Returns true if and only if this matcher matches x.
@@ -271,11 +258,15 @@
   }
 
   // Describes this matcher to an ostream.
-  void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
+  void DescribeTo(::std::ostream* os) const final {
+    GTEST_CHECK_(vtable_ != nullptr);
+    vtable_->describe(*this, os, false);
+  }
 
   // Describes the negation of this matcher to an ostream.
-  void DescribeNegationTo(::std::ostream* os) const {
-    impl_->DescribeNegationTo(os);
+  void DescribeNegationTo(::std::ostream* os) const final {
+    GTEST_CHECK_(vtable_ != nullptr);
+    vtable_->describe(*this, os, true);
   }
 
   // Explains why x matches, or doesn't match, the matcher.
@@ -288,31 +279,194 @@
   // of the describer, which is only guaranteed to be alive when
   // this matcher object is alive.
   const MatcherDescriberInterface* GetDescriber() const {
-    return impl_.get();
+    if (vtable_ == nullptr) return nullptr;
+    return vtable_->get_describer(*this);
   }
 
  protected:
-  MatcherBase() {}
+  MatcherBase() : vtable_(nullptr) {}
 
   // Constructs a matcher from its implementation.
-  explicit MatcherBase(const MatcherInterface<const T&>* impl) : impl_(impl) {}
-
   template <typename U>
-  explicit MatcherBase(
-      const MatcherInterface<U>* impl,
-      typename std::enable_if<!std::is_same<U, const U&>::value>::type* =
-          nullptr)
-      : impl_(new internal::MatcherInterfaceAdapter<U>(impl)) {}
+  explicit MatcherBase(const MatcherInterface<U>* impl) {
+    Init(impl);
+  }
 
-  MatcherBase(const MatcherBase&) = default;
-  MatcherBase& operator=(const MatcherBase&) = default;
-  MatcherBase(MatcherBase&&) = default;
-  MatcherBase& operator=(MatcherBase&&) = default;
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  MatcherBase(M&& m) {  // NOLINT
+    Init(std::forward<M>(m));
+  }
 
-  virtual ~MatcherBase() {}
+  MatcherBase(const MatcherBase& other)
+      : vtable_(other.vtable_), buffer_(other.buffer_) {
+    if (IsShared()) buffer_.shared->Ref();
+  }
+
+  MatcherBase& operator=(const MatcherBase& other) {
+    if (this == &other) return *this;
+    Destroy();
+    vtable_ = other.vtable_;
+    buffer_ = other.buffer_;
+    if (IsShared()) buffer_.shared->Ref();
+    return *this;
+  }
+
+  MatcherBase(MatcherBase&& other)
+      : vtable_(other.vtable_), buffer_(other.buffer_) {
+    other.vtable_ = nullptr;
+  }
+
+  MatcherBase& operator=(MatcherBase&& other) {
+    if (this == &other) return *this;
+    Destroy();
+    vtable_ = other.vtable_;
+    buffer_ = other.buffer_;
+    other.vtable_ = nullptr;
+    return *this;
+  }
+
+  ~MatcherBase() override { Destroy(); }
 
  private:
-  std::shared_ptr<const MatcherInterface<const T&>> impl_;
+  struct VTable {
+    bool (*match_and_explain)(const MatcherBase&, const T&,
+                              MatchResultListener*);
+    void (*describe)(const MatcherBase&, std::ostream*, bool negation);
+    // Returns the captured object if it implements the interface, otherwise
+    // returns the MatcherBase itself.
+    const MatcherDescriberInterface* (*get_describer)(const MatcherBase&);
+    // Called on shared instances when the reference count reaches 0.
+    void (*shared_destroy)(SharedPayloadBase*);
+  };
+
+  bool IsShared() const {
+    return vtable_ != nullptr && vtable_->shared_destroy != nullptr;
+  }
+
+  // If the implementation uses a listener, call that.
+  template <typename P>
+  static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
+                                  MatchResultListener* listener)
+      -> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) {
+    return P::Get(m).MatchAndExplain(value, listener->stream());
+  }
+
+  template <typename P>
+  static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
+                                  MatchResultListener* listener)
+      -> decltype(P::Get(m).MatchAndExplain(value, listener)) {
+    return P::Get(m).MatchAndExplain(value, listener);
+  }
+
+  template <typename P>
+  static void DescribeImpl(const MatcherBase& m, std::ostream* os,
+                           bool negation) {
+    if (negation) {
+      P::Get(m).DescribeNegationTo(os);
+    } else {
+      P::Get(m).DescribeTo(os);
+    }
+  }
+
+  template <typename P>
+  static const MatcherDescriberInterface* GetDescriberImpl(
+      const MatcherBase& m) {
+    // If the impl is a MatcherDescriberInterface, then return it.
+    // Otherwise use MatcherBase itself.
+    // This allows us to implement the GetDescriber() function without support
+    // from the impl, but some users really want to get their impl back when
+    // they call GetDescriber().
+    // We use std::get on a tuple as a workaround of not having `if constexpr`.
+    return std::get<(
+        std::is_convertible<decltype(&P::Get(m)),
+                            const MatcherDescriberInterface*>::value
+            ? 1
+            : 0)>(std::make_tuple(&m, &P::Get(m)));
+  }
+
+  template <typename P>
+  const VTable* GetVTable() {
+    static constexpr VTable kVTable = {&MatchAndExplainImpl<P>,
+                                       &DescribeImpl<P>, &GetDescriberImpl<P>,
+                                       P::shared_destroy};
+    return &kVTable;
+  }
+
+  union Buffer {
+    // Add some types to give Buffer some common alignment/size use cases.
+    void* ptr;
+    double d;
+    int64_t i;
+    // And add one for the out-of-line cases.
+    SharedPayloadBase* shared;
+  };
+
+  void Destroy() {
+    if (IsShared() && buffer_.shared->Unref()) {
+      vtable_->shared_destroy(buffer_.shared);
+    }
+  }
+
+  template <typename M>
+  static constexpr bool IsInlined() {
+    return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) &&
+           std::is_trivially_copy_constructible<M>::value &&
+           std::is_trivially_destructible<M>::value;
+  }
+
+  template <typename M, bool = MatcherBase::IsInlined<M>()>
+  struct ValuePolicy {
+    static const M& Get(const MatcherBase& m) {
+      // When inlined along with Init, need to be explicit to avoid violating
+      // strict aliasing rules.
+      const M *ptr = static_cast<const M*>(
+          static_cast<const void*>(&m.buffer_));
+      return *ptr;
+    }
+    static void Init(MatcherBase& m, M impl) {
+      ::new (static_cast<void*>(&m.buffer_)) M(impl);
+    }
+    static constexpr auto shared_destroy = nullptr;
+  };
+
+  template <typename M>
+  struct ValuePolicy<M, false> {
+    using Shared = SharedPayload<M>;
+    static const M& Get(const MatcherBase& m) {
+      return static_cast<Shared*>(m.buffer_.shared)->value;
+    }
+    template <typename Arg>
+    static void Init(MatcherBase& m, Arg&& arg) {
+      m.buffer_.shared = new Shared(std::forward<Arg>(arg));
+    }
+    static constexpr auto shared_destroy = &Shared::Destroy;
+  };
+
+  template <typename U, bool B>
+  struct ValuePolicy<const MatcherInterface<U>*, B> {
+    using M = const MatcherInterface<U>;
+    using Shared = SharedPayload<std::unique_ptr<M>>;
+    static const M& Get(const MatcherBase& m) {
+      return *static_cast<Shared*>(m.buffer_.shared)->value;
+    }
+    static void Init(MatcherBase& m, M* impl) {
+      m.buffer_.shared = new Shared(std::unique_ptr<M>(impl));
+    }
+
+    static constexpr auto shared_destroy = &Shared::Destroy;
+  };
+
+  template <typename M>
+  void Init(M&& m) {
+    using MM = typename std::decay<M>::type;
+    using Policy = ValuePolicy<MM>;
+    vtable_ = GetVTable<Policy>();
+    Policy::Init(*this, std::forward<M>(m));
+  }
+
+  const VTable* vtable_;
+  Buffer buffer_;
 };
 
 }  // namespace internal
@@ -340,6 +494,10 @@
           nullptr)
       : internal::MatcherBase<T>(impl) {}
 
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m) : internal::MatcherBase<T>(std::forward<M>(m)) {}  // NOLINT
+
   // Implicit constructor here allows people to write
   // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
   Matcher(T value);  // NOLINT
@@ -357,6 +515,11 @@
   explicit Matcher(const MatcherInterface<const std::string&>* impl)
       : internal::MatcherBase<const std::string&>(impl) {}
 
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m)  // NOLINT
+      : internal::MatcherBase<const std::string&>(std::forward<M>(m)) {}
+
   // Allows the user to write str instead of Eq(str) sometimes, where
   // str is a std::string object.
   Matcher(const std::string& s);  // NOLINT
@@ -376,6 +539,11 @@
   explicit Matcher(const MatcherInterface<std::string>* impl)
       : internal::MatcherBase<std::string>(impl) {}
 
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m)  // NOLINT
+      : internal::MatcherBase<std::string>(std::forward<M>(m)) {}
+
   // Allows the user to write str instead of Eq(str) sometimes, where
   // str is a string object.
   Matcher(const std::string& s);  // NOLINT
@@ -384,18 +552,24 @@
   Matcher(const char* s);  // NOLINT
 };
 
-#if GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_STRING_VIEW
 // The following two specializations allow the user to write str
 // instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view
 // matcher is expected.
 template <>
-class GTEST_API_ Matcher<const absl::string_view&>
-    : public internal::MatcherBase<const absl::string_view&> {
+class GTEST_API_ Matcher<const internal::StringView&>
+    : public internal::MatcherBase<const internal::StringView&> {
  public:
   Matcher() {}
 
-  explicit Matcher(const MatcherInterface<const absl::string_view&>* impl)
-      : internal::MatcherBase<const absl::string_view&>(impl) {}
+  explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
+      : internal::MatcherBase<const internal::StringView&>(impl) {}
+
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m)  // NOLINT
+      : internal::MatcherBase<const internal::StringView&>(std::forward<M>(m)) {
+  }
 
   // Allows the user to write str instead of Eq(str) sometimes, where
   // str is a std::string object.
@@ -404,20 +578,25 @@
   // Allows the user to write "foo" instead of Eq("foo") sometimes.
   Matcher(const char* s);  // NOLINT
 
-  // Allows the user to pass absl::string_views directly.
-  Matcher(absl::string_view s);  // NOLINT
+  // Allows the user to pass absl::string_views or std::string_views directly.
+  Matcher(internal::StringView s);  // NOLINT
 };
 
 template <>
-class GTEST_API_ Matcher<absl::string_view>
-    : public internal::MatcherBase<absl::string_view> {
+class GTEST_API_ Matcher<internal::StringView>
+    : public internal::MatcherBase<internal::StringView> {
  public:
   Matcher() {}
 
-  explicit Matcher(const MatcherInterface<const absl::string_view&>* impl)
-      : internal::MatcherBase<absl::string_view>(impl) {}
-  explicit Matcher(const MatcherInterface<absl::string_view>* impl)
-      : internal::MatcherBase<absl::string_view>(impl) {}
+  explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
+      : internal::MatcherBase<internal::StringView>(impl) {}
+  explicit Matcher(const MatcherInterface<internal::StringView>* impl)
+      : internal::MatcherBase<internal::StringView>(impl) {}
+
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m)  // NOLINT
+      : internal::MatcherBase<internal::StringView>(std::forward<M>(m)) {}
 
   // Allows the user to write str instead of Eq(str) sometimes, where
   // str is a std::string object.
@@ -426,10 +605,10 @@
   // Allows the user to write "foo" instead of Eq("foo") sometimes.
   Matcher(const char* s);  // NOLINT
 
-  // Allows the user to pass absl::string_views directly.
-  Matcher(absl::string_view s);  // NOLINT
+  // Allows the user to pass absl::string_views or std::string_views directly.
+  Matcher(internal::StringView s);  // NOLINT
 };
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
 // Prints a matcher in a human-readable format.
 template <typename T>
@@ -474,13 +653,13 @@
    public:
     explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
 
-    virtual void DescribeTo(::std::ostream* os) const { impl_.DescribeTo(os); }
+    void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); }
 
-    virtual void DescribeNegationTo(::std::ostream* os) const {
+    void DescribeNegationTo(::std::ostream* os) const override {
       impl_.DescribeNegationTo(os);
     }
 
-    virtual bool MatchAndExplain(T x, MatchResultListener* listener) const {
+    bool MatchAndExplain(T x, MatchResultListener* listener) const override {
       return impl_.MatchAndExplain(x, listener);
     }
 
@@ -529,37 +708,32 @@
 class ComparisonBase {
  public:
   explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
+
+  using is_gtest_matcher = void;
+
   template <typename Lhs>
-  operator Matcher<Lhs>() const {
-    return Matcher<Lhs>(new Impl<const Lhs&>(rhs_));
+  bool MatchAndExplain(const Lhs& lhs, std::ostream*) const {
+    return Op()(lhs, Unwrap(rhs_));
+  }
+  void DescribeTo(std::ostream* os) const {
+    *os << D::Desc() << " ";
+    UniversalPrint(Unwrap(rhs_), os);
+  }
+  void DescribeNegationTo(std::ostream* os) const {
+    *os << D::NegatedDesc() << " ";
+    UniversalPrint(Unwrap(rhs_), os);
   }
 
  private:
   template <typename T>
-  static const T& Unwrap(const T& v) { return v; }
+  static const T& Unwrap(const T& v) {
+    return v;
+  }
   template <typename T>
-  static const T& Unwrap(std::reference_wrapper<T> v) { return v; }
+  static const T& Unwrap(std::reference_wrapper<T> v) {
+    return v;
+  }
 
-  template <typename Lhs, typename = Rhs>
-  class Impl : public MatcherInterface<Lhs> {
-   public:
-    explicit Impl(const Rhs& rhs) : rhs_(rhs) {}
-    bool MatchAndExplain(Lhs lhs,
-                         MatchResultListener* /* listener */) const override {
-      return Op()(lhs, Unwrap(rhs_));
-    }
-    void DescribeTo(::std::ostream* os) const override {
-      *os << D::Desc() << " ";
-      UniversalPrint(Unwrap(rhs_), os);
-    }
-    void DescribeNegationTo(::std::ostream* os) const override {
-      *os << D::NegatedDesc() <<  " ";
-      UniversalPrint(Unwrap(rhs_), os);
-    }
-
-   private:
-    Rhs rhs_;
-  };
   Rhs rhs_;
 };
 
@@ -612,6 +786,10 @@
   static const char* NegatedDesc() { return "isn't >="; }
 };
 
+template <typename T, typename = typename std::enable_if<
+                          std::is_constructible<std::string, T>::value>::type>
+using StringLike = T;
+
 // Implements polymorphic matchers MatchesRegex(regex) and
 // ContainsRegex(regex), which can be used as a Matcher<T> as long as
 // T can be converted to a string.
@@ -620,12 +798,12 @@
   MatchesRegexMatcher(const RE* regex, bool full_match)
       : regex_(regex), full_match_(full_match) {}
 
-#if GTEST_HAS_ABSL
-  bool MatchAndExplain(const absl::string_view& s,
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
                        MatchResultListener* listener) const {
     return MatchAndExplain(std::string(s), listener);
   }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
   // Accepts pointer types, particularly:
   //   const char*
@@ -672,9 +850,10 @@
     const internal::RE* regex) {
   return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true));
 }
-inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
-    const std::string& regex) {
-  return MatchesRegex(new internal::RE(regex));
+template <typename T = std::string>
+PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
+    const internal::StringLike<T>& regex) {
+  return MatchesRegex(new internal::RE(std::string(regex)));
 }
 
 // Matches a string that contains regular expression 'regex'.
@@ -683,9 +862,10 @@
     const internal::RE* regex) {
   return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false));
 }
-inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
-    const std::string& regex) {
-  return ContainsRegex(new internal::RE(regex));
+template <typename T = std::string>
+PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
+    const internal::StringLike<T>& regex) {
+  return ContainsRegex(new internal::RE(std::string(regex)));
 }
 
 // Creates a polymorphic matcher that matches anything equal to x.
@@ -747,4 +927,4 @@
 
 GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251 5046
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest-message.h b/ext/googletest/googletest/include/gtest/gtest-message.h
index 4a80e11..becfd49 100644
--- a/ext/googletest/googletest/include/gtest/gtest-message.h
+++ b/ext/googletest/googletest/include/gtest/gtest-message.h
@@ -44,11 +44,12 @@
 
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
-#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
 
 #include <limits>
 #include <memory>
+#include <sstream>
 
 #include "gtest/internal/gtest-port.h"
 
@@ -215,4 +216,4 @@
 
 GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest-param-test.h b/ext/googletest/googletest/include/gtest/gtest-param-test.h
index c2e6eae..804e702 100644
--- a/ext/googletest/googletest/include/gtest/gtest-param-test.h
+++ b/ext/googletest/googletest/include/gtest/gtest-param-test.h
@@ -30,12 +30,9 @@
 // Macros and functions for implementing parameterized tests
 // in Google C++ Testing and Mocking Framework (Google Test)
 //
-// This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
-//
 // GOOGLETEST_CM0001 DO NOT DELETE
-#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
-#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
-
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
 
 // Value-parameterized tests allow you to test your code with different
 // parameters without writing multiple copies of the same test.
@@ -371,8 +368,6 @@
 //     std::tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
 //     of elements from sequences produces by gen1, gen2, ..., genN.
 //
-// Combine can have up to 10 arguments.
-//
 // Example:
 //
 // This will instantiate tests in test suite AnimalTest each one with
@@ -416,19 +411,20 @@
       : public test_suite_name {                                               \
    public:                                                                     \
     GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {}                    \
-    virtual void TestBody();                                                   \
+    void TestBody() override;                                                  \
                                                                                \
    private:                                                                    \
     static int AddToRegistry() {                                               \
       ::testing::UnitTest::GetInstance()                                       \
           ->parameterized_test_registry()                                      \
           .GetTestSuitePatternHolder<test_suite_name>(                         \
-              #test_suite_name,                                                \
+              GTEST_STRINGIFY_(test_suite_name),                               \
               ::testing::internal::CodeLocation(__FILE__, __LINE__))           \
           ->AddTestPattern(                                                    \
               GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name),  \
               new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_( \
-                  test_suite_name, test_name)>());                             \
+                  test_suite_name, test_name)>(),                              \
+              ::testing::internal::CodeLocation(__FILE__, __LINE__));          \
       return 0;                                                                \
     }                                                                          \
     static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_;               \
@@ -483,13 +479,21 @@
           ::testing::UnitTest::GetInstance()                                  \
               ->parameterized_test_registry()                                 \
               .GetTestSuitePatternHolder<test_suite_name>(                    \
-                  #test_suite_name,                                           \
+                  GTEST_STRINGIFY_(test_suite_name),                          \
                   ::testing::internal::CodeLocation(__FILE__, __LINE__))      \
               ->AddTestSuiteInstantiation(                                    \
-                  #prefix, &gtest_##prefix##test_suite_name##_EvalGenerator_, \
+                  GTEST_STRINGIFY_(prefix),                                   \
+                  &gtest_##prefix##test_suite_name##_EvalGenerator_,          \
                   &gtest_##prefix##test_suite_name##_EvalGenerateName_,       \
                   __FILE__, __LINE__)
 
+
+// Allow Marking a Parameterized test class as not needing to be instantiated.
+#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T)                   \
+  namespace gtest_do_not_use_outside_namespace_scope {}                   \
+  static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \
+      GTEST_STRINGIFY_(T))
+
 // Legacy API is deprecated but still available
 #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 #define INSTANTIATE_TEST_CASE_P                                            \
@@ -500,4 +504,4 @@
 
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest-printers.h b/ext/googletest/googletest/include/gtest/gtest-printers.h
index 56a0545..8a3431d 100644
--- a/ext/googletest/googletest/include/gtest/gtest-printers.h
+++ b/ext/googletest/googletest/include/gtest/gtest-printers.h
@@ -97,10 +97,11 @@
 
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
-#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
 
 #include <functional>
+#include <memory>
 #include <ostream>  // NOLINT
 #include <sstream>
 #include <string>
@@ -108,64 +109,124 @@
 #include <type_traits>
 #include <utility>
 #include <vector>
+
 #include "gtest/internal/gtest-internal.h"
 #include "gtest/internal/gtest-port.h"
 
-#if GTEST_HAS_ABSL
-#include "absl/strings/string_view.h"
-#include "absl/types/optional.h"
-#include "absl/types/variant.h"
-#endif  // GTEST_HAS_ABSL
-
 namespace testing {
 
-// Definitions in the 'internal' and 'internal2' name spaces are
-// subject to change without notice.  DO NOT USE THEM IN USER CODE!
-namespace internal2 {
+// Definitions in the internal* namespaces are subject to change without notice.
+// DO NOT USE THEM IN USER CODE!
+namespace internal {
 
-// Prints the given number of bytes in the given object to the given
-// ostream.
-GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes,
-                                     size_t count,
-                                     ::std::ostream* os);
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os);
 
-// For selecting which printer to use when a given type has neither <<
-// nor PrintTo().
-enum TypeKind {
-  kProtobuf,              // a protobuf type
-  kConvertibleToInteger,  // a type implicitly convertible to BiggestInt
-                          // (e.g. a named or unnamed enum type)
-#if GTEST_HAS_ABSL
-  kConvertibleToStringView,  // a type implicitly convertible to
-                             // absl::string_view
-#endif
-  kOtherType  // anything else
-};
+// Used to print an STL-style container when the user doesn't define
+// a PrintTo() for it.
+struct ContainerPrinter {
+  template <typename T,
+            typename = typename std::enable_if<
+                (sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) &&
+                !IsRecursiveContainer<T>::value>::type>
+  static void PrintValue(const T& container, std::ostream* os) {
+    const size_t kMaxCount = 32;  // The maximum number of elements to print.
+    *os << '{';
+    size_t count = 0;
+    for (auto&& elem : container) {
+      if (count > 0) {
+        *os << ',';
+        if (count == kMaxCount) {  // Enough has been printed.
+          *os << " ...";
+          break;
+        }
+      }
+      *os << ' ';
+      // We cannot call PrintTo(elem, os) here as PrintTo() doesn't
+      // handle `elem` being a native array.
+      internal::UniversalPrint(elem, os);
+      ++count;
+    }
 
-// TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called
-// by the universal printer to print a value of type T when neither
-// operator<< nor PrintTo() is defined for T, where kTypeKind is the
-// "kind" of T as defined by enum TypeKind.
-template <typename T, TypeKind kTypeKind>
-class TypeWithoutFormatter {
- public:
-  // This default version is called when kTypeKind is kOtherType.
-  static void PrintValue(const T& value, ::std::ostream* os) {
-    PrintBytesInObjectTo(
-        static_cast<const unsigned char*>(
-            reinterpret_cast<const void*>(std::addressof(value))),
-        sizeof(value), os);
+    if (count > 0) {
+      *os << ' ';
+    }
+    *os << '}';
   }
 };
 
-// We print a protobuf using its ShortDebugString() when the string
-// doesn't exceed this many characters; otherwise we print it using
-// DebugString() for better readability.
-const size_t kProtobufOneLinerMaxLength = 50;
+// Used to print a pointer that is neither a char pointer nor a member
+// pointer, when the user doesn't define PrintTo() for it.  (A member
+// variable pointer or member function pointer doesn't really point to
+// a location in the address space.  Their representation is
+// implementation-defined.  Therefore they will be printed as raw
+// bytes.)
+struct FunctionPointerPrinter {
+  template <typename T, typename = typename std::enable_if<
+                            std::is_function<T>::value>::type>
+  static void PrintValue(T* p, ::std::ostream* os) {
+    if (p == nullptr) {
+      *os << "NULL";
+    } else {
+      // T is a function type, so '*os << p' doesn't do what we want
+      // (it just prints p as bool).  We want to print p as a const
+      // void*.
+      *os << reinterpret_cast<const void*>(p);
+    }
+  }
+};
 
-template <typename T>
-class TypeWithoutFormatter<T, kProtobuf> {
- public:
+struct PointerPrinter {
+  template <typename T>
+  static void PrintValue(T* p, ::std::ostream* os) {
+    if (p == nullptr) {
+      *os << "NULL";
+    } else {
+      // T is not a function type.  We just call << to print p,
+      // relying on ADL to pick up user-defined << for their pointer
+      // types, if any.
+      *os << p;
+    }
+  }
+};
+
+namespace internal_stream_operator_without_lexical_name_lookup {
+
+// The presence of an operator<< here will terminate lexical scope lookup
+// straight away (even though it cannot be a match because of its argument
+// types). Thus, the two operator<< calls in StreamPrinter will find only ADL
+// candidates.
+struct LookupBlocker {};
+void operator<<(LookupBlocker, LookupBlocker);
+
+struct StreamPrinter {
+  template <typename T,
+            // Don't accept member pointers here. We'd print them via implicit
+            // conversion to bool, which isn't useful.
+            typename = typename std::enable_if<
+                !std::is_member_pointer<T>::value>::type,
+            // Only accept types for which we can find a streaming operator via
+            // ADL (possibly involving implicit conversions).
+            typename = decltype(std::declval<std::ostream&>()
+                                << std::declval<const T&>())>
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    // Call streaming operator found by ADL, possibly with implicit conversions
+    // of the arguments.
+    *os << value;
+  }
+};
+
+}  // namespace internal_stream_operator_without_lexical_name_lookup
+
+struct ProtobufPrinter {
+  // We print a protobuf using its ShortDebugString() when the string
+  // doesn't exceed this many characters; otherwise we print it using
+  // DebugString() for better readability.
+  static const size_t kProtobufOneLinerMaxLength = 50;
+
+  template <typename T,
+            typename = typename std::enable_if<
+                internal::HasDebugStringAndShortDebugString<T>::value>::type>
   static void PrintValue(const T& value, ::std::ostream* os) {
     std::string pretty_str = value.ShortDebugString();
     if (pretty_str.length() > kProtobufOneLinerMaxLength) {
@@ -175,9 +236,7 @@
   }
 };
 
-template <typename T>
-class TypeWithoutFormatter<T, kConvertibleToInteger> {
- public:
+struct ConvertibleToIntegerPrinter {
   // Since T has no << operator or PrintTo() but can be implicitly
   // converted to BiggestInt, we print it as a BiggestInt.
   //
@@ -185,113 +244,74 @@
   // case printing it as an integer is the desired behavior.  In case
   // T is not an enum, printing it as an integer is the best we can do
   // given that it has no user-defined printer.
-  static void PrintValue(const T& value, ::std::ostream* os) {
-    const internal::BiggestInt kBigInt = value;
-    *os << kBigInt;
+  static void PrintValue(internal::BiggestInt value, ::std::ostream* os) {
+    *os << value;
   }
 };
 
-#if GTEST_HAS_ABSL
-template <typename T>
-class TypeWithoutFormatter<T, kConvertibleToStringView> {
- public:
-  // Since T has neither operator<< nor PrintTo() but can be implicitly
-  // converted to absl::string_view, we print it as a absl::string_view.
-  //
-  // Note: the implementation is further below, as it depends on
-  // internal::PrintTo symbol which is defined later in the file.
-  static void PrintValue(const T& value, ::std::ostream* os);
+struct ConvertibleToStringViewPrinter {
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  static void PrintValue(internal::StringView value, ::std::ostream* os) {
+    internal::UniversalPrint(value, os);
+  }
+#endif
 };
-#endif
 
-// Prints the given value to the given ostream.  If the value is a
-// protocol message, its debug string is printed; if it's an enum or
-// of a type implicitly convertible to BiggestInt, it's printed as an
-// integer; otherwise the bytes in the value are printed.  This is
-// what UniversalPrinter<T>::Print() does when it knows nothing about
-// type T and T has neither << operator nor PrintTo().
-//
-// A user can override this behavior for a class type Foo by defining
-// a << operator in the namespace where Foo is defined.
-//
-// We put this operator in namespace 'internal2' instead of 'internal'
-// to simplify the implementation, as much code in 'internal' needs to
-// use << in STL, which would conflict with our own << were it defined
-// in 'internal'.
-//
-// Note that this operator<< takes a generic std::basic_ostream<Char,
-// CharTraits> type instead of the more restricted std::ostream.  If
-// we define it to take an std::ostream instead, we'll get an
-// "ambiguous overloads" compiler error when trying to print a type
-// Foo that supports streaming to std::basic_ostream<Char,
-// CharTraits>, as the compiler cannot tell whether
-// operator<<(std::ostream&, const T&) or
-// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more
-// specific.
-template <typename Char, typename CharTraits, typename T>
-::std::basic_ostream<Char, CharTraits>& operator<<(
-    ::std::basic_ostream<Char, CharTraits>& os, const T& x) {
-  TypeWithoutFormatter<T, (internal::IsAProtocolMessage<T>::value
-                               ? kProtobuf
-                               : std::is_convertible<
-                                     const T&, internal::BiggestInt>::value
-                                     ? kConvertibleToInteger
-                                     :
-#if GTEST_HAS_ABSL
-                                     std::is_convertible<
-                                         const T&, absl::string_view>::value
-                                         ? kConvertibleToStringView
-                                         :
-#endif
-                                         kOtherType)>::PrintValue(x, &os);
-  return os;
-}
 
-}  // namespace internal2
-}  // namespace testing
+// Prints the given number of bytes in the given object to the given
+// ostream.
+GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes,
+                                     size_t count,
+                                     ::std::ostream* os);
+struct RawBytesPrinter {
+  // SFINAE on `sizeof` to make sure we have a complete type.
+  template <typename T, size_t = sizeof(T)>
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    PrintBytesInObjectTo(
+        static_cast<const unsigned char*>(
+            // Load bearing cast to void* to support iOS
+            reinterpret_cast<const void*>(std::addressof(value))),
+        sizeof(value), os);
+  }
+};
 
-// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up
-// magic needed for implementing UniversalPrinter won't work.
-namespace testing_internal {
+struct FallbackPrinter {
+  template <typename T>
+  static void PrintValue(const T&, ::std::ostream* os) {
+    *os << "(incomplete type)";
+  }
+};
 
-// Used to print a value that is not an STL-style container when the
-// user doesn't define PrintTo() for it.
+// Try every printer in order and return the first one that works.
+template <typename T, typename E, typename Printer, typename... Printers>
+struct FindFirstPrinter : FindFirstPrinter<T, E, Printers...> {};
+
+template <typename T, typename Printer, typename... Printers>
+struct FindFirstPrinter<
+    T, decltype(Printer::PrintValue(std::declval<const T&>(), nullptr)),
+    Printer, Printers...> {
+  using type = Printer;
+};
+
+// Select the best printer in the following order:
+//  - Print containers (they have begin/end/etc).
+//  - Print function pointers.
+//  - Print object pointers.
+//  - Use the stream operator, if available.
+//  - Print protocol buffers.
+//  - Print types convertible to BiggestInt.
+//  - Print types convertible to StringView, if available.
+//  - Fallback to printing the raw bytes of the object.
 template <typename T>
-void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) {
-  // With the following statement, during unqualified name lookup,
-  // testing::internal2::operator<< appears as if it was declared in
-  // the nearest enclosing namespace that contains both
-  // ::testing_internal and ::testing::internal2, i.e. the global
-  // namespace.  For more details, refer to the C++ Standard section
-  // 7.3.4-1 [namespace.udir].  This allows us to fall back onto
-  // testing::internal2::operator<< in case T doesn't come with a <<
-  // operator.
-  //
-  // We cannot write 'using ::testing::internal2::operator<<;', which
-  // gcc 3.3 fails to compile due to a compiler bug.
-  using namespace ::testing::internal2;  // NOLINT
-
-  // Assuming T is defined in namespace foo, in the next statement,
-  // the compiler will consider all of:
-  //
-  //   1. foo::operator<< (thanks to Koenig look-up),
-  //   2. ::operator<< (as the current namespace is enclosed in ::),
-  //   3. testing::internal2::operator<< (thanks to the using statement above).
-  //
-  // The operator<< whose type matches T best will be picked.
-  //
-  // We deliberately allow #2 to be a candidate, as sometimes it's
-  // impossible to define #1 (e.g. when foo is ::std, defining
-  // anything in it is undefined behavior unless you are a compiler
-  // vendor.).
-  *os << value;
+void PrintWithFallback(const T& value, ::std::ostream* os) {
+  using Printer = typename FindFirstPrinter<
+      T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter,
+      internal_stream_operator_without_lexical_name_lookup::StreamPrinter,
+      ProtobufPrinter, ConvertibleToIntegerPrinter,
+      ConvertibleToStringViewPrinter, RawBytesPrinter, FallbackPrinter>::type;
+  Printer::PrintValue(value, os);
 }
 
-}  // namespace testing_internal
-
-namespace testing {
-namespace internal {
-
 // FormatForComparison<ToPrint, OtherOperand>::Format(value) formats a
 // value of type ToPrint that is an operand of a comparison assertion
 // (e.g. ASSERT_EQ).  OtherOperand is the type of the other operand in
@@ -340,6 +360,14 @@
 GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char);
 GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t);
 GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t);
+#ifdef __cpp_lib_char8_t
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char8_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char8_t);
+#endif
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char16_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char16_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char32_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char32_t);
 
 #undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_
 
@@ -357,6 +385,14 @@
 
 GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string);
 GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string);
+#ifdef __cpp_char8_t
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char8_t, ::std::u8string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char8_t, ::std::u8string);
+#endif
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char16_t, ::std::u16string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char16_t, ::std::u16string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char32_t, ::std::u32string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char32_t, ::std::u32string);
 
 #if GTEST_HAS_STD_WSTRING
 GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring);
@@ -389,85 +425,6 @@
 template <typename T>
 class UniversalPrinter;
 
-template <typename T>
-void UniversalPrint(const T& value, ::std::ostream* os);
-
-enum DefaultPrinterType {
-  kPrintContainer,
-  kPrintPointer,
-  kPrintFunctionPointer,
-  kPrintOther,
-};
-template <DefaultPrinterType type> struct WrapPrinterType {};
-
-// Used to print an STL-style container when the user doesn't define
-// a PrintTo() for it.
-template <typename C>
-void DefaultPrintTo(WrapPrinterType<kPrintContainer> /* dummy */,
-                    const C& container, ::std::ostream* os) {
-  const size_t kMaxCount = 32;  // The maximum number of elements to print.
-  *os << '{';
-  size_t count = 0;
-  for (typename C::const_iterator it = container.begin();
-       it != container.end(); ++it, ++count) {
-    if (count > 0) {
-      *os << ',';
-      if (count == kMaxCount) {  // Enough has been printed.
-        *os << " ...";
-        break;
-      }
-    }
-    *os << ' ';
-    // We cannot call PrintTo(*it, os) here as PrintTo() doesn't
-    // handle *it being a native array.
-    internal::UniversalPrint(*it, os);
-  }
-
-  if (count > 0) {
-    *os << ' ';
-  }
-  *os << '}';
-}
-
-// Used to print a pointer that is neither a char pointer nor a member
-// pointer, when the user doesn't define PrintTo() for it.  (A member
-// variable pointer or member function pointer doesn't really point to
-// a location in the address space.  Their representation is
-// implementation-defined.  Therefore they will be printed as raw
-// bytes.)
-template <typename T>
-void DefaultPrintTo(WrapPrinterType<kPrintPointer> /* dummy */,
-                    T* p, ::std::ostream* os) {
-  if (p == nullptr) {
-    *os << "NULL";
-  } else {
-    // T is not a function type.  We just call << to print p,
-    // relying on ADL to pick up user-defined << for their pointer
-    // types, if any.
-    *os << p;
-  }
-}
-template <typename T>
-void DefaultPrintTo(WrapPrinterType<kPrintFunctionPointer> /* dummy */,
-                    T* p, ::std::ostream* os) {
-  if (p == nullptr) {
-    *os << "NULL";
-  } else {
-    // T is a function type, so '*os << p' doesn't do what we want
-    // (it just prints p as bool).  We want to print p as a const
-    // void*.
-    *os << reinterpret_cast<const void*>(p);
-  }
-}
-
-// Used to print a non-container, non-pointer value when the user
-// doesn't define PrintTo() for it.
-template <typename T>
-void DefaultPrintTo(WrapPrinterType<kPrintOther> /* dummy */,
-                    const T& value, ::std::ostream* os) {
-  ::testing_internal::DefaultPrintNonContainerTo(value, os);
-}
-
 // Prints the given value using the << operator if it has one;
 // otherwise prints the bytes in it.  This is what
 // UniversalPrinter<T>::Print() does when PrintTo() is not specialized
@@ -481,36 +438,7 @@
 // wants).
 template <typename T>
 void PrintTo(const T& value, ::std::ostream* os) {
-  // DefaultPrintTo() is overloaded.  The type of its first argument
-  // determines which version will be picked.
-  //
-  // Note that we check for container types here, prior to we check
-  // for protocol message types in our operator<<.  The rationale is:
-  //
-  // For protocol messages, we want to give people a chance to
-  // override Google Mock's format by defining a PrintTo() or
-  // operator<<.  For STL containers, other formats can be
-  // incompatible with Google Mock's format for the container
-  // elements; therefore we check for container types here to ensure
-  // that our format is used.
-  //
-  // Note that MSVC and clang-cl do allow an implicit conversion from
-  // pointer-to-function to pointer-to-object, but clang-cl warns on it.
-  // So don't use ImplicitlyConvertible if it can be helped since it will
-  // cause this warning, and use a separate overload of DefaultPrintTo for
-  // function pointers so that the `*os << p` in the object pointer overload
-  // doesn't cause that warning either.
-  DefaultPrintTo(
-      WrapPrinterType <
-                  (sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) &&
-              !IsRecursiveContainer<T>::value
-          ? kPrintContainer
-          : !std::is_pointer<T>::value
-                ? kPrintOther
-                : std::is_function<typename std::remove_pointer<T>::type>::value
-                      ? kPrintFunctionPointer
-                      : kPrintPointer > (),
-      value, os);
+  internal::PrintWithFallback(value, os);
 }
 
 // The following list of PrintTo() overloads tells
@@ -541,6 +469,16 @@
 // is implemented as an unsigned type.
 GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os);
 
+GTEST_API_ void PrintTo(char32_t c, ::std::ostream* os);
+inline void PrintTo(char16_t c, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<char32_t>(c), os);
+}
+#ifdef __cpp_char8_t
+inline void PrintTo(char8_t c, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<char32_t>(c), os);
+}
+#endif
+
 // Overloads for C strings.
 GTEST_API_ void PrintTo(const char* s, ::std::ostream* os);
 inline void PrintTo(char* s, ::std::ostream* os) {
@@ -561,6 +499,23 @@
 inline void PrintTo(unsigned char* s, ::std::ostream* os) {
   PrintTo(ImplicitCast_<const void*>(s), os);
 }
+#ifdef __cpp_char8_t
+// Overloads for u8 strings.
+GTEST_API_ void PrintTo(const char8_t* s, ::std::ostream* os);
+inline void PrintTo(char8_t* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const char8_t*>(s), os);
+}
+#endif
+// Overloads for u16 strings.
+GTEST_API_ void PrintTo(const char16_t* s, ::std::ostream* os);
+inline void PrintTo(char16_t* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const char16_t*>(s), os);
+}
+// Overloads for u32 strings.
+GTEST_API_ void PrintTo(const char32_t* s, ::std::ostream* os);
+inline void PrintTo(char32_t* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const char32_t*>(s), os);
+}
 
 // MSVC can be configured to define wchar_t as a typedef of unsigned
 // short.  It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native
@@ -595,6 +550,26 @@
   PrintStringTo(s, os);
 }
 
+// Overloads for ::std::u8string
+#ifdef __cpp_char8_t
+GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os);
+inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) {
+  PrintU8StringTo(s, os);
+}
+#endif
+
+// Overloads for ::std::u16string
+GTEST_API_ void PrintU16StringTo(const ::std::u16string& s, ::std::ostream* os);
+inline void PrintTo(const ::std::u16string& s, ::std::ostream* os) {
+  PrintU16StringTo(s, os);
+}
+
+// Overloads for ::std::u32string
+GTEST_API_ void PrintU32StringTo(const ::std::u32string& s, ::std::ostream* os);
+inline void PrintTo(const ::std::u32string& s, ::std::ostream* os) {
+  PrintU32StringTo(s, os);
+}
+
 // Overloads for ::std::wstring.
 #if GTEST_HAS_STD_WSTRING
 GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
@@ -603,12 +578,12 @@
 }
 #endif  // GTEST_HAS_STD_WSTRING
 
-#if GTEST_HAS_ABSL
-// Overload for absl::string_view.
-inline void PrintTo(absl::string_view sp, ::std::ostream* os) {
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+// Overload for internal::StringView.
+inline void PrintTo(internal::StringView sp, ::std::ostream* os) {
   PrintTo(::std::string(sp), os);
 }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
 inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; }
 
@@ -617,6 +592,43 @@
   UniversalPrinter<T&>::Print(ref.get(), os);
 }
 
+inline const void* VoidifyPointer(const void* p) { return p; }
+inline const void* VoidifyPointer(volatile const void* p) {
+  return const_cast<const void*>(p);
+}
+
+template <typename T, typename Ptr>
+void PrintSmartPointer(const Ptr& ptr, std::ostream* os, char) {
+  if (ptr == nullptr) {
+    *os << "(nullptr)";
+  } else {
+    // We can't print the value. Just print the pointer..
+    *os << "(" << (VoidifyPointer)(ptr.get()) << ")";
+  }
+}
+template <typename T, typename Ptr,
+          typename = typename std::enable_if<!std::is_void<T>::value &&
+                                             !std::is_array<T>::value>::type>
+void PrintSmartPointer(const Ptr& ptr, std::ostream* os, int) {
+  if (ptr == nullptr) {
+    *os << "(nullptr)";
+  } else {
+    *os << "(ptr = " << (VoidifyPointer)(ptr.get()) << ", value = ";
+    UniversalPrinter<T>::Print(*ptr, os);
+    *os << ")";
+  }
+}
+
+template <typename T, typename D>
+void PrintTo(const std::unique_ptr<T, D>& ptr, std::ostream* os) {
+  (PrintSmartPointer<T>)(ptr, os, 0);
+}
+
+template <typename T>
+void PrintTo(const std::shared_ptr<T>& ptr, std::ostream* os) {
+  (PrintSmartPointer<T>)(ptr, os, 0);
+}
+
 // Helper function for printing a tuple.  T must be instantiated with
 // a tuple type.
 template <typename T>
@@ -682,14 +694,46 @@
   GTEST_DISABLE_MSC_WARNINGS_POP_()
 };
 
-#if GTEST_HAS_ABSL
+// Remove any const-qualifiers before passing a type to UniversalPrinter.
+template <typename T>
+class UniversalPrinter<const T> : public UniversalPrinter<T> {};
 
-// Printer for absl::optional
+#if GTEST_INTERNAL_HAS_ANY
+
+// Printer for std::any / absl::any
+
+template <>
+class UniversalPrinter<Any> {
+ public:
+  static void Print(const Any& value, ::std::ostream* os) {
+    if (value.has_value()) {
+      *os << "value of type " << GetTypeName(value);
+    } else {
+      *os << "no value";
+    }
+  }
+
+ private:
+  static std::string GetTypeName(const Any& value) {
+#if GTEST_HAS_RTTI
+    return internal::GetTypeName(value.type());
+#else
+    static_cast<void>(value);  // possibly unused
+    return "<unknown_type>";
+#endif  // GTEST_HAS_RTTI
+  }
+};
+
+#endif  // GTEST_INTERNAL_HAS_ANY
+
+#if GTEST_INTERNAL_HAS_OPTIONAL
+
+// Printer for std::optional / absl::optional
 
 template <typename T>
-class UniversalPrinter<::absl::optional<T>> {
+class UniversalPrinter<Optional<T>> {
  public:
-  static void Print(const ::absl::optional<T>& value, ::std::ostream* os) {
+  static void Print(const Optional<T>& value, ::std::ostream* os) {
     *os << '(';
     if (!value) {
       *os << "nullopt";
@@ -700,14 +744,22 @@
   }
 };
 
-// Printer for absl::variant
+#endif  // GTEST_INTERNAL_HAS_OPTIONAL
+
+#if GTEST_INTERNAL_HAS_VARIANT
+
+// Printer for std::variant / absl::variant
 
 template <typename... T>
-class UniversalPrinter<::absl::variant<T...>> {
+class UniversalPrinter<Variant<T...>> {
  public:
-  static void Print(const ::absl::variant<T...>& value, ::std::ostream* os) {
+  static void Print(const Variant<T...>& value, ::std::ostream* os) {
     *os << '(';
-    absl::visit(Visitor{os}, value);
+#if GTEST_HAS_ABSL
+    absl::visit(Visitor{os, value.index()}, value);
+#else
+    std::visit(Visitor{os, value.index()}, value);
+#endif  // GTEST_HAS_ABSL
     *os << ')';
   }
 
@@ -715,14 +767,16 @@
   struct Visitor {
     template <typename U>
     void operator()(const U& u) const {
-      *os << "'" << GetTypeName<U>() << "' with value ";
+      *os << "'" << GetTypeName<U>() << "(index = " << index
+          << ")' with value ";
       UniversalPrint(u, os);
     }
     ::std::ostream* os;
+    std::size_t index;
   };
 };
 
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_VARIANT
 
 // UniversalPrintArray(begin, len, os) prints an array of 'len'
 // elements, starting at address 'begin'.
@@ -751,6 +805,20 @@
 GTEST_API_ void UniversalPrintArray(
     const char* begin, size_t len, ::std::ostream* os);
 
+#ifdef __cpp_char8_t
+// This overload prints a (const) char8_t array compactly.
+GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len,
+                                    ::std::ostream* os);
+#endif
+
+// This overload prints a (const) char16_t array compactly.
+GTEST_API_ void UniversalPrintArray(const char16_t* begin, size_t len,
+                                    ::std::ostream* os);
+
+// This overload prints a (const) char32_t array compactly.
+GTEST_API_ void UniversalPrintArray(const char32_t* begin, size_t len,
+                                    ::std::ostream* os);
+
 // This overload prints a (const) wchar_t array compactly.
 GTEST_API_ void UniversalPrintArray(
     const wchar_t* begin, size_t len, ::std::ostream* os);
@@ -823,12 +891,55 @@
   }
 };
 template <>
-class UniversalTersePrinter<char*> {
+class UniversalTersePrinter<char*> : public UniversalTersePrinter<const char*> {
+};
+
+#ifdef __cpp_char8_t
+template <>
+class UniversalTersePrinter<const char8_t*> {
  public:
-  static void Print(char* str, ::std::ostream* os) {
-    UniversalTersePrinter<const char*>::Print(str, os);
+  static void Print(const char8_t* str, ::std::ostream* os) {
+    if (str == nullptr) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(::std::u8string(str), os);
+    }
   }
 };
+template <>
+class UniversalTersePrinter<char8_t*>
+    : public UniversalTersePrinter<const char8_t*> {};
+#endif
+
+template <>
+class UniversalTersePrinter<const char16_t*> {
+ public:
+  static void Print(const char16_t* str, ::std::ostream* os) {
+    if (str == nullptr) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(::std::u16string(str), os);
+    }
+  }
+};
+template <>
+class UniversalTersePrinter<char16_t*>
+    : public UniversalTersePrinter<const char16_t*> {};
+
+template <>
+class UniversalTersePrinter<const char32_t*> {
+ public:
+  static void Print(const char32_t* str, ::std::ostream* os) {
+    if (str == nullptr) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(::std::u32string(str), os);
+    }
+  }
+};
+template <>
+class UniversalTersePrinter<char32_t*>
+    : public UniversalTersePrinter<const char32_t*> {};
 
 #if GTEST_HAS_STD_WSTRING
 template <>
@@ -901,16 +1012,6 @@
 
 }  // namespace internal
 
-#if GTEST_HAS_ABSL
-namespace internal2 {
-template <typename T>
-void TypeWithoutFormatter<T, kConvertibleToStringView>::PrintValue(
-    const T& value, ::std::ostream* os) {
-  internal::PrintTo(absl::string_view(value), os);
-}
-}  // namespace internal2
-#endif
-
 template <typename T>
 ::std::string PrintToString(const T& value) {
   ::std::stringstream ss;
@@ -925,4 +1026,4 @@
 // declarations from this file.
 #include "gtest/internal/custom/gtest-printers.h"
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest-spi.h b/ext/googletest/googletest/include/gtest/gtest-spi.h
index aa38870..eacef44 100644
--- a/ext/googletest/googletest/include/gtest/gtest-spi.h
+++ b/ext/googletest/googletest/include/gtest/gtest-spi.h
@@ -33,8 +33,8 @@
 
 // GOOGLETEST_CM0004 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_
-#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
 
 #include "gtest/gtest.h"
 
@@ -235,4 +235,4 @@
     }\
   } while (::testing::internal::AlwaysFalse())
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_SPI_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest-test-part.h b/ext/googletest/googletest/include/gtest/gtest-test-part.h
index 05a7985..203fdf9 100644
--- a/ext/googletest/googletest/include/gtest/gtest-test-part.h
+++ b/ext/googletest/googletest/include/gtest/gtest-test-part.h
@@ -29,8 +29,8 @@
 //
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
-#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
 
 #include <iosfwd>
 #include <vector>
@@ -181,4 +181,4 @@
 
 GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest-typed-test.h b/ext/googletest/googletest/include/gtest/gtest-typed-test.h
index 095ce05..9fdc6be 100644
--- a/ext/googletest/googletest/include/gtest/gtest-typed-test.h
+++ b/ext/googletest/googletest/include/gtest/gtest-typed-test.h
@@ -27,11 +27,10 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
-#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
 
 // This header implements typed tests and type-parameterized tests.
 
@@ -170,13 +169,12 @@
 
 #endif  // 0
 
+#include "gtest/internal/gtest-internal.h"
 #include "gtest/internal/gtest-port.h"
 #include "gtest/internal/gtest-type-util.h"
 
 // Implements typed tests.
 
-#if GTEST_HAS_TYPED_TEST
-
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
 //
 // Expands to the name of the typedef for the type parameters of the
@@ -188,24 +186,25 @@
 #define GTEST_NAME_GENERATOR_(TestSuiteName) \
   gtest_type_params_##TestSuiteName##_NameGenerator
 
-#define TYPED_TEST_SUITE(CaseName, Types, ...)                           \
-  typedef ::testing::internal::TypeList<Types>::type GTEST_TYPE_PARAMS_( \
-      CaseName);                                                         \
-  typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type  \
+#define TYPED_TEST_SUITE(CaseName, Types, ...)                          \
+  typedef ::testing::internal::GenerateTypeList<Types>::type            \
+      GTEST_TYPE_PARAMS_(CaseName);                                     \
+  typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
       GTEST_NAME_GENERATOR_(CaseName)
 
-# define TYPED_TEST(CaseName, TestName)                                       \
+#define TYPED_TEST(CaseName, TestName)                                        \
+  static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1,                       \
+                "test-name must not be empty");                               \
   template <typename gtest_TypeParam_>                                        \
   class GTEST_TEST_CLASS_NAME_(CaseName, TestName)                            \
       : public CaseName<gtest_TypeParam_> {                                   \
    private:                                                                   \
     typedef CaseName<gtest_TypeParam_> TestFixture;                           \
     typedef gtest_TypeParam_ TypeParam;                                       \
-    virtual void TestBody();                                                  \
+    void TestBody() override;                                                 \
   };                                                                          \
   static bool gtest_##CaseName##_##TestName##_registered_                     \
-        GTEST_ATTRIBUTE_UNUSED_ =                                             \
-      ::testing::internal::TypeParameterizedTest<                             \
+      GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest<   \
           CaseName,                                                           \
           ::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName,   \
                                                                   TestName)>, \
@@ -213,7 +212,8 @@
               CaseName)>::Register("",                                        \
                                    ::testing::internal::CodeLocation(         \
                                        __FILE__, __LINE__),                   \
-                                   #CaseName, #TestName, 0,                   \
+                                   GTEST_STRINGIFY_(CaseName),                \
+                                   GTEST_STRINGIFY_(TestName), 0,             \
                                    ::testing::internal::GenerateNames<        \
                                        GTEST_NAME_GENERATOR_(CaseName),       \
                                        GTEST_TYPE_PARAMS_(CaseName)>());      \
@@ -228,12 +228,8 @@
   TYPED_TEST_SUITE
 #endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 
-#endif  // GTEST_HAS_TYPED_TEST
-
 // Implements type-parameterized tests.
 
-#if GTEST_HAS_TYPED_TEST_P
-
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
 //
 // Expands to the namespace name that the type-parameterized tests for
@@ -276,24 +272,26 @@
      private:                                                         \
       typedef SuiteName<gtest_TypeParam_> TestFixture;                \
       typedef gtest_TypeParam_ TypeParam;                             \
-      virtual void TestBody();                                        \
+      void TestBody() override;                                       \
     };                                                                \
     static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
         GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName(       \
-            __FILE__, __LINE__, #SuiteName, #TestName);               \
+            __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName),          \
+            GTEST_STRINGIFY_(TestName));                              \
   }                                                                   \
   template <typename gtest_TypeParam_>                                \
   void GTEST_SUITE_NAMESPACE_(                                        \
       SuiteName)::TestName<gtest_TypeParam_>::TestBody()
 
-#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...)                            \
-  namespace GTEST_SUITE_NAMESPACE_(SuiteName) {                                \
-    typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \
-  }                                                                            \
-  static const char* const GTEST_REGISTERED_TEST_NAMES_(                       \
-      SuiteName) GTEST_ATTRIBUTE_UNUSED_ =                                     \
-      GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames(    \
-          __FILE__, __LINE__, #__VA_ARGS__)
+// Note: this won't work correctly if the trailing arguments are macros.
+#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...)                         \
+  namespace GTEST_SUITE_NAMESPACE_(SuiteName) {                             \
+    typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_;    \
+  }                                                                         \
+  static const char* const GTEST_REGISTERED_TEST_NAMES_(                    \
+      SuiteName) GTEST_ATTRIBUTE_UNUSED_ =                                  \
+      GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \
+          GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__)
 
 // Legacy API is deprecated but still available
 #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
@@ -304,18 +302,21 @@
 #endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 
 #define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...)       \
+  static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1,                       \
+                "test-suit-prefix must not be empty");                      \
   static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ =        \
       ::testing::internal::TypeParameterizedTestSuite<                      \
           SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_,    \
-          ::testing::internal::TypeList<Types>::type>::                     \
-          Register(#Prefix,                                                 \
+          ::testing::internal::GenerateTypeList<Types>::type>::             \
+          Register(GTEST_STRINGIFY_(Prefix),                                \
                    ::testing::internal::CodeLocation(__FILE__, __LINE__),   \
-                   &GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName), #SuiteName, \
+                   &GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName),             \
+                   GTEST_STRINGIFY_(SuiteName),                             \
                    GTEST_REGISTERED_TEST_NAMES_(SuiteName),                 \
                    ::testing::internal::GenerateNames<                      \
                        ::testing::internal::NameGeneratorSelector<          \
                            __VA_ARGS__>::type,                              \
-                       ::testing::internal::TypeList<Types>::type>())
+                       ::testing::internal::GenerateTypeList<Types>::type>())
 
 // Legacy API is deprecated but still available
 #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
@@ -325,6 +326,4 @@
   INSTANTIATE_TYPED_TEST_SUITE_P
 #endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 
-#endif  // GTEST_HAS_TYPED_TEST_P
-
-#endif  // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest.h b/ext/googletest/googletest/include/gtest/gtest.h
index dbe5b1c..482228a 100644
--- a/ext/googletest/googletest/include/gtest/gtest.h
+++ b/ext/googletest/googletest/include/gtest/gtest.h
@@ -49,8 +49,8 @@
 
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
-#define GTEST_INCLUDE_GTEST_GTEST_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_
 
 #include <cstddef>
 #include <limits>
@@ -73,17 +73,6 @@
 GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
 /* class A needs to have dll-interface to be used by clients of class B */)
 
-namespace testing {
-
-// Silence C4100 (unreferenced formal parameter) and 4805
-// unsafe mix of type 'const int' and type 'const bool'
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable:4805)
-# pragma warning(disable:4100)
-#endif
-
-
 // Declares the flags.
 
 // This flag temporary enables the disabled tests.
@@ -101,6 +90,10 @@
 // to let Google Test decide.
 GTEST_DECLARE_string_(color);
 
+// This flag controls whether the test runner should continue execution past
+// first failure.
+GTEST_DECLARE_bool_(fail_fast);
+
 // This flag sets up the filter to select by name using a glob pattern
 // the tests to run. If the filter is not given all tests are executed.
 GTEST_DECLARE_string_(filter);
@@ -117,6 +110,9 @@
 // in addition to its normal textual output.
 GTEST_DECLARE_string_(output);
 
+// This flags control whether Google Test prints only test failures.
+GTEST_DECLARE_bool_(brief);
+
 // This flags control whether Google Test prints the elapsed time for each
 // test.
 GTEST_DECLARE_bool_(print_time);
@@ -131,6 +127,12 @@
 // is 1. If the value is -1 the tests are repeating forever.
 GTEST_DECLARE_int32_(repeat);
 
+// This flag controls whether Google Test Environments are recreated for each
+// repeat of the tests. The default value is true. If set to false the global
+// test Environment objects are only set up once, for the first iteration, and
+// only torn down once, for the last.
+GTEST_DECLARE_bool_(recreate_environments_when_repeating);
+
 // This flag controls whether Google Test includes Google Test internal
 // stack frames in failure stack traces.
 GTEST_DECLARE_bool_(show_internal_stack_frames);
@@ -156,6 +158,16 @@
 GTEST_DECLARE_string_(flagfile);
 #endif  // GTEST_USE_OWN_FLAGFILE_FLAG_
 
+namespace testing {
+
+// Silence C4100 (unreferenced formal parameter) and 4805
+// unsafe mix of type 'const int' and type 'const bool'
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4805)
+#pragma warning(disable : 4100)
+#endif
+
 // The upper limit for valid stack trace depths.
 const int kMaxStackTraceDepth = 100;
 
@@ -177,6 +189,7 @@
 class UnitTestImpl* GetUnitTestImpl();
 void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
                                     const std::string& message);
+std::set<std::string>* GetIgnoredParameterizedTestSuites();
 
 }  // namespace internal
 
@@ -278,7 +291,11 @@
   // Used in EXPECT_TRUE/FALSE(assertion_result).
   AssertionResult(const AssertionResult& other);
 
-#if defined(_MSC_VER) && _MSC_VER < 1910
+// C4800 is a level 3 warning in Visual Studio 2015 and earlier.
+// This warning is not emitted in Visual Studio 2017.
+// This warning is off by default starting in Visual Studio 2019 but can be
+// enabled with command-line options.
+#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920)
   GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */)
 #endif
 
@@ -298,7 +315,7 @@
       = nullptr)
       : success_(success) {}
 
-#if defined(_MSC_VER) && _MSC_VER < 1910
+#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920)
   GTEST_DISABLE_MSC_WARNINGS_POP_()
 #endif
 
@@ -406,27 +423,24 @@
   // The d'tor is virtual as we intend to inherit from Test.
   virtual ~Test();
 
-  // Sets up the stuff shared by all tests in this test case.
+  // Sets up the stuff shared by all tests in this test suite.
   //
   // Google Test will call Foo::SetUpTestSuite() before running the first
-  // test in test case Foo.  Hence a sub-class can define its own
+  // test in test suite Foo.  Hence a sub-class can define its own
   // SetUpTestSuite() method to shadow the one defined in the super
   // class.
-  // Failures that happen during SetUpTestSuite are logged but otherwise
-  // ignored.
   static void SetUpTestSuite() {}
 
   // Tears down the stuff shared by all tests in this test suite.
   //
   // Google Test will call Foo::TearDownTestSuite() after running the last
-  // test in test case Foo.  Hence a sub-class can define its own
+  // test in test suite Foo.  Hence a sub-class can define its own
   // TearDownTestSuite() method to shadow the one defined in the super
   // class.
-  // Failures that happen during TearDownTestSuite are logged but otherwise
-  // ignored.
   static void TearDownTestSuite() {}
 
-  // Legacy API is deprecated but still available
+  // Legacy API is deprecated but still available. Use SetUpTestSuite and
+  // TearDownTestSuite instead.
 #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
   static void TearDownTestCase() {}
   static void SetUpTestCase() {}
@@ -664,7 +678,7 @@
 
   // Protects mutable state of the property vector and of owned
   // properties, whose values may be updated.
-  internal::Mutex test_properites_mutex_;
+  internal::Mutex test_properties_mutex_;
 
   // The vector of TestPartResults
   std::vector<TestPartResult> test_part_results_;
@@ -794,6 +808,9 @@
   // deletes it.
   void Run();
 
+  // Skip and records the test result for this object.
+  void Skip();
+
   static void ClearTestResult(TestInfo* test_info) {
     test_info->result_.Clear();
   }
@@ -889,7 +906,9 @@
   bool Passed() const { return !Failed(); }
 
   // Returns true if and only if the test suite failed.
-  bool Failed() const { return failed_test_count() > 0; }
+  bool Failed() const {
+    return failed_test_count() > 0 || ad_hoc_test_result().Failed();
+  }
 
   // Returns the elapsed time, in milliseconds.
   TimeInMillis elapsed_time() const { return elapsed_time_; }
@@ -940,6 +959,9 @@
   // Runs every test in this TestSuite.
   void Run();
 
+  // Skips the execution of tests under this TestSuite
+  void Skip();
+
   // Runs SetUpTestSuite() for this TestSuite.  This wrapper is needed
   // for catching exceptions thrown from SetUpTestSuite().
   void RunSetUpTestSuite() {
@@ -1420,6 +1442,7 @@
   friend class internal::StreamingListenerTest;
   friend class internal::UnitTestRecordPropertyTestHelper;
   friend Environment* AddGlobalTestEnvironment(Environment* env);
+  friend std::set<std::string>* internal::GetIgnoredParameterizedTestSuites();
   friend internal::UnitTestImpl* internal::GetUnitTestImpl();
   friend void internal::ReportFailureInUnknownLocation(
       TestPartResult::Type result_type,
@@ -1531,14 +1554,6 @@
   return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs);
 }
 
-// With this overloaded version, we allow anonymous enums to be used
-// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums
-// can be implicitly cast to BiggestInt.
-GTEST_API_ AssertionResult CmpHelperEQ(const char* lhs_expression,
-                                       const char* rhs_expression,
-                                       BiggestInt lhs,
-                                       BiggestInt rhs);
-
 class EqHelper {
  public:
   // This templatized version is for the general case.
@@ -1595,11 +1610,6 @@
 // ASSERT_?? and EXPECT_??.  It is here just to avoid copy-and-paste
 // of similar code.
 //
-// For each templatized helper function, we also define an overloaded
-// version for BiggestInt in order to reduce code bloat and allow
-// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled
-// with gcc 4.
-//
 // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
 
 #define GTEST_IMPL_CMP_HELPER_(op_name, op)\
@@ -1611,22 +1621,20 @@
   } else {\
     return CmpHelperOpFailure(expr1, expr2, val1, val2, #op);\
   }\
-}\
-GTEST_API_ AssertionResult CmpHelper##op_name(\
-    const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2)
+}
 
 // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
 
 // Implements the helper function for {ASSERT|EXPECT}_NE
-GTEST_IMPL_CMP_HELPER_(NE, !=);
+GTEST_IMPL_CMP_HELPER_(NE, !=)
 // Implements the helper function for {ASSERT|EXPECT}_LE
-GTEST_IMPL_CMP_HELPER_(LE, <=);
+GTEST_IMPL_CMP_HELPER_(LE, <=)
 // Implements the helper function for {ASSERT|EXPECT}_LT
-GTEST_IMPL_CMP_HELPER_(LT, <);
+GTEST_IMPL_CMP_HELPER_(LT, <)
 // Implements the helper function for {ASSERT|EXPECT}_GE
-GTEST_IMPL_CMP_HELPER_(GE, >=);
+GTEST_IMPL_CMP_HELPER_(GE, >=)
 // Implements the helper function for {ASSERT|EXPECT}_GT
-GTEST_IMPL_CMP_HELPER_(GT, >);
+GTEST_IMPL_CMP_HELPER_(GT, >)
 
 #undef GTEST_IMPL_CMP_HELPER_
 
@@ -1803,12 +1811,6 @@
   GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper);
 };
 
-enum GTestColor { COLOR_DEFAULT, COLOR_RED, COLOR_GREEN, COLOR_YELLOW };
-
-GTEST_API_ GTEST_ATTRIBUTE_PRINTF_(2, 3) void ColoredPrintf(GTestColor color,
-                                                            const char* fmt,
-                                                            ...);
-
 }  // namespace internal
 
 // The pure interface class that all value-parameterized tests inherit from.
@@ -1889,7 +1891,7 @@
 // Skips test in runtime.
 // Skipping test aborts current function.
 // Skipped tests are neither successful nor failed.
-#define GTEST_SKIP() GTEST_SKIP_("Skipped")
+#define GTEST_SKIP() GTEST_SKIP_("")
 
 // ADD_FAILURE unconditionally adds a failure to the current test.
 // SUCCEED generates a success - it doesn't automatically make the
@@ -1965,19 +1967,38 @@
 // Boolean assertions. Condition can be either a Boolean expression or an
 // AssertionResult. For more information on how to use AssertionResult with
 // these macros see comments on that class.
-#define EXPECT_TRUE(condition) \
+#define GTEST_EXPECT_TRUE(condition) \
   GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
                       GTEST_NONFATAL_FAILURE_)
-#define EXPECT_FALSE(condition) \
+#define GTEST_EXPECT_FALSE(condition) \
   GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
                       GTEST_NONFATAL_FAILURE_)
-#define ASSERT_TRUE(condition) \
+#define GTEST_ASSERT_TRUE(condition) \
   GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
                       GTEST_FATAL_FAILURE_)
-#define ASSERT_FALSE(condition) \
+#define GTEST_ASSERT_FALSE(condition) \
   GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
                       GTEST_FATAL_FAILURE_)
 
+// Define these macros to 1 to omit the definition of the corresponding
+// EXPECT or ASSERT, which clashes with some users' own code.
+
+#if !GTEST_DONT_DEFINE_EXPECT_TRUE
+#define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition)
+#endif
+
+#if !GTEST_DONT_DEFINE_EXPECT_FALSE
+#define EXPECT_FALSE(condition) GTEST_EXPECT_FALSE(condition)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_TRUE
+#define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_FALSE
+#define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition)
+#endif
+
 // Macros for testing equalities and inequalities.
 //
 //    * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2
@@ -2298,8 +2319,7 @@
 // to cause a compiler error.
 template <typename T1, typename T2>
 constexpr bool StaticAssertTypeEq() noexcept {
-  static_assert(std::is_same<T1, T2>::value,
-                "type1 and type2 are not the same type");
+  static_assert(std::is_same<T1, T2>::value, "T1 and T2 are not the same type");
   return true;
 }
 
@@ -2365,9 +2385,11 @@
 //   }
 //
 // GOOGLETEST_CM0011 DO NOT DELETE
+#if !GTEST_DONT_DEFINE_TEST
 #define TEST_F(test_fixture, test_name)\
   GTEST_TEST_(test_fixture, test_name, test_fixture, \
               ::testing::internal::GetTypeId<test_fixture>())
+#endif  // !GTEST_DONT_DEFINE_TEST
 
 // Returns a path to temporary directory.
 // Tries to determine an appropriate directory for the platform.
@@ -2475,4 +2497,4 @@
 
 GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest_pred_impl.h b/ext/googletest/googletest/include/gtest/gtest_pred_impl.h
index d514255..5029a9b 100644
--- a/ext/googletest/googletest/include/gtest/gtest_pred_impl.h
+++ b/ext/googletest/googletest/include/gtest/gtest_pred_impl.h
@@ -33,8 +33,8 @@
 // Implements a family of generic predicate assertion macros.
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
-#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
 
 #include "gtest/gtest.h"
 
@@ -356,4 +356,4 @@
 
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
diff --git a/ext/googletest/googletest/include/gtest/gtest_prod.h b/ext/googletest/googletest/include/gtest/gtest_prod.h
index e651671..38b9d85 100644
--- a/ext/googletest/googletest/include/gtest/gtest_prod.h
+++ b/ext/googletest/googletest/include/gtest/gtest_prod.h
@@ -31,8 +31,8 @@
 // Google C++ Testing and Mocking Framework definitions useful in production code.
 // GOOGLETEST_CM0003 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
-#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
 
 // When you need to test the private or protected members of a class,
 // use the FRIEND_TEST macro to declare your tests as friends of the
@@ -58,4 +58,4 @@
 #define FRIEND_TEST(test_case_name, test_name)\
 friend class test_case_name##_##test_name##_Test
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/custom/README.md b/ext/googletest/googletest/include/gtest/internal/custom/README.md
index ff391fb..0af3539 100644
--- a/ext/googletest/googletest/include/gtest/internal/custom/README.md
+++ b/ext/googletest/googletest/include/gtest/internal/custom/README.md
@@ -26,6 +26,8 @@
 *   `GTEST_DEFINE_bool_(name, default_val, doc)`
 *   `GTEST_DEFINE_int32_(name, default_val, doc)`
 *   `GTEST_DEFINE_string_(name, default_val, doc)`
+*   `GTEST_FLAG_GET(flag_name)`
+*   `GTEST_FLAG_SET(flag_name, value)`
 
 ### Logging:
 
diff --git a/ext/googletest/googletest/include/gtest/internal/custom/gtest-port.h b/ext/googletest/googletest/include/gtest/internal/custom/gtest-port.h
index cd85d95..db02881 100644
--- a/ext/googletest/googletest/include/gtest/internal/custom/gtest-port.h
+++ b/ext/googletest/googletest/include/gtest/internal/custom/gtest-port.h
@@ -31,7 +31,7 @@
 //
 // ** Custom implementation starts here **
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/custom/gtest-printers.h b/ext/googletest/googletest/include/gtest/internal/custom/gtest-printers.h
index eb4467a..b9495d8 100644
--- a/ext/googletest/googletest/include/gtest/internal/custom/gtest-printers.h
+++ b/ext/googletest/googletest/include/gtest/internal/custom/gtest-printers.h
@@ -36,7 +36,7 @@
 //
 // ** Custom implementation starts here **
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/custom/gtest.h b/ext/googletest/googletest/include/gtest/internal/custom/gtest.h
index 4c8e07b..afaaf17 100644
--- a/ext/googletest/googletest/include/gtest/internal/custom/gtest.h
+++ b/ext/googletest/googletest/include/gtest/internal/custom/gtest.h
@@ -31,7 +31,7 @@
 //
 // ** Custom implementation starts here **
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h b/ext/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h
index 68bd353..44277c3 100644
--- a/ext/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h
+++ b/ext/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h
@@ -33,8 +33,8 @@
 // death tests.  They are subject to change without notice.
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
 
 #include "gtest/gtest-matchers.h"
 #include "gtest/internal/gtest-internal.h"
@@ -42,11 +42,11 @@
 #include <stdio.h>
 #include <memory>
 
+GTEST_DECLARE_string_(internal_run_death_test);
+
 namespace testing {
 namespace internal {
 
-GTEST_DECLARE_string_(internal_run_death_test);
-
 // Names of the flags (needed for parsing Google Test flags).
 const char kDeathTestStyleFlag[] = "death_test_style";
 const char kDeathTestUseFork[] = "death_test_use_fork";
@@ -236,8 +236,6 @@
           gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE);   \
           break;                                                               \
         }                                                                      \
-        default:                                                               \
-          break;                                                               \
       }                                                                        \
     }                                                                          \
   } else                                                                       \
@@ -301,4 +299,4 @@
 }  // namespace internal
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/gtest-filepath.h b/ext/googletest/googletest/include/gtest/internal/gtest-filepath.h
index c11b101..0c033ab 100644
--- a/ext/googletest/googletest/include/gtest/internal/gtest-filepath.h
+++ b/ext/googletest/googletest/include/gtest/internal/gtest-filepath.h
@@ -37,8 +37,8 @@
 
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
 
 #include "gtest/internal/gtest-string.h"
 
@@ -195,7 +195,7 @@
 
   void Normalize();
 
-  // Returns a pointer to the last occurence of a valid path separator in
+  // Returns a pointer to the last occurrence of a valid path separator in
   // the FilePath. On Windows, for example, both '/' and '\' are valid path
   // separators. Returns NULL if no path separator was found.
   const char* FindLastPathSeparator() const;
@@ -208,4 +208,4 @@
 
 GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/gtest-internal.h b/ext/googletest/googletest/include/gtest/internal/gtest-internal.h
index 94c816a..f8cbdbd 100644
--- a/ext/googletest/googletest/include/gtest/internal/gtest-internal.h
+++ b/ext/googletest/googletest/include/gtest/internal/gtest-internal.h
@@ -34,8 +34,8 @@
 
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
 
 #include "gtest/internal/gtest-port.h"
 
@@ -53,6 +53,7 @@
 #include <ctype.h>
 #include <float.h>
 #include <string.h>
+#include <cstdint>
 #include <iomanip>
 #include <limits>
 #include <map>
@@ -78,9 +79,20 @@
 #define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar
 
 // Stringifies its argument.
-#define GTEST_STRINGIFY_(name) #name
+// Work around a bug in visual studio which doesn't accept code like this:
+//
+//   #define GTEST_STRINGIFY_(name) #name
+//   #define MACRO(a, b, c) ... GTEST_STRINGIFY_(a) ...
+//   MACRO(, x, y)
+//
+// Complaining about the argument to GTEST_STRINGIFY_ being empty.
+// This is allowed by the spec.
+#define GTEST_STRINGIFY_HELPER_(name, ...) #name
+#define GTEST_STRINGIFY_(...) GTEST_STRINGIFY_HELPER_(__VA_ARGS__, )
 
-namespace proto2 { class Message; }
+namespace proto2 {
+class MessageLite;
+}
 
 namespace testing {
 
@@ -275,7 +287,7 @@
   //
   // See the following article for more details on ULP:
   // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
-  static const size_t kMaxUlps = 4;
+  static const uint32_t kMaxUlps = 4;
 
   // Constructs a FloatingPoint from a raw floating-point number.
   //
@@ -508,6 +520,7 @@
 
   static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename,
                                                         int line_num) {
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
     SetUpTearDownSuiteFuncType test_case_fp =
         GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase);
     SetUpTearDownSuiteFuncType test_suite_fp =
@@ -519,10 +532,16 @@
         << filename << ":" << line_num;
 
     return test_case_fp != nullptr ? test_case_fp : test_suite_fp;
+#else
+    (void)(filename);
+    (void)(line_num);
+    return &T::SetUpTestSuite;
+#endif
   }
 
   static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename,
                                                            int line_num) {
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
     SetUpTearDownSuiteFuncType test_case_fp =
         GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase);
     SetUpTearDownSuiteFuncType test_suite_fp =
@@ -534,6 +553,11 @@
         << filename << ":" << line_num;
 
     return test_case_fp != nullptr ? test_case_fp : test_suite_fp;
+#else
+    (void)(filename);
+    (void)(line_num);
+    return &T::TearDownTestSuite;
+#endif
   }
 };
 
@@ -542,11 +566,11 @@
 //
 // Arguments:
 //
-//   test_suite_name:   name of the test suite
+//   test_suite_name:  name of the test suite
 //   name:             name of the test
-//   type_param        the name of the test's type parameter, or NULL if
+//   type_param:       the name of the test's type parameter, or NULL if
 //                     this is not a typed or a type-parameterized test.
-//   value_param       text representation of the test's value parameter,
+//   value_param:      text representation of the test's value parameter,
 //                     or NULL if this is not a type-parameterized test.
 //   code_location:    code location where the test is defined
 //   fixture_class_id: ID of the test fixture class
@@ -566,8 +590,6 @@
 // and returns false.  None of pstr, *pstr, and prefix can be NULL.
 GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr);
 
-#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
-
 GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
 /* class A needs to have dll-interface to be used by clients of class B */)
 
@@ -607,8 +629,9 @@
   // Verifies that registered_tests match the test names in
   // defined_test_names_; returns registered_tests if successful, or
   // aborts the program otherwise.
-  const char* VerifyRegisteredTestNames(
-      const char* file, int line, const char* registered_tests);
+  const char* VerifyRegisteredTestNames(const char* test_suite_name,
+                                        const char* file, int line,
+                                        const char* registered_tests);
 
  private:
   typedef ::std::map<std::string, CodeLocation> RegisteredTestsMap;
@@ -662,7 +685,7 @@
 };
 
 template <typename NameGenerator>
-void GenerateNamesRecursively(Types0, std::vector<std::string>*, int) {}
+void GenerateNamesRecursively(internal::None, std::vector<std::string>*, int) {}
 
 template <typename NameGenerator, typename Types>
 void GenerateNamesRecursively(Types, std::vector<std::string>* result, int i) {
@@ -729,7 +752,7 @@
 
 // The base case for the compile time recursion.
 template <GTEST_TEMPLATE_ Fixture, class TestSel>
-class TypeParameterizedTest<Fixture, TestSel, Types0> {
+class TypeParameterizedTest<Fixture, TestSel, internal::None> {
  public:
   static bool Register(const char* /*prefix*/, const CodeLocation&,
                        const char* /*case_name*/, const char* /*test_names*/,
@@ -740,6 +763,11 @@
   }
 };
 
+GTEST_API_ void RegisterTypeParameterizedTestSuite(const char* test_suite_name,
+                                                   CodeLocation code_location);
+GTEST_API_ void RegisterTypeParameterizedTestSuiteInstantiation(
+    const char* case_name);
+
 // TypeParameterizedTestSuite<Fixture, Tests, Types>::Register()
 // registers *all combinations* of 'Tests' and 'Types' with Google
 // Test.  The return value is insignificant - we just need to return
@@ -752,6 +780,7 @@
                        const char* test_names,
                        const std::vector<std::string>& type_names =
                            GenerateNames<DefaultNameGenerator, Types>()) {
+    RegisterTypeParameterizedTestSuiteInstantiation(case_name);
     std::string test_name = StripTrailingSpaces(
         GetPrefixUntilComma(test_names));
     if (!state->TestExists(test_name)) {
@@ -781,7 +810,7 @@
 
 // The base case for the compile time recursion.
 template <GTEST_TEMPLATE_ Fixture, typename Types>
-class TypeParameterizedTestSuite<Fixture, Templates0, Types> {
+class TypeParameterizedTestSuite<Fixture, internal::None, Types> {
  public:
   static bool Register(const char* /*prefix*/, const CodeLocation&,
                        const TypedTestSuitePState* /*state*/,
@@ -792,8 +821,6 @@
   }
 };
 
-#endif  // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
-
 // Returns the current OS stack trace as an std::string.
 //
 // The maximum number of stack frames to be included is specified by
@@ -825,6 +852,16 @@
   const char* value;
 };
 
+// Helper for declaring std::string within 'if' statement
+// in pre C++17 build environment.
+struct TrueWithString {
+  TrueWithString() = default;
+  explicit TrueWithString(const char* str) : value(str) {}
+  explicit TrueWithString(const std::string& str) : value(str) {}
+  explicit operator bool() const { return true; }
+  std::string value;
+};
+
 // A simple Linear Congruential Generator for generating random
 // numbers with a uniform distribution.  Unlike rand() and srand(), it
 // doesn't use global state (and therefore can't interfere with user
@@ -832,18 +869,18 @@
 // but it's good enough for our purposes.
 class GTEST_API_ Random {
  public:
-  static const UInt32 kMaxRange = 1u << 31;
+  static const uint32_t kMaxRange = 1u << 31;
 
-  explicit Random(UInt32 seed) : state_(seed) {}
+  explicit Random(uint32_t seed) : state_(seed) {}
 
-  void Reseed(UInt32 seed) { state_ = seed; }
+  void Reseed(uint32_t seed) { state_ = seed; }
 
   // Generates a random number from [0, range).  Crashes if 'range' is
   // 0 or greater than kMaxRange.
-  UInt32 Generate(UInt32 range);
+  uint32_t Generate(uint32_t range);
 
  private:
-  UInt32 state_;
+  uint32_t state_;
   GTEST_DISALLOW_COPY_AND_ASSIGN_(Random);
 };
 
@@ -851,12 +888,34 @@
 #define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \
   typename std::remove_const<typename std::remove_reference<T>::type>::type
 
-// IsAProtocolMessage<T>::value is a compile-time bool constant that's
-// true if and only if T is type proto2::Message or a subclass of it.
+// HasDebugStringAndShortDebugString<T>::value is a compile-time bool constant
+// that's true if and only if T has methods DebugString() and ShortDebugString()
+// that return std::string.
 template <typename T>
-struct IsAProtocolMessage
-    : public bool_constant<
-          std::is_convertible<const T*, const ::proto2::Message*>::value> {};
+class HasDebugStringAndShortDebugString {
+ private:
+  template <typename C>
+  static auto CheckDebugString(C*) -> typename std::is_same<
+      std::string, decltype(std::declval<const C>().DebugString())>::type;
+  template <typename>
+  static std::false_type CheckDebugString(...);
+
+  template <typename C>
+  static auto CheckShortDebugString(C*) -> typename std::is_same<
+      std::string, decltype(std::declval<const C>().ShortDebugString())>::type;
+  template <typename>
+  static std::false_type CheckShortDebugString(...);
+
+  using HasDebugStringType = decltype(CheckDebugString<T>(nullptr));
+  using HasShortDebugStringType = decltype(CheckShortDebugString<T>(nullptr));
+
+ public:
+  static constexpr bool value =
+      HasDebugStringType::value && HasShortDebugStringType::value;
+};
+
+template <typename T>
+constexpr bool HasDebugStringAndShortDebugString<T>::value;
 
 // When the compiler sees expression IsContainerTest<C>(0), if C is an
 // STL-style container class, the first overload of IsContainerTest
@@ -1092,8 +1151,6 @@
   const Element* array_;
   size_t size_;
   void (NativeArray::*clone_)(const Element*, size_t);
-
-  GTEST_DISALLOW_ASSIGN_(NativeArray);
 };
 
 // Backport of std::index_sequence.
@@ -1117,32 +1174,44 @@
 // Backport of std::make_index_sequence.
 // It uses O(ln(N)) instantiation depth.
 template <size_t N>
-struct MakeIndexSequence
-    : DoubleSequence<N % 2 == 1, typename MakeIndexSequence<N / 2>::type,
+struct MakeIndexSequenceImpl
+    : DoubleSequence<N % 2 == 1, typename MakeIndexSequenceImpl<N / 2>::type,
                      N / 2>::type {};
 
 template <>
-struct MakeIndexSequence<0> : IndexSequence<> {};
+struct MakeIndexSequenceImpl<0> : IndexSequence<> {};
 
-// FIXME: This implementation of ElemFromList is O(1) in instantiation depth,
-// but it is O(N^2) in total instantiations. Not sure if this is the best
-// tradeoff, as it will make it somewhat slow to compile.
-template <typename T, size_t, size_t>
-struct ElemFromListImpl {};
+template <size_t N>
+using MakeIndexSequence = typename MakeIndexSequenceImpl<N>::type;
 
-template <typename T, size_t I>
-struct ElemFromListImpl<T, I, I> {
-  using type = T;
+template <typename... T>
+using IndexSequenceFor = typename MakeIndexSequence<sizeof...(T)>::type;
+
+template <size_t>
+struct Ignore {
+  Ignore(...);  // NOLINT
 };
 
-// Get the Nth element from T...
-// It uses O(1) instantiation depth.
-template <size_t N, typename I, typename... T>
-struct ElemFromList;
+template <typename>
+struct ElemFromListImpl;
+template <size_t... I>
+struct ElemFromListImpl<IndexSequence<I...>> {
+  // We make Ignore a template to solve a problem with MSVC.
+  // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but
+  // MSVC doesn't understand how to deal with that pack expansion.
+  // Use `0 * I` to have a single instantiation of Ignore.
+  template <typename R>
+  static R Apply(Ignore<0 * I>..., R (*)(), ...);
+};
 
-template <size_t N, size_t... I, typename... T>
-struct ElemFromList<N, IndexSequence<I...>, T...>
-    : ElemFromListImpl<T, N, I>... {};
+template <size_t N, typename... T>
+struct ElemFromList {
+  using type =
+      decltype(ElemFromListImpl<typename MakeIndexSequence<N>::type>::Apply(
+          static_cast<T (*)()>(nullptr)...));
+};
+
+struct FlatTupleConstructTag {};
 
 template <typename... T>
 class FlatTuple;
@@ -1152,11 +1221,11 @@
 
 template <typename... T, size_t I>
 struct FlatTupleElemBase<FlatTuple<T...>, I> {
-  using value_type =
-      typename ElemFromList<I, typename MakeIndexSequence<sizeof...(T)>::type,
-                            T...>::type;
+  using value_type = typename ElemFromList<I, T...>::type;
   FlatTupleElemBase() = default;
-  explicit FlatTupleElemBase(value_type t) : value(std::move(t)) {}
+  template <typename Arg>
+  explicit FlatTupleElemBase(FlatTupleConstructTag, Arg&& t)
+      : value(std::forward<Arg>(t)) {}
   value_type value;
 };
 
@@ -1168,13 +1237,35 @@
     : FlatTupleElemBase<FlatTuple<T...>, Idx>... {
   using Indices = IndexSequence<Idx...>;
   FlatTupleBase() = default;
-  explicit FlatTupleBase(T... t)
-      : FlatTupleElemBase<FlatTuple<T...>, Idx>(std::move(t))... {}
+  template <typename... Args>
+  explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args)
+      : FlatTupleElemBase<FlatTuple<T...>, Idx>(FlatTupleConstructTag{},
+                                                std::forward<Args>(args))... {}
+
+  template <size_t I>
+  const typename ElemFromList<I, T...>::type& Get() const {
+    return FlatTupleElemBase<FlatTuple<T...>, I>::value;
+  }
+
+  template <size_t I>
+  typename ElemFromList<I, T...>::type& Get() {
+    return FlatTupleElemBase<FlatTuple<T...>, I>::value;
+  }
+
+  template <typename F>
+  auto Apply(F&& f) -> decltype(std::forward<F>(f)(this->Get<Idx>()...)) {
+    return std::forward<F>(f)(Get<Idx>()...);
+  }
+
+  template <typename F>
+  auto Apply(F&& f) const -> decltype(std::forward<F>(f)(this->Get<Idx>()...)) {
+    return std::forward<F>(f)(Get<Idx>()...);
+  }
 };
 
 // Analog to std::tuple but with different tradeoffs.
 // This class minimizes the template instantiation depth, thus allowing more
-// elements that std::tuple would. std::tuple has been seen to require an
+// elements than std::tuple would. std::tuple has been seen to require an
 // instantiation depth of more than 10x the number of elements in some
 // implementations.
 // FlatTuple and ElemFromList are not recursive and have a fixed depth
@@ -1185,21 +1276,17 @@
 class FlatTuple
     : private FlatTupleBase<FlatTuple<T...>,
                             typename MakeIndexSequence<sizeof...(T)>::type> {
-  using Indices = typename FlatTuple::FlatTupleBase::Indices;
+  using Indices = typename FlatTupleBase<
+      FlatTuple<T...>, typename MakeIndexSequence<sizeof...(T)>::type>::Indices;
 
  public:
   FlatTuple() = default;
-  explicit FlatTuple(T... t) : FlatTuple::FlatTupleBase(std::move(t)...) {}
+  template <typename... Args>
+  explicit FlatTuple(FlatTupleConstructTag tag, Args&&... args)
+      : FlatTuple::FlatTupleBase(tag, std::forward<Args>(args)...) {}
 
-  template <size_t I>
-  const typename ElemFromList<I, Indices, T...>::type& Get() const {
-    return static_cast<const FlatTupleElemBase<FlatTuple, I>*>(this)->value;
-  }
-
-  template <size_t I>
-  typename ElemFromList<I, Indices, T...>::type& Get() {
-    return static_cast<FlatTupleElemBase<FlatTuple, I>*>(this)->value;
-  }
+  using FlatTuple::FlatTupleBase::Apply;
+  using FlatTuple::FlatTupleBase::Get;
 };
 
 // Utility functions to be called with static_assert to induce deprecation
@@ -1232,6 +1319,22 @@
 }  // namespace internal
 }  // namespace testing
 
+namespace std {
+// Some standard library implementations use `struct tuple_size` and some use
+// `class tuple_size`. Clang warns about the mismatch.
+// https://reviews.llvm.org/D55466
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+template <typename... Ts>
+struct tuple_size<testing::internal::FlatTuple<Ts...>>
+    : std::integral_constant<size_t, sizeof...(Ts)> {};
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+}  // namespace std
+
 #define GTEST_MESSAGE_AT_(file, line, message, result_type) \
   ::testing::internal::AssertHelper(result_type, file, line, message) \
     = ::testing::Message()
@@ -1254,48 +1357,122 @@
 // Suppress MSVC warning 4072 (unreachable code) for the code following
 // statement if it returns or throws (or doesn't return or throw in some
 // situations).
+// NOTE: The "else" is important to keep this expansion to prevent a top-level
+// "else" from attaching to our "if".
 #define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \
-  if (::testing::internal::AlwaysTrue()) { statement; }
+  if (::testing::internal::AlwaysTrue()) {                        \
+    statement;                                                    \
+  } else                     /* NOLINT */                         \
+    static_assert(true, "")  // User must have a semicolon after expansion.
 
-#define GTEST_TEST_THROW_(statement, expected_exception, fail) \
-  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
-  if (::testing::internal::ConstCharPtr gtest_msg = "") { \
-    bool gtest_caught_expected = false; \
-    try { \
-      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
-    } \
-    catch (expected_exception const&) { \
-      gtest_caught_expected = true; \
-    } \
-    catch (...) { \
-      gtest_msg.value = \
-          "Expected: " #statement " throws an exception of type " \
-          #expected_exception ".\n  Actual: it throws a different type."; \
-      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
-    } \
-    if (!gtest_caught_expected) { \
-      gtest_msg.value = \
-          "Expected: " #statement " throws an exception of type " \
-          #expected_exception ".\n  Actual: it throws nothing."; \
-      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \
-    } \
-  } else \
-    GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \
-      fail(gtest_msg.value)
+#if GTEST_HAS_EXCEPTIONS
+
+namespace testing {
+namespace internal {
+
+class NeverThrown {
+ public:
+  const char* what() const noexcept {
+    return "this exception should never be thrown";
+  }
+};
+
+}  // namespace internal
+}  // namespace testing
+
+#if GTEST_HAS_RTTI
+
+#define GTEST_EXCEPTION_TYPE_(e) ::testing::internal::GetTypeName(typeid(e))
+
+#else  // GTEST_HAS_RTTI
+
+#define GTEST_EXCEPTION_TYPE_(e) \
+  std::string { "an std::exception-derived error" }
+
+#endif  // GTEST_HAS_RTTI
+
+#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception)   \
+  catch (typename std::conditional<                                            \
+         std::is_same<typename std::remove_cv<typename std::remove_reference<  \
+                          expected_exception>::type>::type,                    \
+                      std::exception>::value,                                  \
+         const ::testing::internal::NeverThrown&, const std::exception&>::type \
+             e) {                                                              \
+    gtest_msg.value = "Expected: " #statement                                  \
+                      " throws an exception of type " #expected_exception      \
+                      ".\n  Actual: it throws ";                               \
+    gtest_msg.value += GTEST_EXCEPTION_TYPE_(e);                               \
+    gtest_msg.value += " with description \"";                                 \
+    gtest_msg.value += e.what();                                               \
+    gtest_msg.value += "\".";                                                  \
+    goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);                \
+  }
+
+#else  // GTEST_HAS_EXCEPTIONS
+
+#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception)
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
+#define GTEST_TEST_THROW_(statement, expected_exception, fail)              \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                             \
+  if (::testing::internal::TrueWithString gtest_msg{}) {                    \
+    bool gtest_caught_expected = false;                                     \
+    try {                                                                   \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement);            \
+    } catch (expected_exception const&) {                                   \
+      gtest_caught_expected = true;                                         \
+    }                                                                       \
+    GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception)    \
+    catch (...) {                                                           \
+      gtest_msg.value = "Expected: " #statement                             \
+                        " throws an exception of type " #expected_exception \
+                        ".\n  Actual: it throws a different type.";         \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);           \
+    }                                                                       \
+    if (!gtest_caught_expected) {                                           \
+      gtest_msg.value = "Expected: " #statement                             \
+                        " throws an exception of type " #expected_exception \
+                        ".\n  Actual: it throws nothing.";                  \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);           \
+    }                                                                       \
+  } else /*NOLINT*/                                                         \
+    GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__)                   \
+        : fail(gtest_msg.value.c_str())
+
+#if GTEST_HAS_EXCEPTIONS
+
+#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_()                \
+  catch (std::exception const& e) {                               \
+    gtest_msg.value = "it throws ";                               \
+    gtest_msg.value += GTEST_EXCEPTION_TYPE_(e);                  \
+    gtest_msg.value += " with description \"";                    \
+    gtest_msg.value += e.what();                                  \
+    gtest_msg.value += "\".";                                     \
+    goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \
+  }
+
+#else  // GTEST_HAS_EXCEPTIONS
+
+#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_()
+
+#endif  // GTEST_HAS_EXCEPTIONS
 
 #define GTEST_TEST_NO_THROW_(statement, fail) \
   GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
-  if (::testing::internal::AlwaysTrue()) { \
+  if (::testing::internal::TrueWithString gtest_msg{}) { \
     try { \
       GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
     } \
+    GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \
     catch (...) { \
+      gtest_msg.value = "it throws."; \
       goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \
     } \
   } else \
     GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \
-      fail("Expected: " #statement " doesn't throw an exception.\n" \
-           "  Actual: it throws.")
+      fail(("Expected: " #statement " doesn't throw an exception.\n" \
+            "  Actual: " + gtest_msg.value).c_str())
 
 #define GTEST_TEST_ANY_THROW_(statement, fail) \
   GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
@@ -1318,7 +1495,7 @@
 
 // Implements Boolean test assertions such as EXPECT_TRUE. expression can be
 // either a boolean expression or an AssertionResult. text is a textual
-// represenation of expression as it was passed into the EXPECT_TRUE.
+// representation of expression as it was passed into the EXPECT_TRUE.
 #define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \
   GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
   if (const ::testing::AssertionResult gtest_ar_ = \
@@ -1355,13 +1532,16 @@
   class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                    \
       : public parent_class {                                                 \
    public:                                                                    \
-    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {}                   \
-                                                                              \
-   private:                                                                   \
-    virtual void TestBody();                                                  \
-    static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;     \
+    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default;           \
+    ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \
     GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name,   \
                                                            test_name));       \
+    GTEST_DISALLOW_MOVE_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name,   \
+                                                           test_name));       \
+                                                                              \
+   private:                                                                   \
+    void TestBody() override;                                                 \
+    static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;     \
   };                                                                          \
                                                                               \
   ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name,          \
@@ -1377,4 +1557,4 @@
               test_suite_name, test_name)>);                                  \
   void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/gtest-param-util.h b/ext/googletest/googletest/include/gtest/internal/gtest-param-util.h
index 9753399..c2ef6e3 100644
--- a/ext/googletest/googletest/include/gtest/internal/gtest-param-util.h
+++ b/ext/googletest/googletest/include/gtest/internal/gtest-param-util.h
@@ -32,8 +32,8 @@
 
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
 
 #include <ctype.h>
 
@@ -42,12 +42,14 @@
 #include <memory>
 #include <set>
 #include <tuple>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "gtest/internal/gtest-internal.h"
 #include "gtest/internal/gtest-port.h"
 #include "gtest/gtest-printers.h"
+#include "gtest/gtest-test-part.h"
 
 namespace testing {
 // Input to a parameterized test name generator, describing a test parameter.
@@ -457,7 +459,7 @@
 
   // Base part of test suite name for display purposes.
   virtual const std::string& GetTestSuiteName() const = 0;
-  // Test case id to verify identity.
+  // Test suite id to verify identity.
   virtual TypeId GetTestSuiteTypeId() const = 0;
   // UnitTest class invokes this method to register tests in this
   // test suite right before running them in RUN_ALL_TESTS macro.
@@ -474,6 +476,17 @@
 
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
 //
+// Report a the name of a test_suit as safe to ignore
+// as the side effect of construction of this type.
+struct GTEST_API_ MarkAsIgnored {
+  explicit MarkAsIgnored(const char* test_suite);
+};
+
+GTEST_API_ void InsertSyntheticTestCase(const std::string& name,
+                                        CodeLocation location, bool has_test_p);
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
 // ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P
 // macro invocations for a particular test suite and generators
 // obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that
@@ -494,11 +507,11 @@
                                       CodeLocation code_location)
       : test_suite_name_(name), code_location_(code_location) {}
 
-  // Test case base name for display purposes.
+  // Test suite base name for display purposes.
   const std::string& GetTestSuiteName() const override {
     return test_suite_name_;
   }
-  // Test case id to verify identity.
+  // Test suite id to verify identity.
   TypeId GetTestSuiteTypeId() const override { return GetTypeId<TestSuite>(); }
   // TEST_P macro uses AddTestPattern() to record information
   // about a single test in a LocalTestInfo structure.
@@ -507,9 +520,10 @@
   // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
   // test suite base name and DoBar is test base name.
   void AddTestPattern(const char* test_suite_name, const char* test_base_name,
-                      TestMetaFactoryBase<ParamType>* meta_factory) {
-    tests_.push_back(std::shared_ptr<TestInfo>(
-        new TestInfo(test_suite_name, test_base_name, meta_factory)));
+                      TestMetaFactoryBase<ParamType>* meta_factory,
+                      CodeLocation code_location) {
+    tests_.push_back(std::shared_ptr<TestInfo>(new TestInfo(
+        test_suite_name, test_base_name, meta_factory, code_location)));
   }
   // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information
   // about a generator.
@@ -522,11 +536,13 @@
     return 0;  // Return value used only to run this method in namespace scope.
   }
   // UnitTest class invokes this method to register tests in this test suite
-  // test suites right before running tests in RUN_ALL_TESTS macro.
+  // right before running tests in RUN_ALL_TESTS macro.
   // This method should not be called more than once on any single
   // instance of a ParameterizedTestSuiteInfoBase derived class.
   // UnitTest has a guard to prevent from calling this method more than once.
   void RegisterTests() override {
+    bool generated_instantiations = false;
+
     for (typename TestInfoContainer::iterator test_it = tests_.begin();
          test_it != tests_.end(); ++test_it) {
       std::shared_ptr<TestInfo> test_info = *test_it;
@@ -549,6 +565,8 @@
         for (typename ParamGenerator<ParamType>::iterator param_it =
                  generator.begin();
              param_it != generator.end(); ++param_it, ++i) {
+          generated_instantiations = true;
+
           Message test_name_stream;
 
           std::string param_name = name_func(
@@ -572,7 +590,7 @@
           MakeAndRegisterTestInfo(
               test_suite_name.c_str(), test_name_stream.GetString().c_str(),
               nullptr,  // No type parameter.
-              PrintToString(*param_it).c_str(), code_location_,
+              PrintToString(*param_it).c_str(), test_info->code_location,
               GetTestSuiteTypeId(),
               SuiteApiResolver<TestSuite>::GetSetUpCaseOrSuite(file, line),
               SuiteApiResolver<TestSuite>::GetTearDownCaseOrSuite(file, line),
@@ -580,6 +598,12 @@
         }  // for param_it
       }  // for gen_it
     }  // for test_it
+
+    if (!generated_instantiations) {
+      // There are no generaotrs, or they all generate nothing ...
+      InsertSyntheticTestCase(GetTestSuiteName(), code_location_,
+                              !tests_.empty());
+    }
   }    // RegisterTests
 
  private:
@@ -587,14 +611,17 @@
   // with TEST_P macro.
   struct TestInfo {
     TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name,
-             TestMetaFactoryBase<ParamType>* a_test_meta_factory)
+             TestMetaFactoryBase<ParamType>* a_test_meta_factory,
+             CodeLocation a_code_location)
         : test_suite_base_name(a_test_suite_base_name),
           test_base_name(a_test_base_name),
-          test_meta_factory(a_test_meta_factory) {}
+          test_meta_factory(a_test_meta_factory),
+          code_location(a_code_location) {}
 
     const std::string test_suite_base_name;
     const std::string test_base_name;
     const std::unique_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
+    const CodeLocation code_location;
   };
   using TestInfoContainer = ::std::vector<std::shared_ptr<TestInfo> >;
   // Records data received from INSTANTIATE_TEST_SUITE_P macros:
@@ -627,7 +654,7 @@
 
     // Check for invalid characters
     for (std::string::size_type index = 0; index < name.size(); ++index) {
-      if (!isalnum(name[index]) && name[index] != '_')
+      if (!IsAlNum(name[index]) && name[index] != '_')
         return false;
     }
 
@@ -717,6 +744,34 @@
   GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteRegistry);
 };
 
+// Keep track of what type-parameterized test suite are defined and
+// where as well as which are intatiated. This allows susequently
+// identifying suits that are defined but never used.
+class TypeParameterizedTestSuiteRegistry {
+ public:
+  // Add a suite definition
+  void RegisterTestSuite(const char* test_suite_name,
+                         CodeLocation code_location);
+
+  // Add an instantiation of a suit.
+  void RegisterInstantiation(const char* test_suite_name);
+
+  // For each suit repored as defined but not reported as instantiation,
+  // emit a test that reports that fact (configurably, as an error).
+  void CheckForInstantiations();
+
+ private:
+  struct TypeParameterizedTestSuiteInfo {
+    explicit TypeParameterizedTestSuiteInfo(CodeLocation c)
+        : code_location(c), instantiated(false) {}
+
+    CodeLocation code_location;
+    bool instantiated;
+  };
+
+  std::map<std::string, TypeParameterizedTestSuiteInfo> suites_;
+};
+
 }  // namespace internal
 
 // Forward declarations of ValuesIn(), which is implemented in
@@ -728,10 +783,15 @@
 namespace internal {
 // Used in the Values() function to provide polymorphic capabilities.
 
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+
 template <typename... Ts>
 class ValueArray {
  public:
-  ValueArray(Ts... v) : v_{std::move(v)...} {}
+  explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {}
 
   template <typename T>
   operator ParamGenerator<T>() const {  // NOLINT
@@ -747,6 +807,10 @@
   FlatTuple<Ts...> v_;
 };
 
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
 template <typename... T>
 class CartesianProductGenerator
     : public ParamGeneratorInterface<::std::tuple<T...>> {
@@ -880,4 +944,4 @@
 }  // namespace internal
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/gtest-port-arch.h b/ext/googletest/googletest/include/gtest/internal/gtest-port-arch.h
index cece93d..4dcdc89 100644
--- a/ext/googletest/googletest/include/gtest/internal/gtest-port-arch.h
+++ b/ext/googletest/googletest/include/gtest/internal/gtest-port-arch.h
@@ -32,8 +32,8 @@
 // This header file defines the GTEST_OS_* macro.
 // It is separate from gtest-port.h so that custom/gtest-port.h can include it.
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
 
 // Determines the platform on which Google Test is compiled.
 #ifdef __CYGWIN__
@@ -68,6 +68,7 @@
 # define GTEST_OS_OS2 1
 #elif defined __APPLE__
 # define GTEST_OS_MAC 1
+# include <TargetConditionals.h>
 # if TARGET_OS_IPHONE
 #  define GTEST_OS_IOS 1
 # endif
@@ -77,6 +78,8 @@
 # define GTEST_OS_FREEBSD 1
 #elif defined __Fuchsia__
 # define GTEST_OS_FUCHSIA 1
+#elif defined(__GNU__)
+# define GTEST_OS_GNU_HURD 1
 #elif defined(__GLIBC__) && defined(__FreeBSD_kernel__)
 # define GTEST_OS_GNU_KFREEBSD 1
 #elif defined __linux__
@@ -102,6 +105,12 @@
 # define GTEST_OS_QNX 1
 #elif defined(__HAIKU__)
 #define GTEST_OS_HAIKU 1
+#elif defined ESP8266
+#define GTEST_OS_ESP8266 1
+#elif defined ESP32
+#define GTEST_OS_ESP32 1
+#elif defined(__XTENSA__)
+#define GTEST_OS_XTENSA 1
 #endif  // __CYGWIN__
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/gtest-port.h b/ext/googletest/googletest/include/gtest/internal/gtest-port.h
index 063fcb1..524bbeb 100644
--- a/ext/googletest/googletest/include/gtest/internal/gtest-port.h
+++ b/ext/googletest/googletest/include/gtest/internal/gtest-port.h
@@ -40,8 +40,8 @@
 
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
 
 // Environment-describing macros
 // -----------------------------
@@ -116,6 +116,7 @@
 //   GTEST_OS_DRAGONFLY - DragonFlyBSD
 //   GTEST_OS_FREEBSD  - FreeBSD
 //   GTEST_OS_FUCHSIA  - Fuchsia
+//   GTEST_OS_GNU_HURD - GNU/Hurd
 //   GTEST_OS_GNU_KFREEBSD - GNU/kFreeBSD
 //   GTEST_OS_HAIKU    - Haiku
 //   GTEST_OS_HPUX     - HP-UX
@@ -190,13 +191,27 @@
 //   GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning.
 //   GTEST_ATTRIBUTE_UNUSED_  - declares that a class' instances or a
 //                              variable don't have to be used.
-//   GTEST_DISALLOW_ASSIGN_   - disables operator=.
+//   GTEST_DISALLOW_ASSIGN_   - disables copy operator=.
 //   GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=.
+//   GTEST_DISALLOW_MOVE_ASSIGN_   - disables move operator=.
+//   GTEST_DISALLOW_MOVE_AND_ASSIGN_ - disables move ctor and operator=.
 //   GTEST_MUST_USE_RESULT_   - declares that a function's result must be used.
 //   GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is
 //                                        suppressed (constant conditional).
 //   GTEST_INTENTIONAL_CONST_COND_POP_  - finish code section where MSVC C4127
 //                                        is suppressed.
+//   GTEST_INTERNAL_HAS_ANY - for enabling UniversalPrinter<std::any> or
+//                            UniversalPrinter<absl::any> specializations.
+//   GTEST_INTERNAL_HAS_OPTIONAL - for enabling UniversalPrinter<std::optional>
+//   or
+//                                 UniversalPrinter<absl::optional>
+//                                 specializations.
+//   GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher<std::string_view> or
+//                                    Matcher<absl::string_view>
+//                                    specializations.
+//   GTEST_INTERNAL_HAS_VARIANT - for enabling UniversalPrinter<std::variant> or
+//                                UniversalPrinter<absl::variant>
+//                                specializations.
 //
 // Synchronization:
 //   Mutex, MutexLock, ThreadLocal, GetThreadCount()
@@ -223,8 +238,7 @@
 //
 // Integer types:
 //   TypeWithSize   - maps an integer to a int type.
-//   Int32, UInt32, Int64, UInt64, TimeInMillis
-//                  - integers of known sizes.
+//   TimeInMillis   - integers of known sizes.
 //   BiggestInt     - the biggest signed integer type.
 //
 // Command-line utilities:
@@ -235,7 +249,7 @@
 // Environment variable utilities:
 //   GetEnv()             - gets the value of an environment variable.
 //   BoolFromGTestEnv()   - parses a bool environment variable.
-//   Int32FromGTestEnv()  - parses an Int32 environment variable.
+//   Int32FromGTestEnv()  - parses an int32_t environment variable.
 //   StringFromGTestEnv() - parses a string environment variable.
 //
 // Deprecation warnings:
@@ -248,7 +262,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <memory>
+
+#include <cerrno>
+#include <cstdint>
+#include <limits>
 #include <type_traits>
 
 #ifndef _WIN32_WCE
@@ -261,16 +278,15 @@
 # include <TargetConditionals.h>
 #endif
 
-#include <algorithm>  // NOLINT
-#include <iostream>   // NOLINT
-#include <sstream>    // NOLINT
-#include <string>     // NOLINT
+#include <iostream>  // NOLINT
+#include <locale>
+#include <memory>
+#include <string>  // NOLINT
 #include <tuple>
-#include <utility>
 #include <vector>  // NOLINT
 
-#include "gtest/internal/gtest-port-arch.h"
 #include "gtest/internal/custom/gtest-port.h"
+#include "gtest/internal/gtest-port-arch.h"
 
 #if !defined(GTEST_DEV_EMAIL_)
 # define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
@@ -344,6 +360,10 @@
 // WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION.
 typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
 #endif
+#elif GTEST_OS_XTENSA
+#include <unistd.h>
+// Xtensa toolchains define strcasecmp in the string.h header instead of
+// strings.h. string.h is already included.
 #else
 // This assumes that non-Windows OSes provide unistd.h. For OSes where this
 // is not the case, we need to include headers that provide the functions
@@ -364,7 +384,7 @@
 // On Android, <regex.h> is only available starting with Gingerbread.
 #  define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9)
 # else
-#  define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS)
+#define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS && !GTEST_OS_XTENSA)
 # endif
 #endif
 
@@ -441,15 +461,6 @@
 # endif  // defined(_MSC_VER) || defined(__BORLANDC__)
 #endif  // GTEST_HAS_EXCEPTIONS
 
-#if !defined(GTEST_HAS_STD_STRING)
-// Even though we don't use this macro any longer, we keep it in case
-// some clients still depend on it.
-# define GTEST_HAS_STD_STRING 1
-#elif !GTEST_HAS_STD_STRING
-// The user told us that ::std::string isn't available.
-# error "::std::string isn't available."
-#endif  // !defined(GTEST_HAS_STD_STRING)
-
 #ifndef GTEST_HAS_STD_WSTRING
 // The user didn't tell us whether ::std::wstring is available, so we need
 // to figure it out.
@@ -458,7 +469,7 @@
 // no support for it at least as recent as Froyo (2.2).
 #define GTEST_HAS_STD_WSTRING                                         \
   (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
-     GTEST_OS_HAIKU))
+     GTEST_OS_HAIKU || GTEST_OS_ESP32 || GTEST_OS_ESP8266 || GTEST_OS_XTENSA))
 
 #endif  // GTEST_HAS_STD_WSTRING
 
@@ -537,7 +548,7 @@
   (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX ||          \
    GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \
    GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_OPENBSD ||          \
-   GTEST_OS_HAIKU)
+   GTEST_OS_HAIKU || GTEST_OS_GNU_HURD)
 #endif  // GTEST_HAS_PTHREAD
 
 #if GTEST_HAS_PTHREAD
@@ -582,7 +593,8 @@
 #ifndef GTEST_HAS_STREAM_REDIRECTION
 // By default, we assume that stream redirection is supported on all
 // platforms except known mobile ones.
-# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \
+    GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA
 #  define GTEST_HAS_STREAM_REDIRECTION 0
 # else
 #  define GTEST_HAS_STREAM_REDIRECTION 1
@@ -596,7 +608,8 @@
      (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER) || GTEST_OS_WINDOWS_MINGW ||  \
      GTEST_OS_AIX || GTEST_OS_HPUX || GTEST_OS_OPENBSD || GTEST_OS_QNX || \
      GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA ||           \
-     GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_HAIKU)
+     GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_HAIKU ||     \
+     GTEST_OS_GNU_HURD)
 # define GTEST_HAS_DEATH_TEST 1
 #endif
 
@@ -616,7 +629,8 @@
 
 // Determines whether test results can be streamed to a socket.
 #if GTEST_OS_LINUX || GTEST_OS_GNU_KFREEBSD || GTEST_OS_DRAGONFLY || \
-    GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_OPENBSD
+    GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_OPENBSD ||       \
+    GTEST_OS_GNU_HURD
 # define GTEST_CAN_STREAM_RESULTS_ 1
 #endif
 
@@ -676,16 +690,27 @@
 #endif
 
 
-// A macro to disallow operator=
+// A macro to disallow copy operator=
 // This should be used in the private: declarations for a class.
 #define GTEST_DISALLOW_ASSIGN_(type) \
-  void operator=(type const &) = delete
+  type& operator=(type const &) = delete
 
 // A macro to disallow copy constructor and operator=
 // This should be used in the private: declarations for a class.
 #define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \
-  type(type const &) = delete; \
-  GTEST_DISALLOW_ASSIGN_(type)
+  type(type const&) = delete;                 \
+  type& operator=(type const&) = delete
+
+// A macro to disallow move operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_MOVE_ASSIGN_(type) \
+  type& operator=(type &&) noexcept = delete
+
+// A macro to disallow move constructor and operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_MOVE_AND_ASSIGN_(type) \
+  type(type&&) noexcept = delete;             \
+  type& operator=(type&&) noexcept = delete
 
 // Tell the compiler to warn about unused return values for functions declared
 // with this macro.  The macro should be used on function declarations
@@ -856,9 +881,6 @@
 // expression is false, compiler will issue an error containing this identifier.
 #define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg)
 
-// Evaluates to the number of elements in 'array'.
-#define GTEST_ARRAY_SIZE_(array) (sizeof(array) / sizeof(array[0]))
-
 // A helper for suppressing warnings on constant condition.  It just
 // returns 'condition'.
 GTEST_API_ bool IsTrue(bool condition);
@@ -915,8 +937,6 @@
   const char* full_pattern_;  // For FullMatch();
 
 # endif
-
-  GTEST_DISALLOW_ASSIGN_(RE);
 };
 
 #endif  // GTEST_USES_PCRE
@@ -1599,7 +1619,7 @@
   class DefaultValueHolderFactory : public ValueHolderFactory {
    public:
     DefaultValueHolderFactory() {}
-    virtual ValueHolder* MakeNewHolder() const { return new ValueHolder(); }
+    ValueHolder* MakeNewHolder() const override { return new ValueHolder(); }
 
    private:
     GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory);
@@ -1608,7 +1628,7 @@
   class InstanceValueHolderFactory : public ValueHolderFactory {
    public:
     explicit InstanceValueHolderFactory(const T& value) : value_(value) {}
-    virtual ValueHolder* MakeNewHolder() const {
+    ValueHolder* MakeNewHolder() const override {
       return new ValueHolder(value_);
     }
 
@@ -1808,7 +1828,7 @@
   class DefaultValueHolderFactory : public ValueHolderFactory {
    public:
     DefaultValueHolderFactory() {}
-    virtual ValueHolder* MakeNewHolder() const { return new ValueHolder(); }
+    ValueHolder* MakeNewHolder() const override { return new ValueHolder(); }
 
    private:
     GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory);
@@ -1817,7 +1837,7 @@
   class InstanceValueHolderFactory : public ValueHolderFactory {
    public:
     explicit InstanceValueHolderFactory(const T& value) : value_(value) {}
-    virtual ValueHolder* MakeNewHolder() const {
+    ValueHolder* MakeNewHolder() const override {
       return new ValueHolder(value_);
     }
 
@@ -1887,18 +1907,12 @@
 // we cannot detect it.
 GTEST_API_ size_t GetThreadCount();
 
-template <bool B>
-using bool_constant = std::integral_constant<bool, B>;
-
 #if GTEST_OS_WINDOWS
 # define GTEST_PATH_SEP_ "\\"
 # define GTEST_HAS_ALT_PATH_SEP_ 1
-// The biggest signed integer type the compiler supports.
-typedef __int64 BiggestInt;
 #else
 # define GTEST_PATH_SEP_ "/"
 # define GTEST_HAS_ALT_PATH_SEP_ 0
-typedef long long BiggestInt;  // NOLINT
 #endif  // GTEST_OS_WINDOWS
 
 // Utilities for char.
@@ -1929,6 +1943,19 @@
 inline bool IsXDigit(char ch) {
   return isxdigit(static_cast<unsigned char>(ch)) != 0;
 }
+#ifdef __cpp_char8_t
+inline bool IsXDigit(char8_t ch) {
+  return isxdigit(static_cast<unsigned char>(ch)) != 0;
+}
+#endif
+inline bool IsXDigit(char16_t ch) {
+  const unsigned char low_byte = static_cast<unsigned char>(ch);
+  return ch == low_byte && isxdigit(low_byte) != 0;
+}
+inline bool IsXDigit(char32_t ch) {
+  const unsigned char low_byte = static_cast<unsigned char>(ch);
+  return ch == low_byte && isxdigit(low_byte) != 0;
+}
 inline bool IsXDigit(wchar_t ch) {
   const unsigned char low_byte = static_cast<unsigned char>(ch);
   return ch == low_byte && isxdigit(low_byte) != 0;
@@ -1963,16 +1990,16 @@
 typedef struct _stat StatStruct;
 
 # ifdef __BORLANDC__
-inline int IsATTY(int fd) { return isatty(fd); }
+inline int DoIsATTY(int fd) { return isatty(fd); }
 inline int StrCaseCmp(const char* s1, const char* s2) {
   return stricmp(s1, s2);
 }
 inline char* StrDup(const char* src) { return strdup(src); }
 # else  // !__BORLANDC__
 #  if GTEST_OS_WINDOWS_MOBILE
-inline int IsATTY(int /* fd */) { return 0; }
+inline int DoIsATTY(int /* fd */) { return 0; }
 #  else
-inline int IsATTY(int fd) { return _isatty(fd); }
+inline int DoIsATTY(int fd) { return _isatty(fd); }
 #  endif  // GTEST_OS_WINDOWS_MOBILE
 inline int StrCaseCmp(const char* s1, const char* s2) {
   return _stricmp(s1, s2);
@@ -1993,12 +2020,28 @@
 }
 # endif  // GTEST_OS_WINDOWS_MOBILE
 
+#elif GTEST_OS_ESP8266
+typedef struct stat StatStruct;
+
+inline int FileNo(FILE* file) { return fileno(file); }
+inline int DoIsATTY(int fd) { return isatty(fd); }
+inline int Stat(const char* path, StatStruct* buf) {
+  // stat function not implemented on ESP8266
+  return 0;
+}
+inline int StrCaseCmp(const char* s1, const char* s2) {
+  return strcasecmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+inline int RmDir(const char* dir) { return rmdir(dir); }
+inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
+
 #else
 
 typedef struct stat StatStruct;
 
 inline int FileNo(FILE* file) { return fileno(file); }
-inline int IsATTY(int fd) { return isatty(fd); }
+inline int DoIsATTY(int fd) { return isatty(fd); }
 inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); }
 inline int StrCaseCmp(const char* s1, const char* s2) {
   return strcasecmp(s1, s2);
@@ -2009,23 +2052,39 @@
 
 #endif  // GTEST_OS_WINDOWS
 
+inline int IsATTY(int fd) {
+  // DoIsATTY might change errno (for example ENOTTY in case you redirect stdout
+  // to a file on Linux), which is unexpected, so save the previous value, and
+  // restore it after the call.
+  int savedErrno = errno;
+  int isAttyValue = DoIsATTY(fd);
+  errno = savedErrno;
+
+  return isAttyValue;
+}
+
 // Functions deprecated by MSVC 8.0.
 
 GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
 
-inline const char* StrNCpy(char* dest, const char* src, size_t n) {
-  return strncpy(dest, src, n);
-}
-
 // ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and
 // StrError() aren't needed on Windows CE at this time and thus not
 // defined there.
 
-#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
+#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && \
+    !GTEST_OS_WINDOWS_RT && !GTEST_OS_ESP8266 && !GTEST_OS_XTENSA
 inline int ChDir(const char* dir) { return chdir(dir); }
 #endif
 inline FILE* FOpen(const char* path, const char* mode) {
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
+  struct wchar_codecvt : public std::codecvt<wchar_t, char, std::mbstate_t> {};
+  std::wstring_convert<wchar_codecvt> converter;
+  std::wstring wide_path = converter.from_bytes(path);
+  std::wstring wide_mode = converter.from_bytes(mode);
+  return _wfopen(wide_path.c_str(), wide_mode.c_str());
+#else  // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
   return fopen(path, mode);
+#endif  // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
 }
 #if !GTEST_OS_WINDOWS_MOBILE
 inline FILE *FReopen(const char* path, const char* mode, FILE* stream) {
@@ -2045,8 +2104,9 @@
 inline const char* StrError(int errnum) { return strerror(errnum); }
 #endif
 inline const char* GetEnv(const char* name) {
-#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT
-  // We are on Windows CE, which has no environment variables.
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \
+    GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA
+  // We are on an embedded platform, which has no environment variables.
   static_cast<void>(name);  // To prevent 'unused argument' warning.
   return nullptr;
 #elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9)
@@ -2088,15 +2148,13 @@
 # define GTEST_SNPRINTF_ snprintf
 #endif
 
-// The maximum number a BiggestInt can represent.  This definition
-// works no matter BiggestInt is represented in one's complement or
-// two's complement.
+// The biggest signed integer type the compiler supports.
 //
-// We cannot rely on numeric_limits in STL, as __int64 and long long
-// are not part of standard C++ and numeric_limits doesn't need to be
-// defined for them.
-const BiggestInt kMaxBiggestInt =
-    ~(static_cast<BiggestInt>(1) << (8*sizeof(BiggestInt) - 1));
+// long long is guaranteed to be at least 64-bits in C++11.
+using BiggestInt = long long;  // NOLINT
+
+// The maximum number a BiggestInt can represent.
+constexpr BiggestInt kMaxBiggestInt = (std::numeric_limits<BiggestInt>::max)();
 
 // This template class serves as a compile-time function from size to
 // type.  It maps a size in bytes to a primitive type with that
@@ -2121,40 +2179,27 @@
  public:
   // This prevents the user from using TypeWithSize<N> with incorrect
   // values of N.
-  typedef void UInt;
+  using UInt = void;
 };
 
 // The specialization for size 4.
 template <>
 class TypeWithSize<4> {
  public:
-  // unsigned int has size 4 in both gcc and MSVC.
-  //
-  // As base/basictypes.h doesn't compile on Windows, we cannot use
-  // uint32, uint64, and etc here.
-  typedef int Int;
-  typedef unsigned int UInt;
+  using Int = std::int32_t;
+  using UInt = std::uint32_t;
 };
 
 // The specialization for size 8.
 template <>
 class TypeWithSize<8> {
  public:
-#if GTEST_OS_WINDOWS
-  typedef __int64 Int;
-  typedef unsigned __int64 UInt;
-#else
-  typedef long long Int;  // NOLINT
-  typedef unsigned long long UInt;  // NOLINT
-#endif  // GTEST_OS_WINDOWS
+  using Int = std::int64_t;
+  using UInt = std::uint64_t;
 };
 
 // Integer types of known sizes.
-typedef TypeWithSize<4>::Int Int32;
-typedef TypeWithSize<4>::UInt UInt32;
-typedef TypeWithSize<8>::Int Int64;
-typedef TypeWithSize<8>::UInt UInt64;
-typedef TypeWithSize<8>::Int TimeInMillis;  // Represents time in milliseconds.
+using TimeInMillis = int64_t;  // Represents time in milliseconds.
 
 // Utilities for command line flags and environment variables.
 
@@ -2171,22 +2216,40 @@
 # define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver
 
 // Macros for declaring flags.
-# define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name)
-# define GTEST_DECLARE_int32_(name) \
-    GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name)
-# define GTEST_DECLARE_string_(name) \
-    GTEST_API_ extern ::std::string GTEST_FLAG(name)
+#define GTEST_DECLARE_bool_(name)          \
+  namespace testing {                      \
+  GTEST_API_ extern bool GTEST_FLAG(name); \
+  }
+#define GTEST_DECLARE_int32_(name)                 \
+  namespace testing {                              \
+  GTEST_API_ extern std::int32_t GTEST_FLAG(name); \
+  }
+#define GTEST_DECLARE_string_(name)                 \
+  namespace testing {                               \
+  GTEST_API_ extern ::std::string GTEST_FLAG(name); \
+  }
 
 // Macros for defining flags.
-# define GTEST_DEFINE_bool_(name, default_val, doc) \
-    GTEST_API_ bool GTEST_FLAG(name) = (default_val)
-# define GTEST_DEFINE_int32_(name, default_val, doc) \
-    GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val)
-# define GTEST_DEFINE_string_(name, default_val, doc) \
-    GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val)
+#define GTEST_DEFINE_bool_(name, default_val, doc)  \
+  namespace testing {                               \
+  GTEST_API_ bool GTEST_FLAG(name) = (default_val); \
+  }
+#define GTEST_DEFINE_int32_(name, default_val, doc)         \
+  namespace testing {                                       \
+  GTEST_API_ std::int32_t GTEST_FLAG(name) = (default_val); \
+  }
+#define GTEST_DEFINE_string_(name, default_val, doc)         \
+  namespace testing {                                        \
+  GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val); \
+  }
 
 #endif  // !defined(GTEST_DECLARE_bool_)
 
+#if !defined(GTEST_FLAG_GET)
+#define GTEST_FLAG_GET(name) ::testing::GTEST_FLAG(name)
+#define GTEST_FLAG_SET(name, value) (void)(::testing::GTEST_FLAG(name) = value)
+#endif  // !defined(GTEST_FLAG_GET)
+
 // Thread annotations
 #if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_)
 # define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
@@ -2196,12 +2259,13 @@
 // Parses 'str' for a 32-bit signed integer.  If successful, writes the result
 // to *value and returns true; otherwise leaves *value unchanged and returns
 // false.
-bool ParseInt32(const Message& src_text, const char* str, Int32* value);
+GTEST_API_ bool ParseInt32(const Message& src_text, const char* str,
+                           int32_t* value);
 
-// Parses a bool/Int32/string from the environment variable
+// Parses a bool/int32_t/string from the environment variable
 // corresponding to the given Google Test flag.
 bool BoolFromGTestEnv(const char* flag, bool default_val);
-GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val);
+GTEST_API_ int32_t Int32FromGTestEnv(const char* flag, int32_t default_val);
 std::string OutputFlagAlsoCheckEnvVar();
 const char* StringFromGTestEnv(const char* flag, const char* default_val);
 
@@ -2228,4 +2292,119 @@
 
 #endif  // !defined(GTEST_INTERNAL_DEPRECATED)
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+#if GTEST_HAS_ABSL
+// Always use absl::any for UniversalPrinter<> specializations if googletest
+// is built with absl support.
+#define GTEST_INTERNAL_HAS_ANY 1
+#include "absl/types/any.h"
+namespace testing {
+namespace internal {
+using Any = ::absl::any;
+}  // namespace internal
+}  // namespace testing
+#else
+#ifdef __has_include
+#if __has_include(<any>) && __cplusplus >= 201703L
+// Otherwise for C++17 and higher use std::any for UniversalPrinter<>
+// specializations.
+#define GTEST_INTERNAL_HAS_ANY 1
+#include <any>
+namespace testing {
+namespace internal {
+using Any = ::std::any;
+}  // namespace internal
+}  // namespace testing
+// The case where absl is configured NOT to alias std::any is not
+// supported.
+#endif  // __has_include(<any>) && __cplusplus >= 201703L
+#endif  // __has_include
+#endif  // GTEST_HAS_ABSL
+
+#if GTEST_HAS_ABSL
+// Always use absl::optional for UniversalPrinter<> specializations if
+// googletest is built with absl support.
+#define GTEST_INTERNAL_HAS_OPTIONAL 1
+#include "absl/types/optional.h"
+namespace testing {
+namespace internal {
+template <typename T>
+using Optional = ::absl::optional<T>;
+}  // namespace internal
+}  // namespace testing
+#else
+#ifdef __has_include
+#if __has_include(<optional>) && __cplusplus >= 201703L
+// Otherwise for C++17 and higher use std::optional for UniversalPrinter<>
+// specializations.
+#define GTEST_INTERNAL_HAS_OPTIONAL 1
+#include <optional>
+namespace testing {
+namespace internal {
+template <typename T>
+using Optional = ::std::optional<T>;
+}  // namespace internal
+}  // namespace testing
+// The case where absl is configured NOT to alias std::optional is not
+// supported.
+#endif  // __has_include(<optional>) && __cplusplus >= 201703L
+#endif  // __has_include
+#endif  // GTEST_HAS_ABSL
+
+#if GTEST_HAS_ABSL
+// Always use absl::string_view for Matcher<> specializations if googletest
+// is built with absl support.
+# define GTEST_INTERNAL_HAS_STRING_VIEW 1
+#include "absl/strings/string_view.h"
+namespace testing {
+namespace internal {
+using StringView = ::absl::string_view;
+}  // namespace internal
+}  // namespace testing
+#else
+# ifdef __has_include
+#   if __has_include(<string_view>) && __cplusplus >= 201703L
+// Otherwise for C++17 and higher use std::string_view for Matcher<>
+// specializations.
+#   define GTEST_INTERNAL_HAS_STRING_VIEW 1
+#include <string_view>
+namespace testing {
+namespace internal {
+using StringView = ::std::string_view;
+}  // namespace internal
+}  // namespace testing
+// The case where absl is configured NOT to alias std::string_view is not
+// supported.
+#  endif  // __has_include(<string_view>) && __cplusplus >= 201703L
+# endif  // __has_include
+#endif  // GTEST_HAS_ABSL
+
+#if GTEST_HAS_ABSL
+// Always use absl::variant for UniversalPrinter<> specializations if googletest
+// is built with absl support.
+#define GTEST_INTERNAL_HAS_VARIANT 1
+#include "absl/types/variant.h"
+namespace testing {
+namespace internal {
+template <typename... T>
+using Variant = ::absl::variant<T...>;
+}  // namespace internal
+}  // namespace testing
+#else
+#ifdef __has_include
+#if __has_include(<variant>) && __cplusplus >= 201703L
+// Otherwise for C++17 and higher use std::variant for UniversalPrinter<>
+// specializations.
+#define GTEST_INTERNAL_HAS_VARIANT 1
+#include <variant>
+namespace testing {
+namespace internal {
+template <typename... T>
+using Variant = ::std::variant<T...>;
+}  // namespace internal
+}  // namespace testing
+// The case where absl is configured NOT to alias std::variant is not supported.
+#endif  // __has_include(<variant>) && __cplusplus >= 201703L
+#endif  // __has_include
+#endif  // GTEST_HAS_ABSL
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/gtest-string.h b/ext/googletest/googletest/include/gtest/internal/gtest-string.h
index 82aaa63..10f774f 100644
--- a/ext/googletest/googletest/include/gtest/internal/gtest-string.h
+++ b/ext/googletest/googletest/include/gtest/internal/gtest-string.h
@@ -38,8 +38,8 @@
 
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
 
 #ifdef __BORLANDC__
 // string.h is not guaranteed to provide strcpy on C++ Builder.
@@ -47,6 +47,7 @@
 #endif
 
 #include <string.h>
+#include <cstdint>
 #include <string>
 
 #include "gtest/internal/gtest-port.h"
@@ -148,11 +149,14 @@
   // Formats an int value as "%02d".
   static std::string FormatIntWidth2(int value);  // "%02d" for width == 2
 
+  // Formats an int value to given width with leading zeros.
+  static std::string FormatIntWidthN(int value, int width);
+
   // Formats an int value as "%X".
   static std::string FormatHexInt(int value);
 
   // Formats an int value as "%X".
-  static std::string FormatHexUInt32(UInt32 value);
+  static std::string FormatHexUInt32(uint32_t value);
 
   // Formats a byte as "%02X".
   static std::string FormatByte(unsigned char value);
@@ -168,4 +172,4 @@
 }  // namespace internal
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/gtest-type-util.h b/ext/googletest/googletest/include/gtest/internal/gtest-type-util.h
index 3d7542d..b87a2e2 100644
--- a/ext/googletest/googletest/include/gtest/internal/gtest-type-util.h
+++ b/ext/googletest/googletest/include/gtest/internal/gtest-type-util.h
@@ -1,7 +1,3 @@
-// This file was GENERATED by command:
-//     pump.py gtest-type-util.h.pump
-// DO NOT EDIT BY HAND!!!
-
 // Copyright 2008 Google Inc.
 // All Rights Reserved.
 //
@@ -32,17 +28,12 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // Type utilities needed for implementing typed and type-parameterized
-// tests.  This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
-//
-// Currently we support at most 50 types in a list, and at most 50
-// type-parameterized tests in one type-parameterized test suite.
-// Please contact googletestframework@googlegroups.com if you need
-// more.
+// tests.
 
 // GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
 
 #include "gtest/internal/gtest-port.h"
 
@@ -73,1556 +64,43 @@
   return s;
 }
 
-// GetTypeName<T>() returns a human-readable name of type T.
-// NB: This function is also used in Google Mock, so don't move it inside of
-// the typed-test-only section below.
-template <typename T>
-std::string GetTypeName() {
-# if GTEST_HAS_RTTI
-
-  const char* const name = typeid(T).name();
-#  if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
+#if GTEST_HAS_RTTI
+// GetTypeName(const std::type_info&) returns a human-readable name of type T.
+inline std::string GetTypeName(const std::type_info& type) {
+  const char* const name = type.name();
+#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
   int status = 0;
   // gcc's implementation of typeid(T).name() mangles the type name,
   // so we have to demangle it.
-#   if GTEST_HAS_CXXABI_H_
+#if GTEST_HAS_CXXABI_H_
   using abi::__cxa_demangle;
-#   endif  // GTEST_HAS_CXXABI_H_
+#endif  // GTEST_HAS_CXXABI_H_
   char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status);
   const std::string name_str(status == 0 ? readable_name : name);
   free(readable_name);
   return CanonicalizeForStdLibVersioning(name_str);
-#  else
+#else
   return name;
-#  endif  // GTEST_HAS_CXXABI_H_ || __HP_aCC
+#endif  // GTEST_HAS_CXXABI_H_ || __HP_aCC
+}
+#endif  // GTEST_HAS_RTTI
 
-# else
-
+// GetTypeName<T>() returns a human-readable name of type T if and only if
+// RTTI is enabled, otherwise it returns a dummy type name.
+// NB: This function is also used in Google Mock, so don't move it inside of
+// the typed-test-only section below.
+template <typename T>
+std::string GetTypeName() {
+#if GTEST_HAS_RTTI
+  return GetTypeName(typeid(T));
+#else
   return "<type>";
-
-# endif  // GTEST_HAS_RTTI
+#endif  // GTEST_HAS_RTTI
 }
 
-#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
-
-// A unique type used as the default value for the arguments of class
-// template Types.  This allows us to simulate variadic templates
-// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't
-// support directly.
+// A unique type indicating an empty node
 struct None {};
 
-// The following family of struct and struct templates are used to
-// represent type lists.  In particular, TypesN<T1, T2, ..., TN>
-// represents a type list with N types (T1, T2, ..., and TN) in it.
-// Except for Types0, every struct in the family has two member types:
-// Head for the first type in the list, and Tail for the rest of the
-// list.
-
-// The empty type list.
-struct Types0 {};
-
-// Type lists of length 1, 2, 3, and so on.
-
-template <typename T1>
-struct Types1 {
-  typedef T1 Head;
-  typedef Types0 Tail;
-};
-template <typename T1, typename T2>
-struct Types2 {
-  typedef T1 Head;
-  typedef Types1<T2> Tail;
-};
-
-template <typename T1, typename T2, typename T3>
-struct Types3 {
-  typedef T1 Head;
-  typedef Types2<T2, T3> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4>
-struct Types4 {
-  typedef T1 Head;
-  typedef Types3<T2, T3, T4> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5>
-struct Types5 {
-  typedef T1 Head;
-  typedef Types4<T2, T3, T4, T5> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6>
-struct Types6 {
-  typedef T1 Head;
-  typedef Types5<T2, T3, T4, T5, T6> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7>
-struct Types7 {
-  typedef T1 Head;
-  typedef Types6<T2, T3, T4, T5, T6, T7> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8>
-struct Types8 {
-  typedef T1 Head;
-  typedef Types7<T2, T3, T4, T5, T6, T7, T8> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9>
-struct Types9 {
-  typedef T1 Head;
-  typedef Types8<T2, T3, T4, T5, T6, T7, T8, T9> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10>
-struct Types10 {
-  typedef T1 Head;
-  typedef Types9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11>
-struct Types11 {
-  typedef T1 Head;
-  typedef Types10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12>
-struct Types12 {
-  typedef T1 Head;
-  typedef Types11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13>
-struct Types13 {
-  typedef T1 Head;
-  typedef Types12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14>
-struct Types14 {
-  typedef T1 Head;
-  typedef Types13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15>
-struct Types15 {
-  typedef T1 Head;
-  typedef Types14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16>
-struct Types16 {
-  typedef T1 Head;
-  typedef Types15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17>
-struct Types17 {
-  typedef T1 Head;
-  typedef Types16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18>
-struct Types18 {
-  typedef T1 Head;
-  typedef Types17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19>
-struct Types19 {
-  typedef T1 Head;
-  typedef Types18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20>
-struct Types20 {
-  typedef T1 Head;
-  typedef Types19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21>
-struct Types21 {
-  typedef T1 Head;
-  typedef Types20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22>
-struct Types22 {
-  typedef T1 Head;
-  typedef Types21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23>
-struct Types23 {
-  typedef T1 Head;
-  typedef Types22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24>
-struct Types24 {
-  typedef T1 Head;
-  typedef Types23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25>
-struct Types25 {
-  typedef T1 Head;
-  typedef Types24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26>
-struct Types26 {
-  typedef T1 Head;
-  typedef Types25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27>
-struct Types27 {
-  typedef T1 Head;
-  typedef Types26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28>
-struct Types28 {
-  typedef T1 Head;
-  typedef Types27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29>
-struct Types29 {
-  typedef T1 Head;
-  typedef Types28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30>
-struct Types30 {
-  typedef T1 Head;
-  typedef Types29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31>
-struct Types31 {
-  typedef T1 Head;
-  typedef Types30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32>
-struct Types32 {
-  typedef T1 Head;
-  typedef Types31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33>
-struct Types33 {
-  typedef T1 Head;
-  typedef Types32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34>
-struct Types34 {
-  typedef T1 Head;
-  typedef Types33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35>
-struct Types35 {
-  typedef T1 Head;
-  typedef Types34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36>
-struct Types36 {
-  typedef T1 Head;
-  typedef Types35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37>
-struct Types37 {
-  typedef T1 Head;
-  typedef Types36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38>
-struct Types38 {
-  typedef T1 Head;
-  typedef Types37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39>
-struct Types39 {
-  typedef T1 Head;
-  typedef Types38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40>
-struct Types40 {
-  typedef T1 Head;
-  typedef Types39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41>
-struct Types41 {
-  typedef T1 Head;
-  typedef Types40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42>
-struct Types42 {
-  typedef T1 Head;
-  typedef Types41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43>
-struct Types43 {
-  typedef T1 Head;
-  typedef Types42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
-      T43> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44>
-struct Types44 {
-  typedef T1 Head;
-  typedef Types43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-      T44> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45>
-struct Types45 {
-  typedef T1 Head;
-  typedef Types44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-      T44, T45> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46>
-struct Types46 {
-  typedef T1 Head;
-  typedef Types45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-      T44, T45, T46> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46, typename T47>
-struct Types47 {
-  typedef T1 Head;
-  typedef Types46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-      T44, T45, T46, T47> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46, typename T47, typename T48>
-struct Types48 {
-  typedef T1 Head;
-  typedef Types47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-      T44, T45, T46, T47, T48> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46, typename T47, typename T48, typename T49>
-struct Types49 {
-  typedef T1 Head;
-  typedef Types48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-      T44, T45, T46, T47, T48, T49> Tail;
-};
-
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46, typename T47, typename T48, typename T49, typename T50>
-struct Types50 {
-  typedef T1 Head;
-  typedef Types49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-      T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-      T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-      T44, T45, T46, T47, T48, T49, T50> Tail;
-};
-
-
-}  // namespace internal
-
-// We don't want to require the users to write TypesN<...> directly,
-// as that would require them to count the length.  Types<...> is much
-// easier to write, but generates horrible messages when there is a
-// compiler error, as gcc insists on printing out each template
-// argument, even if it has the default value (this means Types<int>
-// will appear as Types<int, None, None, ..., None> in the compiler
-// errors).
-//
-// Our solution is to combine the best part of the two approaches: a
-// user would write Types<T1, ..., TN>, and Google Test will translate
-// that to TypesN<T1, ..., TN> internally to make error messages
-// readable.  The translation is done by the 'type' member of the
-// Types template.
-template <typename T1 = internal::None, typename T2 = internal::None,
-    typename T3 = internal::None, typename T4 = internal::None,
-    typename T5 = internal::None, typename T6 = internal::None,
-    typename T7 = internal::None, typename T8 = internal::None,
-    typename T9 = internal::None, typename T10 = internal::None,
-    typename T11 = internal::None, typename T12 = internal::None,
-    typename T13 = internal::None, typename T14 = internal::None,
-    typename T15 = internal::None, typename T16 = internal::None,
-    typename T17 = internal::None, typename T18 = internal::None,
-    typename T19 = internal::None, typename T20 = internal::None,
-    typename T21 = internal::None, typename T22 = internal::None,
-    typename T23 = internal::None, typename T24 = internal::None,
-    typename T25 = internal::None, typename T26 = internal::None,
-    typename T27 = internal::None, typename T28 = internal::None,
-    typename T29 = internal::None, typename T30 = internal::None,
-    typename T31 = internal::None, typename T32 = internal::None,
-    typename T33 = internal::None, typename T34 = internal::None,
-    typename T35 = internal::None, typename T36 = internal::None,
-    typename T37 = internal::None, typename T38 = internal::None,
-    typename T39 = internal::None, typename T40 = internal::None,
-    typename T41 = internal::None, typename T42 = internal::None,
-    typename T43 = internal::None, typename T44 = internal::None,
-    typename T45 = internal::None, typename T46 = internal::None,
-    typename T47 = internal::None, typename T48 = internal::None,
-    typename T49 = internal::None, typename T50 = internal::None>
-struct Types {
-  typedef internal::Types50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
-};
-
-template <>
-struct Types<internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types0 type;
-};
-template <typename T1>
-struct Types<T1, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types1<T1> type;
-};
-template <typename T1, typename T2>
-struct Types<T1, T2, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types2<T1, T2> type;
-};
-template <typename T1, typename T2, typename T3>
-struct Types<T1, T2, T3, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types3<T1, T2, T3> type;
-};
-template <typename T1, typename T2, typename T3, typename T4>
-struct Types<T1, T2, T3, T4, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types4<T1, T2, T3, T4> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5>
-struct Types<T1, T2, T3, T4, T5, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types5<T1, T2, T3, T4, T5> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6>
-struct Types<T1, T2, T3, T4, T5, T6, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types6<T1, T2, T3, T4, T5, T6> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7>
-struct Types<T1, T2, T3, T4, T5, T6, T7, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types7<T1, T2, T3, T4, T5, T6, T7> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types8<T1, T2, T3, T4, T5, T6, T7, T8> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,
-      T12> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25,
-      T26> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39,
-      T40> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, internal::None,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None, internal::None> {
-  typedef internal::Types43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42, T43> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None, internal::None> {
-  typedef internal::Types44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42, T43, T44> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
-    internal::None, internal::None, internal::None, internal::None,
-    internal::None> {
-  typedef internal::Types45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42, T43, T44, T45> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
-    T46, internal::None, internal::None, internal::None, internal::None> {
-  typedef internal::Types46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42, T43, T44, T45, T46> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46, typename T47>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
-    T46, T47, internal::None, internal::None, internal::None> {
-  typedef internal::Types47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42, T43, T44, T45, T46, T47> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46, typename T47, typename T48>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
-    T46, T47, T48, internal::None, internal::None> {
-  typedef internal::Types48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42, T43, T44, T45, T46, T47, T48> type;
-};
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46, typename T47, typename T48, typename T49>
-struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
-    T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30,
-    T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45,
-    T46, T47, T48, T49, internal::None> {
-  typedef internal::Types49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42, T43, T44, T45, T46, T47, T48, T49> type;
-};
-
-namespace internal {
-
 # define GTEST_TEMPLATE_ template <typename T> class
 
 // The template "selector" struct TemplateSel<Tmpl> is used to
@@ -1644,1692 +122,62 @@
 # define GTEST_BIND_(TmplSel, T) \
   TmplSel::template Bind<T>::type
 
-// A unique struct template used as the default value for the
-// arguments of class template Templates.  This allows us to simulate
-// variadic templates (e.g. Templates<int>, Templates<int, double>,
-// and etc), which C++ doesn't support directly.
-template <typename T>
-struct NoneT {};
-
-// The following family of struct and struct templates are used to
-// represent template lists.  In particular, TemplatesN<T1, T2, ...,
-// TN> represents a list of N templates (T1, T2, ..., and TN).  Except
-// for Templates0, every struct in the family has two member types:
-// Head for the selector of the first template in the list, and Tail
-// for the rest of the list.
-
-// The empty template list.
-struct Templates0 {};
-
-// Template lists of length 1, 2, 3, and so on.
-
-template <GTEST_TEMPLATE_ T1>
-struct Templates1 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates0 Tail;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
-struct Templates2 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates1<T2> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3>
-struct Templates3 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates2<T2, T3> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4>
-struct Templates4 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates3<T2, T3, T4> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5>
-struct Templates5 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates4<T2, T3, T4, T5> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6>
-struct Templates6 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates5<T2, T3, T4, T5, T6> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7>
-struct Templates7 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates6<T2, T3, T4, T5, T6, T7> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8>
-struct Templates8 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates7<T2, T3, T4, T5, T6, T7, T8> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9>
-struct Templates9 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates8<T2, T3, T4, T5, T6, T7, T8, T9> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10>
-struct Templates10 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11>
-struct Templates11 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12>
-struct Templates12 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13>
-struct Templates13 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14>
-struct Templates14 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15>
-struct Templates15 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16>
-struct Templates16 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17>
-struct Templates17 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18>
-struct Templates18 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19>
-struct Templates19 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20>
-struct Templates20 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21>
-struct Templates21 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22>
-struct Templates22 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23>
-struct Templates23 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24>
-struct Templates24 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25>
-struct Templates25 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26>
-struct Templates26 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27>
-struct Templates27 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28>
-struct Templates28 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29>
-struct Templates29 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30>
-struct Templates30 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31>
-struct Templates31 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32>
-struct Templates32 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33>
-struct Templates33 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34>
-struct Templates34 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35>
-struct Templates35 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36>
-struct Templates36 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37>
-struct Templates37 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38>
-struct Templates38 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39>
-struct Templates39 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40>
-struct Templates40 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41>
-struct Templates41 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42>
-struct Templates42 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43>
-struct Templates43 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
-      T43> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44>
-struct Templates44 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
-      T43, T44> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45>
-struct Templates45 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
-      T43, T44, T45> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
-    GTEST_TEMPLATE_ T46>
-struct Templates46 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
-      T43, T44, T45, T46> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
-    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47>
-struct Templates47 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
-      T43, T44, T45, T46, T47> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
-    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48>
-struct Templates48 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
-      T43, T44, T45, T46, T47, T48> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
-    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
-    GTEST_TEMPLATE_ T49>
-struct Templates49 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
-      T43, T44, T45, T46, T47, T48, T49> Tail;
-};
-
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
-    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
-    GTEST_TEMPLATE_ T49, GTEST_TEMPLATE_ T50>
-struct Templates50 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-      T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-      T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42,
-      T43, T44, T45, T46, T47, T48, T49, T50> Tail;
-};
-
-
-// We don't want to require the users to write TemplatesN<...> directly,
-// as that would require them to count the length.  Templates<...> is much
-// easier to write, but generates horrible messages when there is a
-// compiler error, as gcc insists on printing out each template
-// argument, even if it has the default value (this means Templates<list>
-// will appear as Templates<list, NoneT, NoneT, ..., NoneT> in the compiler
-// errors).
-//
-// Our solution is to combine the best part of the two approaches: a
-// user would write Templates<T1, ..., TN>, and Google Test will translate
-// that to TemplatesN<T1, ..., TN> internally to make error messages
-// readable.  The translation is done by the 'type' member of the
-// Templates template.
-template <GTEST_TEMPLATE_ T1 = NoneT, GTEST_TEMPLATE_ T2 = NoneT,
-    GTEST_TEMPLATE_ T3 = NoneT, GTEST_TEMPLATE_ T4 = NoneT,
-    GTEST_TEMPLATE_ T5 = NoneT, GTEST_TEMPLATE_ T6 = NoneT,
-    GTEST_TEMPLATE_ T7 = NoneT, GTEST_TEMPLATE_ T8 = NoneT,
-    GTEST_TEMPLATE_ T9 = NoneT, GTEST_TEMPLATE_ T10 = NoneT,
-    GTEST_TEMPLATE_ T11 = NoneT, GTEST_TEMPLATE_ T12 = NoneT,
-    GTEST_TEMPLATE_ T13 = NoneT, GTEST_TEMPLATE_ T14 = NoneT,
-    GTEST_TEMPLATE_ T15 = NoneT, GTEST_TEMPLATE_ T16 = NoneT,
-    GTEST_TEMPLATE_ T17 = NoneT, GTEST_TEMPLATE_ T18 = NoneT,
-    GTEST_TEMPLATE_ T19 = NoneT, GTEST_TEMPLATE_ T20 = NoneT,
-    GTEST_TEMPLATE_ T21 = NoneT, GTEST_TEMPLATE_ T22 = NoneT,
-    GTEST_TEMPLATE_ T23 = NoneT, GTEST_TEMPLATE_ T24 = NoneT,
-    GTEST_TEMPLATE_ T25 = NoneT, GTEST_TEMPLATE_ T26 = NoneT,
-    GTEST_TEMPLATE_ T27 = NoneT, GTEST_TEMPLATE_ T28 = NoneT,
-    GTEST_TEMPLATE_ T29 = NoneT, GTEST_TEMPLATE_ T30 = NoneT,
-    GTEST_TEMPLATE_ T31 = NoneT, GTEST_TEMPLATE_ T32 = NoneT,
-    GTEST_TEMPLATE_ T33 = NoneT, GTEST_TEMPLATE_ T34 = NoneT,
-    GTEST_TEMPLATE_ T35 = NoneT, GTEST_TEMPLATE_ T36 = NoneT,
-    GTEST_TEMPLATE_ T37 = NoneT, GTEST_TEMPLATE_ T38 = NoneT,
-    GTEST_TEMPLATE_ T39 = NoneT, GTEST_TEMPLATE_ T40 = NoneT,
-    GTEST_TEMPLATE_ T41 = NoneT, GTEST_TEMPLATE_ T42 = NoneT,
-    GTEST_TEMPLATE_ T43 = NoneT, GTEST_TEMPLATE_ T44 = NoneT,
-    GTEST_TEMPLATE_ T45 = NoneT, GTEST_TEMPLATE_ T46 = NoneT,
-    GTEST_TEMPLATE_ T47 = NoneT, GTEST_TEMPLATE_ T48 = NoneT,
-    GTEST_TEMPLATE_ T49 = NoneT, GTEST_TEMPLATE_ T50 = NoneT>
+template <GTEST_TEMPLATE_ Head_, GTEST_TEMPLATE_... Tail_>
 struct Templates {
-  typedef Templates50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
+  using Head = TemplateSel<Head_>;
+  using Tail = Templates<Tail_...>;
 };
 
-template <>
-struct Templates<NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT> {
-  typedef Templates0 type;
-};
-template <GTEST_TEMPLATE_ T1>
-struct Templates<T1, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT> {
-  typedef Templates1<T1> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
-struct Templates<T1, T2, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT> {
-  typedef Templates2<T1, T2> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3>
-struct Templates<T1, T2, T3, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates3<T1, T2, T3> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4>
-struct Templates<T1, T2, T3, T4, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates4<T1, T2, T3, T4> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5>
-struct Templates<T1, T2, T3, T4, T5, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates5<T1, T2, T3, T4, T5> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6>
-struct Templates<T1, T2, T3, T4, T5, T6, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates6<T1, T2, T3, T4, T5, T6> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates7<T1, T2, T3, T4, T5, T6, T7> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates8<T1, T2, T3, T4, T5, T6, T7, T8> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT> {
-  typedef Templates22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT> {
-  typedef Templates23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT> {
-  typedef Templates24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT> {
-  typedef Templates25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT> {
-  typedef Templates26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT> {
-  typedef Templates27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT> {
-  typedef Templates28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT> {
-  typedef Templates29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, NoneT, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, NoneT, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, NoneT, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, NoneT, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, NoneT,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42, T43> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
-    NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42, T43, T44> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
-    T45, NoneT, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42, T43, T44, T45> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
-    GTEST_TEMPLATE_ T46>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
-    T45, T46, NoneT, NoneT, NoneT, NoneT> {
-  typedef Templates46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42, T43, T44, T45, T46> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
-    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
-    T45, T46, T47, NoneT, NoneT, NoneT> {
-  typedef Templates47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42, T43, T44, T45, T46, T47> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
-    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
-    T45, T46, T47, T48, NoneT, NoneT> {
-  typedef Templates48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42, T43, T44, T45, T46, T47, T48> type;
-};
-template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3,
-    GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6,
-    GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9,
-    GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12,
-    GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15,
-    GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18,
-    GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21,
-    GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24,
-    GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27,
-    GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30,
-    GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33,
-    GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36,
-    GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39,
-    GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42,
-    GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45,
-    GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48,
-    GTEST_TEMPLATE_ T49>
-struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
-    T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29,
-    T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44,
-    T45, T46, T47, T48, T49, NoneT> {
-  typedef Templates49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-      T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27,
-      T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41,
-      T42, T43, T44, T45, T46, T47, T48, T49> type;
+template <GTEST_TEMPLATE_ Head_>
+struct Templates<Head_> {
+  using Head = TemplateSel<Head_>;
+  using Tail = None;
 };
 
-// The TypeList template makes it possible to use either a single type
-// or a Types<...> list in TYPED_TEST_SUITE() and
-// INSTANTIATE_TYPED_TEST_SUITE_P().
+// Tuple-like type lists
+template <typename Head_, typename... Tail_>
+struct Types {
+  using Head = Head_;
+  using Tail = Types<Tail_...>;
+};
 
+template <typename Head_>
+struct Types<Head_> {
+  using Head = Head_;
+  using Tail = None;
+};
+
+// Helper metafunctions to tell apart a single type from types
+// generated by ::testing::Types
+template <typename... Ts>
+struct ProxyTypeList {
+  using type = Types<Ts...>;
+};
+
+template <typename>
+struct is_proxy_type_list : std::false_type {};
+
+template <typename... Ts>
+struct is_proxy_type_list<ProxyTypeList<Ts...>> : std::true_type {};
+
+// Generator which conditionally creates type lists.
+// It recognizes if a requested type list should be created
+// and prevents creating a new type list nested within another one.
 template <typename T>
-struct TypeList {
-  typedef Types1<T> type;
-};
+struct GenerateTypeList {
+ private:
+  using proxy = typename std::conditional<is_proxy_type_list<T>::value, T,
+                                          ProxyTypeList<T>>::type;
 
-template <typename T1, typename T2, typename T3, typename T4, typename T5,
-    typename T6, typename T7, typename T8, typename T9, typename T10,
-    typename T11, typename T12, typename T13, typename T14, typename T15,
-    typename T16, typename T17, typename T18, typename T19, typename T20,
-    typename T21, typename T22, typename T23, typename T24, typename T25,
-    typename T26, typename T27, typename T28, typename T29, typename T30,
-    typename T31, typename T32, typename T33, typename T34, typename T35,
-    typename T36, typename T37, typename T38, typename T39, typename T40,
-    typename T41, typename T42, typename T43, typename T44, typename T45,
-    typename T46, typename T47, typename T48, typename T49, typename T50>
-struct TypeList<Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
-    T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28,
-    T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43,
-    T44, T45, T46, T47, T48, T49, T50> > {
-  typedef typename Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,
-      T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
-      T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
-      T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>::type type;
+ public:
+  using type = typename proxy::type;
 };
 
-#endif  // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
-
 }  // namespace internal
+
+template <typename... Ts>
+using Types = internal::ProxyTypeList<Ts...>;
+
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
diff --git a/ext/googletest/googletest/include/gtest/internal/gtest-type-util.h.pump b/ext/googletest/googletest/include/gtest/internal/gtest-type-util.h.pump
deleted file mode 100644
index 5e31b7b..0000000
--- a/ext/googletest/googletest/include/gtest/internal/gtest-type-util.h.pump
+++ /dev/null
@@ -1,302 +0,0 @@
-$$ -*- mode: c++; -*-
-$var n = 50  $$ Maximum length of type lists we want to support.
-// Copyright 2008 Google Inc.
-// All Rights Reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-// Type utilities needed for implementing typed and type-parameterized
-// tests.  This file is generated by a SCRIPT.  DO NOT EDIT BY HAND!
-//
-// Currently we support at most $n types in a list, and at most $n
-// type-parameterized tests in one type-parameterized test suite.
-// Please contact googletestframework@googlegroups.com if you need
-// more.
-
-// GOOGLETEST_CM0001 DO NOT DELETE
-
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
-
-#include "gtest/internal/gtest-port.h"
-
-// #ifdef __GNUC__ is too general here.  It is possible to use gcc without using
-// libstdc++ (which is where cxxabi.h comes from).
-# if GTEST_HAS_CXXABI_H_
-#  include <cxxabi.h>
-# elif defined(__HP_aCC)
-#  include <acxx_demangle.h>
-# endif  // GTEST_HASH_CXXABI_H_
-
-namespace testing {
-namespace internal {
-
-// Canonicalizes a given name with respect to the Standard C++ Library.
-// This handles removing the inline namespace within `std` that is
-// used by various standard libraries (e.g., `std::__1`).  Names outside
-// of namespace std are returned unmodified.
-inline std::string CanonicalizeForStdLibVersioning(std::string s) {
-  static const char prefix[] = "std::__";
-  if (s.compare(0, strlen(prefix), prefix) == 0) {
-    std::string::size_type end = s.find("::", strlen(prefix));
-    if (end != s.npos) {
-      // Erase everything between the initial `std` and the second `::`.
-      s.erase(strlen("std"), end - strlen("std"));
-    }
-  }
-  return s;
-}
-
-// GetTypeName<T>() returns a human-readable name of type T.
-// NB: This function is also used in Google Mock, so don't move it inside of
-// the typed-test-only section below.
-template <typename T>
-std::string GetTypeName() {
-# if GTEST_HAS_RTTI
-
-  const char* const name = typeid(T).name();
-#  if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
-  int status = 0;
-  // gcc's implementation of typeid(T).name() mangles the type name,
-  // so we have to demangle it.
-#   if GTEST_HAS_CXXABI_H_
-  using abi::__cxa_demangle;
-#   endif  // GTEST_HAS_CXXABI_H_
-  char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status);
-  const std::string name_str(status == 0 ? readable_name : name);
-  free(readable_name);
-  return CanonicalizeForStdLibVersioning(name_str);
-#  else
-  return name;
-#  endif  // GTEST_HAS_CXXABI_H_ || __HP_aCC
-
-# else
-
-  return "<type>";
-
-# endif  // GTEST_HAS_RTTI
-}
-
-#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
-
-// A unique type used as the default value for the arguments of class
-// template Types.  This allows us to simulate variadic templates
-// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't
-// support directly.
-struct None {};
-
-// The following family of struct and struct templates are used to
-// represent type lists.  In particular, TypesN<T1, T2, ..., TN>
-// represents a type list with N types (T1, T2, ..., and TN) in it.
-// Except for Types0, every struct in the family has two member types:
-// Head for the first type in the list, and Tail for the rest of the
-// list.
-
-// The empty type list.
-struct Types0 {};
-
-// Type lists of length 1, 2, 3, and so on.
-
-template <typename T1>
-struct Types1 {
-  typedef T1 Head;
-  typedef Types0 Tail;
-};
-
-$range i 2..n
-
-$for i [[
-$range j 1..i
-$range k 2..i
-template <$for j, [[typename T$j]]>
-struct Types$i {
-  typedef T1 Head;
-  typedef Types$(i-1)<$for k, [[T$k]]> Tail;
-};
-
-
-]]
-
-}  // namespace internal
-
-// We don't want to require the users to write TypesN<...> directly,
-// as that would require them to count the length.  Types<...> is much
-// easier to write, but generates horrible messages when there is a
-// compiler error, as gcc insists on printing out each template
-// argument, even if it has the default value (this means Types<int>
-// will appear as Types<int, None, None, ..., None> in the compiler
-// errors).
-//
-// Our solution is to combine the best part of the two approaches: a
-// user would write Types<T1, ..., TN>, and Google Test will translate
-// that to TypesN<T1, ..., TN> internally to make error messages
-// readable.  The translation is done by the 'type' member of the
-// Types template.
-
-$range i 1..n
-template <$for i, [[typename T$i = internal::None]]>
-struct Types {
-  typedef internal::Types$n<$for i, [[T$i]]> type;
-};
-
-template <>
-struct Types<$for i, [[internal::None]]> {
-  typedef internal::Types0 type;
-};
-
-$range i 1..n-1
-$for i [[
-$range j 1..i
-$range k i+1..n
-template <$for j, [[typename T$j]]>
-struct Types<$for j, [[T$j]]$for k[[, internal::None]]> {
-  typedef internal::Types$i<$for j, [[T$j]]> type;
-};
-
-]]
-
-namespace internal {
-
-# define GTEST_TEMPLATE_ template <typename T> class
-
-// The template "selector" struct TemplateSel<Tmpl> is used to
-// represent Tmpl, which must be a class template with one type
-// parameter, as a type.  TemplateSel<Tmpl>::Bind<T>::type is defined
-// as the type Tmpl<T>.  This allows us to actually instantiate the
-// template "selected" by TemplateSel<Tmpl>.
-//
-// This trick is necessary for simulating typedef for class templates,
-// which C++ doesn't support directly.
-template <GTEST_TEMPLATE_ Tmpl>
-struct TemplateSel {
-  template <typename T>
-  struct Bind {
-    typedef Tmpl<T> type;
-  };
-};
-
-# define GTEST_BIND_(TmplSel, T) \
-  TmplSel::template Bind<T>::type
-
-// A unique struct template used as the default value for the
-// arguments of class template Templates.  This allows us to simulate
-// variadic templates (e.g. Templates<int>, Templates<int, double>,
-// and etc), which C++ doesn't support directly.
-template <typename T>
-struct NoneT {};
-
-// The following family of struct and struct templates are used to
-// represent template lists.  In particular, TemplatesN<T1, T2, ...,
-// TN> represents a list of N templates (T1, T2, ..., and TN).  Except
-// for Templates0, every struct in the family has two member types:
-// Head for the selector of the first template in the list, and Tail
-// for the rest of the list.
-
-// The empty template list.
-struct Templates0 {};
-
-// Template lists of length 1, 2, 3, and so on.
-
-template <GTEST_TEMPLATE_ T1>
-struct Templates1 {
-  typedef TemplateSel<T1> Head;
-  typedef Templates0 Tail;
-};
-
-$range i 2..n
-
-$for i [[
-$range j 1..i
-$range k 2..i
-template <$for j, [[GTEST_TEMPLATE_ T$j]]>
-struct Templates$i {
-  typedef TemplateSel<T1> Head;
-  typedef Templates$(i-1)<$for k, [[T$k]]> Tail;
-};
-
-
-]]
-
-// We don't want to require the users to write TemplatesN<...> directly,
-// as that would require them to count the length.  Templates<...> is much
-// easier to write, but generates horrible messages when there is a
-// compiler error, as gcc insists on printing out each template
-// argument, even if it has the default value (this means Templates<list>
-// will appear as Templates<list, NoneT, NoneT, ..., NoneT> in the compiler
-// errors).
-//
-// Our solution is to combine the best part of the two approaches: a
-// user would write Templates<T1, ..., TN>, and Google Test will translate
-// that to TemplatesN<T1, ..., TN> internally to make error messages
-// readable.  The translation is done by the 'type' member of the
-// Templates template.
-
-$range i 1..n
-template <$for i, [[GTEST_TEMPLATE_ T$i = NoneT]]>
-struct Templates {
-  typedef Templates$n<$for i, [[T$i]]> type;
-};
-
-template <>
-struct Templates<$for i, [[NoneT]]> {
-  typedef Templates0 type;
-};
-
-$range i 1..n-1
-$for i [[
-$range j 1..i
-$range k i+1..n
-template <$for j, [[GTEST_TEMPLATE_ T$j]]>
-struct Templates<$for j, [[T$j]]$for k[[, NoneT]]> {
-  typedef Templates$i<$for j, [[T$j]]> type;
-};
-
-]]
-
-// The TypeList template makes it possible to use either a single type
-// or a Types<...> list in TYPED_TEST_SUITE() and
-// INSTANTIATE_TYPED_TEST_SUITE_P().
-
-template <typename T>
-struct TypeList {
-  typedef Types1<T> type;
-};
-
-
-$range i 1..n
-template <$for i, [[typename T$i]]>
-struct TypeList<Types<$for i, [[T$i]]> > {
-  typedef typename Types<$for i, [[T$i]]>::type type;
-};
-
-#endif  // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P
-
-}  // namespace internal
-}  // namespace testing
-
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
diff --git a/ext/googletest/googletest/samples/prime_tables.h b/ext/googletest/googletest/samples/prime_tables.h
index 72539bf..3a10352 100644
--- a/ext/googletest/googletest/samples/prime_tables.h
+++ b/ext/googletest/googletest/samples/prime_tables.h
@@ -33,8 +33,8 @@
 // prime and determines a next prime number. This interface is used
 // in Google Test samples demonstrating use of parameterized tests.
 
-#ifndef GTEST_SAMPLES_PRIME_TABLES_H_
-#define GTEST_SAMPLES_PRIME_TABLES_H_
+#ifndef GOOGLETEST_SAMPLES_PRIME_TABLES_H_
+#define GOOGLETEST_SAMPLES_PRIME_TABLES_H_
 
 #include <algorithm>
 
@@ -66,11 +66,11 @@
   }
 
   int GetNextPrime(int p) const override {
-    for (int n = p + 1; n > 0; n++) {
+    if (p < 0) return -1;
+
+    for (int n = p + 1;; n++) {
       if (IsPrime(n)) return n;
     }
-
-    return -1;
   }
 };
 
@@ -123,4 +123,4 @@
   void operator=(const PreCalculatedPrimeTable& rhs);
 };
 
-#endif  // GTEST_SAMPLES_PRIME_TABLES_H_
+#endif  // GOOGLETEST_SAMPLES_PRIME_TABLES_H_
diff --git a/ext/googletest/googletest/samples/sample1.h b/ext/googletest/googletest/samples/sample1.h
index 12e49de..ba392cf 100644
--- a/ext/googletest/googletest/samples/sample1.h
+++ b/ext/googletest/googletest/samples/sample1.h
@@ -29,8 +29,8 @@
 
 // A sample program demonstrating using Google C++ testing framework.
 
-#ifndef GTEST_SAMPLES_SAMPLE1_H_
-#define GTEST_SAMPLES_SAMPLE1_H_
+#ifndef GOOGLETEST_SAMPLES_SAMPLE1_H_
+#define GOOGLETEST_SAMPLES_SAMPLE1_H_
 
 // Returns n! (the factorial of n).  For negative n, n! is defined to be 1.
 int Factorial(int n);
@@ -38,4 +38,4 @@
 // Returns true if and only if n is a prime number.
 bool IsPrime(int n);
 
-#endif  // GTEST_SAMPLES_SAMPLE1_H_
+#endif  // GOOGLETEST_SAMPLES_SAMPLE1_H_
diff --git a/ext/googletest/googletest/samples/sample2.h b/ext/googletest/googletest/samples/sample2.h
index e9a5a70..0f98689 100644
--- a/ext/googletest/googletest/samples/sample2.h
+++ b/ext/googletest/googletest/samples/sample2.h
@@ -29,8 +29,8 @@
 
 // A sample program demonstrating using Google C++ testing framework.
 
-#ifndef GTEST_SAMPLES_SAMPLE2_H_
-#define GTEST_SAMPLES_SAMPLE2_H_
+#ifndef GOOGLETEST_SAMPLES_SAMPLE2_H_
+#define GOOGLETEST_SAMPLES_SAMPLE2_H_
 
 #include <string.h>
 
@@ -77,5 +77,4 @@
   void Set(const char* c_string);
 };
 
-
-#endif  // GTEST_SAMPLES_SAMPLE2_H_
+#endif  // GOOGLETEST_SAMPLES_SAMPLE2_H_
diff --git a/ext/googletest/googletest/samples/sample3-inl.h b/ext/googletest/googletest/samples/sample3-inl.h
index 80ba6b9..659e0f0 100644
--- a/ext/googletest/googletest/samples/sample3-inl.h
+++ b/ext/googletest/googletest/samples/sample3-inl.h
@@ -29,8 +29,8 @@
 
 // A sample program demonstrating using Google C++ testing framework.
 
-#ifndef GTEST_SAMPLES_SAMPLE3_INL_H_
-#define GTEST_SAMPLES_SAMPLE3_INL_H_
+#ifndef GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
+#define GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
 
 #include <stddef.h>
 
@@ -169,4 +169,4 @@
   const Queue& operator = (const Queue&);
 };
 
-#endif  // GTEST_SAMPLES_SAMPLE3_INL_H_
+#endif  // GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
diff --git a/ext/googletest/googletest/samples/sample4.h b/ext/googletest/googletest/samples/sample4.h
index e256f40..0c4ed92 100644
--- a/ext/googletest/googletest/samples/sample4.h
+++ b/ext/googletest/googletest/samples/sample4.h
@@ -28,8 +28,8 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // A sample program demonstrating using Google C++ testing framework.
-#ifndef GTEST_SAMPLES_SAMPLE4_H_
-#define GTEST_SAMPLES_SAMPLE4_H_
+#ifndef GOOGLETEST_SAMPLES_SAMPLE4_H_
+#define GOOGLETEST_SAMPLES_SAMPLE4_H_
 
 // A simple monotonic counter.
 class Counter {
@@ -50,4 +50,4 @@
   void Print() const;
 };
 
-#endif  // GTEST_SAMPLES_SAMPLE4_H_
+#endif  // GOOGLETEST_SAMPLES_SAMPLE4_H_
diff --git a/ext/googletest/googletest/samples/sample6_unittest.cc b/ext/googletest/googletest/samples/sample6_unittest.cc
index 0266e27..da317ee 100644
--- a/ext/googletest/googletest/samples/sample6_unittest.cc
+++ b/ext/googletest/googletest/samples/sample6_unittest.cc
@@ -73,8 +73,6 @@
   PrimeTable* const table_;
 };
 
-#if GTEST_HAS_TYPED_TEST
-
 using testing::Types;
 
 // Google Test offers two ways for reusing tests for different types.
@@ -134,10 +132,6 @@
 // in the type list specified in TYPED_TEST_SUITE.  Sit back and be
 // happy that you don't have to define them multiple times.
 
-#endif  // GTEST_HAS_TYPED_TEST
-
-#if GTEST_HAS_TYPED_TEST_P
-
 using testing::Types;
 
 // Sometimes, however, you don't yet know all the types that you want
@@ -220,5 +214,4 @@
                                PrimeTableTest2,             // Test case name
                                PrimeTableImplementations);  // Type list
 
-#endif  // GTEST_HAS_TYPED_TEST_P
 }  // namespace
diff --git a/ext/googletest/googletest/scripts/README.md b/ext/googletest/googletest/scripts/README.md
new file mode 100644
index 0000000..fa359fe
--- /dev/null
+++ b/ext/googletest/googletest/scripts/README.md
@@ -0,0 +1,5 @@
+# Please Note:
+
+Files in this directory are no longer supported by the maintainers. They
+represent mosty historical artifacts and supported by the community only. There
+is no guarantee whatsoever that these scripts still work.
diff --git a/ext/googletest/googletest/scripts/gen_gtest_pred_impl.py b/ext/googletest/googletest/scripts/gen_gtest_pred_impl.py
index b43efdf..e09a6e0 100755
--- a/ext/googletest/googletest/scripts/gen_gtest_pred_impl.py
+++ b/ext/googletest/googletest/scripts/gen_gtest_pred_impl.py
@@ -78,7 +78,7 @@
     }
 
   return (
-"""// Copyright 2006, Google Inc.
+  """// Copyright 2006, Google Inc.
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -111,6 +111,8 @@
 // '%(command)s'.  DO NOT EDIT BY HAND!
 //
 // Implements a family of generic predicate assertion macros.
+// GOOGLETEST_CM0001 DO NOT DELETE
+
 
 #ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
 #define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
@@ -246,8 +248,10 @@
 
   impl += ' << ") evaluates to false, where"'
 
-  impl += Iter(n, """
-                            << "\\n" << e%s << " evaluates to " << v%s""")
+  impl += Iter(
+      n, """
+      << "\\n" << e%s << " evaluates to " << ::testing::PrintToString(v%s)"""
+  )
 
   impl += """;
 }
@@ -333,7 +337,7 @@
     }
 
   return (
-"""// Copyright 2006, Google Inc.
+  """// Copyright 2006, Google Inc.
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -427,7 +431,7 @@
     }
 
   tests = (
-"""// Sample functions/functors for testing %(arity)s predicate assertions.
+  """// Sample functions/functors for testing %(arity)s predicate assertions.
 
 // A %(arity)s predicate function.
 template <%(types)s>
@@ -435,9 +439,8 @@
   return %(v_sum)s > 0;
 }
 
-// The following two functions are needed to circumvent a bug in
-// gcc 2.95.3, which sometimes has problem with the above template
-// function.
+// The following two functions are needed because a compiler doesn't have
+// a context yet to know which template function must be instantiated.
 bool PredFunction%(n)sInt(%(int_vs)s) {
   return %(v_sum)s > 0;
 }
@@ -510,7 +513,7 @@
 
 class Predicate%(n)sTest : public testing::Test {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     expected_to_finish_ = true;
     finished_ = false;""" % DEFS
 
@@ -520,7 +523,7 @@
 """
 
   tests += """
-  virtual void TearDown() {
+  void TearDown() override {
     // Verifies that each of the predicate's arguments was evaluated
     // exactly once."""
 
@@ -540,10 +543,10 @@
     }
   }
 
-  // true iff the test function is expected to run to finish.
+  // true if and only if the test function is expected to run to finish.
   static bool expected_to_finish_;
 
-  // true iff the test function did run to finish.
+  // true if and only if the test function did run to finish.
   static bool finished_;
 """ % DEFS
 
@@ -572,12 +575,12 @@
     """Returns the test for a predicate assertion macro.
 
     Args:
-      use_format:     true iff the assertion is a *_PRED_FORMAT*.
-      use_assert:     true iff the assertion is a ASSERT_*.
-      expect_failure: true iff the assertion is expected to fail.
-      use_functor:    true iff the first argument of the assertion is
+      use_format:     true if and only if the assertion is a *_PRED_FORMAT*.
+      use_assert:     true if and only if the assertion is a ASSERT_*.
+      expect_failure: true if and only if the assertion is expected to fail.
+      use_functor:    true if and only if the first argument of the assertion is
                       a functor (as opposed to a function)
-      use_user_type:  true iff the predicate functor/function takes
+      use_user_type:  true if and only if the predicate functor/function takes
                       argument(s) of a user-defined type.
 
     Example:
@@ -588,7 +591,7 @@
 
     if use_assert:
       assrt = 'ASSERT'  # 'assert' is reserved, so we cannot use
-                        # that identifier here.
+      # that identifier here.
     else:
       assrt = 'EXPECT'
 
diff --git a/ext/googletest/googletest/scripts/pump.py b/ext/googletest/googletest/scripts/pump.py
deleted file mode 100755
index 5efb653..0000000
--- a/ext/googletest/googletest/scripts/pump.py
+++ /dev/null
@@ -1,855 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2008, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""pump v0.2.0 - Pretty Useful for Meta Programming.
-
-A tool for preprocessor meta programming.  Useful for generating
-repetitive boilerplate code.  Especially useful for writing C++
-classes, functions, macros, and templates that need to work with
-various number of arguments.
-
-USAGE:
-       pump.py SOURCE_FILE
-
-EXAMPLES:
-       pump.py foo.cc.pump
-         Converts foo.cc.pump to foo.cc.
-
-GRAMMAR:
-       CODE ::= ATOMIC_CODE*
-       ATOMIC_CODE ::= $var ID = EXPRESSION
-           | $var ID = [[ CODE ]]
-           | $range ID EXPRESSION..EXPRESSION
-           | $for ID SEPARATOR [[ CODE ]]
-           | $($)
-           | $ID
-           | $(EXPRESSION)
-           | $if EXPRESSION [[ CODE ]] ELSE_BRANCH
-           | [[ CODE ]]
-           | RAW_CODE
-       SEPARATOR ::= RAW_CODE | EMPTY
-       ELSE_BRANCH ::= $else [[ CODE ]]
-           | $elif EXPRESSION [[ CODE ]] ELSE_BRANCH
-           | EMPTY
-       EXPRESSION has Python syntax.
-"""
-
-__author__ = 'wan@google.com (Zhanyong Wan)'
-
-import os
-import re
-import sys
-
-
-TOKEN_TABLE = [
-    (re.compile(r'\$var\s+'), '$var'),
-    (re.compile(r'\$elif\s+'), '$elif'),
-    (re.compile(r'\$else\s+'), '$else'),
-    (re.compile(r'\$for\s+'), '$for'),
-    (re.compile(r'\$if\s+'), '$if'),
-    (re.compile(r'\$range\s+'), '$range'),
-    (re.compile(r'\$[_A-Za-z]\w*'), '$id'),
-    (re.compile(r'\$\(\$\)'), '$($)'),
-    (re.compile(r'\$'), '$'),
-    (re.compile(r'\[\[\n?'), '[['),
-    (re.compile(r'\]\]\n?'), ']]'),
-    ]
-
-
-class Cursor:
-  """Represents a position (line and column) in a text file."""
-
-  def __init__(self, line=-1, column=-1):
-    self.line = line
-    self.column = column
-
-  def __eq__(self, rhs):
-    return self.line == rhs.line and self.column == rhs.column
-
-  def __ne__(self, rhs):
-    return not self == rhs
-
-  def __lt__(self, rhs):
-    return self.line < rhs.line or (
-        self.line == rhs.line and self.column < rhs.column)
-
-  def __le__(self, rhs):
-    return self < rhs or self == rhs
-
-  def __gt__(self, rhs):
-    return rhs < self
-
-  def __ge__(self, rhs):
-    return rhs <= self
-
-  def __str__(self):
-    if self == Eof():
-      return 'EOF'
-    else:
-      return '%s(%s)' % (self.line + 1, self.column)
-
-  def __add__(self, offset):
-    return Cursor(self.line, self.column + offset)
-
-  def __sub__(self, offset):
-    return Cursor(self.line, self.column - offset)
-
-  def Clone(self):
-    """Returns a copy of self."""
-
-    return Cursor(self.line, self.column)
-
-
-# Special cursor to indicate the end-of-file.
-def Eof():
-  """Returns the special cursor to denote the end-of-file."""
-  return Cursor(-1, -1)
-
-
-class Token:
-  """Represents a token in a Pump source file."""
-
-  def __init__(self, start=None, end=None, value=None, token_type=None):
-    if start is None:
-      self.start = Eof()
-    else:
-      self.start = start
-    if end is None:
-      self.end = Eof()
-    else:
-      self.end = end
-    self.value = value
-    self.token_type = token_type
-
-  def __str__(self):
-    return 'Token @%s: \'%s\' type=%s' % (
-        self.start, self.value, self.token_type)
-
-  def Clone(self):
-    """Returns a copy of self."""
-
-    return Token(self.start.Clone(), self.end.Clone(), self.value,
-                 self.token_type)
-
-
-def StartsWith(lines, pos, string):
-  """Returns True iff the given position in lines starts with 'string'."""
-
-  return lines[pos.line][pos.column:].startswith(string)
-
-
-def FindFirstInLine(line, token_table):
-  best_match_start = -1
-  for (regex, token_type) in token_table:
-    m = regex.search(line)
-    if m:
-      # We found regex in lines
-      if best_match_start < 0 or m.start() < best_match_start:
-        best_match_start = m.start()
-        best_match_length = m.end() - m.start()
-        best_match_token_type = token_type
-
-  if best_match_start < 0:
-    return None
-
-  return (best_match_start, best_match_length, best_match_token_type)
-
-
-def FindFirst(lines, token_table, cursor):
-  """Finds the first occurrence of any string in strings in lines."""
-
-  start = cursor.Clone()
-  cur_line_number = cursor.line
-  for line in lines[start.line:]:
-    if cur_line_number == start.line:
-      line = line[start.column:]
-    m = FindFirstInLine(line, token_table)
-    if m:
-      # We found a regex in line.
-      (start_column, length, token_type) = m
-      if cur_line_number == start.line:
-        start_column += start.column
-      found_start = Cursor(cur_line_number, start_column)
-      found_end = found_start + length
-      return MakeToken(lines, found_start, found_end, token_type)
-    cur_line_number += 1
-  # We failed to find str in lines
-  return None
-
-
-def SubString(lines, start, end):
-  """Returns a substring in lines."""
-
-  if end == Eof():
-    end = Cursor(len(lines) - 1, len(lines[-1]))
-
-  if start >= end:
-    return ''
-
-  if start.line == end.line:
-    return lines[start.line][start.column:end.column]
-
-  result_lines = ([lines[start.line][start.column:]] +
-                  lines[start.line + 1:end.line] +
-                  [lines[end.line][:end.column]])
-  return ''.join(result_lines)
-
-
-def StripMetaComments(str):
-  """Strip meta comments from each line in the given string."""
-
-  # First, completely remove lines containing nothing but a meta
-  # comment, including the trailing \n.
-  str = re.sub(r'^\s*\$\$.*\n', '', str)
-
-  # Then, remove meta comments from contentful lines.
-  return re.sub(r'\s*\$\$.*', '', str)
-
-
-def MakeToken(lines, start, end, token_type):
-  """Creates a new instance of Token."""
-
-  return Token(start, end, SubString(lines, start, end), token_type)
-
-
-def ParseToken(lines, pos, regex, token_type):
-  line = lines[pos.line][pos.column:]
-  m = regex.search(line)
-  if m and not m.start():
-    return MakeToken(lines, pos, pos + m.end(), token_type)
-  else:
-    print 'ERROR: %s expected at %s.' % (token_type, pos)
-    sys.exit(1)
-
-
-ID_REGEX = re.compile(r'[_A-Za-z]\w*')
-EQ_REGEX = re.compile(r'=')
-REST_OF_LINE_REGEX = re.compile(r'.*?(?=$|\$\$)')
-OPTIONAL_WHITE_SPACES_REGEX = re.compile(r'\s*')
-WHITE_SPACE_REGEX = re.compile(r'\s')
-DOT_DOT_REGEX = re.compile(r'\.\.')
-
-
-def Skip(lines, pos, regex):
-  line = lines[pos.line][pos.column:]
-  m = re.search(regex, line)
-  if m and not m.start():
-    return pos + m.end()
-  else:
-    return pos
-
-
-def SkipUntil(lines, pos, regex, token_type):
-  line = lines[pos.line][pos.column:]
-  m = re.search(regex, line)
-  if m:
-    return pos + m.start()
-  else:
-    print ('ERROR: %s expected on line %s after column %s.' %
-           (token_type, pos.line + 1, pos.column))
-    sys.exit(1)
-
-
-def ParseExpTokenInParens(lines, pos):
-  def ParseInParens(pos):
-    pos = Skip(lines, pos, OPTIONAL_WHITE_SPACES_REGEX)
-    pos = Skip(lines, pos, r'\(')
-    pos = Parse(pos)
-    pos = Skip(lines, pos, r'\)')
-    return pos
-
-  def Parse(pos):
-    pos = SkipUntil(lines, pos, r'\(|\)', ')')
-    if SubString(lines, pos, pos + 1) == '(':
-      pos = Parse(pos + 1)
-      pos = Skip(lines, pos, r'\)')
-      return Parse(pos)
-    else:
-      return pos
-
-  start = pos.Clone()
-  pos = ParseInParens(pos)
-  return MakeToken(lines, start, pos, 'exp')
-
-
-def RStripNewLineFromToken(token):
-  if token.value.endswith('\n'):
-    return Token(token.start, token.end, token.value[:-1], token.token_type)
-  else:
-    return token
-
-
-def TokenizeLines(lines, pos):
-  while True:
-    found = FindFirst(lines, TOKEN_TABLE, pos)
-    if not found:
-      yield MakeToken(lines, pos, Eof(), 'code')
-      return
-
-    if found.start == pos:
-      prev_token = None
-      prev_token_rstripped = None
-    else:
-      prev_token = MakeToken(lines, pos, found.start, 'code')
-      prev_token_rstripped = RStripNewLineFromToken(prev_token)
-
-    if found.token_type == '$var':
-      if prev_token_rstripped:
-        yield prev_token_rstripped
-      yield found
-      id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
-      yield id_token
-      pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX)
-
-      eq_token = ParseToken(lines, pos, EQ_REGEX, '=')
-      yield eq_token
-      pos = Skip(lines, eq_token.end, r'\s*')
-
-      if SubString(lines, pos, pos + 2) != '[[':
-        exp_token = ParseToken(lines, pos, REST_OF_LINE_REGEX, 'exp')
-        yield exp_token
-        pos = Cursor(exp_token.end.line + 1, 0)
-    elif found.token_type == '$for':
-      if prev_token_rstripped:
-        yield prev_token_rstripped
-      yield found
-      id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
-      yield id_token
-      pos = Skip(lines, id_token.end, WHITE_SPACE_REGEX)
-    elif found.token_type == '$range':
-      if prev_token_rstripped:
-        yield prev_token_rstripped
-      yield found
-      id_token = ParseToken(lines, found.end, ID_REGEX, 'id')
-      yield id_token
-      pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX)
-
-      dots_pos = SkipUntil(lines, pos, DOT_DOT_REGEX, '..')
-      yield MakeToken(lines, pos, dots_pos, 'exp')
-      yield MakeToken(lines, dots_pos, dots_pos + 2, '..')
-      pos = dots_pos + 2
-      new_pos = Cursor(pos.line + 1, 0)
-      yield MakeToken(lines, pos, new_pos, 'exp')
-      pos = new_pos
-    elif found.token_type == '$':
-      if prev_token:
-        yield prev_token
-      yield found
-      exp_token = ParseExpTokenInParens(lines, found.end)
-      yield exp_token
-      pos = exp_token.end
-    elif (found.token_type == ']]' or found.token_type == '$if' or
-          found.token_type == '$elif' or found.token_type == '$else'):
-      if prev_token_rstripped:
-        yield prev_token_rstripped
-      yield found
-      pos = found.end
-    else:
-      if prev_token:
-        yield prev_token
-      yield found
-      pos = found.end
-
-
-def Tokenize(s):
-  """A generator that yields the tokens in the given string."""
-  if s != '':
-    lines = s.splitlines(True)
-    for token in TokenizeLines(lines, Cursor(0, 0)):
-      yield token
-
-
-class CodeNode:
-  def __init__(self, atomic_code_list=None):
-    self.atomic_code = atomic_code_list
-
-
-class VarNode:
-  def __init__(self, identifier=None, atomic_code=None):
-    self.identifier = identifier
-    self.atomic_code = atomic_code
-
-
-class RangeNode:
-  def __init__(self, identifier=None, exp1=None, exp2=None):
-    self.identifier = identifier
-    self.exp1 = exp1
-    self.exp2 = exp2
-
-
-class ForNode:
-  def __init__(self, identifier=None, sep=None, code=None):
-    self.identifier = identifier
-    self.sep = sep
-    self.code = code
-
-
-class ElseNode:
-  def __init__(self, else_branch=None):
-    self.else_branch = else_branch
-
-
-class IfNode:
-  def __init__(self, exp=None, then_branch=None, else_branch=None):
-    self.exp = exp
-    self.then_branch = then_branch
-    self.else_branch = else_branch
-
-
-class RawCodeNode:
-  def __init__(self, token=None):
-    self.raw_code = token
-
-
-class LiteralDollarNode:
-  def __init__(self, token):
-    self.token = token
-
-
-class ExpNode:
-  def __init__(self, token, python_exp):
-    self.token = token
-    self.python_exp = python_exp
-
-
-def PopFront(a_list):
-  head = a_list[0]
-  a_list[:1] = []
-  return head
-
-
-def PushFront(a_list, elem):
-  a_list[:0] = [elem]
-
-
-def PopToken(a_list, token_type=None):
-  token = PopFront(a_list)
-  if token_type is not None and token.token_type != token_type:
-    print 'ERROR: %s expected at %s' % (token_type, token.start)
-    print 'ERROR: %s found instead' % (token,)
-    sys.exit(1)
-
-  return token
-
-
-def PeekToken(a_list):
-  if not a_list:
-    return None
-
-  return a_list[0]
-
-
-def ParseExpNode(token):
-  python_exp = re.sub(r'([_A-Za-z]\w*)', r'self.GetValue("\1")', token.value)
-  return ExpNode(token, python_exp)
-
-
-def ParseElseNode(tokens):
-  def Pop(token_type=None):
-    return PopToken(tokens, token_type)
-
-  next = PeekToken(tokens)
-  if not next:
-    return None
-  if next.token_type == '$else':
-    Pop('$else')
-    Pop('[[')
-    code_node = ParseCodeNode(tokens)
-    Pop(']]')
-    return code_node
-  elif next.token_type == '$elif':
-    Pop('$elif')
-    exp = Pop('code')
-    Pop('[[')
-    code_node = ParseCodeNode(tokens)
-    Pop(']]')
-    inner_else_node = ParseElseNode(tokens)
-    return CodeNode([IfNode(ParseExpNode(exp), code_node, inner_else_node)])
-  elif not next.value.strip():
-    Pop('code')
-    return ParseElseNode(tokens)
-  else:
-    return None
-
-
-def ParseAtomicCodeNode(tokens):
-  def Pop(token_type=None):
-    return PopToken(tokens, token_type)
-
-  head = PopFront(tokens)
-  t = head.token_type
-  if t == 'code':
-    return RawCodeNode(head)
-  elif t == '$var':
-    id_token = Pop('id')
-    Pop('=')
-    next = PeekToken(tokens)
-    if next.token_type == 'exp':
-      exp_token = Pop()
-      return VarNode(id_token, ParseExpNode(exp_token))
-    Pop('[[')
-    code_node = ParseCodeNode(tokens)
-    Pop(']]')
-    return VarNode(id_token, code_node)
-  elif t == '$for':
-    id_token = Pop('id')
-    next_token = PeekToken(tokens)
-    if next_token.token_type == 'code':
-      sep_token = next_token
-      Pop('code')
-    else:
-      sep_token = None
-    Pop('[[')
-    code_node = ParseCodeNode(tokens)
-    Pop(']]')
-    return ForNode(id_token, sep_token, code_node)
-  elif t == '$if':
-    exp_token = Pop('code')
-    Pop('[[')
-    code_node = ParseCodeNode(tokens)
-    Pop(']]')
-    else_node = ParseElseNode(tokens)
-    return IfNode(ParseExpNode(exp_token), code_node, else_node)
-  elif t == '$range':
-    id_token = Pop('id')
-    exp1_token = Pop('exp')
-    Pop('..')
-    exp2_token = Pop('exp')
-    return RangeNode(id_token, ParseExpNode(exp1_token),
-                     ParseExpNode(exp2_token))
-  elif t == '$id':
-    return ParseExpNode(Token(head.start + 1, head.end, head.value[1:], 'id'))
-  elif t == '$($)':
-    return LiteralDollarNode(head)
-  elif t == '$':
-    exp_token = Pop('exp')
-    return ParseExpNode(exp_token)
-  elif t == '[[':
-    code_node = ParseCodeNode(tokens)
-    Pop(']]')
-    return code_node
-  else:
-    PushFront(tokens, head)
-    return None
-
-
-def ParseCodeNode(tokens):
-  atomic_code_list = []
-  while True:
-    if not tokens:
-      break
-    atomic_code_node = ParseAtomicCodeNode(tokens)
-    if atomic_code_node:
-      atomic_code_list.append(atomic_code_node)
-    else:
-      break
-  return CodeNode(atomic_code_list)
-
-
-def ParseToAST(pump_src_text):
-  """Convert the given Pump source text into an AST."""
-  tokens = list(Tokenize(pump_src_text))
-  code_node = ParseCodeNode(tokens)
-  return code_node
-
-
-class Env:
-  def __init__(self):
-    self.variables = []
-    self.ranges = []
-
-  def Clone(self):
-    clone = Env()
-    clone.variables = self.variables[:]
-    clone.ranges = self.ranges[:]
-    return clone
-
-  def PushVariable(self, var, value):
-    # If value looks like an int, store it as an int.
-    try:
-      int_value = int(value)
-      if ('%s' % int_value) == value:
-        value = int_value
-    except Exception:
-      pass
-    self.variables[:0] = [(var, value)]
-
-  def PopVariable(self):
-    self.variables[:1] = []
-
-  def PushRange(self, var, lower, upper):
-    self.ranges[:0] = [(var, lower, upper)]
-
-  def PopRange(self):
-    self.ranges[:1] = []
-
-  def GetValue(self, identifier):
-    for (var, value) in self.variables:
-      if identifier == var:
-        return value
-
-    print 'ERROR: meta variable %s is undefined.' % (identifier,)
-    sys.exit(1)
-
-  def EvalExp(self, exp):
-    try:
-      result = eval(exp.python_exp)
-    except Exception, e:
-      print 'ERROR: caught exception %s: %s' % (e.__class__.__name__, e)
-      print ('ERROR: failed to evaluate meta expression %s at %s' %
-             (exp.python_exp, exp.token.start))
-      sys.exit(1)
-    return result
-
-  def GetRange(self, identifier):
-    for (var, lower, upper) in self.ranges:
-      if identifier == var:
-        return (lower, upper)
-
-    print 'ERROR: range %s is undefined.' % (identifier,)
-    sys.exit(1)
-
-
-class Output:
-  def __init__(self):
-    self.string = ''
-
-  def GetLastLine(self):
-    index = self.string.rfind('\n')
-    if index < 0:
-      return ''
-
-    return self.string[index + 1:]
-
-  def Append(self, s):
-    self.string += s
-
-
-def RunAtomicCode(env, node, output):
-  if isinstance(node, VarNode):
-    identifier = node.identifier.value.strip()
-    result = Output()
-    RunAtomicCode(env.Clone(), node.atomic_code, result)
-    value = result.string
-    env.PushVariable(identifier, value)
-  elif isinstance(node, RangeNode):
-    identifier = node.identifier.value.strip()
-    lower = int(env.EvalExp(node.exp1))
-    upper = int(env.EvalExp(node.exp2))
-    env.PushRange(identifier, lower, upper)
-  elif isinstance(node, ForNode):
-    identifier = node.identifier.value.strip()
-    if node.sep is None:
-      sep = ''
-    else:
-      sep = node.sep.value
-    (lower, upper) = env.GetRange(identifier)
-    for i in range(lower, upper + 1):
-      new_env = env.Clone()
-      new_env.PushVariable(identifier, i)
-      RunCode(new_env, node.code, output)
-      if i != upper:
-        output.Append(sep)
-  elif isinstance(node, RawCodeNode):
-    output.Append(node.raw_code.value)
-  elif isinstance(node, IfNode):
-    cond = env.EvalExp(node.exp)
-    if cond:
-      RunCode(env.Clone(), node.then_branch, output)
-    elif node.else_branch is not None:
-      RunCode(env.Clone(), node.else_branch, output)
-  elif isinstance(node, ExpNode):
-    value = env.EvalExp(node)
-    output.Append('%s' % (value,))
-  elif isinstance(node, LiteralDollarNode):
-    output.Append('$')
-  elif isinstance(node, CodeNode):
-    RunCode(env.Clone(), node, output)
-  else:
-    print 'BAD'
-    print node
-    sys.exit(1)
-
-
-def RunCode(env, code_node, output):
-  for atomic_code in code_node.atomic_code:
-    RunAtomicCode(env, atomic_code, output)
-
-
-def IsSingleLineComment(cur_line):
-  return '//' in cur_line
-
-
-def IsInPreprocessorDirective(prev_lines, cur_line):
-  if cur_line.lstrip().startswith('#'):
-    return True
-  return prev_lines and prev_lines[-1].endswith('\\')
-
-
-def WrapComment(line, output):
-  loc = line.find('//')
-  before_comment = line[:loc].rstrip()
-  if before_comment == '':
-    indent = loc
-  else:
-    output.append(before_comment)
-    indent = len(before_comment) - len(before_comment.lstrip())
-  prefix = indent*' ' + '// '
-  max_len = 80 - len(prefix)
-  comment = line[loc + 2:].strip()
-  segs = [seg for seg in re.split(r'(\w+\W*)', comment) if seg != '']
-  cur_line = ''
-  for seg in segs:
-    if len((cur_line + seg).rstrip()) < max_len:
-      cur_line += seg
-    else:
-      if cur_line.strip() != '':
-        output.append(prefix + cur_line.rstrip())
-      cur_line = seg.lstrip()
-  if cur_line.strip() != '':
-    output.append(prefix + cur_line.strip())
-
-
-def WrapCode(line, line_concat, output):
-  indent = len(line) - len(line.lstrip())
-  prefix = indent*' '  # Prefix of the current line
-  max_len = 80 - indent - len(line_concat)  # Maximum length of the current line
-  new_prefix = prefix + 4*' '  # Prefix of a continuation line
-  new_max_len = max_len - 4  # Maximum length of a continuation line
-  # Prefers to wrap a line after a ',' or ';'.
-  segs = [seg for seg in re.split(r'([^,;]+[,;]?)', line.strip()) if seg != '']
-  cur_line = ''  # The current line without leading spaces.
-  for seg in segs:
-    # If the line is still too long, wrap at a space.
-    while cur_line == '' and len(seg.strip()) > max_len:
-      seg = seg.lstrip()
-      split_at = seg.rfind(' ', 0, max_len)
-      output.append(prefix + seg[:split_at].strip() + line_concat)
-      seg = seg[split_at + 1:]
-      prefix = new_prefix
-      max_len = new_max_len
-
-    if len((cur_line + seg).rstrip()) < max_len:
-      cur_line = (cur_line + seg).lstrip()
-    else:
-      output.append(prefix + cur_line.rstrip() + line_concat)
-      prefix = new_prefix
-      max_len = new_max_len
-      cur_line = seg.lstrip()
-  if cur_line.strip() != '':
-    output.append(prefix + cur_line.strip())
-
-
-def WrapPreprocessorDirective(line, output):
-  WrapCode(line, ' \\', output)
-
-
-def WrapPlainCode(line, output):
-  WrapCode(line, '', output)
-
-
-def IsMultiLineIWYUPragma(line):
-  return re.search(r'/\* IWYU pragma: ', line)
-
-
-def IsHeaderGuardIncludeOrOneLineIWYUPragma(line):
-  return (re.match(r'^#(ifndef|define|endif\s*//)\s*[\w_]+\s*$', line) or
-          re.match(r'^#include\s', line) or
-          # Don't break IWYU pragmas, either; that causes iwyu.py problems.
-          re.search(r'// IWYU pragma: ', line))
-
-
-def WrapLongLine(line, output):
-  line = line.rstrip()
-  if len(line) <= 80:
-    output.append(line)
-  elif IsSingleLineComment(line):
-    if IsHeaderGuardIncludeOrOneLineIWYUPragma(line):
-      # The style guide made an exception to allow long header guard lines,
-      # includes and IWYU pragmas.
-      output.append(line)
-    else:
-      WrapComment(line, output)
-  elif IsInPreprocessorDirective(output, line):
-    if IsHeaderGuardIncludeOrOneLineIWYUPragma(line):
-      # The style guide made an exception to allow long header guard lines,
-      # includes and IWYU pragmas.
-      output.append(line)
-    else:
-      WrapPreprocessorDirective(line, output)
-  elif IsMultiLineIWYUPragma(line):
-    output.append(line)
-  else:
-    WrapPlainCode(line, output)
-
-
-def BeautifyCode(string):
-  lines = string.splitlines()
-  output = []
-  for line in lines:
-    WrapLongLine(line, output)
-  output2 = [line.rstrip() for line in output]
-  return '\n'.join(output2) + '\n'
-
-
-def ConvertFromPumpSource(src_text):
-  """Return the text generated from the given Pump source text."""
-  ast = ParseToAST(StripMetaComments(src_text))
-  output = Output()
-  RunCode(Env(), ast, output)
-  return BeautifyCode(output.string)
-
-
-def main(argv):
-  if len(argv) == 1:
-    print __doc__
-    sys.exit(1)
-
-  file_path = argv[-1]
-  output_str = ConvertFromPumpSource(file(file_path, 'r').read())
-  if file_path.endswith('.pump'):
-    output_file_path = file_path[:-5]
-  else:
-    output_file_path = '-'
-  if output_file_path == '-':
-    print output_str,
-  else:
-    output_file = file(output_file_path, 'w')
-    output_file.write('// This file was GENERATED by command:\n')
-    output_file.write('//     %s %s\n' %
-                      (os.path.basename(__file__), os.path.basename(file_path)))
-    output_file.write('// DO NOT EDIT BY HAND!!!\n\n')
-    output_file.write(output_str)
-    output_file.close()
-
-
-if __name__ == '__main__':
-  main(sys.argv)
diff --git a/ext/googletest/googletest/scripts/release_docs.py b/ext/googletest/googletest/scripts/release_docs.py
index 1291347..8d24f28 100755
--- a/ext/googletest/googletest/scripts/release_docs.py
+++ b/ext/googletest/googletest/scripts/release_docs.py
@@ -37,7 +37,7 @@
        interlinked wiki files.  When we release a new version of
        Google Test or Google Mock, we need to branch the wiki files
        such that users of a specific version of Google Test/Mock can
-       look up documenation relevant for that version.  This script
+       look up documentation relevant for that version.  This script
        automates that process by:
 
          - branching the current wiki pages (which document the
diff --git a/ext/googletest/googletest/scripts/run_with_path.py b/ext/googletest/googletest/scripts/run_with_path.py
new file mode 100755
index 0000000..d46ab4d
--- /dev/null
+++ b/ext/googletest/googletest/scripts/run_with_path.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Google Inc. All Rights Reserved.
+
+"""Runs program specified in the command line with the substituted PATH.
+
+   This script is needed for to support building under Pulse which is unable
+   to override the existing PATH variable.
+"""
+
+import os
+import subprocess
+import sys
+
+SUBST_PATH_ENV_VAR_NAME = "SUBST_PATH"
+
+def main():
+  if SUBST_PATH_ENV_VAR_NAME in os.environ:
+    os.environ["PATH"] = os.environ[SUBST_PATH_ENV_VAR_NAME]
+
+  exit_code = subprocess.Popen(sys.argv[1:]).wait()
+
+  # exit_code is negative (-signal) if the process has been terminated by
+  # a signal. Returning negative exit code is not portable and so we return
+  # 100 instead.
+  if exit_code < 0:
+    exit_code = 100
+
+  sys.exit(exit_code)
+
+if __name__ == "__main__":
+  main()
diff --git a/ext/googletest/googletest/scripts/upload.py b/ext/googletest/googletest/scripts/upload.py
index c852e4c..eba5711 100755
--- a/ext/googletest/googletest/scripts/upload.py
+++ b/ext/googletest/googletest/scripts/upload.py
@@ -1,18 +1,33 @@
 #!/usr/bin/env python
 #
-# Copyright 2007 Google Inc.
+# Copyright 2007, Google Inc.
+# All rights reserved.
 #
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
 #
-#     http://www.apache.org/licenses/LICENSE-2.0
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
 #
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 """Tool for uploading diffs from a version control system to the codereview app.
 
diff --git a/ext/googletest/googletest/src/gtest-death-test.cc b/ext/googletest/googletest/src/gtest-death-test.cc
index da09a1c..52af2c7 100644
--- a/ext/googletest/googletest/src/gtest-death-test.cc
+++ b/ext/googletest/googletest/src/gtest-death-test.cc
@@ -32,6 +32,7 @@
 
 #include "gtest/gtest-death-test.h"
 
+#include <functional>
 #include <utility>
 
 #include "gtest/internal/gtest-port.h"
@@ -95,9 +96,12 @@
 // used internally at Google, is "threadsafe".
 static const char kDefaultDeathTestStyle[] = GTEST_DEFAULT_DEATH_TEST_STYLE;
 
+}  // namespace testing
+
 GTEST_DEFINE_string_(
     death_test_style,
-    internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle),
+    testing::internal::StringFromGTestEnv("death_test_style",
+                                          testing::kDefaultDeathTestStyle),
     "Indicates how to run a death test in a forked child process: "
     "\"threadsafe\" (child process re-executes the test binary "
     "from the beginning, running only the specific death test) or "
@@ -106,7 +110,7 @@
 
 GTEST_DEFINE_bool_(
     death_test_use_fork,
-    internal::BoolFromGTestEnv("death_test_use_fork", false),
+    testing::internal::BoolFromGTestEnv("death_test_use_fork", false),
     "Instructs to use fork()/_exit() instead of clone() in death tests. "
     "Ignored and always uses fork() on POSIX systems where clone() is not "
     "implemented. Useful when running under valgrind or similar tools if "
@@ -116,7 +120,6 @@
     "work in 99% of the cases. Once valgrind is fixed, this flag will "
     "most likely be removed.");
 
-namespace internal {
 GTEST_DEFINE_string_(
     internal_run_death_test, "",
     "Indicates the file, line number, temporal index of "
@@ -125,7 +128,8 @@
     "the '|' characters.  This flag is specified if and only if the "
     "current process is a sub-process launched for running a thread-safe "
     "death test.  FOR INTERNAL USE ONLY.");
-}  // namespace internal
+
+namespace testing {
 
 #if GTEST_HAS_DEATH_TEST
 
@@ -147,12 +151,12 @@
 
   // On Windows and Fuchsia, death tests are thread-safe regardless of the value
   // of the death_test_style flag.
-  return !GTEST_FLAG(internal_run_death_test).empty();
+  return !GTEST_FLAG_GET(internal_run_death_test).empty();
 
 # else
 
-  if (GTEST_FLAG(death_test_style) == "threadsafe")
-    return !GTEST_FLAG(internal_run_death_test).empty();
+  if (GTEST_FLAG_GET(death_test_style) == "threadsafe")
+    return !GTEST_FLAG_GET(internal_run_death_test).empty();
   else
     return g_in_fast_death_test_child;
 #endif
@@ -247,7 +251,7 @@
     msg << "detected " << thread_count << " threads.";
   }
   msg << " See "
-         "https://github.com/google/googletest/blob/master/googletest/docs/"
+         "https://github.com/google/googletest/blob/master/docs/"
          "advanced.md#death-tests-and-threads"
       << " for more explanation and suggested solutions, especially if"
       << " this is the last message you see before your test times out.";
@@ -755,18 +759,18 @@
       nullptr));  // The even is unnamed.
   GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != nullptr);
   const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ +
-                                  kFilterFlag + "=" + info->test_suite_name() +
-                                  "." + info->name();
+                                  "filter=" + info->test_suite_name() + "." +
+                                  info->name();
   const std::string internal_flag =
-      std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag +
-      "=" + file_ + "|" + StreamableToString(line_) + "|" +
-      StreamableToString(death_test_index) + "|" +
+      std::string("--") + GTEST_FLAG_PREFIX_ +
+      "internal_run_death_test=" + file_ + "|" + StreamableToString(line_) +
+      "|" + StreamableToString(death_test_index) + "|" +
       StreamableToString(static_cast<unsigned int>(::GetCurrentProcessId())) +
       // size_t has the same width as pointers on both 32-bit and 64-bit
       // Windows platforms.
       // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx.
-      "|" + StreamableToString(reinterpret_cast<size_t>(write_handle)) +
-      "|" + StreamableToString(reinterpret_cast<size_t>(event_handle_.Get()));
+      "|" + StreamableToString(reinterpret_cast<size_t>(write_handle)) + "|" +
+      StreamableToString(reinterpret_cast<size_t>(event_handle_.Get()));
 
   char executable_path[_MAX_PATH + 1];  // NOLINT
   GTEST_DEATH_TEST_CHECK_(_MAX_PATH + 1 != ::GetModuleFileNameA(nullptr,
@@ -864,7 +868,7 @@
   }
 
   int size() {
-    return args_.size() - 1;
+    return static_cast<int>(args_.size()) - 1;
   }
 
  private:
@@ -890,18 +894,17 @@
 
   // Register to wait for the child process to terminate.
   status_zx = child_process_.wait_async(
-      port, kProcessKey, ZX_PROCESS_TERMINATED, ZX_WAIT_ASYNC_ONCE);
+      port, kProcessKey, ZX_PROCESS_TERMINATED, 0);
   GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
 
   // Register to wait for the socket to be readable or closed.
   status_zx = stderr_socket_.wait_async(
-      port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED,
-      ZX_WAIT_ASYNC_ONCE);
+      port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, 0);
   GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
 
   // Register to wait for an exception.
   status_zx = exception_channel_.wait_async(
-      port, kExceptionKey, ZX_CHANNEL_READABLE, ZX_WAIT_ASYNC_ONCE);
+      port, kExceptionKey, ZX_CHANNEL_READABLE, 0);
   GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
 
   bool process_terminated = false;
@@ -941,8 +944,7 @@
         } else {
           GTEST_DEATH_TEST_CHECK_(status_zx == ZX_ERR_SHOULD_WAIT);
           status_zx = stderr_socket_.wait_async(
-              port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED,
-              ZX_WAIT_ASYNC_ONCE);
+              port, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, 0);
           GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
         }
       } else {
@@ -955,12 +957,12 @@
   ReadAndInterpretStatusByte();
 
   zx_info_process_t buffer;
-  status_zx = child_process_.get_info(
-      ZX_INFO_PROCESS, &buffer, sizeof(buffer), nullptr, nullptr);
+  status_zx = child_process_.get_info(ZX_INFO_PROCESS, &buffer, sizeof(buffer),
+                                      nullptr, nullptr);
   GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK);
 
-  GTEST_DEATH_TEST_CHECK_(buffer.exited);
-  set_status(buffer.return_code);
+  GTEST_DEATH_TEST_CHECK_(buffer.flags & ZX_INFO_PROCESS_FLAG_EXITED);
+  set_status(static_cast<int>(buffer.return_code));
   return status();
 }
 
@@ -988,8 +990,8 @@
 
   // Build the child process command line.
   const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ +
-                                  kFilterFlag + "=" + info->test_suite_name() +
-                                  "." + info->name();
+                                  "filter=" + info->test_suite_name() + "." +
+                                  info->name();
   const std::string internal_flag =
       std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "="
       + file_ + "|"
@@ -1225,21 +1227,9 @@
   int close_fd;       // File descriptor to close; the read end of a pipe
 };
 
-#  if GTEST_OS_MAC
-inline char** GetEnviron() {
-  // When Google Test is built as a framework on MacOS X, the environ variable
-  // is unavailable. Apple's documentation (man environ) recommends using
-  // _NSGetEnviron() instead.
-  return *_NSGetEnviron();
-}
-#  else
-// Some POSIX platforms expect you to declare environ. extern "C" makes
-// it reside in the global namespace.
+#  if GTEST_OS_QNX
 extern "C" char** environ;
-inline char** GetEnviron() { return environ; }
-#  endif  // GTEST_OS_MAC
-
-#  if !GTEST_OS_QNX
+#  else  // GTEST_OS_QNX
 // The main function for a threadsafe-style death test child process.
 // This function is called in a clone()-ed process and thus must avoid
 // any potentially unsafe operations like malloc or libc functions.
@@ -1259,18 +1249,18 @@
     return EXIT_FAILURE;
   }
 
-  // We can safely call execve() as it's a direct system call.  We
+  // We can safely call execv() as it's almost a direct system call. We
   // cannot use execvp() as it's a libc function and thus potentially
-  // unsafe.  Since execve() doesn't search the PATH, the user must
+  // unsafe.  Since execv() doesn't search the PATH, the user must
   // invoke the test program via a valid path that contains at least
   // one path separator.
-  execve(args->argv[0], args->argv, GetEnviron());
-  DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " +
+  execv(args->argv[0], args->argv);
+  DeathTestAbort(std::string("execv(") + args->argv[0] + ", ...) in " +
                  original_dir + " failed: " +
                  GetLastErrnoDescription());
   return EXIT_FAILURE;
 }
-#  endif  // !GTEST_OS_QNX
+#  endif  // GTEST_OS_QNX
 
 #  if GTEST_HAS_CLONE
 // Two utility routines that together determine the direction the stack
@@ -1284,19 +1274,24 @@
 // correct answer.
 static void StackLowerThanAddress(const void* ptr,
                                   bool* result) GTEST_NO_INLINE_;
+// Make sure sanitizers do not tamper with the stack here.
+// Ideally, we want to use `__builtin_frame_address` instead of a local variable
+// address with sanitizer disabled, but it does not work when the
+// compiler optimizes the stack frame out, which happens on PowerPC targets.
 // HWAddressSanitizer add a random tag to the MSB of the local variable address,
 // making comparison result unpredictable.
+GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
 GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
 static void StackLowerThanAddress(const void* ptr, bool* result) {
-  int dummy;
-  *result = (&dummy < ptr);
+  int dummy = 0;
+  *result = std::less<const void*>()(&dummy, ptr);
 }
 
 // Make sure AddressSanitizer does not tamper with the stack here.
 GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
 GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
 static bool StackGrowsDown() {
-  int dummy;
+  int dummy = 0;
   bool result;
   StackLowerThanAddress(&dummy, &result);
   return result;
@@ -1339,8 +1334,7 @@
                                         fd_flags | FD_CLOEXEC));
   struct inheritance inherit = {0};
   // spawn is a system call.
-  child_pid =
-      spawn(args.argv[0], 0, nullptr, &inherit, args.argv, GetEnviron());
+  child_pid = spawn(args.argv[0], 0, nullptr, &inherit, args.argv, environ);
   // Restores the current working directory.
   GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1);
   GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd));
@@ -1360,11 +1354,11 @@
 #   endif  // GTEST_OS_LINUX
 
 #   if GTEST_HAS_CLONE
-  const bool use_fork = GTEST_FLAG(death_test_use_fork);
+  const bool use_fork = GTEST_FLAG_GET(death_test_use_fork);
 
   if (!use_fork) {
     static const bool stack_grows_down = StackGrowsDown();
-    const auto stack_size = static_cast<size_t>(getpagesize());
+    const auto stack_size = static_cast<size_t>(getpagesize() * 2);
     // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead.
     void* const stack = mmap(nullptr, stack_size, PROT_READ | PROT_WRITE,
                              MAP_ANON | MAP_PRIVATE, -1, 0);
@@ -1429,13 +1423,13 @@
   GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1);
 
   const std::string filter_flag = std::string("--") + GTEST_FLAG_PREFIX_ +
-                                  kFilterFlag + "=" + info->test_suite_name() +
-                                  "." + info->name();
-  const std::string internal_flag =
-      std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "="
-      + file_ + "|" + StreamableToString(line_) + "|"
-      + StreamableToString(death_test_index) + "|"
-      + StreamableToString(pipe_fd[1]);
+                                  "filter=" + info->test_suite_name() + "." +
+                                  info->name();
+  const std::string internal_flag = std::string("--") + GTEST_FLAG_PREFIX_ +
+                                    "internal_run_death_test=" + file_ + "|" +
+                                    StreamableToString(line_) + "|" +
+                                    StreamableToString(death_test_index) + "|" +
+                                    StreamableToString(pipe_fd[1]);
   Arguments args;
   args.AddArguments(GetArgvsForDeathTestChildProcess());
   args.AddArgument(filter_flag.c_str());
@@ -1491,32 +1485,32 @@
 
 # if GTEST_OS_WINDOWS
 
-  if (GTEST_FLAG(death_test_style) == "threadsafe" ||
-      GTEST_FLAG(death_test_style) == "fast") {
+  if (GTEST_FLAG_GET(death_test_style) == "threadsafe" ||
+      GTEST_FLAG_GET(death_test_style) == "fast") {
     *test = new WindowsDeathTest(statement, std::move(matcher), file, line);
   }
 
 # elif GTEST_OS_FUCHSIA
 
-  if (GTEST_FLAG(death_test_style) == "threadsafe" ||
-      GTEST_FLAG(death_test_style) == "fast") {
+  if (GTEST_FLAG_GET(death_test_style) == "threadsafe" ||
+      GTEST_FLAG_GET(death_test_style) == "fast") {
     *test = new FuchsiaDeathTest(statement, std::move(matcher), file, line);
   }
 
 # else
 
-  if (GTEST_FLAG(death_test_style) == "threadsafe") {
+  if (GTEST_FLAG_GET(death_test_style) == "threadsafe") {
     *test = new ExecDeathTest(statement, std::move(matcher), file, line);
-  } else if (GTEST_FLAG(death_test_style) == "fast") {
+  } else if (GTEST_FLAG_GET(death_test_style) == "fast") {
     *test = new NoExecDeathTest(statement, std::move(matcher));
   }
 
 # endif  // GTEST_OS_WINDOWS
 
   else {  // NOLINT - this is more readable than unbalanced brackets inside #if.
-    DeathTest::set_last_death_test_message(
-        "Unknown death test style \"" + GTEST_FLAG(death_test_style)
-        + "\" encountered");
+    DeathTest::set_last_death_test_message("Unknown death test style \"" +
+                                           GTEST_FLAG_GET(death_test_style) +
+                                           "\" encountered");
     return false;
   }
 
@@ -1593,14 +1587,14 @@
 // initialized from the GTEST_FLAG(internal_run_death_test) flag if
 // the flag is specified; otherwise returns NULL.
 InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
-  if (GTEST_FLAG(internal_run_death_test) == "") return nullptr;
+  if (GTEST_FLAG_GET(internal_run_death_test) == "") return nullptr;
 
   // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we
   // can use it here.
   int line = -1;
   int index = -1;
   ::std::vector< ::std::string> fields;
-  SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields);
+  SplitString(GTEST_FLAG_GET(internal_run_death_test), '|', &fields);
   int write_fd = -1;
 
 # if GTEST_OS_WINDOWS
@@ -1616,7 +1610,7 @@
       || !ParseNaturalNumber(fields[4], &write_handle_as_size_t)
       || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) {
     DeathTestAbort("Bad --gtest_internal_run_death_test flag: " +
-                   GTEST_FLAG(internal_run_death_test));
+                   GTEST_FLAG_GET(internal_run_death_test));
   }
   write_fd = GetStatusFileDescriptor(parent_process_id,
                                      write_handle_as_size_t,
@@ -1627,8 +1621,8 @@
   if (fields.size() != 3
       || !ParseNaturalNumber(fields[1], &line)
       || !ParseNaturalNumber(fields[2], &index)) {
-    DeathTestAbort("Bad --gtest_internal_run_death_test flag: "
-        + GTEST_FLAG(internal_run_death_test));
+    DeathTestAbort("Bad --gtest_internal_run_death_test flag: " +
+                   GTEST_FLAG_GET(internal_run_death_test));
   }
 
 # else
@@ -1637,8 +1631,8 @@
       || !ParseNaturalNumber(fields[1], &line)
       || !ParseNaturalNumber(fields[2], &index)
       || !ParseNaturalNumber(fields[3], &write_fd)) {
-    DeathTestAbort("Bad --gtest_internal_run_death_test flag: "
-        + GTEST_FLAG(internal_run_death_test));
+    DeathTestAbort("Bad --gtest_internal_run_death_test flag: " +
+                   GTEST_FLAG_GET(internal_run_death_test));
   }
 
 # endif  // GTEST_OS_WINDOWS
diff --git a/ext/googletest/googletest/src/gtest-filepath.cc b/ext/googletest/googletest/src/gtest-filepath.cc
index bd7b99f..0b56294 100644
--- a/ext/googletest/googletest/src/gtest-filepath.cc
+++ b/ext/googletest/googletest/src/gtest-filepath.cc
@@ -92,8 +92,9 @@
 
 // Returns the current working directory, or "" if unsuccessful.
 FilePath FilePath::GetCurrentDir() {
-#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \
-    GTEST_OS_WINDOWS_RT || ARDUINO || defined(ESP_PLATFORM)
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE ||         \
+    GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_ESP32 || \
+    GTEST_OS_XTENSA
   // These platforms do not have a current directory, so we just return
   // something reasonable.
   return FilePath(kCurrentDirectoryString);
@@ -209,7 +210,7 @@
   delete [] unicode;
   return attributes != kInvalidFileAttributes;
 #else
-  posix::StatStruct file_stat;
+  posix::StatStruct file_stat{};
   return posix::Stat(pathname_.c_str(), &file_stat) == 0;
 #endif  // GTEST_OS_WINDOWS_MOBILE
 }
@@ -236,7 +237,7 @@
     result = true;
   }
 #else
-  posix::StatStruct file_stat;
+  posix::StatStruct file_stat{};
   result = posix::Stat(path.c_str(), &file_stat) == 0 &&
       posix::IsDir(file_stat);
 #endif  // GTEST_OS_WINDOWS_MOBILE
@@ -323,6 +324,9 @@
   delete [] unicode;
 #elif GTEST_OS_WINDOWS
   int result = _mkdir(pathname_.c_str());
+#elif GTEST_OS_ESP8266 || GTEST_OS_XTENSA
+  // do nothing
+  int result = 0;
 #else
   int result = mkdir(pathname_.c_str(), 0777);
 #endif  // GTEST_OS_WINDOWS_MOBILE
@@ -346,33 +350,19 @@
 // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
 // redundancies that might be in a pathname involving "." or "..".
 void FilePath::Normalize() {
-  if (pathname_.c_str() == nullptr) {
-    pathname_ = "";
-    return;
-  }
-  const char* src = pathname_.c_str();
-  char* const dest = new char[pathname_.length() + 1];
-  char* dest_ptr = dest;
-  memset(dest_ptr, 0, pathname_.length() + 1);
+  auto out = pathname_.begin();
 
-  while (*src != '\0') {
-    *dest_ptr = *src;
-    if (!IsPathSeparator(*src)) {
-      src++;
+  for (const char character : pathname_) {
+    if (!IsPathSeparator(character)) {
+      *(out++) = character;
+    } else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) {
+      *(out++) = kPathSeparator;
     } else {
-#if GTEST_HAS_ALT_PATH_SEP_
-      if (*dest_ptr == kAlternatePathSeparator) {
-        *dest_ptr = kPathSeparator;
-      }
-#endif
-      while (IsPathSeparator(*src))
-        src++;
+      continue;
     }
-    dest_ptr++;
   }
-  *dest_ptr = '\0';
-  pathname_ = dest;
-  delete[] dest;
+
+  pathname_.erase(out, pathname_.end());
 }
 
 }  // namespace internal
diff --git a/ext/googletest/googletest/src/gtest-internal-inl.h b/ext/googletest/googletest/src/gtest-internal-inl.h
index 8ed70da..075b84c 100644
--- a/ext/googletest/googletest/src/gtest-internal-inl.h
+++ b/ext/googletest/googletest/src/gtest-internal-inl.h
@@ -31,8 +31,8 @@
 // This file contains purely Google Test's internal implementation.  Please
 // DO NOT #INCLUDE IT IN A USER PROGRAM.
 
-#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_
-#define GTEST_SRC_GTEST_INTERNAL_INL_H_
+#ifndef GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_
+#define GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_
 
 #ifndef _WIN32_WCE
 # include <errno.h>
@@ -42,6 +42,7 @@
 #include <string.h>  // For memmove.
 
 #include <algorithm>
+#include <cstdint>
 #include <memory>
 #include <string>
 #include <vector>
@@ -63,8 +64,6 @@
 GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
 /* class A needs to have dll-interface to be used by clients of class B */)
 
-namespace testing {
-
 // Declares the flags.
 //
 // We don't want the users to modify this flag in the code, but want
@@ -72,30 +71,13 @@
 // declare it here as opposed to in gtest.h.
 GTEST_DECLARE_bool_(death_test_use_fork);
 
+namespace testing {
 namespace internal {
 
 // The value of GetTestTypeId() as seen from within the Google Test
 // library.  This is solely for testing GetTestTypeId().
 GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest;
 
-// Names of the flags (needed for parsing Google Test flags).
-const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests";
-const char kBreakOnFailureFlag[] = "break_on_failure";
-const char kCatchExceptionsFlag[] = "catch_exceptions";
-const char kColorFlag[] = "color";
-const char kFilterFlag[] = "filter";
-const char kListTestsFlag[] = "list_tests";
-const char kOutputFlag[] = "output";
-const char kPrintTimeFlag[] = "print_time";
-const char kPrintUTF8Flag[] = "print_utf8";
-const char kRandomSeedFlag[] = "random_seed";
-const char kRepeatFlag[] = "repeat";
-const char kShuffleFlag[] = "shuffle";
-const char kStackTraceDepthFlag[] = "stack_trace_depth";
-const char kStreamResultToFlag[] = "stream_result_to";
-const char kThrowOnFailureFlag[] = "throw_on_failure";
-const char kFlagfileFlag[] = "flagfile";
-
 // A valid random seed must be in [1, kMaxRandomSeed].
 const int kMaxRandomSeed = 99999;
 
@@ -122,12 +104,11 @@
 //
 // On success, stores the value of the flag in *value, and returns
 // true.  On failure, returns false without changing *value.
-GTEST_API_ bool ParseInt32Flag(
-    const char* str, const char* flag, Int32* value);
+GTEST_API_ bool ParseFlag(const char* str, const char* flag, int32_t* value);
 
 // Returns a random seed in range [1, kMaxRandomSeed] based on the
 // given --gtest_random_seed flag value.
-inline int GetRandomSeedFromFlag(Int32 random_seed_flag) {
+inline int GetRandomSeedFromFlag(int32_t random_seed_flag) {
   const unsigned int raw_seed = (random_seed_flag == 0) ?
       static_cast<unsigned int>(GetTimeInMillis()) :
       static_cast<unsigned int>(random_seed_flag);
@@ -157,46 +138,54 @@
  public:
   // The c'tor.
   GTestFlagSaver() {
-    also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests);
-    break_on_failure_ = GTEST_FLAG(break_on_failure);
-    catch_exceptions_ = GTEST_FLAG(catch_exceptions);
-    color_ = GTEST_FLAG(color);
-    death_test_style_ = GTEST_FLAG(death_test_style);
-    death_test_use_fork_ = GTEST_FLAG(death_test_use_fork);
-    filter_ = GTEST_FLAG(filter);
-    internal_run_death_test_ = GTEST_FLAG(internal_run_death_test);
-    list_tests_ = GTEST_FLAG(list_tests);
-    output_ = GTEST_FLAG(output);
-    print_time_ = GTEST_FLAG(print_time);
-    print_utf8_ = GTEST_FLAG(print_utf8);
-    random_seed_ = GTEST_FLAG(random_seed);
-    repeat_ = GTEST_FLAG(repeat);
-    shuffle_ = GTEST_FLAG(shuffle);
-    stack_trace_depth_ = GTEST_FLAG(stack_trace_depth);
-    stream_result_to_ = GTEST_FLAG(stream_result_to);
-    throw_on_failure_ = GTEST_FLAG(throw_on_failure);
+    also_run_disabled_tests_ = GTEST_FLAG_GET(also_run_disabled_tests);
+    break_on_failure_ = GTEST_FLAG_GET(break_on_failure);
+    catch_exceptions_ = GTEST_FLAG_GET(catch_exceptions);
+    color_ = GTEST_FLAG_GET(color);
+    death_test_style_ = GTEST_FLAG_GET(death_test_style);
+    death_test_use_fork_ = GTEST_FLAG_GET(death_test_use_fork);
+    fail_fast_ = GTEST_FLAG_GET(fail_fast);
+    filter_ = GTEST_FLAG_GET(filter);
+    internal_run_death_test_ = GTEST_FLAG_GET(internal_run_death_test);
+    list_tests_ = GTEST_FLAG_GET(list_tests);
+    output_ = GTEST_FLAG_GET(output);
+    brief_ = GTEST_FLAG_GET(brief);
+    print_time_ = GTEST_FLAG_GET(print_time);
+    print_utf8_ = GTEST_FLAG_GET(print_utf8);
+    random_seed_ = GTEST_FLAG_GET(random_seed);
+    repeat_ = GTEST_FLAG_GET(repeat);
+    recreate_environments_when_repeating_ =
+        GTEST_FLAG_GET(recreate_environments_when_repeating);
+    shuffle_ = GTEST_FLAG_GET(shuffle);
+    stack_trace_depth_ = GTEST_FLAG_GET(stack_trace_depth);
+    stream_result_to_ = GTEST_FLAG_GET(stream_result_to);
+    throw_on_failure_ = GTEST_FLAG_GET(throw_on_failure);
   }
 
   // The d'tor is not virtual.  DO NOT INHERIT FROM THIS CLASS.
   ~GTestFlagSaver() {
-    GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_;
-    GTEST_FLAG(break_on_failure) = break_on_failure_;
-    GTEST_FLAG(catch_exceptions) = catch_exceptions_;
-    GTEST_FLAG(color) = color_;
-    GTEST_FLAG(death_test_style) = death_test_style_;
-    GTEST_FLAG(death_test_use_fork) = death_test_use_fork_;
-    GTEST_FLAG(filter) = filter_;
-    GTEST_FLAG(internal_run_death_test) = internal_run_death_test_;
-    GTEST_FLAG(list_tests) = list_tests_;
-    GTEST_FLAG(output) = output_;
-    GTEST_FLAG(print_time) = print_time_;
-    GTEST_FLAG(print_utf8) = print_utf8_;
-    GTEST_FLAG(random_seed) = random_seed_;
-    GTEST_FLAG(repeat) = repeat_;
-    GTEST_FLAG(shuffle) = shuffle_;
-    GTEST_FLAG(stack_trace_depth) = stack_trace_depth_;
-    GTEST_FLAG(stream_result_to) = stream_result_to_;
-    GTEST_FLAG(throw_on_failure) = throw_on_failure_;
+    GTEST_FLAG_SET(also_run_disabled_tests, also_run_disabled_tests_);
+    GTEST_FLAG_SET(break_on_failure, break_on_failure_);
+    GTEST_FLAG_SET(catch_exceptions, catch_exceptions_);
+    GTEST_FLAG_SET(color, color_);
+    GTEST_FLAG_SET(death_test_style, death_test_style_);
+    GTEST_FLAG_SET(death_test_use_fork, death_test_use_fork_);
+    GTEST_FLAG_SET(filter, filter_);
+    GTEST_FLAG_SET(fail_fast, fail_fast_);
+    GTEST_FLAG_SET(internal_run_death_test, internal_run_death_test_);
+    GTEST_FLAG_SET(list_tests, list_tests_);
+    GTEST_FLAG_SET(output, output_);
+    GTEST_FLAG_SET(brief, brief_);
+    GTEST_FLAG_SET(print_time, print_time_);
+    GTEST_FLAG_SET(print_utf8, print_utf8_);
+    GTEST_FLAG_SET(random_seed, random_seed_);
+    GTEST_FLAG_SET(repeat, repeat_);
+    GTEST_FLAG_SET(recreate_environments_when_repeating,
+                   recreate_environments_when_repeating_);
+    GTEST_FLAG_SET(shuffle, shuffle_);
+    GTEST_FLAG_SET(stack_trace_depth, stack_trace_depth_);
+    GTEST_FLAG_SET(stream_result_to, stream_result_to_);
+    GTEST_FLAG_SET(throw_on_failure, throw_on_failure_);
   }
 
  private:
@@ -207,16 +196,19 @@
   std::string color_;
   std::string death_test_style_;
   bool death_test_use_fork_;
+  bool fail_fast_;
   std::string filter_;
   std::string internal_run_death_test_;
   bool list_tests_;
   std::string output_;
+  bool brief_;
   bool print_time_;
   bool print_utf8_;
-  internal::Int32 random_seed_;
-  internal::Int32 repeat_;
+  int32_t random_seed_;
+  int32_t repeat_;
+  bool recreate_environments_when_repeating_;
   bool shuffle_;
-  internal::Int32 stack_trace_depth_;
+  int32_t stack_trace_depth_;
   std::string stream_result_to_;
   bool throw_on_failure_;
 } GTEST_ATTRIBUTE_UNUSED_;
@@ -227,7 +219,7 @@
 // If the code_point is not a valid Unicode code point
 // (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted
 // to "(Invalid Unicode 0xXXXXXXXX)".
-GTEST_API_ std::string CodePointToUtf8(UInt32 code_point);
+GTEST_API_ std::string CodePointToUtf8(uint32_t code_point);
 
 // Converts a wide string to a narrow string in UTF-8 encoding.
 // The wide string is assumed to have the following encoding:
@@ -260,10 +252,10 @@
                             const char* shard_index_str,
                             bool in_subprocess_for_death_test);
 
-// Parses the environment variable var as an Int32. If it is unset,
-// returns default_val. If it is not an Int32, prints an error and
+// Parses the environment variable var as a 32-bit integer. If it is unset,
+// returns default_val. If it is not a 32-bit integer, prints an error and
 // and aborts.
-GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val);
+GTEST_API_ int32_t Int32FromEnvOrDie(const char* env_var, int32_t default_val);
 
 // Given the total number of shards, the shard index, and the test id,
 // returns true if and only if the test should be run on this shard. The test id
@@ -323,7 +315,7 @@
     const int last_in_range = begin + range_width - 1;
     const int selected =
         begin +
-        static_cast<int>(random->Generate(static_cast<UInt32>(range_width)));
+        static_cast<int>(random->Generate(static_cast<uint32_t>(range_width)));
     std::swap((*v)[static_cast<size_t>(selected)],
               (*v)[static_cast<size_t>(last_in_range)]);
   }
@@ -385,13 +377,6 @@
 
   // Functions for processing the gtest_filter flag.
 
-  // Returns true if and only if the wildcard pattern matches the string.
-  // The first ':' or '\0' character in pattern marks the end of it.
-  //
-  // This recursive algorithm isn't very efficient, but is clear and
-  // works well enough for matching test names, which are short.
-  static bool PatternMatchesString(const char *pattern, const char *str);
-
   // Returns true if and only if the user-specified filter matches the test
   // suite name and the test name.
   static bool FilterMatchesTest(const std::string& test_suite_name,
@@ -646,10 +631,10 @@
   // Arguments:
   //
   //   test_suite_name: name of the test suite
-  //   type_param:     the name of the test's type parameter, or NULL if
-  //                   this is not a typed or a type-parameterized test.
-  //   set_up_tc:      pointer to the function that sets up the test suite
-  //   tear_down_tc:   pointer to the function that tears down the test suite
+  //   type_param:      the name of the test's type parameter, or NULL if
+  //                    this is not a typed or a type-parameterized test.
+  //   set_up_tc:       pointer to the function that sets up the test suite
+  //   tear_down_tc:    pointer to the function that tears down the test suite
   TestSuite* GetTestSuite(const char* test_suite_name, const char* type_param,
                           internal::SetUpTestSuiteFunc set_up_tc,
                           internal::TearDownTestSuiteFunc tear_down_tc);
@@ -673,6 +658,7 @@
   void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc,
                    internal::TearDownTestSuiteFunc tear_down_tc,
                    TestInfo* test_info) {
+#if GTEST_HAS_DEATH_TEST
     // In order to support thread-safe death tests, we need to
     // remember the original working directory when the test program
     // was first invoked.  We cannot do this in RUN_ALL_TESTS(), as
@@ -685,6 +671,7 @@
       GTEST_CHECK_(!original_working_dir_.IsEmpty())
           << "Failed to get the current working directory.";
     }
+#endif  // GTEST_HAS_DEATH_TEST
 
     GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
                  set_up_tc, tear_down_tc)
@@ -697,6 +684,17 @@
     return parameterized_test_registry_;
   }
 
+  std::set<std::string>* ignored_parameterized_test_suites() {
+    return &ignored_parameterized_test_suites_;
+  }
+
+  // Returns TypeParameterizedTestSuiteRegistry object used to keep track of
+  // type-parameterized tests and instantiations of them.
+  internal::TypeParameterizedTestSuiteRegistry&
+  type_parameterized_test_registry() {
+    return type_parameterized_test_registry_;
+  }
+
   // Sets the TestSuite object for the test that's currently running.
   void set_current_test_suite(TestSuite* a_current_test_suite) {
     current_test_suite_ = a_current_test_suite;
@@ -873,6 +871,12 @@
   // ParameterizedTestRegistry object used to register value-parameterized
   // tests.
   internal::ParameterizedTestSuiteRegistry parameterized_test_registry_;
+  internal::TypeParameterizedTestSuiteRegistry
+      type_parameterized_test_registry_;
+
+  // The set holding the name of parameterized
+  // test suites that may go uninstantiated.
+  std::set<std::string> ignored_parameterized_test_suites_;
 
   // Indicates whether RegisterParameterizedTests() has been called already.
   bool parameterized_tests_registered_;
@@ -999,20 +1003,9 @@
   char* end;
   // BiggestConvertible is the largest integer type that system-provided
   // string-to-number conversion routines can return.
+  using BiggestConvertible = unsigned long long;  // NOLINT
 
-# if GTEST_OS_WINDOWS && !defined(__GNUC__)
-
-  // MSVC and C++ Builder define __int64 instead of the standard long long.
-  typedef unsigned __int64 BiggestConvertible;
-  const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10);
-
-# else
-
-  typedef unsigned long long BiggestConvertible;  // NOLINT
-  const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10);
-
-# endif  // GTEST_OS_WINDOWS && !defined(__GNUC__)
-
+  const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10);  // NOLINT
   const bool parse_success = *end == '\0' && errno == 0;
 
   GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed));
@@ -1154,13 +1147,13 @@
   }
 
   // Note that "event=TestCaseStart" is a wire format and has to remain
-  // "case" for compatibilty
+  // "case" for compatibility
   void OnTestCaseStart(const TestCase& test_case) override {
     SendLn(std::string("event=TestCaseStart&name=") + test_case.name());
   }
 
   // Note that "event=TestCaseEnd" is a wire format and has to remain
-  // "case" for compatibilty
+  // "case" for compatibility
   void OnTestCaseEnd(const TestCase& test_case) override {
     SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) +
            "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) +
@@ -1208,4 +1201,4 @@
 
 GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
 
-#endif  // GTEST_SRC_GTEST_INTERNAL_INL_H_
+#endif  // GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_
diff --git a/ext/googletest/googletest/src/gtest-matchers.cc b/ext/googletest/googletest/src/gtest-matchers.cc
index 7d2fb68..65104eb 100644
--- a/ext/googletest/googletest/src/gtest-matchers.cc
+++ b/ext/googletest/googletest/src/gtest-matchers.cc
@@ -58,40 +58,40 @@
 // s.
 Matcher<std::string>::Matcher(const char* s) { *this = Eq(std::string(s)); }
 
-#if GTEST_HAS_ABSL
-// Constructs a matcher that matches a const absl::string_view& whose value is
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+// Constructs a matcher that matches a const StringView& whose value is
 // equal to s.
-Matcher<const absl::string_view&>::Matcher(const std::string& s) {
+Matcher<const internal::StringView&>::Matcher(const std::string& s) {
   *this = Eq(s);
 }
 
-// Constructs a matcher that matches a const absl::string_view& whose value is
+// Constructs a matcher that matches a const StringView& whose value is
 // equal to s.
-Matcher<const absl::string_view&>::Matcher(const char* s) {
+Matcher<const internal::StringView&>::Matcher(const char* s) {
   *this = Eq(std::string(s));
 }
 
-// Constructs a matcher that matches a const absl::string_view& whose value is
+// Constructs a matcher that matches a const StringView& whose value is
 // equal to s.
-Matcher<const absl::string_view&>::Matcher(absl::string_view s) {
+Matcher<const internal::StringView&>::Matcher(internal::StringView s) {
   *this = Eq(std::string(s));
 }
 
-// Constructs a matcher that matches a absl::string_view whose value is equal to
+// Constructs a matcher that matches a StringView whose value is equal to
 // s.
-Matcher<absl::string_view>::Matcher(const std::string& s) { *this = Eq(s); }
+Matcher<internal::StringView>::Matcher(const std::string& s) { *this = Eq(s); }
 
-// Constructs a matcher that matches a absl::string_view whose value is equal to
+// Constructs a matcher that matches a StringView whose value is equal to
 // s.
-Matcher<absl::string_view>::Matcher(const char* s) {
+Matcher<internal::StringView>::Matcher(const char* s) {
   *this = Eq(std::string(s));
 }
 
-// Constructs a matcher that matches a absl::string_view whose value is equal to
+// Constructs a matcher that matches a StringView whose value is equal to
 // s.
-Matcher<absl::string_view>::Matcher(absl::string_view s) {
+Matcher<internal::StringView>::Matcher(internal::StringView s) {
   *this = Eq(std::string(s));
 }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
 }  // namespace testing
diff --git a/ext/googletest/googletest/src/gtest-port.cc b/ext/googletest/googletest/src/gtest-port.cc
index fc5ba6b..c3c93e6 100644
--- a/ext/googletest/googletest/src/gtest-port.cc
+++ b/ext/googletest/googletest/src/gtest-port.cc
@@ -34,6 +34,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <cstdint>
 #include <fstream>
 #include <memory>
 
@@ -97,7 +98,7 @@
 const int kStdErrFileno = STDERR_FILENO;
 #endif  // _MSC_VER
 
-#if GTEST_OS_LINUX
+#if GTEST_OS_LINUX || GTEST_OS_GNU_HURD
 
 namespace {
 template <typename T>
@@ -197,7 +198,8 @@
   if (sysctl(mib, miblen, NULL, &size, NULL, 0)) {
     return 0;
   }
-  mib[5] = size / mib[4];
+
+  mib[5] = static_cast<int>(size / static_cast<size_t>(mib[4]));
 
   // populate array of structs
   struct kinfo_proc info[mib[5]];
@@ -206,8 +208,8 @@
   }
 
   // exclude empty members
-  int nthreads = 0;
-  for (int i = 0; i < size / mib[4]; i++) {
+  size_t nthreads = 0;
+  for (size_t i = 0; i < size / static_cast<size_t>(mib[4]); i++) {
     if (info[i].p_tid != -1)
       nthreads++;
   }
@@ -536,6 +538,9 @@
   // Returns a value that can be used to identify the thread from other threads.
   static ThreadLocalValueHolderBase* GetValueOnCurrentThread(
       const ThreadLocalBase* thread_local_instance) {
+#ifdef _MSC_VER
+    MemoryIsNotDeallocated memory_is_not_deallocated;
+#endif  // _MSC_VER
     DWORD current_thread = ::GetCurrentThreadId();
     MutexLock lock(&mutex_);
     ThreadIdToThreadLocals* const thread_to_thread_locals =
@@ -683,8 +688,8 @@
   static Mutex thread_map_mutex_;
 };
 
-Mutex ThreadLocalRegistryImpl::mutex_(Mutex::kStaticMutex);
-Mutex ThreadLocalRegistryImpl::thread_map_mutex_(Mutex::kStaticMutex);
+Mutex ThreadLocalRegistryImpl::mutex_(Mutex::kStaticMutex);  // NOLINT
+Mutex ThreadLocalRegistryImpl::thread_map_mutex_(Mutex::kStaticMutex);  // NOLINT
 
 ThreadLocalValueHolderBase* ThreadLocalRegistry::GetValueOnCurrentThread(
       const ThreadLocalBase* thread_local_instance) {
@@ -1090,9 +1095,9 @@
     filename_ = temp_file_path;
 # else
     // There's no guarantee that a test has write access to the current
-    // directory, so we create the temporary file in the /tmp directory
-    // instead. We use /tmp on most systems, and /sdcard on Android.
-    // That's because Android doesn't have /tmp.
+    // directory, so we create the temporary file in a temporary directory.
+    std::string name_template;
+
 #  if GTEST_OS_LINUX_ANDROID
     // Note: Android applications are expected to call the framework's
     // Context.getExternalStorageDirectory() method through JNI to get
@@ -1105,17 +1110,46 @@
     // The location /data/local/tmp is directly accessible from native code.
     // '/sdcard' and other variants cannot be relied on, as they are not
     // guaranteed to be mounted, or may have a delay in mounting.
-    char name_template[] = "/data/local/tmp/gtest_captured_stream.XXXXXX";
+    name_template = "/data/local/tmp/";
+#  elif GTEST_OS_IOS
+    char user_temp_dir[PATH_MAX + 1];
+
+    // Documented alternative to NSTemporaryDirectory() (for obtaining creating
+    // a temporary directory) at
+    // https://developer.apple.com/library/archive/documentation/Security/Conceptual/SecureCodingGuide/Articles/RaceConditions.html#//apple_ref/doc/uid/TP40002585-SW10
+    //
+    // _CS_DARWIN_USER_TEMP_DIR (as well as _CS_DARWIN_USER_CACHE_DIR) is not
+    // documented in the confstr() man page at
+    // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/confstr.3.html#//apple_ref/doc/man/3/confstr
+    // but are still available, according to the WebKit patches at
+    // https://trac.webkit.org/changeset/262004/webkit
+    // https://trac.webkit.org/changeset/263705/webkit
+    //
+    // The confstr() implementation falls back to getenv("TMPDIR"). See
+    // https://opensource.apple.com/source/Libc/Libc-1439.100.3/gen/confstr.c.auto.html
+    ::confstr(_CS_DARWIN_USER_TEMP_DIR, user_temp_dir, sizeof(user_temp_dir));
+
+    name_template = user_temp_dir;
+    if (name_template.back() != GTEST_PATH_SEP_[0])
+      name_template.push_back(GTEST_PATH_SEP_[0]);
 #  else
-    char name_template[] = "/tmp/captured_stream.XXXXXX";
-#  endif  // GTEST_OS_LINUX_ANDROID
-    const int captured_fd = mkstemp(name_template);
+    name_template = "/tmp/";
+#  endif
+    name_template.append("gtest_captured_stream.XXXXXX");
+
+    // mkstemp() modifies the string bytes in place, and does not go beyond the
+    // string's length. This results in well-defined behavior in C++17.
+    //
+    // The const_cast is needed below C++17. The constraints on std::string
+    // implementations in C++11 and above make assumption behind the const_cast
+    // fairly safe.
+    const int captured_fd = ::mkstemp(const_cast<char*>(name_template.data()));
     if (captured_fd == -1) {
       GTEST_LOG_(WARNING)
           << "Failed to create tmp file " << name_template
           << " for test; does the test have access to the /tmp directory?";
     }
-    filename_ = name_template;
+    filename_ = std::move(name_template);
 # endif  // GTEST_OS_WINDOWS
     fflush(nullptr);
     dup2(captured_fd, fd_);
@@ -1286,7 +1320,7 @@
 // Parses 'str' for a 32-bit signed integer.  If successful, writes
 // the result to *value and returns true; otherwise leaves *value
 // unchanged and returns false.
-bool ParseInt32(const Message& src_text, const char* str, Int32* value) {
+bool ParseInt32(const Message& src_text, const char* str, int32_t* value) {
   // Parses the environment variable as a decimal integer.
   char* end = nullptr;
   const long long_value = strtol(str, &end, 10);  // NOLINT
@@ -1303,13 +1337,13 @@
     return false;
   }
 
-  // Is the parsed value in the range of an Int32?
-  const Int32 result = static_cast<Int32>(long_value);
+  // Is the parsed value in the range of an int32_t?
+  const auto result = static_cast<int32_t>(long_value);
   if (long_value == LONG_MAX || long_value == LONG_MIN ||
       // The parsed value overflows as a long.  (strtol() returns
       // LONG_MAX or LONG_MIN when the input overflows.)
       result != long_value
-      // The parsed value overflows as an Int32.
+      // The parsed value overflows as an int32_t.
       ) {
     Message msg;
     msg << "WARNING: " << src_text
@@ -1342,7 +1376,7 @@
 // Reads and returns a 32-bit integer stored in the environment
 // variable corresponding to the given flag; if it isn't set or
 // doesn't represent a valid 32-bit integer, returns default_value.
-Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
+int32_t Int32FromGTestEnv(const char* flag, int32_t default_value) {
 #if defined(GTEST_GET_INT32_FROM_ENV_)
   return GTEST_GET_INT32_FROM_ENV_(flag, default_value);
 #else
@@ -1353,7 +1387,7 @@
     return default_value;
   }
 
-  Int32 result = default_value;
+  int32_t result = default_value;
   if (!ParseInt32(Message() << "Environment variable " << env_var,
                   string_value, &result)) {
     printf("The default value %s is used.\n",
diff --git a/ext/googletest/googletest/src/gtest-printers.cc b/ext/googletest/googletest/src/gtest-printers.cc
index 3337be3..41e29cc 100644
--- a/ext/googletest/googletest/src/gtest-printers.cc
+++ b/ext/googletest/googletest/src/gtest-printers.cc
@@ -42,11 +42,16 @@
 // defines Foo.
 
 #include "gtest/gtest-printers.h"
+
 #include <stdio.h>
+
 #include <cctype>
+#include <cstdint>
 #include <cwchar>
 #include <ostream>  // NOLINT
 #include <string>
+#include <type_traits>
+
 #include "gtest/internal/gtest-port.h"
 #include "src/gtest-internal-inl.h"
 
@@ -102,9 +107,19 @@
   *os << ">";
 }
 
+// Helpers for widening a character to char32_t. Since the standard does not
+// specify if char / wchar_t is signed or unsigned, it is important to first
+// convert it to the unsigned type of the same width before widening it to
+// char32_t.
+template <typename CharType>
+char32_t ToChar32(CharType in) {
+  return static_cast<char32_t>(
+      static_cast<typename std::make_unsigned<CharType>::type>(in));
+}
+
 }  // namespace
 
-namespace internal2 {
+namespace internal {
 
 // Delegates to PrintBytesInObjectToImpl() to print the bytes in the
 // given object.  The delegation simplifies the implementation, which
@@ -116,10 +131,6 @@
   PrintBytesInObjectToImpl(obj_bytes, count, os);
 }
 
-}  // namespace internal2
-
-namespace internal {
-
 // Depending on the value of a char (or wchar_t), we print it in one
 // of three formats:
 //   - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
@@ -134,18 +145,15 @@
 // Returns true if c is a printable ASCII character.  We test the
 // value of c directly instead of calling isprint(), which is buggy on
 // Windows Mobile.
-inline bool IsPrintableAscii(wchar_t c) {
-  return 0x20 <= c && c <= 0x7E;
-}
+inline bool IsPrintableAscii(char32_t c) { return 0x20 <= c && c <= 0x7E; }
 
-// Prints a wide or narrow char c as a character literal without the
-// quotes, escaping it when necessary; returns how c was formatted.
-// The template argument UnsignedChar is the unsigned version of Char,
-// which is the type of c.
-template <typename UnsignedChar, typename Char>
+// Prints c (of type char, char8_t, char16_t, char32_t, or wchar_t) as a
+// character literal without the quotes, escaping it when necessary; returns how
+// c was formatted.
+template <typename Char>
 static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
-  wchar_t w_c = static_cast<wchar_t>(c);
-  switch (w_c) {
+  const char32_t u_c = ToChar32(c);
+  switch (u_c) {
     case L'\0':
       *os << "\\0";
       break;
@@ -177,13 +185,12 @@
       *os << "\\v";
       break;
     default:
-      if (IsPrintableAscii(w_c)) {
+      if (IsPrintableAscii(u_c)) {
         *os << static_cast<char>(c);
         return kAsIs;
       } else {
         ostream::fmtflags flags = os->flags();
-        *os << "\\x" << std::hex << std::uppercase
-            << static_cast<int>(static_cast<UnsignedChar>(c));
+        *os << "\\x" << std::hex << std::uppercase << static_cast<int>(u_c);
         os->flags(flags);
         return kHexEscape;
       }
@@ -191,9 +198,9 @@
   return kSpecialEscape;
 }
 
-// Prints a wchar_t c as if it's part of a string literal, escaping it when
+// Prints a char32_t c as if it's part of a string literal, escaping it when
 // necessary; returns how c was formatted.
-static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) {
+static CharFormat PrintAsStringLiteralTo(char32_t c, ostream* os) {
   switch (c) {
     case L'\'':
       *os << "'";
@@ -202,26 +209,68 @@
       *os << "\\\"";
       return kSpecialEscape;
     default:
-      return PrintAsCharLiteralTo<wchar_t>(c, os);
+      return PrintAsCharLiteralTo(c, os);
   }
 }
 
+static const char* GetCharWidthPrefix(char) {
+  return "";
+}
+
+static const char* GetCharWidthPrefix(signed char) {
+  return "";
+}
+
+static const char* GetCharWidthPrefix(unsigned char) {
+  return "";
+}
+
+#ifdef __cpp_char8_t
+static const char* GetCharWidthPrefix(char8_t) {
+  return "u8";
+}
+#endif
+
+static const char* GetCharWidthPrefix(char16_t) {
+  return "u";
+}
+
+static const char* GetCharWidthPrefix(char32_t) {
+  return "U";
+}
+
+static const char* GetCharWidthPrefix(wchar_t) {
+  return "L";
+}
+
 // Prints a char c as if it's part of a string literal, escaping it when
 // necessary; returns how c was formatted.
 static CharFormat PrintAsStringLiteralTo(char c, ostream* os) {
-  return PrintAsStringLiteralTo(
-      static_cast<wchar_t>(static_cast<unsigned char>(c)), os);
+  return PrintAsStringLiteralTo(ToChar32(c), os);
 }
 
-// Prints a wide or narrow character c and its code.  '\0' is printed
-// as "'\\0'", other unprintable characters are also properly escaped
-// using the standard C++ escape sequence.  The template argument
-// UnsignedChar is the unsigned version of Char, which is the type of c.
-template <typename UnsignedChar, typename Char>
+#ifdef __cpp_char8_t
+static CharFormat PrintAsStringLiteralTo(char8_t c, ostream* os) {
+  return PrintAsStringLiteralTo(ToChar32(c), os);
+}
+#endif
+
+static CharFormat PrintAsStringLiteralTo(char16_t c, ostream* os) {
+  return PrintAsStringLiteralTo(ToChar32(c), os);
+}
+
+static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) {
+  return PrintAsStringLiteralTo(ToChar32(c), os);
+}
+
+// Prints a character c (of type char, char8_t, char16_t, char32_t, or wchar_t)
+// and its code. '\0' is printed as "'\\0'", other unprintable characters are
+// also properly escaped using the standard C++ escape sequence.
+template <typename Char>
 void PrintCharAndCodeTo(Char c, ostream* os) {
   // First, print c as a literal in the most readable form we can find.
-  *os << ((sizeof(c) > 1) ? "L'" : "'");
-  const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os);
+  *os << GetCharWidthPrefix(c) << "'";
+  const CharFormat format = PrintAsCharLiteralTo(c, os);
   *os << "'";
 
   // To aid user debugging, we also print c's code in decimal, unless
@@ -242,21 +291,21 @@
   *os << ")";
 }
 
-void PrintTo(unsigned char c, ::std::ostream* os) {
-  PrintCharAndCodeTo<unsigned char>(c, os);
-}
-void PrintTo(signed char c, ::std::ostream* os) {
-  PrintCharAndCodeTo<unsigned char>(c, os);
-}
+void PrintTo(unsigned char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); }
+void PrintTo(signed char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); }
 
 // Prints a wchar_t as a symbol if it is printable or as its internal
 // code otherwise and also as its code.  L'\0' is printed as "L'\\0'".
-void PrintTo(wchar_t wc, ostream* os) {
-  PrintCharAndCodeTo<wchar_t>(wc, os);
+void PrintTo(wchar_t wc, ostream* os) { PrintCharAndCodeTo(wc, os); }
+
+// TODO(dcheng): Consider making this delegate to PrintCharAndCodeTo() as well.
+void PrintTo(char32_t c, ::std::ostream* os) {
+  *os << std::hex << "U+" << std::uppercase << std::setfill('0') << std::setw(4)
+      << static_cast<uint32_t>(c);
 }
 
 // Prints the given array of characters to the ostream.  CharType must be either
-// char or wchar_t.
+// char, char8_t, char16_t, char32_t, or wchar_t.
 // The array starts at begin, the length is len, it may include '\0' characters
 // and may not be NUL-terminated.
 template <typename CharType>
@@ -266,8 +315,8 @@
 GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
 static CharFormat PrintCharsAsStringTo(
     const CharType* begin, size_t len, ostream* os) {
-  const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\"";
-  *os << kQuoteBegin;
+  const char* const quote_prefix = GetCharWidthPrefix(*begin);
+  *os << quote_prefix << "\"";
   bool is_previous_hex = false;
   CharFormat print_format = kAsIs;
   for (size_t index = 0; index < len; ++index) {
@@ -276,7 +325,7 @@
       // Previous character is of '\x..' form and this character can be
       // interpreted as another hexadecimal digit in its number. Break string to
       // disambiguate.
-      *os << "\" " << kQuoteBegin;
+      *os << "\" " << quote_prefix << "\"";
     }
     is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape;
     // Remember if any characters required hex escaping.
@@ -322,22 +371,57 @@
   UniversalPrintCharArray(begin, len, os);
 }
 
+#ifdef __cpp_char8_t
+// Prints a (const) char8_t array of 'len' elements, starting at address
+// 'begin'.
+void UniversalPrintArray(const char8_t* begin, size_t len, ostream* os) {
+  UniversalPrintCharArray(begin, len, os);
+}
+#endif
+
+// Prints a (const) char16_t array of 'len' elements, starting at address
+// 'begin'.
+void UniversalPrintArray(const char16_t* begin, size_t len, ostream* os) {
+  UniversalPrintCharArray(begin, len, os);
+}
+
+// Prints a (const) char32_t array of 'len' elements, starting at address
+// 'begin'.
+void UniversalPrintArray(const char32_t* begin, size_t len, ostream* os) {
+  UniversalPrintCharArray(begin, len, os);
+}
+
 // Prints a (const) wchar_t array of 'len' elements, starting at address
 // 'begin'.
 void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) {
   UniversalPrintCharArray(begin, len, os);
 }
 
-// Prints the given C string to the ostream.
-void PrintTo(const char* s, ostream* os) {
+namespace {
+
+// Prints a null-terminated C-style string to the ostream.
+template <typename Char>
+void PrintCStringTo(const Char* s, ostream* os) {
   if (s == nullptr) {
     *os << "NULL";
   } else {
     *os << ImplicitCast_<const void*>(s) << " pointing to ";
-    PrintCharsAsStringTo(s, strlen(s), os);
+    PrintCharsAsStringTo(s, std::char_traits<Char>::length(s), os);
   }
 }
 
+}  // anonymous namespace
+
+void PrintTo(const char* s, ostream* os) { PrintCStringTo(s, os); }
+
+#ifdef __cpp_char8_t
+void PrintTo(const char8_t* s, ostream* os) { PrintCStringTo(s, os); }
+#endif
+
+void PrintTo(const char16_t* s, ostream* os) { PrintCStringTo(s, os); }
+
+void PrintTo(const char32_t* s, ostream* os) { PrintCStringTo(s, os); }
+
 // MSVC compiler can be configured to define whar_t as a typedef
 // of unsigned short. Defining an overload for const wchar_t* in that case
 // would cause pointers to unsigned shorts be printed as wide strings,
@@ -346,14 +430,7 @@
 // wchar_t is implemented as a native type.
 #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
 // Prints the given wide C string to the ostream.
-void PrintTo(const wchar_t* s, ostream* os) {
-  if (s == nullptr) {
-    *os << "NULL";
-  } else {
-    *os << ImplicitCast_<const void*>(s) << " pointing to ";
-    PrintCharsAsStringTo(s, wcslen(s), os);
-  }
-}
+void PrintTo(const wchar_t* s, ostream* os) { PrintCStringTo(s, os); }
 #endif  // wchar_t is native
 
 namespace {
@@ -425,12 +502,26 @@
 
 void PrintStringTo(const ::std::string& s, ostream* os) {
   if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) {
-    if (GTEST_FLAG(print_utf8)) {
+    if (GTEST_FLAG_GET(print_utf8)) {
       ConditionalPrintAsText(s.data(), s.size(), os);
     }
   }
 }
 
+#ifdef __cpp_char8_t
+void PrintU8StringTo(const ::std::u8string& s, ostream* os) {
+  PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif
+
+void PrintU16StringTo(const ::std::u16string& s, ostream* os) {
+  PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+
+void PrintU32StringTo(const ::std::u32string& s, ostream* os) {
+  PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+
 #if GTEST_HAS_STD_WSTRING
 void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
   PrintCharsAsStringTo(s.data(), s.size(), os);
diff --git a/ext/googletest/googletest/src/gtest-test-part.cc b/ext/googletest/googletest/src/gtest-test-part.cc
index 178317a..a938683 100644
--- a/ext/googletest/googletest/src/gtest-test-part.cc
+++ b/ext/googletest/googletest/src/gtest-test-part.cc
@@ -31,6 +31,8 @@
 // The Google C++ Testing and Mocking Framework (Google Test)
 
 #include "gtest/gtest-test-part.h"
+
+#include "gtest/internal/gtest-port.h"
 #include "src/gtest-internal-inl.h"
 
 namespace testing {
@@ -46,7 +48,9 @@
 
 // Prints a TestPartResult object.
 std::ostream& operator<<(std::ostream& os, const TestPartResult& result) {
-  return os << result.file_name() << ":" << result.line_number() << ": "
+  return os << internal::FormatFileLocation(result.file_name(),
+                                            result.line_number())
+            << " "
             << (result.type() == TestPartResult::kSuccess
                     ? "Success"
                     : result.type() == TestPartResult::kSkip
diff --git a/ext/googletest/googletest/src/gtest-typed-test.cc b/ext/googletest/googletest/src/gtest-typed-test.cc
index 8677caf..c02c3df 100644
--- a/ext/googletest/googletest/src/gtest-typed-test.cc
+++ b/ext/googletest/googletest/src/gtest-typed-test.cc
@@ -35,8 +35,6 @@
 namespace testing {
 namespace internal {
 
-#if GTEST_HAS_TYPED_TEST_P
-
 // Skips to the first non-space char in str. Returns an empty string if str
 // contains only whitespace characters.
 static const char* SkipSpaces(const char* str) {
@@ -58,7 +56,10 @@
 // registered_tests_; returns registered_tests if successful, or
 // aborts the program otherwise.
 const char* TypedTestSuitePState::VerifyRegisteredTestNames(
-    const char* file, int line, const char* registered_tests) {
+    const char* test_suite_name, const char* file, int line,
+    const char* registered_tests) {
+  RegisterTypeParameterizedTestSuite(test_suite_name, CodeLocation(file, line));
+
   typedef RegisteredTestsMap::const_iterator RegisteredTestIter;
   registered_ = true;
 
@@ -75,17 +76,7 @@
       continue;
     }
 
-    bool found = false;
-    for (RegisteredTestIter it = registered_tests_.begin();
-         it != registered_tests_.end();
-         ++it) {
-      if (name == it->first) {
-        found = true;
-        break;
-      }
-    }
-
-    if (found) {
+    if (registered_tests_.count(name) != 0) {
       tests.insert(name);
     } else {
       errors << "No test named " << name
@@ -112,7 +103,5 @@
   return registered_tests;
 }
 
-#endif  // GTEST_HAS_TYPED_TEST_P
-
 }  // namespace internal
 }  // namespace testing
diff --git a/ext/googletest/googletest/src/gtest.cc b/ext/googletest/googletest/src/gtest.cc
index a5b4e5a..5a38768 100644
--- a/ext/googletest/googletest/src/gtest.cc
+++ b/ext/googletest/googletest/src/gtest.cc
@@ -35,7 +35,6 @@
 #include "gtest/gtest-spi.h"
 
 #include <ctype.h>
-#include <math.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -44,6 +43,9 @@
 #include <wctype.h>
 
 #include <algorithm>
+#include <chrono>  // NOLINT
+#include <cmath>
+#include <cstdint>
 #include <iomanip>
 #include <limits>
 #include <list>
@@ -54,8 +56,6 @@
 
 #if GTEST_OS_LINUX
 
-# define GTEST_HAS_GETTIMEOFDAY_ 1
-
 # include <fcntl.h>  // NOLINT
 # include <limits.h>  // NOLINT
 # include <sched.h>  // NOLINT
@@ -67,7 +67,6 @@
 # include <string>
 
 #elif GTEST_OS_ZOS
-# define GTEST_HAS_GETTIMEOFDAY_ 1
 # include <sys/time.h>  // NOLINT
 
 // On z/OS we additionally need strings.h for strcasecmp.
@@ -83,24 +82,21 @@
 # include <windows.h>  // NOLINT
 # undef min
 
+#ifdef _MSC_VER
 # include <crtdbg.h>  // NOLINT
-# include <debugapi.h>  // NOLINT
+#endif
+
 # include <io.h>  // NOLINT
 # include <sys/timeb.h>  // NOLINT
 # include <sys/types.h>  // NOLINT
 # include <sys/stat.h>  // NOLINT
 
 # if GTEST_OS_WINDOWS_MINGW
-// MinGW has gettimeofday() but not _ftime64().
-#  define GTEST_HAS_GETTIMEOFDAY_ 1
 #  include <sys/time.h>  // NOLINT
 # endif  // GTEST_OS_WINDOWS_MINGW
 
 #else
 
-// Assume other platforms have gettimeofday().
-# define GTEST_HAS_GETTIMEOFDAY_ 1
-
 // cpplint thinks that the header is already included, so we want to
 // silence it.
 # include <sys/time.h>  // NOLINT
@@ -209,24 +205,44 @@
   return kUniversalFilter;
 }
 
+// Bazel passes in the argument to '--test_runner_fail_fast' via the
+// TESTBRIDGE_TEST_RUNNER_FAIL_FAST environment variable.
+static bool GetDefaultFailFast() {
+  const char* const testbridge_test_runner_fail_fast =
+      internal::posix::GetEnv("TESTBRIDGE_TEST_RUNNER_FAIL_FAST");
+  if (testbridge_test_runner_fail_fast != nullptr) {
+    return strcmp(testbridge_test_runner_fail_fast, "1") == 0;
+  }
+  return false;
+}
+
+}  // namespace testing
+
+GTEST_DEFINE_bool_(
+    fail_fast,
+    testing::internal::BoolFromGTestEnv("fail_fast",
+                                        testing::GetDefaultFailFast()),
+    "True if and only if a test failure should stop further test execution.");
+
 GTEST_DEFINE_bool_(
     also_run_disabled_tests,
-    internal::BoolFromGTestEnv("also_run_disabled_tests", false),
+    testing::internal::BoolFromGTestEnv("also_run_disabled_tests", false),
     "Run disabled tests too, in addition to the tests normally being run.");
 
 GTEST_DEFINE_bool_(
-    break_on_failure, internal::BoolFromGTestEnv("break_on_failure", false),
+    break_on_failure,
+    testing::internal::BoolFromGTestEnv("break_on_failure", false),
     "True if and only if a failed assertion should be a debugger "
     "break-point.");
 
 GTEST_DEFINE_bool_(catch_exceptions,
-                   internal::BoolFromGTestEnv("catch_exceptions", true),
+                   testing::internal::BoolFromGTestEnv("catch_exceptions",
+                                                       true),
                    "True if and only if " GTEST_NAME_
                    " should catch exceptions and treat them as test failures.");
 
 GTEST_DEFINE_string_(
-    color,
-    internal::StringFromGTestEnv("color", "auto"),
+    color, testing::internal::StringFromGTestEnv("color", "auto"),
     "Whether to use colors in the output.  Valid values: yes, no, "
     "and auto.  'auto' means to use colors if the output is "
     "being sent to a terminal and the TERM environment variable "
@@ -234,7 +250,8 @@
 
 GTEST_DEFINE_string_(
     filter,
-    internal::StringFromGTestEnv("filter", GetDefaultFilter()),
+    testing::internal::StringFromGTestEnv("filter",
+                                          testing::GetDefaultFilter()),
     "A colon-separated list of glob (not regex) patterns "
     "for filtering the tests to run, optionally followed by a "
     "'-' and a : separated list of negative patterns (tests to "
@@ -243,8 +260,10 @@
 
 GTEST_DEFINE_bool_(
     install_failure_signal_handler,
-    internal::BoolFromGTestEnv("install_failure_signal_handler", false),
-    "If true and supported on the current platform, " GTEST_NAME_ " should "
+    testing::internal::BoolFromGTestEnv("install_failure_signal_handler",
+                                        false),
+    "If true and supported on the current platform, " GTEST_NAME_
+    " should "
     "install a signal handler that dumps debugging information when fatal "
     "signals are raised.");
 
@@ -258,8 +277,8 @@
 //   ''
 GTEST_DEFINE_string_(
     output,
-    internal::StringFromGTestEnv("output",
-      internal::OutputFlagAlsoCheckEnvVar().c_str()),
+    testing::internal::StringFromGTestEnv(
+        "output", testing::internal::OutputFlagAlsoCheckEnvVar().c_str()),
     "A format (defaults to \"xml\" but can be specified to be \"json\"), "
     "optionally followed by a colon and an output file name or directory. "
     "A directory is indicated by a trailing pathname separator. "
@@ -269,71 +288,89 @@
     "executable's name and, if necessary, made unique by adding "
     "digits.");
 
-GTEST_DEFINE_bool_(print_time, internal::BoolFromGTestEnv("print_time", true),
+GTEST_DEFINE_bool_(
+    brief, testing::internal::BoolFromGTestEnv("brief", false),
+    "True if only test failures should be displayed in text output.");
+
+GTEST_DEFINE_bool_(print_time,
+                   testing::internal::BoolFromGTestEnv("print_time", true),
                    "True if and only if " GTEST_NAME_
                    " should display elapsed time in text output.");
 
-GTEST_DEFINE_bool_(print_utf8, internal::BoolFromGTestEnv("print_utf8", true),
+GTEST_DEFINE_bool_(print_utf8,
+                   testing::internal::BoolFromGTestEnv("print_utf8", true),
                    "True if and only if " GTEST_NAME_
                    " prints UTF8 characters as text.");
 
 GTEST_DEFINE_int32_(
-    random_seed,
-    internal::Int32FromGTestEnv("random_seed", 0),
+    random_seed, testing::internal::Int32FromGTestEnv("random_seed", 0),
     "Random number seed to use when shuffling test orders.  Must be in range "
     "[1, 99999], or 0 to use a seed based on the current time.");
 
 GTEST_DEFINE_int32_(
-    repeat,
-    internal::Int32FromGTestEnv("repeat", 1),
+    repeat, testing::internal::Int32FromGTestEnv("repeat", 1),
     "How many times to repeat each test.  Specify a negative number "
     "for repeating forever.  Useful for shaking out flaky tests.");
 
+GTEST_DEFINE_bool_(
+    recreate_environments_when_repeating,
+    testing::internal::BoolFromGTestEnv("recreate_environments_when_repeating",
+                                        true),
+    "Controls whether global test environments are recreated for each repeat "
+    "of the tests. If set to false the global test environments are only set "
+    "up once, for the first iteration, and only torn down once, for the last. "
+    "Useful for shaking out flaky tests with stable, expensive test "
+    "environments. If --gtest_repeat is set to a negative number, meaning "
+    "there is no last run, the environments will always be recreated to avoid "
+    "leaks.");
+
 GTEST_DEFINE_bool_(show_internal_stack_frames, false,
                    "True if and only if " GTEST_NAME_
                    " should include internal stack frames when "
                    "printing test failure stack traces.");
 
-GTEST_DEFINE_bool_(shuffle, internal::BoolFromGTestEnv("shuffle", false),
+GTEST_DEFINE_bool_(shuffle,
+                   testing::internal::BoolFromGTestEnv("shuffle", false),
                    "True if and only if " GTEST_NAME_
                    " should randomize tests' order on every run.");
 
 GTEST_DEFINE_int32_(
     stack_trace_depth,
-    internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth),
+    testing::internal::Int32FromGTestEnv("stack_trace_depth",
+                                         testing::kMaxStackTraceDepth),
     "The maximum number of stack frames to print when an "
     "assertion fails.  The valid range is 0 through 100, inclusive.");
 
 GTEST_DEFINE_string_(
     stream_result_to,
-    internal::StringFromGTestEnv("stream_result_to", ""),
+    testing::internal::StringFromGTestEnv("stream_result_to", ""),
     "This flag specifies the host name and the port number on which to stream "
     "test results. Example: \"localhost:555\". The flag is effective only on "
     "Linux.");
 
 GTEST_DEFINE_bool_(
     throw_on_failure,
-    internal::BoolFromGTestEnv("throw_on_failure", false),
+    testing::internal::BoolFromGTestEnv("throw_on_failure", false),
     "When this flag is specified, a failed assertion will throw an exception "
     "if exceptions are enabled or exit the program with a non-zero code "
     "otherwise. For use with an external test framework.");
 
 #if GTEST_USE_OWN_FLAGFILE_FLAG_
 GTEST_DEFINE_string_(
-    flagfile,
-    internal::StringFromGTestEnv("flagfile", ""),
+    flagfile, testing::internal::StringFromGTestEnv("flagfile", ""),
     "This flag specifies the flagfile to read command-line flags from.");
 #endif  // GTEST_USE_OWN_FLAGFILE_FLAG_
 
+namespace testing {
 namespace internal {
 
 // Generates a random number from [0, range), using a Linear
 // Congruential Generator (LCG).  Crashes if 'range' is 0 or greater
 // than kMaxRange.
-UInt32 Random::Generate(UInt32 range) {
+uint32_t Random::Generate(uint32_t range) {
   // These constants are the same as are used in glibc's rand(3).
   // Use wider types than necessary to prevent unsigned overflow diagnostics.
-  state_ = static_cast<UInt32>(1103515245ULL*state_ + 12345U) % kMaxRange;
+  state_ = static_cast<uint32_t>(1103515245ULL*state_ + 12345U) % kMaxRange;
 
   GTEST_CHECK_(range > 0)
       << "Cannot generate a number in the range [0, 0).";
@@ -403,6 +440,162 @@
                       );  // NOLINT
 }
 
+namespace {
+
+// When TEST_P is found without a matching INSTANTIATE_TEST_SUITE_P
+// to creates test cases for it, a syntetic test case is
+// inserted to report ether an error or a log message.
+//
+// This configuration bit will likely be removed at some point.
+constexpr bool kErrorOnUninstantiatedParameterizedTest = true;
+constexpr bool kErrorOnUninstantiatedTypeParameterizedTest = true;
+
+// A test that fails at a given file/line location with a given message.
+class FailureTest : public Test {
+ public:
+  explicit FailureTest(const CodeLocation& loc, std::string error_message,
+                       bool as_error)
+      : loc_(loc),
+        error_message_(std::move(error_message)),
+        as_error_(as_error) {}
+
+  void TestBody() override {
+    if (as_error_) {
+      AssertHelper(TestPartResult::kNonFatalFailure, loc_.file.c_str(),
+                   loc_.line, "") = Message() << error_message_;
+    } else {
+      std::cout << error_message_ << std::endl;
+    }
+  }
+
+ private:
+  const CodeLocation loc_;
+  const std::string error_message_;
+  const bool as_error_;
+};
+
+
+}  // namespace
+
+std::set<std::string>* GetIgnoredParameterizedTestSuites() {
+  return UnitTest::GetInstance()->impl()->ignored_parameterized_test_suites();
+}
+
+// Add a given test_suit to the list of them allow to go un-instantiated.
+MarkAsIgnored::MarkAsIgnored(const char* test_suite) {
+  GetIgnoredParameterizedTestSuites()->insert(test_suite);
+}
+
+// If this parameterized test suite has no instantiations (and that
+// has not been marked as okay), emit a test case reporting that.
+void InsertSyntheticTestCase(const std::string& name, CodeLocation location,
+                             bool has_test_p) {
+  const auto& ignored = *GetIgnoredParameterizedTestSuites();
+  if (ignored.find(name) != ignored.end()) return;
+
+  const char kMissingInstantiation[] =  //
+      " is defined via TEST_P, but never instantiated. None of the test cases "
+      "will run. Either no INSTANTIATE_TEST_SUITE_P is provided or the only "
+      "ones provided expand to nothing."
+      "\n\n"
+      "Ideally, TEST_P definitions should only ever be included as part of "
+      "binaries that intend to use them. (As opposed to, for example, being "
+      "placed in a library that may be linked in to get other utilities.)";
+
+  const char kMissingTestCase[] =  //
+      " is instantiated via INSTANTIATE_TEST_SUITE_P, but no tests are "
+      "defined via TEST_P . No test cases will run."
+      "\n\n"
+      "Ideally, INSTANTIATE_TEST_SUITE_P should only ever be invoked from "
+      "code that always depend on code that provides TEST_P. Failing to do "
+      "so is often an indication of dead code, e.g. the last TEST_P was "
+      "removed but the rest got left behind.";
+
+  std::string message =
+      "Parameterized test suite " + name +
+      (has_test_p ? kMissingInstantiation : kMissingTestCase) +
+      "\n\n"
+      "To suppress this error for this test suite, insert the following line "
+      "(in a non-header) in the namespace it is defined in:"
+      "\n\n"
+      "GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(" + name + ");";
+
+  std::string full_name = "UninstantiatedParameterizedTestSuite<" + name + ">";
+  RegisterTest(  //
+      "GoogleTestVerification", full_name.c_str(),
+      nullptr,  // No type parameter.
+      nullptr,  // No value parameter.
+      location.file.c_str(), location.line, [message, location] {
+        return new FailureTest(location, message,
+                               kErrorOnUninstantiatedParameterizedTest);
+      });
+}
+
+void RegisterTypeParameterizedTestSuite(const char* test_suite_name,
+                                        CodeLocation code_location) {
+  GetUnitTestImpl()->type_parameterized_test_registry().RegisterTestSuite(
+      test_suite_name, code_location);
+}
+
+void RegisterTypeParameterizedTestSuiteInstantiation(const char* case_name) {
+  GetUnitTestImpl()
+      ->type_parameterized_test_registry()
+      .RegisterInstantiation(case_name);
+}
+
+void TypeParameterizedTestSuiteRegistry::RegisterTestSuite(
+    const char* test_suite_name, CodeLocation code_location) {
+  suites_.emplace(std::string(test_suite_name),
+                 TypeParameterizedTestSuiteInfo(code_location));
+}
+
+void TypeParameterizedTestSuiteRegistry::RegisterInstantiation(
+        const char* test_suite_name) {
+  auto it = suites_.find(std::string(test_suite_name));
+  if (it != suites_.end()) {
+    it->second.instantiated = true;
+  } else {
+    GTEST_LOG_(ERROR) << "Unknown type parameterized test suit '"
+                      << test_suite_name << "'";
+  }
+}
+
+void TypeParameterizedTestSuiteRegistry::CheckForInstantiations() {
+  const auto& ignored = *GetIgnoredParameterizedTestSuites();
+  for (const auto& testcase : suites_) {
+    if (testcase.second.instantiated) continue;
+    if (ignored.find(testcase.first) != ignored.end()) continue;
+
+    std::string message =
+        "Type parameterized test suite " + testcase.first +
+        " is defined via REGISTER_TYPED_TEST_SUITE_P, but never instantiated "
+        "via INSTANTIATE_TYPED_TEST_SUITE_P. None of the test cases will run."
+        "\n\n"
+        "Ideally, TYPED_TEST_P definitions should only ever be included as "
+        "part of binaries that intend to use them. (As opposed to, for "
+        "example, being placed in a library that may be linked in to get other "
+        "utilities.)"
+        "\n\n"
+        "To suppress this error for this test suite, insert the following line "
+        "(in a non-header) in the namespace it is defined in:"
+        "\n\n"
+        "GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(" +
+        testcase.first + ");";
+
+    std::string full_name =
+        "UninstantiatedTypeParameterizedTestSuite<" + testcase.first + ">";
+    RegisterTest(  //
+        "GoogleTestVerification", full_name.c_str(),
+        nullptr,  // No type parameter.
+        nullptr,  // No value parameter.
+        testcase.second.code_location.file.c_str(),
+        testcase.second.code_location.line, [message, testcase] {
+          return new FailureTest(testcase.second.code_location, message,
+                                 kErrorOnUninstantiatedTypeParameterizedTest);
+        });
+  }
+}
+
 // A copy of all command line arguments.  Set by InitGoogleTest().
 static ::std::vector<std::string> g_argvs;
 
@@ -435,7 +628,8 @@
 
 // Returns the output format, or "" for normal printed output.
 std::string UnitTestOptions::GetOutputFormat() {
-  const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
+  std::string s = GTEST_FLAG_GET(output);
+  const char* const gtest_output_flag = s.c_str();
   const char* const colon = strchr(gtest_output_flag, ':');
   return (colon == nullptr)
              ? std::string(gtest_output_flag)
@@ -446,7 +640,8 @@
 // Returns the name of the requested output file, or the default if none
 // was explicitly specified.
 std::string UnitTestOptions::GetAbsolutePathToOutputFile() {
-  const char* const gtest_output_flag = GTEST_FLAG(output).c_str();
+  std::string s = GTEST_FLAG_GET(output);
+  const char* const gtest_output_flag = s.c_str();
 
   std::string format = GetOutputFormat();
   if (format.empty())
@@ -475,47 +670,82 @@
   return result.string();
 }
 
-// Returns true if and only if the wildcard pattern matches the string.
-// The first ':' or '\0' character in pattern marks the end of it.
+// Returns true if and only if the wildcard pattern matches the string. Each
+// pattern consists of regular characters, single-character wildcards (?), and
+// multi-character wildcards (*).
 //
-// This recursive algorithm isn't very efficient, but is clear and
-// works well enough for matching test names, which are short.
-bool UnitTestOptions::PatternMatchesString(const char *pattern,
-                                           const char *str) {
-  switch (*pattern) {
-    case '\0':
-    case ':':  // Either ':' or '\0' marks the end of the pattern.
-      return *str == '\0';
-    case '?':  // Matches any single character.
-      return *str != '\0' && PatternMatchesString(pattern + 1, str + 1);
-    case '*':  // Matches any string (possibly empty) of characters.
-      return (*str != '\0' && PatternMatchesString(pattern, str + 1)) ||
-          PatternMatchesString(pattern + 1, str);
-    default:  // Non-special character.  Matches itself.
-      return *pattern == *str &&
-          PatternMatchesString(pattern + 1, str + 1);
+// This function implements a linear-time string globbing algorithm based on
+// https://research.swtch.com/glob.
+static bool PatternMatchesString(const std::string& name_str,
+                                 const char* pattern, const char* pattern_end) {
+  const char* name = name_str.c_str();
+  const char* const name_begin = name;
+  const char* const name_end = name + name_str.size();
+
+  const char* pattern_next = pattern;
+  const char* name_next = name;
+
+  while (pattern < pattern_end || name < name_end) {
+    if (pattern < pattern_end) {
+      switch (*pattern) {
+        default:  // Match an ordinary character.
+          if (name < name_end && *name == *pattern) {
+            ++pattern;
+            ++name;
+            continue;
+          }
+          break;
+        case '?':  // Match any single character.
+          if (name < name_end) {
+            ++pattern;
+            ++name;
+            continue;
+          }
+          break;
+        case '*':
+          // Match zero or more characters. Start by skipping over the wildcard
+          // and matching zero characters from name. If that fails, restart and
+          // match one more character than the last attempt.
+          pattern_next = pattern;
+          name_next = name + 1;
+          ++pattern;
+          continue;
+      }
+    }
+    // Failed to match a character. Restart if possible.
+    if (name_begin < name_next && name_next <= name_end) {
+      pattern = pattern_next;
+      name = name_next;
+      continue;
+    }
+    return false;
   }
+  return true;
 }
 
-bool UnitTestOptions::MatchesFilter(
-    const std::string& name, const char* filter) {
-  const char *cur_pattern = filter;
-  for (;;) {
-    if (PatternMatchesString(cur_pattern, name.c_str())) {
+bool UnitTestOptions::MatchesFilter(const std::string& name_str,
+                                    const char* filter) {
+  // The filter is a list of patterns separated by colons (:).
+  const char* pattern = filter;
+  while (true) {
+    // Find the bounds of this pattern.
+    const char* const next_sep = strchr(pattern, ':');
+    const char* const pattern_end =
+        next_sep != nullptr ? next_sep : pattern + strlen(pattern);
+
+    // Check if this pattern matches name_str.
+    if (PatternMatchesString(name_str, pattern, pattern_end)) {
       return true;
     }
 
-    // Finds the next pattern in the filter.
-    cur_pattern = strchr(cur_pattern, ':');
-
-    // Returns if no more pattern can be found.
-    if (cur_pattern == nullptr) {
+    // Give up on this pattern. However, if we found a pattern separator (:),
+    // advance to the next pattern (skipping over the separator) and restart.
+    if (next_sep == nullptr) {
       return false;
     }
-
-    // Skips the pattern separater (the ':' character).
-    cur_pattern++;
+    pattern = next_sep + 1;
   }
+  return true;
 }
 
 // Returns true if and only if the user-specified filter matches the test
@@ -526,12 +756,13 @@
 
   // Split --gtest_filter at '-', if there is one, to separate into
   // positive filter and negative filter portions
-  const char* const p = GTEST_FLAG(filter).c_str();
+  std::string str = GTEST_FLAG_GET(filter);
+  const char* const p = str.c_str();
   const char* const dash = strchr(p, '-');
   std::string positive;
   std::string negative;
   if (dash == nullptr) {
-    positive = GTEST_FLAG(filter).c_str();  // Whole string is a positive filter
+    positive = str.c_str();  // Whole string is a positive filter
     negative = "";
   } else {
     positive = std::string(p, dash);   // Everything up to the dash
@@ -565,7 +796,7 @@
 
   bool should_handle = true;
 
-  if (!GTEST_FLAG(catch_exceptions))
+  if (!GTEST_FLAG_GET(catch_exceptions))
     should_handle = false;
   else if (exception_code == EXCEPTION_BREAKPOINT)
     should_handle = false;
@@ -818,51 +1049,36 @@
 // trace but Bar() and CurrentOsStackTraceExceptTop() won't.
 std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) {
   return os_stack_trace_getter()->CurrentStackTrace(
-      static_cast<int>(GTEST_FLAG(stack_trace_depth)),
-      skip_count + 1
+      static_cast<int>(GTEST_FLAG_GET(stack_trace_depth)), skip_count + 1
       // Skips the user-specified number of frames plus this function
       // itself.
-      );  // NOLINT
+  );  // NOLINT
 }
 
-// Returns the current time in milliseconds.
-TimeInMillis GetTimeInMillis() {
-#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__)
-  // Difference between 1970-01-01 and 1601-01-01 in milliseconds.
-  // http://analogous.blogspot.com/2005/04/epoch.html
-  const TimeInMillis kJavaEpochToWinFileTimeDelta =
-    static_cast<TimeInMillis>(116444736UL) * 100000UL;
-  const DWORD kTenthMicrosInMilliSecond = 10000;
+// A helper class for measuring elapsed times.
+class Timer {
+ public:
+  Timer() : start_(std::chrono::steady_clock::now()) {}
 
-  SYSTEMTIME now_systime;
-  FILETIME now_filetime;
-  ULARGE_INTEGER now_int64;
-  GetSystemTime(&now_systime);
-  if (SystemTimeToFileTime(&now_systime, &now_filetime)) {
-    now_int64.LowPart = now_filetime.dwLowDateTime;
-    now_int64.HighPart = now_filetime.dwHighDateTime;
-    now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) -
-      kJavaEpochToWinFileTimeDelta;
-    return now_int64.QuadPart;
+  // Return time elapsed in milliseconds since the timer was created.
+  TimeInMillis Elapsed() {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(
+               std::chrono::steady_clock::now() - start_)
+        .count();
   }
-  return 0;
-#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_
-  __timeb64 now;
 
-  // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996
-  // (deprecated function) there.
-  GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
-  _ftime64(&now);
-  GTEST_DISABLE_MSC_DEPRECATED_POP_()
+ private:
+  std::chrono::steady_clock::time_point start_;
+};
 
-  return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm;
-#elif GTEST_HAS_GETTIMEOFDAY_
-  struct timeval now;
-  gettimeofday(&now, nullptr);
-  return static_cast<TimeInMillis>(now.tv_sec) * 1000 + now.tv_usec / 1000;
-#else
-# error "Don't know how to get the current time on your system."
-#endif
+// Returns a timestamp as milliseconds since the epoch. Note this time may jump
+// around subject to adjustments by the system, to measure elapsed time use
+// Timer instead.
+TimeInMillis GetTimeInMillis() {
+  return std::chrono::duration_cast<std::chrono::milliseconds>(
+             std::chrono::system_clock::now() -
+             std::chrono::system_clock::from_time_t(0))
+      .count();
 }
 
 // Utilities
@@ -1377,6 +1593,31 @@
   const double diff = fabs(val1 - val2);
   if (diff <= abs_error) return AssertionSuccess();
 
+  // Find the value which is closest to zero.
+  const double min_abs = std::min(fabs(val1), fabs(val2));
+  // Find the distance to the next double from that value.
+  const double epsilon =
+      nextafter(min_abs, std::numeric_limits<double>::infinity()) - min_abs;
+  // Detect the case where abs_error is so small that EXPECT_NEAR is
+  // effectively the same as EXPECT_EQUAL, and give an informative error
+  // message so that the situation can be more easily understood without
+  // requiring exotic floating-point knowledge.
+  // Don't do an epsilon check if abs_error is zero because that implies
+  // that an equality check was actually intended.
+  if (!(std::isnan)(val1) && !(std::isnan)(val2) && abs_error > 0 &&
+      abs_error < epsilon) {
+    return AssertionFailure()
+           << "The difference between " << expr1 << " and " << expr2 << " is "
+           << diff << ", where\n"
+           << expr1 << " evaluates to " << val1 << ",\n"
+           << expr2 << " evaluates to " << val2 << ".\nThe abs_error parameter "
+           << abs_error_expr << " evaluates to " << abs_error
+           << " which is smaller than the minimum distance between doubles for "
+              "numbers of this magnitude which is "
+           << epsilon
+           << ", thus making this EXPECT_NEAR check equivalent to "
+              "EXPECT_EQUAL. Consider using EXPECT_DOUBLE_EQ instead.";
+  }
   return AssertionFailure()
       << "The difference between " << expr1 << " and " << expr2
       << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n"
@@ -1439,57 +1680,6 @@
 
 namespace internal {
 
-// The helper function for {ASSERT|EXPECT}_EQ with int or enum
-// arguments.
-AssertionResult CmpHelperEQ(const char* lhs_expression,
-                            const char* rhs_expression,
-                            BiggestInt lhs,
-                            BiggestInt rhs) {
-  if (lhs == rhs) {
-    return AssertionSuccess();
-  }
-
-  return EqFailure(lhs_expression,
-                   rhs_expression,
-                   FormatForComparisonFailureMessage(lhs, rhs),
-                   FormatForComparisonFailureMessage(rhs, lhs),
-                   false);
-}
-
-// A macro for implementing the helper functions needed to implement
-// ASSERT_?? and EXPECT_?? with integer or enum arguments.  It is here
-// just to avoid copy-and-paste of similar code.
-#define GTEST_IMPL_CMP_HELPER_(op_name, op)\
-AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
-                                   BiggestInt val1, BiggestInt val2) {\
-  if (val1 op val2) {\
-    return AssertionSuccess();\
-  } else {\
-    return AssertionFailure() \
-        << "Expected: (" << expr1 << ") " #op " (" << expr2\
-        << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\
-        << " vs " << FormatForComparisonFailureMessage(val2, val1);\
-  }\
-}
-
-// Implements the helper function for {ASSERT|EXPECT}_NE with int or
-// enum arguments.
-GTEST_IMPL_CMP_HELPER_(NE, !=)
-// Implements the helper function for {ASSERT|EXPECT}_LE with int or
-// enum arguments.
-GTEST_IMPL_CMP_HELPER_(LE, <=)
-// Implements the helper function for {ASSERT|EXPECT}_LT with int or
-// enum arguments.
-GTEST_IMPL_CMP_HELPER_(LT, < )
-// Implements the helper function for {ASSERT|EXPECT}_GE with int or
-// enum arguments.
-GTEST_IMPL_CMP_HELPER_(GE, >=)
-// Implements the helper function for {ASSERT|EXPECT}_GT with int or
-// enum arguments.
-GTEST_IMPL_CMP_HELPER_(GT, > )
-
-#undef GTEST_IMPL_CMP_HELPER_
-
 // The helper function for {ASSERT|EXPECT}_STREQ.
 AssertionResult CmpHelperSTREQ(const char* lhs_expression,
                                const char* rhs_expression,
@@ -1735,33 +1925,33 @@
 //  17 - 21 bits       11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
 
 // The maximum code-point a one-byte UTF-8 sequence can represent.
-const UInt32 kMaxCodePoint1 = (static_cast<UInt32>(1) <<  7) - 1;
+constexpr uint32_t kMaxCodePoint1 = (static_cast<uint32_t>(1) <<  7) - 1;
 
 // The maximum code-point a two-byte UTF-8 sequence can represent.
-const UInt32 kMaxCodePoint2 = (static_cast<UInt32>(1) << (5 + 6)) - 1;
+constexpr uint32_t kMaxCodePoint2 = (static_cast<uint32_t>(1) << (5 + 6)) - 1;
 
 // The maximum code-point a three-byte UTF-8 sequence can represent.
-const UInt32 kMaxCodePoint3 = (static_cast<UInt32>(1) << (4 + 2*6)) - 1;
+constexpr uint32_t kMaxCodePoint3 = (static_cast<uint32_t>(1) << (4 + 2*6)) - 1;
 
 // The maximum code-point a four-byte UTF-8 sequence can represent.
-const UInt32 kMaxCodePoint4 = (static_cast<UInt32>(1) << (3 + 3*6)) - 1;
+constexpr uint32_t kMaxCodePoint4 = (static_cast<uint32_t>(1) << (3 + 3*6)) - 1;
 
 // Chops off the n lowest bits from a bit pattern.  Returns the n
 // lowest bits.  As a side effect, the original bit pattern will be
 // shifted to the right by n bits.
-inline UInt32 ChopLowBits(UInt32* bits, int n) {
-  const UInt32 low_bits = *bits & ((static_cast<UInt32>(1) << n) - 1);
+inline uint32_t ChopLowBits(uint32_t* bits, int n) {
+  const uint32_t low_bits = *bits & ((static_cast<uint32_t>(1) << n) - 1);
   *bits >>= n;
   return low_bits;
 }
 
 // Converts a Unicode code point to a narrow string in UTF-8 encoding.
-// code_point parameter is of type UInt32 because wchar_t may not be
+// code_point parameter is of type uint32_t because wchar_t may not be
 // wide enough to contain a code point.
 // If the code_point is not a valid Unicode code point
 // (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted
 // to "(Invalid Unicode 0xXXXXXXXX)".
-std::string CodePointToUtf8(UInt32 code_point) {
+std::string CodePointToUtf8(uint32_t code_point) {
   if (code_point > kMaxCodePoint4) {
     return "(Invalid Unicode 0x" + String::FormatHexUInt32(code_point) + ")";
   }
@@ -1802,11 +1992,11 @@
 }
 
 // Creates a Unicode code point from UTF16 surrogate pair.
-inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first,
-                                                    wchar_t second) {
-  const auto first_u = static_cast<UInt32>(first);
-  const auto second_u = static_cast<UInt32>(second);
-  const UInt32 mask = (1 << 10) - 1;
+inline uint32_t CreateCodePointFromUtf16SurrogatePair(wchar_t first,
+                                                      wchar_t second) {
+  const auto first_u = static_cast<uint32_t>(first);
+  const auto second_u = static_cast<uint32_t>(second);
+  const uint32_t mask = (1 << 10) - 1;
   return (sizeof(wchar_t) == 2)
              ? (((first_u & mask) << 10) | (second_u & mask)) + 0x10000
              :
@@ -1834,7 +2024,7 @@
 
   ::std::stringstream stream;
   for (int i = 0; i < num_chars; ++i) {
-    UInt32 unicode_code_point;
+    uint32_t unicode_code_point;
 
     if (str[i] == L'\0') {
       break;
@@ -1843,7 +2033,7 @@
                                                                  str[i + 1]);
       i++;
     } else {
-      unicode_code_point = static_cast<UInt32>(str[i]);
+      unicode_code_point = static_cast<uint32_t>(str[i]);
     }
 
     stream << CodePointToUtf8(unicode_code_point);
@@ -1963,13 +2153,18 @@
 
 // Formats an int value as "%02d".
 std::string String::FormatIntWidth2(int value) {
+  return FormatIntWidthN(value, 2);
+}
+
+// Formats an int value to given width with leading zeros.
+std::string String::FormatIntWidthN(int value, int width) {
   std::stringstream ss;
-  ss << std::setfill('0') << std::setw(2) << value;
+  ss << std::setfill('0') << std::setw(width) << value;
   return ss.str();
 }
 
 // Formats an int value as "%X".
-std::string String::FormatHexUInt32(UInt32 value) {
+std::string String::FormatHexUInt32(uint32_t value) {
   std::stringstream ss;
   ss << std::hex << std::uppercase << value;
   return ss.str();
@@ -1977,7 +2172,7 @@
 
 // Formats an int value as "%X".
 std::string String::FormatHexInt(int value) {
-  return FormatHexUInt32(static_cast<UInt32>(value));
+  return FormatHexUInt32(static_cast<uint32_t>(value));
 }
 
 // Formats a byte as "%02X".
@@ -2016,7 +2211,9 @@
   if (user_msg_string.empty()) {
     return gtest_msg;
   }
-
+  if (gtest_msg.empty()) {
+    return user_msg_string;
+  }
   return gtest_msg + "\n" + user_msg_string;
 }
 
@@ -2068,7 +2265,7 @@
   if (!ValidateTestProperty(xml_element, test_property)) {
     return;
   }
-  internal::MutexLock lock(&test_properites_mutex_);
+  internal::MutexLock lock(&test_properties_mutex_);
   const std::vector<TestProperty>::iterator property_with_matching_key =
       std::find_if(test_properties_.begin(), test_properties_.end(),
                    internal::TestPropertyKeyIs(test_property.key()));
@@ -2095,7 +2292,8 @@
 // The list of reserved attributes used in the <testsuite> element of XML
 // output.
 static const char* const kReservedTestSuiteAttributes[] = {
-    "disabled", "errors", "failures", "name", "tests", "time", "timestamp"};
+    "disabled", "errors", "failures",  "name",
+    "tests",    "time",   "timestamp", "skipped"};
 
 // The list of reserved attributes used in the <testcase> element of XML output.
 static const char* const kReservedTestCaseAttributes[] = {
@@ -2108,7 +2306,7 @@
     "classname",   "name", "status", "time",   "type_param",
     "value_param", "file", "line",   "result", "timestamp"};
 
-template <int kSize>
+template <size_t kSize>
 std::vector<std::string> ArrayAsVector(const char* const (&array)[kSize]) {
   return std::vector<std::string>(array, array + kSize);
 }
@@ -2449,7 +2647,7 @@
   // try {
   //   // Perform the test method.
   // } catch (...) {
-  //   if (GTEST_FLAG(catch_exceptions))
+  //   if (GTEST_FLAG_GET(catch_exceptions))
   //     // Report the exception as failure.
   //   else
   //     throw;  // Re-throws the original exception.
@@ -2552,6 +2750,7 @@
       should_run_(false),
       is_disabled_(false),
       matches_filter_(false),
+      is_in_another_shard_(false),
       factory_(factory),
       result_() {}
 
@@ -2565,7 +2764,7 @@
 //
 // Arguments:
 //
-//   test_suite_name:   name of the test suite
+//   test_suite_name:  name of the test suite
 //   name:             name of the test
 //   type_param:       the name of the test's type parameter, or NULL if
 //                     this is not a typed or a type-parameterized test.
@@ -2646,6 +2845,7 @@
 void UnitTestImpl::RegisterParameterizedTests() {
   if (!parameterized_tests_registered_) {
     parameterized_test_registry_.RegisterTests();
+    type_parameterized_test_registry_.CheckForInstantiations();
     parameterized_tests_registered_ = true;
   }
 }
@@ -2666,7 +2866,8 @@
   // Notifies the unit test event listeners that a test is about to start.
   repeater->OnTestStart(*this);
 
-  const TimeInMillis start = internal::GetTimeInMillis();
+  result_.set_start_timestamp(internal::GetTimeInMillis());
+  internal::Timer timer;
 
   impl->os_stack_trace_getter()->UponLeavingGTest();
 
@@ -2691,8 +2892,7 @@
         test, &Test::DeleteSelf_, "the test fixture's destructor");
   }
 
-  result_.set_start_timestamp(start);
-  result_.set_elapsed_time(internal::GetTimeInMillis() - start);
+  result_.set_elapsed_time(timer.Elapsed());
 
   // Notifies the unit test event listener that a test has just finished.
   repeater->OnTestEnd(*this);
@@ -2702,6 +2902,28 @@
   impl->set_current_test_info(nullptr);
 }
 
+// Skip and records a skipped test result for this object.
+void TestInfo::Skip() {
+  if (!should_run_) return;
+
+  internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+  impl->set_current_test_info(this);
+
+  TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
+
+  // Notifies the unit test event listeners that a test is about to start.
+  repeater->OnTestStart(*this);
+
+  const TestPartResult test_part_result =
+      TestPartResult(TestPartResult::kSkip, this->file(), this->line(), "");
+  impl->GetTestPartResultReporterForCurrentThread()->ReportTestPartResult(
+      test_part_result);
+
+  // Notifies the unit test event listener that a test has just finished.
+  repeater->OnTestEnd(*this);
+  impl->set_current_test_info(nullptr);
+}
+
 // class TestSuite
 
 // Gets the number of successful tests in this test suite.
@@ -2748,7 +2970,7 @@
 //
 // Arguments:
 //
-//   name:         name of the test suite
+//   a_name:       name of the test suite
 //   a_type_param: the name of the test suite's type parameter, or NULL if
 //                 this is not a typed or a type-parameterized test suite.
 //   set_up_tc:    pointer to the function that sets up the test suite
@@ -2803,19 +3025,27 @@
   // Call both legacy and the new API
   repeater->OnTestSuiteStart(*this);
 //  Legacy API is deprecated but still available
-#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
   repeater->OnTestCaseStart(*this);
-#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 
   impl->os_stack_trace_getter()->UponLeavingGTest();
   internal::HandleExceptionsInMethodIfSupported(
       this, &TestSuite::RunSetUpTestSuite, "SetUpTestSuite()");
 
   start_timestamp_ = internal::GetTimeInMillis();
+  internal::Timer timer;
   for (int i = 0; i < total_test_count(); i++) {
     GetMutableTestInfo(i)->Run();
+    if (GTEST_FLAG_GET(fail_fast) &&
+        GetMutableTestInfo(i)->result()->Failed()) {
+      for (int j = i + 1; j < total_test_count(); j++) {
+        GetMutableTestInfo(j)->Skip();
+      }
+      break;
+    }
   }
-  elapsed_time_ = internal::GetTimeInMillis() - start_timestamp_;
+  elapsed_time_ = timer.Elapsed();
 
   impl->os_stack_trace_getter()->UponLeavingGTest();
   internal::HandleExceptionsInMethodIfSupported(
@@ -2824,9 +3054,39 @@
   // Call both legacy and the new API
   repeater->OnTestSuiteEnd(*this);
 //  Legacy API is deprecated but still available
-#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
   repeater->OnTestCaseEnd(*this);
-#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  impl->set_current_test_suite(nullptr);
+}
+
+// Skips all tests under this TestSuite.
+void TestSuite::Skip() {
+  if (!should_run_) return;
+
+  internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
+  impl->set_current_test_suite(this);
+
+  TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
+
+  // Call both legacy and the new API
+  repeater->OnTestSuiteStart(*this);
+//  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  repeater->OnTestCaseStart(*this);
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  for (int i = 0; i < total_test_count(); i++) {
+    GetMutableTestInfo(i)->Skip();
+  }
+
+  // Call both legacy and the new API
+  repeater->OnTestSuiteEnd(*this);
+  // Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  repeater->OnTestCaseEnd(*this);
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 
   impl->set_current_test_suite(nullptr);
 }
@@ -2878,7 +3138,7 @@
 static const char * TestPartResultTypeToString(TestPartResult::Type type) {
   switch (type) {
     case TestPartResult::kSkip:
-      return "Skipped";
+      return "Skipped\n";
     case TestPartResult::kSuccess:
       return "Success";
 
@@ -2895,6 +3155,9 @@
 }
 
 namespace internal {
+namespace {
+enum class GTestColor { kDefault, kRed, kGreen, kYellow };
+}  // namespace
 
 // Prints a TestPartResult to an std::string.
 static std::string PrintTestPartResultToString(
@@ -2932,9 +3195,12 @@
 // Returns the character attribute for the given color.
 static WORD GetColorAttribute(GTestColor color) {
   switch (color) {
-    case COLOR_RED:    return FOREGROUND_RED;
-    case COLOR_GREEN:  return FOREGROUND_GREEN;
-    case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN;
+    case GTestColor::kRed:
+      return FOREGROUND_RED;
+    case GTestColor::kGreen:
+      return FOREGROUND_GREEN;
+    case GTestColor::kYellow:
+      return FOREGROUND_RED | FOREGROUND_GREEN;
     default:           return 0;
   }
 }
@@ -2972,13 +3238,16 @@
 
 #else
 
-// Returns the ANSI color code for the given color.  COLOR_DEFAULT is
+// Returns the ANSI color code for the given color. GTestColor::kDefault is
 // an invalid input.
 static const char* GetAnsiColorCode(GTestColor color) {
   switch (color) {
-    case COLOR_RED:     return "1";
-    case COLOR_GREEN:   return "2";
-    case COLOR_YELLOW:  return "3";
+    case GTestColor::kRed:
+      return "1";
+    case GTestColor::kGreen:
+      return "2";
+    case GTestColor::kYellow:
+      return "3";
     default:
       return nullptr;
   }
@@ -2988,7 +3257,8 @@
 
 // Returns true if and only if Google Test should use colors in the output.
 bool ShouldUseColor(bool stdout_is_tty) {
-  const char* const gtest_color = GTEST_FLAG(color).c_str();
+  std::string c = GTEST_FLAG_GET(color);
+  const char* const gtest_color = c.c_str();
 
   if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) {
 #if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
@@ -3027,7 +3297,9 @@
 // cannot simply emit special characters and have the terminal change colors.
 // This routine must actually emit the characters rather than return a string
 // that would be colored when printed, as can be done on Linux.
-void ColoredPrintf(GTestColor color, const char* fmt, ...) {
+
+GTEST_ATTRIBUTE_PRINTF_(2, 3)
+static void ColoredPrintf(GTestColor color, const char *fmt, ...) {
   va_list args;
   va_start(args, fmt);
 
@@ -3037,7 +3309,7 @@
 #else
   static const bool in_color_mode =
       ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0);
-  const bool use_color = in_color_mode && (color != COLOR_DEFAULT);
+  const bool use_color = in_color_mode && (color != GTestColor::kDefault);
 #endif  // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_ZOS
 
   if (!use_color) {
@@ -3134,39 +3406,40 @@
 
  private:
   static void PrintFailedTests(const UnitTest& unit_test);
+  static void PrintFailedTestSuites(const UnitTest& unit_test);
   static void PrintSkippedTests(const UnitTest& unit_test);
 };
 
   // Fired before each iteration of tests starts.
 void PrettyUnitTestResultPrinter::OnTestIterationStart(
     const UnitTest& unit_test, int iteration) {
-  if (GTEST_FLAG(repeat) != 1)
+  if (GTEST_FLAG_GET(repeat) != 1)
     printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1);
 
-  const char* const filter = GTEST_FLAG(filter).c_str();
+  std::string f = GTEST_FLAG_GET(filter);
+  const char* const filter = f.c_str();
 
   // Prints the filter if it's not *.  This reminds the user that some
   // tests may be skipped.
   if (!String::CStringEquals(filter, kUniversalFilter)) {
-    ColoredPrintf(COLOR_YELLOW,
-                  "Note: %s filter = %s\n", GTEST_NAME_, filter);
+    ColoredPrintf(GTestColor::kYellow, "Note: %s filter = %s\n", GTEST_NAME_,
+                  filter);
   }
 
   if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) {
-    const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1);
-    ColoredPrintf(COLOR_YELLOW,
-                  "Note: This is test shard %d of %s.\n",
+    const int32_t shard_index = Int32FromEnvOrDie(kTestShardIndex, -1);
+    ColoredPrintf(GTestColor::kYellow, "Note: This is test shard %d of %s.\n",
                   static_cast<int>(shard_index) + 1,
                   internal::posix::GetEnv(kTestTotalShards));
   }
 
-  if (GTEST_FLAG(shuffle)) {
-    ColoredPrintf(COLOR_YELLOW,
+  if (GTEST_FLAG_GET(shuffle)) {
+    ColoredPrintf(GTestColor::kYellow,
                   "Note: Randomizing tests' orders with a seed of %d .\n",
                   unit_test.random_seed());
   }
 
-  ColoredPrintf(COLOR_GREEN,  "[==========] ");
+  ColoredPrintf(GTestColor::kGreen, "[==========] ");
   printf("Running %s from %s.\n",
          FormatTestCount(unit_test.test_to_run_count()).c_str(),
          FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str());
@@ -3175,7 +3448,7 @@
 
 void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart(
     const UnitTest& /*unit_test*/) {
-  ColoredPrintf(COLOR_GREEN,  "[----------] ");
+  ColoredPrintf(GTestColor::kGreen, "[----------] ");
   printf("Global test environment set-up.\n");
   fflush(stdout);
 }
@@ -3184,7 +3457,7 @@
 void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) {
   const std::string counts =
       FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
-  ColoredPrintf(COLOR_GREEN, "[----------] ");
+  ColoredPrintf(GTestColor::kGreen, "[----------] ");
   printf("%s from %s", counts.c_str(), test_case.name());
   if (test_case.type_param() == nullptr) {
     printf("\n");
@@ -3198,7 +3471,7 @@
     const TestSuite& test_suite) {
   const std::string counts =
       FormatCountableNoun(test_suite.test_to_run_count(), "test", "tests");
-  ColoredPrintf(COLOR_GREEN, "[----------] ");
+  ColoredPrintf(GTestColor::kGreen, "[----------] ");
   printf("%s from %s", counts.c_str(), test_suite.name());
   if (test_suite.type_param() == nullptr) {
     printf("\n");
@@ -3210,7 +3483,7 @@
 #endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 
 void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) {
-  ColoredPrintf(COLOR_GREEN,  "[ RUN      ] ");
+  ColoredPrintf(GTestColor::kGreen, "[ RUN      ] ");
   PrintTestName(test_info.test_suite_name(), test_info.name());
   printf("\n");
   fflush(stdout);
@@ -3220,9 +3493,7 @@
 void PrettyUnitTestResultPrinter::OnTestPartResult(
     const TestPartResult& result) {
   switch (result.type()) {
-    // If the test part succeeded, or was skipped,
-    // we don't need to do anything.
-    case TestPartResult::kSkip:
+    // If the test part succeeded, we don't need to do anything.
     case TestPartResult::kSuccess:
       return;
     default:
@@ -3235,17 +3506,17 @@
 
 void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) {
   if (test_info.result()->Passed()) {
-    ColoredPrintf(COLOR_GREEN, "[       OK ] ");
+    ColoredPrintf(GTestColor::kGreen, "[       OK ] ");
   } else if (test_info.result()->Skipped()) {
-    ColoredPrintf(COLOR_GREEN, "[  SKIPPED ] ");
+    ColoredPrintf(GTestColor::kGreen, "[  SKIPPED ] ");
   } else {
-    ColoredPrintf(COLOR_RED, "[  FAILED  ] ");
+    ColoredPrintf(GTestColor::kRed, "[  FAILED  ] ");
   }
   PrintTestName(test_info.test_suite_name(), test_info.name());
   if (test_info.result()->Failed())
     PrintFullTestCommentIfPresent(test_info);
 
-  if (GTEST_FLAG(print_time)) {
+  if (GTEST_FLAG_GET(print_time)) {
     printf(" (%s ms)\n", internal::StreamableToString(
            test_info.result()->elapsed_time()).c_str());
   } else {
@@ -3256,22 +3527,22 @@
 
 #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) {
-  if (!GTEST_FLAG(print_time)) return;
+  if (!GTEST_FLAG_GET(print_time)) return;
 
   const std::string counts =
       FormatCountableNoun(test_case.test_to_run_count(), "test", "tests");
-  ColoredPrintf(COLOR_GREEN, "[----------] ");
+  ColoredPrintf(GTestColor::kGreen, "[----------] ");
   printf("%s from %s (%s ms total)\n\n", counts.c_str(), test_case.name(),
          internal::StreamableToString(test_case.elapsed_time()).c_str());
   fflush(stdout);
 }
 #else
 void PrettyUnitTestResultPrinter::OnTestSuiteEnd(const TestSuite& test_suite) {
-  if (!GTEST_FLAG(print_time)) return;
+  if (!GTEST_FLAG_GET(print_time)) return;
 
   const std::string counts =
       FormatCountableNoun(test_suite.test_to_run_count(), "test", "tests");
-  ColoredPrintf(COLOR_GREEN, "[----------] ");
+  ColoredPrintf(GTestColor::kGreen, "[----------] ");
   printf("%s from %s (%s ms total)\n\n", counts.c_str(), test_suite.name(),
          internal::StreamableToString(test_suite.elapsed_time()).c_str());
   fflush(stdout);
@@ -3280,7 +3551,7 @@
 
 void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart(
     const UnitTest& /*unit_test*/) {
-  ColoredPrintf(COLOR_GREEN,  "[----------] ");
+  ColoredPrintf(GTestColor::kGreen, "[----------] ");
   printf("Global test environment tear-down\n");
   fflush(stdout);
 }
@@ -3288,9 +3559,8 @@
 // Internal helper for printing the list of failed tests.
 void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) {
   const int failed_test_count = unit_test.failed_test_count();
-  if (failed_test_count == 0) {
-    return;
-  }
+  ColoredPrintf(GTestColor::kRed, "[  FAILED  ] ");
+  printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str());
 
   for (int i = 0; i < unit_test.total_test_suite_count(); ++i) {
     const TestSuite& test_suite = *unit_test.GetTestSuite(i);
@@ -3302,12 +3572,36 @@
       if (!test_info.should_run() || !test_info.result()->Failed()) {
         continue;
       }
-      ColoredPrintf(COLOR_RED, "[  FAILED  ] ");
+      ColoredPrintf(GTestColor::kRed, "[  FAILED  ] ");
       printf("%s.%s", test_suite.name(), test_info.name());
       PrintFullTestCommentIfPresent(test_info);
       printf("\n");
     }
   }
+  printf("\n%2d FAILED %s\n", failed_test_count,
+         failed_test_count == 1 ? "TEST" : "TESTS");
+}
+
+// Internal helper for printing the list of test suite failures not covered by
+// PrintFailedTests.
+void PrettyUnitTestResultPrinter::PrintFailedTestSuites(
+    const UnitTest& unit_test) {
+  int suite_failure_count = 0;
+  for (int i = 0; i < unit_test.total_test_suite_count(); ++i) {
+    const TestSuite& test_suite = *unit_test.GetTestSuite(i);
+    if (!test_suite.should_run()) {
+      continue;
+    }
+    if (test_suite.ad_hoc_test_result().Failed()) {
+      ColoredPrintf(GTestColor::kRed, "[  FAILED  ] ");
+      printf("%s: SetUpTestSuite or TearDownTestSuite\n", test_suite.name());
+      ++suite_failure_count;
+    }
+  }
+  if (suite_failure_count > 0) {
+    printf("\n%2d FAILED TEST %s\n", suite_failure_count,
+           suite_failure_count == 1 ? "SUITE" : "SUITES");
+  }
 }
 
 // Internal helper for printing the list of skipped tests.
@@ -3327,7 +3621,7 @@
       if (!test_info.should_run() || !test_info.result()->Skipped()) {
         continue;
       }
-      ColoredPrintf(COLOR_GREEN, "[  SKIPPED ] ");
+      ColoredPrintf(GTestColor::kGreen, "[  SKIPPED ] ");
       printf("%s.%s", test_suite.name(), test_info.name());
       printf("\n");
     }
@@ -3336,44 +3630,37 @@
 
 void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
                                                      int /*iteration*/) {
-  ColoredPrintf(COLOR_GREEN,  "[==========] ");
+  ColoredPrintf(GTestColor::kGreen, "[==========] ");
   printf("%s from %s ran.",
          FormatTestCount(unit_test.test_to_run_count()).c_str(),
          FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str());
-  if (GTEST_FLAG(print_time)) {
+  if (GTEST_FLAG_GET(print_time)) {
     printf(" (%s ms total)",
            internal::StreamableToString(unit_test.elapsed_time()).c_str());
   }
   printf("\n");
-  ColoredPrintf(COLOR_GREEN,  "[  PASSED  ] ");
+  ColoredPrintf(GTestColor::kGreen, "[  PASSED  ] ");
   printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str());
 
   const int skipped_test_count = unit_test.skipped_test_count();
   if (skipped_test_count > 0) {
-    ColoredPrintf(COLOR_GREEN, "[  SKIPPED ] ");
+    ColoredPrintf(GTestColor::kGreen, "[  SKIPPED ] ");
     printf("%s, listed below:\n", FormatTestCount(skipped_test_count).c_str());
     PrintSkippedTests(unit_test);
   }
 
-  int num_failures = unit_test.failed_test_count();
   if (!unit_test.Passed()) {
-    const int failed_test_count = unit_test.failed_test_count();
-    ColoredPrintf(COLOR_RED,  "[  FAILED  ] ");
-    printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str());
     PrintFailedTests(unit_test);
-    printf("\n%2d FAILED %s\n", num_failures,
-                        num_failures == 1 ? "TEST" : "TESTS");
+    PrintFailedTestSuites(unit_test);
   }
 
   int num_disabled = unit_test.reportable_disabled_test_count();
-  if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) {
-    if (!num_failures) {
+  if (num_disabled && !GTEST_FLAG_GET(also_run_disabled_tests)) {
+    if (unit_test.Passed()) {
       printf("\n");  // Add a spacer if no FAILURE banner is displayed.
     }
-    ColoredPrintf(COLOR_YELLOW,
-                  "  YOU HAVE %d DISABLED %s\n\n",
-                  num_disabled,
-                  num_disabled == 1 ? "TEST" : "TESTS");
+    ColoredPrintf(GTestColor::kYellow, "  YOU HAVE %d DISABLED %s\n\n",
+                  num_disabled, num_disabled == 1 ? "TEST" : "TESTS");
   }
   // Ensure that Google Test output is printed before, e.g., heapchecker output.
   fflush(stdout);
@@ -3381,6 +3668,110 @@
 
 // End PrettyUnitTestResultPrinter
 
+// This class implements the TestEventListener interface.
+//
+// Class BriefUnitTestResultPrinter is copyable.
+class BriefUnitTestResultPrinter : public TestEventListener {
+ public:
+  BriefUnitTestResultPrinter() {}
+  static void PrintTestName(const char* test_suite, const char* test) {
+    printf("%s.%s", test_suite, test);
+  }
+
+  // The following methods override what's in the TestEventListener class.
+  void OnTestProgramStart(const UnitTest& /*unit_test*/) override {}
+  void OnTestIterationStart(const UnitTest& /*unit_test*/,
+                            int /*iteration*/) override {}
+  void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {}
+  void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {}
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  void OnTestCaseStart(const TestCase& /*test_case*/) override {}
+#else
+  void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {}
+#endif  // OnTestCaseStart
+
+  void OnTestStart(const TestInfo& /*test_info*/) override {}
+
+  void OnTestPartResult(const TestPartResult& result) override;
+  void OnTestEnd(const TestInfo& test_info) override;
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  void OnTestCaseEnd(const TestCase& /*test_case*/) override {}
+#else
+  void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {}
+#endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {}
+  void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {}
+  void OnTestIterationEnd(const UnitTest& unit_test, int iteration) override;
+  void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {}
+};
+
+// Called after an assertion failure.
+void BriefUnitTestResultPrinter::OnTestPartResult(
+    const TestPartResult& result) {
+  switch (result.type()) {
+    // If the test part succeeded, we don't need to do anything.
+    case TestPartResult::kSuccess:
+      return;
+    default:
+      // Print failure message from the assertion
+      // (e.g. expected this and got that).
+      PrintTestPartResult(result);
+      fflush(stdout);
+  }
+}
+
+void BriefUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) {
+  if (test_info.result()->Failed()) {
+    ColoredPrintf(GTestColor::kRed, "[  FAILED  ] ");
+    PrintTestName(test_info.test_suite_name(), test_info.name());
+    PrintFullTestCommentIfPresent(test_info);
+
+    if (GTEST_FLAG_GET(print_time)) {
+      printf(" (%s ms)\n",
+             internal::StreamableToString(test_info.result()->elapsed_time())
+                 .c_str());
+    } else {
+      printf("\n");
+    }
+    fflush(stdout);
+  }
+}
+
+void BriefUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test,
+                                                    int /*iteration*/) {
+  ColoredPrintf(GTestColor::kGreen, "[==========] ");
+  printf("%s from %s ran.",
+         FormatTestCount(unit_test.test_to_run_count()).c_str(),
+         FormatTestSuiteCount(unit_test.test_suite_to_run_count()).c_str());
+  if (GTEST_FLAG_GET(print_time)) {
+    printf(" (%s ms total)",
+           internal::StreamableToString(unit_test.elapsed_time()).c_str());
+  }
+  printf("\n");
+  ColoredPrintf(GTestColor::kGreen, "[  PASSED  ] ");
+  printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str());
+
+  const int skipped_test_count = unit_test.skipped_test_count();
+  if (skipped_test_count > 0) {
+    ColoredPrintf(GTestColor::kGreen, "[  SKIPPED ] ");
+    printf("%s.\n", FormatTestCount(skipped_test_count).c_str());
+  }
+
+  int num_disabled = unit_test.reportable_disabled_test_count();
+  if (num_disabled && !GTEST_FLAG_GET(also_run_disabled_tests)) {
+    if (unit_test.Passed()) {
+      printf("\n");  // Add a spacer if no FAILURE banner is displayed.
+    }
+    ColoredPrintf(GTestColor::kYellow, "  YOU HAVE %d DISABLED %s\n\n",
+                  num_disabled, num_disabled == 1 ? "TEST" : "TESTS");
+  }
+  // Ensure that Google Test output is printed before, e.g., heapchecker output.
+  fflush(stdout);
+}
+
+// End BriefUnitTestResultPrinter
+
 // class TestEventRepeater
 //
 // This class forwards events to other event listeners.
@@ -3564,6 +3955,16 @@
   // Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
   static void OutputXmlCDataSection(::std::ostream* stream, const char* data);
 
+  // Streams a test suite XML stanza containing the given test result.
+  //
+  // Requires: result.Failed()
+  static void OutputXmlTestSuiteForTestResult(::std::ostream* stream,
+                                              const TestResult& result);
+
+  // Streams an XML representation of a TestResult object.
+  static void OutputXmlTestResult(::std::ostream* stream,
+                                  const TestResult& result);
+
   // Streams an XML representation of a TestInfo object.
   static void OutputXmlTestInfo(::std::ostream* stream,
                                 const char* test_suite_name,
@@ -3722,6 +4123,10 @@
   if (tm_ptr == nullptr) return false;
   *out = *tm_ptr;
   return true;
+#elif defined(__STDC_LIB_EXT1__)
+  // Uses localtime_s when available as localtime_r is only available from
+  // C23 standard.
+  return localtime_s(&seconds, out) != nullptr;
 #else
   return localtime_r(&seconds, out) != nullptr;
 #endif
@@ -3733,13 +4138,14 @@
   struct tm time_struct;
   if (!PortableLocaltime(static_cast<time_t>(ms / 1000), &time_struct))
     return "";
-  // YYYY-MM-DDThh:mm:ss
+  // YYYY-MM-DDThh:mm:ss.sss
   return StreamableToString(time_struct.tm_year + 1900) + "-" +
       String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" +
       String::FormatIntWidth2(time_struct.tm_mday) + "T" +
       String::FormatIntWidth2(time_struct.tm_hour) + ":" +
       String::FormatIntWidth2(time_struct.tm_min) + ":" +
-      String::FormatIntWidth2(time_struct.tm_sec);
+      String::FormatIntWidth2(time_struct.tm_sec) + "." +
+      String::FormatIntWidthN(static_cast<int>(ms % 1000), 3);
 }
 
 // Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
@@ -3778,6 +4184,43 @@
   *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\"";
 }
 
+// Streams a test suite XML stanza containing the given test result.
+void XmlUnitTestResultPrinter::OutputXmlTestSuiteForTestResult(
+    ::std::ostream* stream, const TestResult& result) {
+  // Output the boilerplate for a minimal test suite with one test.
+  *stream << "  <testsuite";
+  OutputXmlAttribute(stream, "testsuite", "name", "NonTestSuiteFailure");
+  OutputXmlAttribute(stream, "testsuite", "tests", "1");
+  OutputXmlAttribute(stream, "testsuite", "failures", "1");
+  OutputXmlAttribute(stream, "testsuite", "disabled", "0");
+  OutputXmlAttribute(stream, "testsuite", "skipped", "0");
+  OutputXmlAttribute(stream, "testsuite", "errors", "0");
+  OutputXmlAttribute(stream, "testsuite", "time",
+                     FormatTimeInMillisAsSeconds(result.elapsed_time()));
+  OutputXmlAttribute(
+      stream, "testsuite", "timestamp",
+      FormatEpochTimeInMillisAsIso8601(result.start_timestamp()));
+  *stream << ">";
+
+  // Output the boilerplate for a minimal test case with a single test.
+  *stream << "    <testcase";
+  OutputXmlAttribute(stream, "testcase", "name", "");
+  OutputXmlAttribute(stream, "testcase", "status", "run");
+  OutputXmlAttribute(stream, "testcase", "result", "completed");
+  OutputXmlAttribute(stream, "testcase", "classname", "");
+  OutputXmlAttribute(stream, "testcase", "time",
+                     FormatTimeInMillisAsSeconds(result.elapsed_time()));
+  OutputXmlAttribute(
+      stream, "testcase", "timestamp",
+      FormatEpochTimeInMillisAsIso8601(result.start_timestamp()));
+
+  // Output the actual test result.
+  OutputXmlTestResult(stream, result);
+
+  // Complete the test suite.
+  *stream << "  </testsuite>\n";
+}
+
 // Prints an XML representation of a TestInfo object.
 void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream,
                                                  const char* test_suite_name,
@@ -3800,7 +4243,7 @@
     OutputXmlAttribute(stream, kTestsuite, "type_param",
                        test_info.type_param());
   }
-  if (GTEST_FLAG(list_tests)) {
+  if (GTEST_FLAG_GET(list_tests)) {
     OutputXmlAttribute(stream, kTestsuite, "file", test_info.file());
     OutputXmlAttribute(stream, kTestsuite, "line",
                        StreamableToString(test_info.line()));
@@ -3821,11 +4264,17 @@
       FormatEpochTimeInMillisAsIso8601(result.start_timestamp()));
   OutputXmlAttribute(stream, kTestsuite, "classname", test_suite_name);
 
+  OutputXmlTestResult(stream, result);
+}
+
+void XmlUnitTestResultPrinter::OutputXmlTestResult(::std::ostream* stream,
+                                                   const TestResult& result) {
   int failures = 0;
+  int skips = 0;
   for (int i = 0; i < result.total_part_count(); ++i) {
     const TestPartResult& part = result.GetTestPartResult(i);
     if (part.failed()) {
-      if (++failures == 1) {
+      if (++failures == 1 && skips == 0) {
         *stream << ">\n";
       }
       const std::string location =
@@ -3833,18 +4282,31 @@
                                                           part.line_number());
       const std::string summary = location + "\n" + part.summary();
       *stream << "      <failure message=\""
-              << EscapeXmlAttribute(summary.c_str())
+              << EscapeXmlAttribute(summary)
               << "\" type=\"\">";
       const std::string detail = location + "\n" + part.message();
       OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str());
       *stream << "</failure>\n";
+    } else if (part.skipped()) {
+      if (++skips == 1 && failures == 0) {
+        *stream << ">\n";
+      }
+      const std::string location =
+          internal::FormatCompilerIndependentFileLocation(part.file_name(),
+                                                          part.line_number());
+      const std::string summary = location + "\n" + part.summary();
+      *stream << "      <skipped message=\""
+              << EscapeXmlAttribute(summary.c_str()) << "\">";
+      const std::string detail = location + "\n" + part.message();
+      OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str());
+      *stream << "</skipped>\n";
     }
   }
 
-  if (failures == 0 && result.test_property_count() == 0) {
+  if (failures == 0 && skips == 0 && result.test_property_count() == 0) {
     *stream << " />\n";
   } else {
-    if (failures == 0) {
+    if (failures == 0 && skips == 0) {
       *stream << ">\n";
     }
     OutputXmlTestProperties(stream, result);
@@ -3860,13 +4322,17 @@
   OutputXmlAttribute(stream, kTestsuite, "name", test_suite.name());
   OutputXmlAttribute(stream, kTestsuite, "tests",
                      StreamableToString(test_suite.reportable_test_count()));
-  if (!GTEST_FLAG(list_tests)) {
+  if (!GTEST_FLAG_GET(list_tests)) {
     OutputXmlAttribute(stream, kTestsuite, "failures",
                        StreamableToString(test_suite.failed_test_count()));
     OutputXmlAttribute(
         stream, kTestsuite, "disabled",
         StreamableToString(test_suite.reportable_disabled_test_count()));
+    OutputXmlAttribute(stream, kTestsuite, "skipped",
+                       StreamableToString(test_suite.skipped_test_count()));
+
     OutputXmlAttribute(stream, kTestsuite, "errors", "0");
+
     OutputXmlAttribute(stream, kTestsuite, "time",
                        FormatTimeInMillisAsSeconds(test_suite.elapsed_time()));
     OutputXmlAttribute(
@@ -3904,7 +4370,7 @@
       stream, kTestsuites, "timestamp",
       FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp()));
 
-  if (GTEST_FLAG(shuffle)) {
+  if (GTEST_FLAG_GET(shuffle)) {
     OutputXmlAttribute(stream, kTestsuites, "random_seed",
                        StreamableToString(unit_test.random_seed()));
   }
@@ -3917,6 +4383,13 @@
     if (unit_test.GetTestSuite(i)->reportable_test_count() > 0)
       PrintXmlTestSuite(stream, *unit_test.GetTestSuite(i));
   }
+
+  // If there was a test failure outside of one of the test suites (like in a
+  // test environment) include that in the output.
+  if (unit_test.ad_hoc_test_result().Failed()) {
+    OutputXmlTestSuiteForTestResult(stream, unit_test.ad_hoc_test_result());
+  }
+
   *stream << "</" << kTestsuites << ">\n";
 }
 
@@ -4007,6 +4480,16 @@
                             const std::string& indent,
                             bool comma = true);
 
+  // Streams a test suite JSON stanza containing the given test result.
+  //
+  // Requires: result.Failed()
+  static void OutputJsonTestSuiteForTestResult(::std::ostream* stream,
+                                               const TestResult& result);
+
+  // Streams a JSON representation of a TestResult object.
+  static void OutputJsonTestResult(::std::ostream* stream,
+                                   const TestResult& result);
+
   // Streams a JSON representation of a TestInfo object.
   static void OutputJsonTestInfo(::std::ostream* stream,
                                  const char* test_suite_name,
@@ -4157,6 +4640,48 @@
     *stream << ",\n";
 }
 
+// Streams a test suite JSON stanza containing the given test result.
+void JsonUnitTestResultPrinter::OutputJsonTestSuiteForTestResult(
+    ::std::ostream* stream, const TestResult& result) {
+  // Output the boilerplate for a new test suite.
+  *stream << Indent(4) << "{\n";
+  OutputJsonKey(stream, "testsuite", "name", "NonTestSuiteFailure", Indent(6));
+  OutputJsonKey(stream, "testsuite", "tests", 1, Indent(6));
+  if (!GTEST_FLAG_GET(list_tests)) {
+    OutputJsonKey(stream, "testsuite", "failures", 1, Indent(6));
+    OutputJsonKey(stream, "testsuite", "disabled", 0, Indent(6));
+    OutputJsonKey(stream, "testsuite", "skipped", 0, Indent(6));
+    OutputJsonKey(stream, "testsuite", "errors", 0, Indent(6));
+    OutputJsonKey(stream, "testsuite", "time",
+                  FormatTimeInMillisAsDuration(result.elapsed_time()),
+                  Indent(6));
+    OutputJsonKey(stream, "testsuite", "timestamp",
+                  FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()),
+                  Indent(6));
+  }
+  *stream << Indent(6) << "\"testsuite\": [\n";
+
+  // Output the boilerplate for a new test case.
+  *stream << Indent(8) << "{\n";
+  OutputJsonKey(stream, "testcase", "name", "", Indent(10));
+  OutputJsonKey(stream, "testcase", "status", "RUN", Indent(10));
+  OutputJsonKey(stream, "testcase", "result", "COMPLETED", Indent(10));
+  OutputJsonKey(stream, "testcase", "timestamp",
+                FormatEpochTimeInMillisAsRFC3339(result.start_timestamp()),
+                Indent(10));
+  OutputJsonKey(stream, "testcase", "time",
+                FormatTimeInMillisAsDuration(result.elapsed_time()),
+                Indent(10));
+  OutputJsonKey(stream, "testcase", "classname", "", Indent(10), false);
+  *stream << TestPropertiesAsJson(result, Indent(10));
+
+  // Output the actual test result.
+  OutputJsonTestResult(stream, result);
+
+  // Finish the test suite.
+  *stream << "\n" << Indent(6) << "]\n" << Indent(4) << "}";
+}
+
 // Prints a JSON representation of a TestInfo object.
 void JsonUnitTestResultPrinter::OutputJsonTestInfo(::std::ostream* stream,
                                                    const char* test_suite_name,
@@ -4176,7 +4701,7 @@
     OutputJsonKey(stream, kTestsuite, "type_param", test_info.type_param(),
                   kIndent);
   }
-  if (GTEST_FLAG(list_tests)) {
+  if (GTEST_FLAG_GET(list_tests)) {
     OutputJsonKey(stream, kTestsuite, "file", test_info.file(), kIndent);
     OutputJsonKey(stream, kTestsuite, "line", test_info.line(), kIndent, false);
     *stream << "\n" << Indent(8) << "}";
@@ -4199,6 +4724,13 @@
                 false);
   *stream << TestPropertiesAsJson(result, kIndent);
 
+  OutputJsonTestResult(stream, result);
+}
+
+void JsonUnitTestResultPrinter::OutputJsonTestResult(::std::ostream* stream,
+                                                     const TestResult& result) {
+  const std::string kIndent = Indent(10);
+
   int failures = 0;
   for (int i = 0; i < result.total_part_count(); ++i) {
     const TestPartResult& part = result.GetTestPartResult(i);
@@ -4233,7 +4765,7 @@
   OutputJsonKey(stream, kTestsuite, "name", test_suite.name(), kIndent);
   OutputJsonKey(stream, kTestsuite, "tests", test_suite.reportable_test_count(),
                 kIndent);
-  if (!GTEST_FLAG(list_tests)) {
+  if (!GTEST_FLAG_GET(list_tests)) {
     OutputJsonKey(stream, kTestsuite, "failures",
                   test_suite.failed_test_count(), kIndent);
     OutputJsonKey(stream, kTestsuite, "disabled",
@@ -4280,7 +4812,7 @@
   OutputJsonKey(stream, kTestsuites, "disabled",
                 unit_test.reportable_disabled_test_count(), kIndent);
   OutputJsonKey(stream, kTestsuites, "errors", 0, kIndent);
-  if (GTEST_FLAG(shuffle)) {
+  if (GTEST_FLAG_GET(shuffle)) {
     OutputJsonKey(stream, kTestsuites, "random_seed", unit_test.random_seed(),
                   kIndent);
   }
@@ -4309,6 +4841,12 @@
     }
   }
 
+  // If there was a test failure outside of one of the test suites (like in a
+  // test environment) include that in the output.
+  if (unit_test.ad_hoc_test_result().Failed()) {
+    OutputJsonTestSuiteForTestResult(stream, unit_test.ad_hoc_test_result());
+  }
+
   *stream << "\n" << kIndent << "]\n" << "}\n";
 }
 
@@ -4451,7 +4989,7 @@
 
   for (int i = 0; i < raw_stack_size; ++i) {
     if (raw_stack[i] == caller_frame &&
-        !GTEST_FLAG(show_internal_stack_frames)) {
+        !GTEST_FLAG_GET(show_internal_stack_frames)) {
       // Add a marker to the trace and stop adding frames.
       absl::StrAppend(&result, kElidedFramesMarker, "\n");
       break;
@@ -4508,6 +5046,7 @@
   }
 
   ~ScopedPrematureExitFile() {
+#if !defined GTEST_OS_ESP8266
     if (!premature_exit_filepath_.empty()) {
       int retval = remove(premature_exit_filepath_.c_str());
       if (retval) {
@@ -4516,6 +5055,7 @@
                           << retval;
       }
     }
+#endif
   }
 
  private:
@@ -4801,7 +5341,7 @@
     // in the code (perhaps in order to use Google Test assertions
     // with another testing framework) and specify the former on the
     // command line for debugging.
-    if (GTEST_FLAG(break_on_failure)) {
+    if (GTEST_FLAG_GET(break_on_failure)) {
 #if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
       // Using DebugBreak on Windows allows gtest to still break into a debugger
       // when a failure happens and both the --gtest_break_on_failure and
@@ -4818,7 +5358,7 @@
       // portability: some debuggers don't correctly trap abort().
       *static_cast<volatile int*>(nullptr) = 1;
 #endif  // GTEST_OS_WINDOWS
-    } else if (GTEST_FLAG(throw_on_failure)) {
+    } else if (GTEST_FLAG_GET(throw_on_failure)) {
 #if GTEST_HAS_EXCEPTIONS
       throw internal::GoogleTestFailureException(result);
 #else
@@ -4847,7 +5387,7 @@
 // from the main thread.
 int UnitTest::Run() {
   const bool in_death_test_child_process =
-      internal::GTEST_FLAG(internal_run_death_test).length() > 0;
+      GTEST_FLAG_GET(internal_run_death_test).length() > 0;
 
   // Google Test implements this protocol for catching that a test
   // program exits before returning control to Google Test:
@@ -4877,7 +5417,7 @@
 
   // Captures the value of GTEST_FLAG(catch_exceptions).  This value will be
   // used for the duration of the program.
-  impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
+  impl()->set_catch_exceptions(GTEST_FLAG_GET(catch_exceptions));
 
 #if GTEST_OS_WINDOWS
   // Either the user wants Google Test to catch exceptions thrown by the
@@ -4904,11 +5444,10 @@
     // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement
     // executed. Google Test will notify the user of any unexpected
     // failure via stderr.
-    if (!GTEST_FLAG(break_on_failure))
+    if (!GTEST_FLAG_GET(break_on_failure))
       _set_abort_behavior(
           0x0,                                    // Clear the following flags:
           _WRITE_ABORT_MSG | _CALL_REPORTFAULT);  // pop-up window, core dump.
-# endif
 
     // In debug mode, the Windows CRT can crash with an assertion over invalid
     // input (e.g. passing an invalid file descriptor).  The default handling
@@ -4919,6 +5458,7 @@
                               _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
       (void)_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
     }
+# endif
   }
 #endif  // GTEST_OS_WINDOWS
 
@@ -5086,7 +5626,7 @@
 // Initializes event listeners for streaming test results in string form.
 // Must not be called before InitGoogleTest.
 void UnitTestImpl::ConfigureStreamingOutput() {
-  const std::string& target = GTEST_FLAG(stream_result_to);
+  const std::string& target = GTEST_FLAG_GET(stream_result_to);
   if (!target.empty()) {
     const size_t pos = target.find(':');
     if (pos != std::string::npos) {
@@ -5129,13 +5669,17 @@
     // to shut down the default XML output before invoking RUN_ALL_TESTS.
     ConfigureXmlOutput();
 
+    if (GTEST_FLAG_GET(brief)) {
+      listeners()->SetDefaultResultPrinter(new BriefUnitTestResultPrinter);
+    }
+
 #if GTEST_CAN_STREAM_RESULTS_
     // Configures listeners for streaming test results to the specified server.
     ConfigureStreamingOutput();
 #endif  // GTEST_CAN_STREAM_RESULTS_
 
 #if GTEST_HAS_ABSL
-    if (GTEST_FLAG(install_failure_signal_handler)) {
+    if (GTEST_FLAG_GET(install_failure_signal_handler)) {
       absl::FailureSignalHandlerOptions options;
       absl::InstallFailureSignalHandler(options);
     }
@@ -5174,10 +5718,10 @@
 // Arguments:
 //
 //   test_suite_name: name of the test suite
-//   type_param:     the name of the test suite's type parameter, or NULL if
-//                   this is not a typed or a type-parameterized test suite.
-//   set_up_tc:      pointer to the function that sets up the test suite
-//   tear_down_tc:   pointer to the function that tears down the test suite
+//   type_param:      the name of the test suite's type parameter, or NULL if
+//                    this is not a typed or a type-parameterized test suite.
+//   set_up_tc:       pointer to the function that sets up the test suite
+//   tear_down_tc:    pointer to the function that tears down the test suite
 TestSuite* UnitTestImpl::GetTestSuite(
     const char* test_suite_name, const char* type_param,
     internal::SetUpTestSuiteFunc set_up_tc,
@@ -5268,14 +5812,15 @@
                                               : IGNORE_SHARDING_PROTOCOL) > 0;
 
   // Lists the tests and exits if the --gtest_list_tests flag was specified.
-  if (GTEST_FLAG(list_tests)) {
+  if (GTEST_FLAG_GET(list_tests)) {
     // This must be called *after* FilterTests() has been called.
     ListTestsMatchingFilter();
     return true;
   }
 
-  random_seed_ = GTEST_FLAG(shuffle) ?
-      GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0;
+  random_seed_ = GTEST_FLAG_GET(shuffle)
+                     ? GetRandomSeedFromFlag(GTEST_FLAG_GET(random_seed))
+                     : 0;
 
   // True if and only if at least one test has failed.
   bool failed = false;
@@ -5287,19 +5832,31 @@
 
   // How many times to repeat the tests?  We don't want to repeat them
   // when we are inside the subprocess of a death test.
-  const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat);
+  const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG_GET(repeat);
+
   // Repeats forever if the repeat count is negative.
   const bool gtest_repeat_forever = repeat < 0;
+
+  // Should test environments be set up and torn down for each repeat, or only
+  // set up on the first and torn down on the last iteration? If there is no
+  // "last" iteration because the tests will repeat forever, always recreate the
+  // environments to avoid leaks in case one of the environments is using
+  // resources that are external to this process. Without this check there would
+  // be no way to clean up those external resources automatically.
+  const bool recreate_environments_when_repeating =
+      GTEST_FLAG_GET(recreate_environments_when_repeating) ||
+      gtest_repeat_forever;
+
   for (int i = 0; gtest_repeat_forever || i != repeat; i++) {
     // We want to preserve failures generated by ad-hoc test
     // assertions executed before RUN_ALL_TESTS().
     ClearNonAdHocTestResult();
 
-    const TimeInMillis start = GetTimeInMillis();
+    Timer timer;
 
     // Shuffles test suites and tests if requested.
-    if (has_tests_to_run && GTEST_FLAG(shuffle)) {
-      random()->Reseed(static_cast<UInt32>(random_seed_));
+    if (has_tests_to_run && GTEST_FLAG_GET(shuffle)) {
+      random()->Reseed(static_cast<uint32_t>(random_seed_));
       // This should be done before calling OnTestIterationStart(),
       // such that a test event listener can see the actual test order
       // in the event.
@@ -5311,10 +5868,13 @@
 
     // Runs each test suite if there is at least one test to run.
     if (has_tests_to_run) {
-      // Sets up all environments beforehand.
-      repeater->OnEnvironmentsSetUpStart(*parent_);
-      ForEach(environments_, SetUpEnvironment);
-      repeater->OnEnvironmentsSetUpEnd(*parent_);
+      // Sets up all environments beforehand. If test environments aren't
+      // recreated for each iteration, only do so on the first iteration.
+      if (i == 0 || recreate_environments_when_repeating) {
+        repeater->OnEnvironmentsSetUpStart(*parent_);
+        ForEach(environments_, SetUpEnvironment);
+        repeater->OnEnvironmentsSetUpEnd(*parent_);
+      }
 
       // Runs the tests only if there was no fatal failure or skip triggered
       // during global set-up.
@@ -5336,17 +5896,36 @@
         for (int test_index = 0; test_index < total_test_suite_count();
              test_index++) {
           GetMutableSuiteCase(test_index)->Run();
+          if (GTEST_FLAG_GET(fail_fast) &&
+              GetMutableSuiteCase(test_index)->Failed()) {
+            for (int j = test_index + 1; j < total_test_suite_count(); j++) {
+              GetMutableSuiteCase(j)->Skip();
+            }
+            break;
+          }
+        }
+      } else if (Test::HasFatalFailure()) {
+        // If there was a fatal failure during the global setup then we know we
+        // aren't going to run any tests. Explicitly mark all of the tests as
+        // skipped to make this obvious in the output.
+        for (int test_index = 0; test_index < total_test_suite_count();
+             test_index++) {
+          GetMutableSuiteCase(test_index)->Skip();
         }
       }
 
-      // Tears down all environments in reverse order afterwards.
-      repeater->OnEnvironmentsTearDownStart(*parent_);
-      std::for_each(environments_.rbegin(), environments_.rend(),
-                    TearDownEnvironment);
-      repeater->OnEnvironmentsTearDownEnd(*parent_);
+      // Tears down all environments in reverse order afterwards. If test
+      // environments aren't recreated for each iteration, only do so on the
+      // last iteration.
+      if (i == repeat - 1 || recreate_environments_when_repeating) {
+        repeater->OnEnvironmentsTearDownStart(*parent_);
+        std::for_each(environments_.rbegin(), environments_.rend(),
+                      TearDownEnvironment);
+        repeater->OnEnvironmentsTearDownEnd(*parent_);
+      }
     }
 
-    elapsed_time_ = GetTimeInMillis() - start;
+    elapsed_time_ = timer.Elapsed();
 
     // Tells the unit test event listener that the tests have just finished.
     repeater->OnTestIterationEnd(*parent_, i);
@@ -5364,7 +5943,7 @@
     // (it's always safe to unshuffle the tests).
     UnshuffleTests();
 
-    if (GTEST_FLAG(shuffle)) {
+    if (GTEST_FLAG_GET(shuffle)) {
       // Picks a new random seed for each iteration.
       random_seed_ = GetNextRandomSeed(random_seed_);
     }
@@ -5374,14 +5953,14 @@
 
   if (!gtest_is_initialized_before_run_all_tests) {
     ColoredPrintf(
-        COLOR_RED,
+        GTestColor::kRed,
         "\nIMPORTANT NOTICE - DO NOT IGNORE:\n"
         "This test program did NOT call " GTEST_INIT_GOOGLE_TEST_NAME_
         "() before calling RUN_ALL_TESTS(). This is INVALID. Soon " GTEST_NAME_
         " will start to enforce the valid usage. "
         "Please fix it ASAP, or IT WILL START TO FAIL.\n");  // NOLINT
 #if GTEST_FOR_GOOGLE_
-    ColoredPrintf(COLOR_RED,
+    ColoredPrintf(GTestColor::kRed,
                   "For more details, see http://wiki/Main/ValidGUnitMain.\n");
 #endif  // GTEST_FOR_GOOGLE_
   }
@@ -5398,7 +5977,7 @@
   if (test_shard_file != nullptr) {
     FILE* const file = posix::FOpen(test_shard_file, "w");
     if (file == nullptr) {
-      ColoredPrintf(COLOR_RED,
+      ColoredPrintf(GTestColor::kRed,
                     "Could not write to the test shard status file \"%s\" "
                     "specified by the %s environment variable.\n",
                     test_shard_file, kTestShardStatusFile);
@@ -5422,8 +6001,8 @@
     return false;
   }
 
-  const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1);
-  const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1);
+  const int32_t total_shards = Int32FromEnvOrDie(total_shards_env, -1);
+  const int32_t shard_index = Int32FromEnvOrDie(shard_index_env, -1);
 
   if (total_shards == -1 && shard_index == -1) {
     return false;
@@ -5432,7 +6011,7 @@
       << "Invalid environment variables: you have "
       << kTestShardIndex << " = " << shard_index
       << ", but have left " << kTestTotalShards << " unset.\n";
-    ColoredPrintf(COLOR_RED, "%s", msg.GetString().c_str());
+    ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str());
     fflush(stdout);
     exit(EXIT_FAILURE);
   } else if (total_shards != -1 && shard_index == -1) {
@@ -5440,7 +6019,7 @@
       << "Invalid environment variables: you have "
       << kTestTotalShards << " = " << total_shards
       << ", but have left " << kTestShardIndex << " unset.\n";
-    ColoredPrintf(COLOR_RED, "%s", msg.GetString().c_str());
+    ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str());
     fflush(stdout);
     exit(EXIT_FAILURE);
   } else if (shard_index < 0 || shard_index >= total_shards) {
@@ -5449,7 +6028,7 @@
       << kTestShardIndex << " < " << kTestTotalShards
       << ", but you have " << kTestShardIndex << "=" << shard_index
       << ", " << kTestTotalShards << "=" << total_shards << ".\n";
-    ColoredPrintf(COLOR_RED, "%s", msg.GetString().c_str());
+    ColoredPrintf(GTestColor::kRed, "%s", msg.GetString().c_str());
     fflush(stdout);
     exit(EXIT_FAILURE);
   }
@@ -5460,13 +6039,13 @@
 // Parses the environment variable var as an Int32. If it is unset,
 // returns default_val. If it is not an Int32, prints an error
 // and aborts.
-Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) {
+int32_t Int32FromEnvOrDie(const char* var, int32_t default_val) {
   const char* str_val = posix::GetEnv(var);
   if (str_val == nullptr) {
     return default_val;
   }
 
-  Int32 result;
+  int32_t result;
   if (!ParseInt32(Message() << "The value of environment variable " << var,
                   str_val, &result)) {
     exit(EXIT_FAILURE);
@@ -5490,9 +6069,9 @@
 // https://github.com/google/googletest/blob/master/googletest/docs/advanced.md
 // . Returns the number of tests that should run.
 int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) {
-  const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ?
+  const int32_t total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ?
       Int32FromEnvOrDie(kTestTotalShards, -1) : -1;
-  const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ?
+  const int32_t shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ?
       Int32FromEnvOrDie(kTestShardIndex, -1) : -1;
 
   // num_runnable_tests are the number of tests that will
@@ -5521,7 +6100,7 @@
       test_info->matches_filter_ = matches_filter;
 
       const bool is_runnable =
-          (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) &&
+          (GTEST_FLAG_GET(also_run_disabled_tests) || !is_disabled) &&
           matches_filter;
 
       const bool is_in_another_shard =
@@ -5732,13 +6311,14 @@
 // part can be omitted.
 //
 // Returns the value of the flag, or NULL if the parsing failed.
-static const char* ParseFlagValue(const char* str, const char* flag,
+static const char* ParseFlagValue(const char* str, const char* flag_name,
                                   bool def_optional) {
   // str and flag must not be NULL.
-  if (str == nullptr || flag == nullptr) return nullptr;
+  if (str == nullptr || flag_name == nullptr) return nullptr;
 
   // The flag must start with "--" followed by GTEST_FLAG_PREFIX_.
-  const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag;
+  const std::string flag_str =
+      std::string("--") + GTEST_FLAG_PREFIX_ + flag_name;
   const size_t flag_len = flag_str.length();
   if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr;
 
@@ -5769,9 +6349,9 @@
 //
 // On success, stores the value of the flag in *value, and returns
 // true.  On failure, returns false without changing *value.
-static bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
+static bool ParseFlag(const char* str, const char* flag_name, bool* value) {
   // Gets the value of the flag as a string.
-  const char* const value_str = ParseFlagValue(str, flag, true);
+  const char* const value_str = ParseFlagValue(str, flag_name, true);
 
   // Aborts if the parsing failed.
   if (value_str == nullptr) return false;
@@ -5781,32 +6361,30 @@
   return true;
 }
 
-// Parses a string for an Int32 flag, in the form of
-// "--flag=value".
+// Parses a string for an int32_t flag, in the form of "--flag=value".
 //
 // On success, stores the value of the flag in *value, and returns
 // true.  On failure, returns false without changing *value.
-bool ParseInt32Flag(const char* str, const char* flag, Int32* value) {
+bool ParseFlag(const char* str, const char* flag_name, int32_t* value) {
   // Gets the value of the flag as a string.
-  const char* const value_str = ParseFlagValue(str, flag, false);
+  const char* const value_str = ParseFlagValue(str, flag_name, false);
 
   // Aborts if the parsing failed.
   if (value_str == nullptr) return false;
 
   // Sets *value to the value of the flag.
-  return ParseInt32(Message() << "The value of flag --" << flag,
-                    value_str, value);
+  return ParseInt32(Message() << "The value of flag --" << flag_name, value_str,
+                    value);
 }
 
-// Parses a string for a string flag, in the form of
-// "--flag=value".
+// Parses a string for a string flag, in the form of "--flag=value".
 //
 // On success, stores the value of the flag in *value, and returns
 // true.  On failure, returns false without changing *value.
 template <typename String>
-static bool ParseStringFlag(const char* str, const char* flag, String* value) {
+static bool ParseFlag(const char* str, const char* flag_name, String* value) {
   // Gets the value of the flag as a string.
-  const char* const value_str = ParseFlagValue(str, flag, false);
+  const char* const value_str = ParseFlagValue(str, flag_name, false);
 
   // Aborts if the parsing failed.
   if (value_str == nullptr) return false;
@@ -5841,7 +6419,7 @@
 //   @D    changes to the default terminal text color.
 //
 static void PrintColorEncoded(const char* str) {
-  GTestColor color = COLOR_DEFAULT;  // The current color.
+  GTestColor color = GTestColor::kDefault;  // The current color.
 
   // Conceptually, we split the string into segments divided by escape
   // sequences.  Then we print one segment at a time.  At the end of
@@ -5861,13 +6439,13 @@
     if (ch == '@') {
       ColoredPrintf(color, "@");
     } else if (ch == 'D') {
-      color = COLOR_DEFAULT;
+      color = GTestColor::kDefault;
     } else if (ch == 'R') {
-      color = COLOR_RED;
+      color = GTestColor::kRed;
     } else if (ch == 'G') {
-      color = COLOR_GREEN;
+      color = GTestColor::kGreen;
     } else if (ch == 'Y') {
-      color = COLOR_YELLOW;
+      color = GTestColor::kYellow;
     } else {
       --str;
     }
@@ -5875,105 +6453,140 @@
 }
 
 static const char kColorEncodedHelpMessage[] =
-"This program contains tests written using " GTEST_NAME_ ". You can use the\n"
-"following command line flags to control its behavior:\n"
-"\n"
-"Test Selection:\n"
-"  @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n"
-"      List the names of all tests instead of running them. The name of\n"
-"      TEST(Foo, Bar) is \"Foo.Bar\".\n"
-"  @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS"
+    "This program contains tests written using " GTEST_NAME_
+    ". You can use the\n"
+    "following command line flags to control its behavior:\n"
+    "\n"
+    "Test Selection:\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "list_tests@D\n"
+    "      List the names of all tests instead of running them. The name of\n"
+    "      TEST(Foo, Bar) is \"Foo.Bar\".\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "filter=@YPOSITIVE_PATTERNS"
     "[@G-@YNEGATIVE_PATTERNS]@D\n"
-"      Run only the tests whose name matches one of the positive patterns but\n"
-"      none of the negative patterns. '?' matches any single character; '*'\n"
-"      matches any substring; ':' separates two patterns.\n"
-"  @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n"
-"      Run all disabled tests too.\n"
-"\n"
-"Test Execution:\n"
-"  @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n"
-"      Run the tests repeatedly; use a negative count to repeat forever.\n"
-"  @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n"
-"      Randomize tests' orders on every iteration.\n"
-"  @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n"
-"      Random number seed to use for shuffling test orders (between 1 and\n"
-"      99999, or 0 to use a seed based on the current time).\n"
-"\n"
-"Test Output:\n"
-"  @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n"
-"      Enable/disable colored output. The default is @Gauto@D.\n"
-"  -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n"
-"      Don't print the elapsed time of each test.\n"
-"  @G--" GTEST_FLAG_PREFIX_ "output=@Y(@Gjson@Y|@Gxml@Y)[@G:@YDIRECTORY_PATH@G"
-    GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n"
-"      Generate a JSON or XML report in the given directory or with the given\n"
-"      file name. @YFILE_PATH@D defaults to @Gtest_detail.xml@D.\n"
+    "      Run only the tests whose name matches one of the positive patterns "
+    "but\n"
+    "      none of the negative patterns. '?' matches any single character; "
+    "'*'\n"
+    "      matches any substring; ':' separates two patterns.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "also_run_disabled_tests@D\n"
+    "      Run all disabled tests too.\n"
+    "\n"
+    "Test Execution:\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "repeat=@Y[COUNT]@D\n"
+    "      Run the tests repeatedly; use a negative count to repeat forever.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "shuffle@D\n"
+    "      Randomize tests' orders on every iteration.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "random_seed=@Y[NUMBER]@D\n"
+    "      Random number seed to use for shuffling test orders (between 1 and\n"
+    "      99999, or 0 to use a seed based on the current time).\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "recreate_environments_when_repeating@D\n"
+    "      Sets up and tears down the global test environment on each repeat\n"
+    "      of the test.\n"
+    "\n"
+    "Test Output:\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n"
+    "      Enable/disable colored output. The default is @Gauto@D.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "brief=1@D\n"
+    "      Only print test failures.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "print_time=0@D\n"
+    "      Don't print the elapsed time of each test.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "output=@Y(@Gjson@Y|@Gxml@Y)[@G:@YDIRECTORY_PATH@G" GTEST_PATH_SEP_
+    "@Y|@G:@YFILE_PATH]@D\n"
+    "      Generate a JSON or XML report in the given directory or with the "
+    "given\n"
+    "      file name. @YFILE_PATH@D defaults to @Gtest_detail.xml@D.\n"
 # if GTEST_CAN_STREAM_RESULTS_
-"  @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n"
-"      Stream test results to the given server.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "stream_result_to=@YHOST@G:@YPORT@D\n"
+    "      Stream test results to the given server.\n"
 # endif  // GTEST_CAN_STREAM_RESULTS_
-"\n"
-"Assertion Behavior:\n"
+    "\n"
+    "Assertion Behavior:\n"
 # if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
-"  @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n"
-"      Set the default death test style.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n"
+    "      Set the default death test style.\n"
 # endif  // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS
-"  @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n"
-"      Turn assertion failures into debugger break-points.\n"
-"  @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n"
-"      Turn assertion failures into C++ exceptions for use by an external\n"
-"      test framework.\n"
-"  @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n"
-"      Do not report exceptions as test failures. Instead, allow them\n"
-"      to crash the program or throw a pop-up (on Windows).\n"
-"\n"
-"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set "
+    "  @G--" GTEST_FLAG_PREFIX_
+    "break_on_failure@D\n"
+    "      Turn assertion failures into debugger break-points.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "throw_on_failure@D\n"
+    "      Turn assertion failures into C++ exceptions for use by an external\n"
+    "      test framework.\n"
+    "  @G--" GTEST_FLAG_PREFIX_
+    "catch_exceptions=0@D\n"
+    "      Do not report exceptions as test failures. Instead, allow them\n"
+    "      to crash the program or throw a pop-up (on Windows).\n"
+    "\n"
+    "Except for @G--" GTEST_FLAG_PREFIX_
+    "list_tests@D, you can alternatively set "
     "the corresponding\n"
-"environment variable of a flag (all letters in upper-case). For example, to\n"
-"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_
+    "environment variable of a flag (all letters in upper-case). For example, "
+    "to\n"
+    "disable colored text output, you can either specify "
+    "@G--" GTEST_FLAG_PREFIX_
     "color=no@D or set\n"
-"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n"
-"\n"
-"For more information, please read the " GTEST_NAME_ " documentation at\n"
-"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n"
-"(not one in your own code or tests), please report it to\n"
-"@G<" GTEST_DEV_EMAIL_ ">@D.\n";
+    "the @G" GTEST_FLAG_PREFIX_UPPER_
+    "COLOR@D environment variable to @Gno@D.\n"
+    "\n"
+    "For more information, please read the " GTEST_NAME_
+    " documentation at\n"
+    "@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_
+    "\n"
+    "(not one in your own code or tests), please report it to\n"
+    "@G<" GTEST_DEV_EMAIL_ ">@D.\n";
 
 static bool ParseGoogleTestFlag(const char* const arg) {
-  return ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag,
-                       &GTEST_FLAG(also_run_disabled_tests)) ||
-      ParseBoolFlag(arg, kBreakOnFailureFlag,
-                    &GTEST_FLAG(break_on_failure)) ||
-      ParseBoolFlag(arg, kCatchExceptionsFlag,
-                    &GTEST_FLAG(catch_exceptions)) ||
-      ParseStringFlag(arg, kColorFlag, &GTEST_FLAG(color)) ||
-      ParseStringFlag(arg, kDeathTestStyleFlag,
-                      &GTEST_FLAG(death_test_style)) ||
-      ParseBoolFlag(arg, kDeathTestUseFork,
-                    &GTEST_FLAG(death_test_use_fork)) ||
-      ParseStringFlag(arg, kFilterFlag, &GTEST_FLAG(filter)) ||
-      ParseStringFlag(arg, kInternalRunDeathTestFlag,
-                      &GTEST_FLAG(internal_run_death_test)) ||
-      ParseBoolFlag(arg, kListTestsFlag, &GTEST_FLAG(list_tests)) ||
-      ParseStringFlag(arg, kOutputFlag, &GTEST_FLAG(output)) ||
-      ParseBoolFlag(arg, kPrintTimeFlag, &GTEST_FLAG(print_time)) ||
-      ParseBoolFlag(arg, kPrintUTF8Flag, &GTEST_FLAG(print_utf8)) ||
-      ParseInt32Flag(arg, kRandomSeedFlag, &GTEST_FLAG(random_seed)) ||
-      ParseInt32Flag(arg, kRepeatFlag, &GTEST_FLAG(repeat)) ||
-      ParseBoolFlag(arg, kShuffleFlag, &GTEST_FLAG(shuffle)) ||
-      ParseInt32Flag(arg, kStackTraceDepthFlag,
-                     &GTEST_FLAG(stack_trace_depth)) ||
-      ParseStringFlag(arg, kStreamResultToFlag,
-                      &GTEST_FLAG(stream_result_to)) ||
-      ParseBoolFlag(arg, kThrowOnFailureFlag,
-                    &GTEST_FLAG(throw_on_failure));
+#define GTEST_INTERNAL_PARSE_FLAG(flag_name)  \
+  do {                                        \
+    auto value = GTEST_FLAG_GET(flag_name);   \
+    if (ParseFlag(arg, #flag_name, &value)) { \
+      GTEST_FLAG_SET(flag_name, value);       \
+      return true;                            \
+    }                                         \
+  } while (false)
+
+  GTEST_INTERNAL_PARSE_FLAG(also_run_disabled_tests);
+  GTEST_INTERNAL_PARSE_FLAG(break_on_failure);
+  GTEST_INTERNAL_PARSE_FLAG(catch_exceptions);
+  GTEST_INTERNAL_PARSE_FLAG(color);
+  GTEST_INTERNAL_PARSE_FLAG(death_test_style);
+  GTEST_INTERNAL_PARSE_FLAG(death_test_use_fork);
+  GTEST_INTERNAL_PARSE_FLAG(fail_fast);
+  GTEST_INTERNAL_PARSE_FLAG(filter);
+  GTEST_INTERNAL_PARSE_FLAG(internal_run_death_test);
+  GTEST_INTERNAL_PARSE_FLAG(list_tests);
+  GTEST_INTERNAL_PARSE_FLAG(output);
+  GTEST_INTERNAL_PARSE_FLAG(brief);
+  GTEST_INTERNAL_PARSE_FLAG(print_time);
+  GTEST_INTERNAL_PARSE_FLAG(print_utf8);
+  GTEST_INTERNAL_PARSE_FLAG(random_seed);
+  GTEST_INTERNAL_PARSE_FLAG(repeat);
+  GTEST_INTERNAL_PARSE_FLAG(recreate_environments_when_repeating);
+  GTEST_INTERNAL_PARSE_FLAG(shuffle);
+  GTEST_INTERNAL_PARSE_FLAG(stack_trace_depth);
+  GTEST_INTERNAL_PARSE_FLAG(stream_result_to);
+  GTEST_INTERNAL_PARSE_FLAG(throw_on_failure);
+  return false;
 }
 
 #if GTEST_USE_OWN_FLAGFILE_FLAG_
 static void LoadFlagsFromFile(const std::string& path) {
   FILE* flagfile = posix::FOpen(path.c_str(), "r");
   if (!flagfile) {
-    GTEST_LOG_(FATAL) << "Unable to open file \"" << GTEST_FLAG(flagfile)
+    GTEST_LOG_(FATAL) << "Unable to open file \"" << GTEST_FLAG_GET(flagfile)
                       << "\"";
   }
   std::string contents(ReadEntireFile(flagfile));
@@ -5994,20 +6607,20 @@
 // instantiated to either char or wchar_t.
 template <typename CharType>
 void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) {
+  std::string flagfile_value;
   for (int i = 1; i < *argc; i++) {
     const std::string arg_string = StreamableToString(argv[i]);
     const char* const arg = arg_string.c_str();
 
-    using internal::ParseBoolFlag;
-    using internal::ParseInt32Flag;
-    using internal::ParseStringFlag;
+    using internal::ParseFlag;
 
     bool remove_flag = false;
     if (ParseGoogleTestFlag(arg)) {
       remove_flag = true;
 #if GTEST_USE_OWN_FLAGFILE_FLAG_
-    } else if (ParseStringFlag(arg, kFlagfileFlag, &GTEST_FLAG(flagfile))) {
-      LoadFlagsFromFile(GTEST_FLAG(flagfile));
+    } else if (ParseFlag(arg, "flagfile", &flagfile_value)) {
+      GTEST_FLAG_SET(flagfile, flagfile_value);
+      LoadFlagsFromFile(flagfile_value);
       remove_flag = true;
 #endif  // GTEST_USE_OWN_FLAGFILE_FLAG_
     } else if (arg_string == "--help" || arg_string == "-h" ||
@@ -6136,20 +6749,31 @@
 std::string TempDir() {
 #if defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_)
   return GTEST_CUSTOM_TEMPDIR_FUNCTION_();
-#endif
-
-#if GTEST_OS_WINDOWS_MOBILE
+#elif GTEST_OS_WINDOWS_MOBILE
   return "\\temp\\";
 #elif GTEST_OS_WINDOWS
   const char* temp_dir = internal::posix::GetEnv("TEMP");
-  if (temp_dir == nullptr || temp_dir[0] == '\0')
+  if (temp_dir == nullptr || temp_dir[0] == '\0') {
     return "\\temp\\";
-  else if (temp_dir[strlen(temp_dir) - 1] == '\\')
+  } else if (temp_dir[strlen(temp_dir) - 1] == '\\') {
     return temp_dir;
-  else
+  } else {
     return std::string(temp_dir) + "\\";
+  }
 #elif GTEST_OS_LINUX_ANDROID
-  return "/sdcard/";
+  const char* temp_dir = internal::posix::GetEnv("TEST_TMPDIR");
+  if (temp_dir == nullptr || temp_dir[0] == '\0') {
+    return "/data/local/tmp/";
+  } else {
+    return temp_dir;
+  }
+#elif GTEST_OS_LINUX
+  const char* temp_dir = internal::posix::GetEnv("TEST_TMPDIR");
+  if (temp_dir == nullptr || temp_dir[0] == '\0') {
+    return "/tmp/";
+  } else {
+    return temp_dir;
+  }
 #else
   return "/tmp/";
 #endif  // GTEST_OS_WINDOWS_MOBILE
diff --git a/ext/googletest/googletest/src/gtest_main.cc b/ext/googletest/googletest/src/gtest_main.cc
index f6e1dd9..46b27c3 100644
--- a/ext/googletest/googletest/src/gtest_main.cc
+++ b/ext/googletest/googletest/src/gtest_main.cc
@@ -30,13 +30,20 @@
 #include <cstdio>
 #include "gtest/gtest.h"
 
-#ifdef ARDUINO
+#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
+#if GTEST_OS_ESP8266
+extern "C" {
+#endif
 void setup() {
   testing::InitGoogleTest();
 }
 
 void loop() { RUN_ALL_TESTS(); }
 
+#if GTEST_OS_ESP8266
+}
+#endif
+
 #else
 
 GTEST_API_ int main(int argc, char **argv) {
diff --git a/ext/googletest/googletest/test/BUILD.bazel b/ext/googletest/googletest/test/BUILD.bazel
index 156d5d4..7b78555 100644
--- a/ext/googletest/googletest/test/BUILD.bazel
+++ b/ext/googletest/googletest/test/BUILD.bazel
@@ -28,8 +28,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #
-# Author: misterg@google.com (Gennadiy Civil)
-#
 # Bazel BUILD for The Google C++ Testing Framework (Google Test)
 
 load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_test")
@@ -37,6 +35,8 @@
 
 licenses(["notice"])
 
+package(default_visibility = ["//:__subpackages__"])
+
 #on windows exclude gtest-tuple.h
 cc_test(
     name = "gtest_all_test",
@@ -56,15 +56,19 @@
             "gtest-listener_test.cc",
             "gtest-unittest-api_test.cc",
             "googletest-param-test-test.cc",
+            "googletest-param-test2-test.cc",
             "googletest-catch-exceptions-test_.cc",
             "googletest-color-test_.cc",
             "googletest-env-var-test_.cc",
+            "googletest-failfast-unittest_.cc",
             "googletest-filter-unittest_.cc",
+            "googletest-global-environment-unittest_.cc",
             "googletest-break-on-failure-unittest_.cc",
             "googletest-listener-test.cc",
             "googletest-output-test_.cc",
             "googletest-list-tests-unittest_.cc",
             "googletest-shuffle-test_.cc",
+            "googletest-setuptestsuite-test_.cc",
             "googletest-uninitialized-test_.cc",
             "googletest-death-test_ex_test.cc",
             "googletest-param-test-test",
@@ -79,6 +83,10 @@
     copts = select({
         "//:windows": ["-DGTEST_USE_OWN_TR1_TUPLE=0"],
         "//conditions:default": ["-DGTEST_USE_OWN_TR1_TUPLE=1"],
+    }) + select({
+        # Ensure MSVC treats source files as UTF-8 encoded.
+        "//:msvc_compiler": ["-utf-8"],
+        "//conditions:default": [],
     }),
     includes = [
         "googletest",
@@ -87,6 +95,7 @@
         "googletest/test",
     ],
     linkopts = select({
+        "//:qnx": [],
         "//:windows": [],
         "//conditions:default": ["-pthread"],
     }),
@@ -142,7 +151,6 @@
     name = "gtest_unittest",
     size = "small",
     srcs = ["gtest_unittest.cc"],
-    args = ["--heap_check=strict"],
     shard_count = 2,
     deps = ["//:gtest_main"],
 )
@@ -153,6 +161,7 @@
     name = "gtest_test_utils",
     testonly = 1,
     srcs = ["gtest_test_utils.py"],
+    imports = ["."],
 )
 
 cc_binary(
@@ -223,6 +232,21 @@
 )
 
 cc_binary(
+    name = "googletest-failfast-unittest_",
+    testonly = 1,
+    srcs = ["googletest-failfast-unittest_.cc"],
+    deps = ["//:gtest"],
+)
+
+py_test(
+    name = "googletest-failfast-unittest",
+    size = "medium",
+    srcs = ["googletest-failfast-unittest.py"],
+    data = [":googletest-failfast-unittest_"],
+    deps = [":gtest_test_utils"],
+)
+
+cc_binary(
     name = "googletest-filter-unittest_",
     testonly = 1,
     srcs = ["googletest-filter-unittest_.cc"],
@@ -238,6 +262,21 @@
 )
 
 cc_binary(
+    name = "googletest-global-environment-unittest_",
+    testonly = 1,
+    srcs = ["googletest-global-environment-unittest_.cc"],
+    deps = ["//:gtest"],
+)
+
+py_test(
+    name = "googletest-global-environment-unittest",
+    size = "medium",
+    srcs = ["googletest-global-environment-unittest.py"],
+    data = [":googletest-global-environment-unittest_"],
+    deps = [":gtest_test_utils"],
+)
+
+cc_binary(
     name = "googletest-break-on-failure-unittest_",
     testonly = 1,
     srcs = ["googletest-break-on-failure-unittest_.cc"],
@@ -296,6 +335,14 @@
 )
 
 py_test(
+    name = "gtest_skip_check_output_test",
+    size = "small",
+    srcs = ["gtest_skip_check_output_test.py"],
+    data = [":gtest_skip_test"],
+    deps = [":gtest_test_utils"],
+)
+
+py_test(
     name = "gtest_skip_environment_check_output_test",
     size = "small",
     srcs = ["gtest_skip_environment_check_output_test.py"],
@@ -416,6 +463,21 @@
 )
 
 cc_binary(
+    name = "googletest-setuptestsuite-test_",
+    testonly = 1,
+    srcs = ["googletest-setuptestsuite-test_.cc"],
+    deps = ["//:gtest_main"],
+)
+
+py_test(
+    name = "googletest-setuptestsuite-test",
+    size = "medium",
+    srcs = ["googletest-setuptestsuite-test.py"],
+    data = [":googletest-setuptestsuite-test_"],
+    deps = [":gtest_test_utils"],
+)
+
+cc_binary(
     name = "googletest-uninitialized-test_",
     testonly = 1,
     srcs = ["googletest-uninitialized-test_.cc"],
@@ -509,6 +571,10 @@
     size = "small",
     srcs = ["googletest-param-test-invalid-name1-test.py"],
     data = [":googletest-param-test-invalid-name1-test_"],
+    tags = [
+        "no_test_msvc2015",
+        "no_test_msvc2017",
+    ],
     deps = [":gtest_test_utils"],
 )
 
@@ -517,5 +583,9 @@
     size = "small",
     srcs = ["googletest-param-test-invalid-name2-test.py"],
     data = [":googletest-param-test-invalid-name2-test_"],
+    tags = [
+        "no_test_msvc2015",
+        "no_test_msvc2017",
+    ],
     deps = [":gtest_test_utils"],
 )
diff --git a/ext/googletest/googletest/test/googletest-death-test-test.cc b/ext/googletest/googletest/test/googletest-death-test-test.cc
index cba906c..e7e0cd7 100644
--- a/ext/googletest/googletest/test/googletest-death-test-test.cc
+++ b/ext/googletest/googletest/test/googletest-death-test-test.cc
@@ -298,6 +298,13 @@
 
 # endif  // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA
 
+// The following code intentionally tests a suboptimal syntax.
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-else"
+#pragma GCC diagnostic ignored "-Wempty-body"
+#pragma GCC diagnostic ignored "-Wpragmas"
+#endif
 // Tests that the death test macros expand to code which may or may not
 // be followed by operator<<, and that in either case the complete text
 // comprises only a single C++ statement.
@@ -321,6 +328,9 @@
   else
     EXPECT_DEATH(_exit(1), "") << 1 << 2 << 3;
 }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
 
 # if GTEST_USES_PCRE
 
@@ -360,14 +370,14 @@
 // Tests that a static member function can be used in a "fast" style
 // death test.
 TEST_F(TestForDeathTest, StaticMemberFunctionFastStyle) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   ASSERT_DEATH(StaticMemberFunction(), "death.*StaticMember");
 }
 
 // Tests that a method of the test fixture can be used in a "fast"
 // style death test.
 TEST_F(TestForDeathTest, MemberFunctionFastStyle) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   should_die_ = true;
   EXPECT_DEATH(MemberFunction(), "inside.*MemberFunction");
 }
@@ -377,7 +387,7 @@
 // Tests that death tests work even if the current directory has been
 // changed.
 TEST_F(TestForDeathTest, FastDeathTestInChangedDir) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
 
   ChangeToRootDir();
   EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), "");
@@ -391,17 +401,19 @@
 
 // Sets SIGPROF action and ITIMER_PROF timer (interval: 1ms).
 void SetSigprofActionAndTimer() {
-  struct itimerval timer;
-  timer.it_interval.tv_sec = 0;
-  timer.it_interval.tv_usec = 1;
-  timer.it_value = timer.it_interval;
-  ASSERT_EQ(0, setitimer(ITIMER_PROF, &timer, nullptr));
   struct sigaction signal_action;
   memset(&signal_action, 0, sizeof(signal_action));
   sigemptyset(&signal_action.sa_mask);
   signal_action.sa_sigaction = SigprofAction;
   signal_action.sa_flags = SA_RESTART | SA_SIGINFO;
   ASSERT_EQ(0, sigaction(SIGPROF, &signal_action, nullptr));
+  // timer comes second, to avoid SIGPROF premature delivery, as suggested at
+  // https://www.gnu.org/software/libc/manual/html_node/Setting-an-Alarm.html
+  struct itimerval timer;
+  timer.it_interval.tv_sec = 0;
+  timer.it_interval.tv_usec = 1;
+  timer.it_value = timer.it_interval;
+  ASSERT_EQ(0, setitimer(ITIMER_PROF, &timer, nullptr));
 }
 
 // Disables ITIMER_PROF timer and ignores SIGPROF signal.
@@ -420,7 +432,7 @@
 
 // Tests that death tests work when SIGPROF handler and timer are set.
 TEST_F(TestForDeathTest, FastSigprofActionSet) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   SetSigprofActionAndTimer();
   EXPECT_DEATH(_exit(1), "");
   struct sigaction old_signal_action;
@@ -429,7 +441,7 @@
 }
 
 TEST_F(TestForDeathTest, ThreadSafeSigprofActionSet) {
-  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  GTEST_FLAG_SET(death_test_style, "threadsafe");
   SetSigprofActionAndTimer();
   EXPECT_DEATH(_exit(1), "");
   struct sigaction old_signal_action;
@@ -441,25 +453,25 @@
 // Repeats a representative sample of death tests in the "threadsafe" style:
 
 TEST_F(TestForDeathTest, StaticMemberFunctionThreadsafeStyle) {
-  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  GTEST_FLAG_SET(death_test_style, "threadsafe");
   ASSERT_DEATH(StaticMemberFunction(), "death.*StaticMember");
 }
 
 TEST_F(TestForDeathTest, MemberFunctionThreadsafeStyle) {
-  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  GTEST_FLAG_SET(death_test_style, "threadsafe");
   should_die_ = true;
   EXPECT_DEATH(MemberFunction(), "inside.*MemberFunction");
 }
 
 TEST_F(TestForDeathTest, ThreadsafeDeathTestInLoop) {
-  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  GTEST_FLAG_SET(death_test_style, "threadsafe");
 
   for (int i = 0; i < 3; ++i)
     EXPECT_EXIT(_exit(i), testing::ExitedWithCode(i), "") << ": i = " << i;
 }
 
 TEST_F(TestForDeathTest, ThreadsafeDeathTestInChangedDir) {
-  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  GTEST_FLAG_SET(death_test_style, "threadsafe");
 
   ChangeToRootDir();
   EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), "");
@@ -469,9 +481,9 @@
 }
 
 TEST_F(TestForDeathTest, MixedStyles) {
-  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  GTEST_FLAG_SET(death_test_style, "threadsafe");
   EXPECT_DEATH(_exit(1), "");
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   EXPECT_DEATH(_exit(1), "");
 }
 
@@ -484,8 +496,8 @@
 }
 
 TEST_F(TestForDeathTest, DoesNotExecuteAtforkHooks) {
-  if (!testing::GTEST_FLAG(death_test_use_fork)) {
-    testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  if (!GTEST_FLAG_GET(death_test_use_fork)) {
+    GTEST_FLAG_SET(death_test_style, "threadsafe");
     pthread_flag = false;
     ASSERT_EQ(0, pthread_atfork(&SetPthreadFlag, nullptr, nullptr));
     ASSERT_DEATH(_exit(1), "");
@@ -728,10 +740,12 @@
          "any pop-up dialogs.\n");
   fflush(stdout);
 
-  EXPECT_DEATH({
-    testing::GTEST_FLAG(catch_exceptions) = false;
-    abort();
-  }, "");
+  EXPECT_DEATH(
+      {
+        GTEST_FLAG_SET(catch_exceptions, false);
+        abort();
+      },
+      "");
 }
 #  endif  // GTEST_OS_WINDOWS
 
@@ -862,19 +876,19 @@
 }
 
 TEST_F(TestForDeathTest, ExitMacrosUsingFork) {
-  testing::GTEST_FLAG(death_test_use_fork) = true;
+  GTEST_FLAG_SET(death_test_use_fork, true);
   TestExitMacros();
 }
 
 TEST_F(TestForDeathTest, InvalidStyle) {
-  testing::GTEST_FLAG(death_test_style) = "rococo";
+  GTEST_FLAG_SET(death_test_style, "rococo");
   EXPECT_NONFATAL_FAILURE({  // NOLINT
     EXPECT_DEATH(_exit(0), "") << "This failure is expected.";
   }, "This failure is expected.");
 }
 
 TEST_F(TestForDeathTest, DeathTestFailedOutput) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   EXPECT_NONFATAL_FAILURE(
       EXPECT_DEATH(DieWithMessage("death\n"),
                    "expected message"),
@@ -883,7 +897,7 @@
 }
 
 TEST_F(TestForDeathTest, DeathTestUnexpectedReturnOutput) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   EXPECT_NONFATAL_FAILURE(
       EXPECT_DEATH({
           fprintf(stderr, "returning\n");
@@ -896,7 +910,7 @@
 }
 
 TEST_F(TestForDeathTest, DeathTestBadExitCodeOutput) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   EXPECT_NONFATAL_FAILURE(
       EXPECT_EXIT(DieWithMessage("exiting with rc 1\n"),
                   testing::ExitedWithCode(3),
@@ -908,7 +922,7 @@
 }
 
 TEST_F(TestForDeathTest, DeathTestMultiLineMatchFail) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   EXPECT_NONFATAL_FAILURE(
       EXPECT_DEATH(DieWithMessage("line 1\nline 2\nline 3\n"),
                    "line 1\nxyz\nline 3\n"),
@@ -919,7 +933,7 @@
 }
 
 TEST_F(TestForDeathTest, DeathTestMultiLineMatchPass) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   EXPECT_DEATH(DieWithMessage("line 1\nline 2\nline 3\n"),
                "line 1\nline 2\nline 3\n");
 }
@@ -1346,7 +1360,7 @@
 }
 
 TEST(InDeathTestChildDeathTest, ReportsDeathTestCorrectlyInFastStyle) {
-  testing::GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   EXPECT_FALSE(InDeathTestChild());
   EXPECT_DEATH({
     fprintf(stderr, InDeathTestChild() ? "Inside" : "Outside");
@@ -1356,7 +1370,7 @@
 }
 
 TEST(InDeathTestChildDeathTest, ReportsDeathTestCorrectlyInThreadSafeStyle) {
-  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  GTEST_FLAG_SET(death_test_style, "threadsafe");
   EXPECT_FALSE(InDeathTestChild());
   EXPECT_DEATH({
     fprintf(stderr, InDeathTestChild() ? "Inside" : "Outside");
@@ -1374,7 +1388,11 @@
 TEST(MatcherDeathTest, DoesNotBreakBareRegexMatching) {
   // googletest tests this, of course; here we ensure that including googlemock
   // has not broken it.
+#if GTEST_USES_POSIX_RE
   EXPECT_DEATH(DieWithMessage("O, I die, Horatio."), "I d[aeiou]e");
+#else
+  EXPECT_DEATH(DieWithMessage("O, I die, Horatio."), "I di?e");
+#endif
 }
 
 TEST(MatcherDeathTest, MonomorphicMatcherMatches) {
@@ -1462,6 +1480,13 @@
 
 namespace {
 
+// The following code intentionally tests a suboptimal syntax.
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-else"
+#pragma GCC diagnostic ignored "-Wempty-body"
+#pragma GCC diagnostic ignored "-Wpragmas"
+#endif
 // Tests that the death test macros expand to code which may or may not
 // be followed by operator<<, and that in either case the complete text
 // comprises only a single C++ statement.
@@ -1487,6 +1512,9 @@
   else
     EXPECT_DEATH_IF_SUPPORTED(_exit(1), "") << 1 << 2 << 3;
 }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
 
 // Tests that conditional death test macros expand to code which interacts
 // well with switch statements.
diff --git a/ext/googletest/googletest/test/googletest-death-test_ex_test.cc b/ext/googletest/googletest/test/googletest-death-test_ex_test.cc
index 7ea5b94..bbacc8a 100644
--- a/ext/googletest/googletest/test/googletest-death-test_ex_test.cc
+++ b/ext/googletest/googletest/test/googletest-death-test_ex_test.cc
@@ -53,13 +53,13 @@
   } catch (...) {  // NOLINT
     FAIL() << "An exception escaped a death test macro invocation "
            << "with catch_exceptions "
-           << (testing::GTEST_FLAG(catch_exceptions) ? "enabled" : "disabled");
+           << (GTEST_FLAG_GET(catch_exceptions) ? "enabled" : "disabled");
   }
 }
 
 class TestException : public std::exception {
  public:
-  const char* what() const throw() override { return "exceptional message"; }
+  const char* what() const noexcept override { return "exceptional message"; }
 };
 
 TEST(CxxExceptionDeathTest, PrintsMessageForStdExceptions) {
@@ -79,7 +79,7 @@
 TEST(SehExceptionDeasTest, CatchExceptionsDoesNotInterfere) {
   EXPECT_DEATH(RaiseException(42, 0x0, 0, NULL), "")
       << "with catch_exceptions "
-      << (testing::GTEST_FLAG(catch_exceptions) ? "enabled" : "disabled");
+      << (GTEST_FLAG_GET(catch_exceptions) ? "enabled" : "disabled");
 }
 # endif
 
@@ -87,6 +87,6 @@
 
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
-  testing::GTEST_FLAG(catch_exceptions) = GTEST_ENABLE_CATCH_EXCEPTIONS_ != 0;
+  GTEST_FLAG_SET(catch_exceptions, GTEST_ENABLE_CATCH_EXCEPTIONS_ != 0);
   return RUN_ALL_TESTS();
 }
diff --git a/ext/googletest/googletest/test/googletest-env-var-test.py b/ext/googletest/googletest/test/googletest-env-var-test.py
index 2f0e406..02c3655 100755
--- a/ext/googletest/googletest/test/googletest-env-var-test.py
+++ b/ext/googletest/googletest/test/googletest-env-var-test.py
@@ -85,9 +85,12 @@
 
     TestFlag('break_on_failure', '1', '0')
     TestFlag('color', 'yes', 'auto')
+    SetEnvVar('TESTBRIDGE_TEST_RUNNER_FAIL_FAST', None)  # For 'fail_fast' test
+    TestFlag('fail_fast', '1', '0')
     TestFlag('filter', 'FooTest.Bar', '*')
     SetEnvVar('XML_OUTPUT_FILE', None)  # For 'output' test
     TestFlag('output', 'xml:tmp/foo.xml', '')
+    TestFlag('brief', '1', '0')
     TestFlag('print_time', '0', '1')
     TestFlag('repeat', '999', '1')
     TestFlag('throw_on_failure', '1', '0')
diff --git a/ext/googletest/googletest/test/googletest-env-var-test_.cc b/ext/googletest/googletest/test/googletest-env-var-test_.cc
index fd2aa82..0ff0152 100644
--- a/ext/googletest/googletest/test/googletest-env-var-test_.cc
+++ b/ext/googletest/googletest/test/googletest-env-var-test_.cc
@@ -48,57 +48,67 @@
 
 void PrintFlag(const char* flag) {
   if (strcmp(flag, "break_on_failure") == 0) {
-    cout << GTEST_FLAG(break_on_failure);
+    cout << GTEST_FLAG_GET(break_on_failure);
     return;
   }
 
   if (strcmp(flag, "catch_exceptions") == 0) {
-    cout << GTEST_FLAG(catch_exceptions);
+    cout << GTEST_FLAG_GET(catch_exceptions);
     return;
   }
 
   if (strcmp(flag, "color") == 0) {
-    cout << GTEST_FLAG(color);
+    cout << GTEST_FLAG_GET(color);
     return;
   }
 
   if (strcmp(flag, "death_test_style") == 0) {
-    cout << GTEST_FLAG(death_test_style);
+    cout << GTEST_FLAG_GET(death_test_style);
     return;
   }
 
   if (strcmp(flag, "death_test_use_fork") == 0) {
-    cout << GTEST_FLAG(death_test_use_fork);
+    cout << GTEST_FLAG_GET(death_test_use_fork);
+    return;
+  }
+
+  if (strcmp(flag, "fail_fast") == 0) {
+    cout << GTEST_FLAG_GET(fail_fast);
     return;
   }
 
   if (strcmp(flag, "filter") == 0) {
-    cout << GTEST_FLAG(filter);
+    cout << GTEST_FLAG_GET(filter);
     return;
   }
 
   if (strcmp(flag, "output") == 0) {
-    cout << GTEST_FLAG(output);
+    cout << GTEST_FLAG_GET(output);
+    return;
+  }
+
+  if (strcmp(flag, "brief") == 0) {
+    cout << GTEST_FLAG_GET(brief);
     return;
   }
 
   if (strcmp(flag, "print_time") == 0) {
-    cout << GTEST_FLAG(print_time);
+    cout << GTEST_FLAG_GET(print_time);
     return;
   }
 
   if (strcmp(flag, "repeat") == 0) {
-    cout << GTEST_FLAG(repeat);
+    cout << GTEST_FLAG_GET(repeat);
     return;
   }
 
   if (strcmp(flag, "stack_trace_depth") == 0) {
-    cout << GTEST_FLAG(stack_trace_depth);
+    cout << GTEST_FLAG_GET(stack_trace_depth);
     return;
   }
 
   if (strcmp(flag, "throw_on_failure") == 0) {
-    cout << GTEST_FLAG(throw_on_failure);
+    cout << GTEST_FLAG_GET(throw_on_failure);
     return;
   }
 
diff --git a/ext/googletest/googletest/test/googletest-failfast-unittest.py b/ext/googletest/googletest/test/googletest-failfast-unittest.py
new file mode 100755
index 0000000..3aeb2df
--- /dev/null
+++ b/ext/googletest/googletest/test/googletest-failfast-unittest.py
@@ -0,0 +1,410 @@
+#!/usr/bin/env python
+#
+# Copyright 2020 Google Inc. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Unit test for Google Test fail_fast.
+
+A user can specify if a Google Test program should continue test execution
+after a test failure via the GTEST_FAIL_FAST environment variable or the
+--gtest_fail_fast flag. The default value of the flag can also be changed
+by Bazel fail fast environment variable TESTBRIDGE_TEST_RUNNER_FAIL_FAST.
+
+This script tests such functionality by invoking googletest-failfast-unittest_
+(a program written with Google Test) with different environments and command
+line flags.
+"""
+
+import os
+import gtest_test_utils
+
+# Constants.
+
+# Bazel testbridge environment variable for fail fast
+BAZEL_FAIL_FAST_ENV_VAR = 'TESTBRIDGE_TEST_RUNNER_FAIL_FAST'
+
+# The environment variable for specifying fail fast.
+FAIL_FAST_ENV_VAR = 'GTEST_FAIL_FAST'
+
+# The command line flag for specifying fail fast.
+FAIL_FAST_FLAG = 'gtest_fail_fast'
+
+# The command line flag to run disabled tests.
+RUN_DISABLED_FLAG = 'gtest_also_run_disabled_tests'
+
+# The command line flag for specifying a filter.
+FILTER_FLAG = 'gtest_filter'
+
+# Command to run the googletest-failfast-unittest_ program.
+COMMAND = gtest_test_utils.GetTestExecutablePath(
+    'googletest-failfast-unittest_')
+
+# The command line flag to tell Google Test to output the list of tests it
+# will run.
+LIST_TESTS_FLAG = '--gtest_list_tests'
+
+# Indicates whether Google Test supports death tests.
+SUPPORTS_DEATH_TESTS = 'HasDeathTest' in gtest_test_utils.Subprocess(
+    [COMMAND, LIST_TESTS_FLAG]).output
+
+# Utilities.
+
+environ = os.environ.copy()
+
+
+def SetEnvVar(env_var, value):
+  """Sets the env variable to 'value'; unsets it when 'value' is None."""
+
+  if value is not None:
+    environ[env_var] = value
+  elif env_var in environ:
+    del environ[env_var]
+
+
+def RunAndReturnOutput(test_suite=None, fail_fast=None, run_disabled=False):
+  """Runs the test program and returns its output."""
+
+  args = []
+  xml_path = os.path.join(gtest_test_utils.GetTempDir(),
+                          '.GTestFailFastUnitTest.xml')
+  args += ['--gtest_output=xml:' + xml_path]
+  if fail_fast is not None:
+    if isinstance(fail_fast, str):
+      args += ['--%s=%s' % (FAIL_FAST_FLAG, fail_fast)]
+    elif fail_fast:
+      args += ['--%s' % FAIL_FAST_FLAG]
+    else:
+      args += ['--no%s' % FAIL_FAST_FLAG]
+  if test_suite:
+    args += ['--%s=%s.*' % (FILTER_FLAG, test_suite)]
+  if run_disabled:
+    args += ['--%s' % RUN_DISABLED_FLAG]
+  txt_out = gtest_test_utils.Subprocess([COMMAND] + args, env=environ).output
+  with open(xml_path) as xml_file:
+    return txt_out, xml_file.read()
+
+
+# The unit test.
+class GTestFailFastUnitTest(gtest_test_utils.TestCase):
+  """Tests the env variable or the command line flag for fail_fast."""
+
+  def testDefaultBehavior(self):
+    """Tests the behavior of not specifying the fail_fast."""
+
+    txt, _ = RunAndReturnOutput()
+    self.assertIn('22 FAILED TEST', txt)
+
+  def testGoogletestFlag(self):
+    txt, _ = RunAndReturnOutput(test_suite='HasSimpleTest', fail_fast=True)
+    self.assertIn('1 FAILED TEST', txt)
+    self.assertIn('[  SKIPPED ] 3 tests', txt)
+
+    txt, _ = RunAndReturnOutput(test_suite='HasSimpleTest', fail_fast=False)
+    self.assertIn('4 FAILED TEST', txt)
+    self.assertNotIn('[  SKIPPED ]', txt)
+
+  def testGoogletestEnvVar(self):
+    """Tests the behavior of specifying fail_fast via Googletest env var."""
+
+    try:
+      SetEnvVar(FAIL_FAST_ENV_VAR, '1')
+      txt, _ = RunAndReturnOutput('HasSimpleTest')
+      self.assertIn('1 FAILED TEST', txt)
+      self.assertIn('[  SKIPPED ] 3 tests', txt)
+
+      SetEnvVar(FAIL_FAST_ENV_VAR, '0')
+      txt, _ = RunAndReturnOutput('HasSimpleTest')
+      self.assertIn('4 FAILED TEST', txt)
+      self.assertNotIn('[  SKIPPED ]', txt)
+    finally:
+      SetEnvVar(FAIL_FAST_ENV_VAR, None)
+
+  def testBazelEnvVar(self):
+    """Tests the behavior of specifying fail_fast via Bazel testbridge."""
+
+    try:
+      SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, '1')
+      txt, _ = RunAndReturnOutput('HasSimpleTest')
+      self.assertIn('1 FAILED TEST', txt)
+      self.assertIn('[  SKIPPED ] 3 tests', txt)
+
+      SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, '0')
+      txt, _ = RunAndReturnOutput('HasSimpleTest')
+      self.assertIn('4 FAILED TEST', txt)
+      self.assertNotIn('[  SKIPPED ]', txt)
+    finally:
+      SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, None)
+
+  def testFlagOverridesEnvVar(self):
+    """Tests precedence of flag over env var."""
+
+    try:
+      SetEnvVar(FAIL_FAST_ENV_VAR, '0')
+      txt, _ = RunAndReturnOutput('HasSimpleTest', True)
+      self.assertIn('1 FAILED TEST', txt)
+      self.assertIn('[  SKIPPED ] 3 tests', txt)
+    finally:
+      SetEnvVar(FAIL_FAST_ENV_VAR, None)
+
+  def testGoogletestEnvVarOverridesBazelEnvVar(self):
+    """Tests that the Googletest native env var over Bazel testbridge."""
+
+    try:
+      SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, '0')
+      SetEnvVar(FAIL_FAST_ENV_VAR, '1')
+      txt, _ = RunAndReturnOutput('HasSimpleTest')
+      self.assertIn('1 FAILED TEST', txt)
+      self.assertIn('[  SKIPPED ] 3 tests', txt)
+    finally:
+      SetEnvVar(FAIL_FAST_ENV_VAR, None)
+      SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, None)
+
+  def testEventListener(self):
+    txt, _ = RunAndReturnOutput(test_suite='HasSkipTest', fail_fast=True)
+    self.assertIn('1 FAILED TEST', txt)
+    self.assertIn('[  SKIPPED ] 3 tests', txt)
+    for expected_count, callback in [(1, 'OnTestSuiteStart'),
+                                     (5, 'OnTestStart'),
+                                     (5, 'OnTestEnd'),
+                                     (5, 'OnTestPartResult'),
+                                     (1, 'OnTestSuiteEnd')]:
+      self.assertEqual(
+          expected_count, txt.count(callback),
+          'Expected %d calls to callback %s match count on output: %s ' %
+          (expected_count, callback, txt))
+
+    txt, _ = RunAndReturnOutput(test_suite='HasSkipTest', fail_fast=False)
+    self.assertIn('3 FAILED TEST', txt)
+    self.assertIn('[  SKIPPED ] 1 test', txt)
+    for expected_count, callback in [(1, 'OnTestSuiteStart'),
+                                     (5, 'OnTestStart'),
+                                     (5, 'OnTestEnd'),
+                                     (5, 'OnTestPartResult'),
+                                     (1, 'OnTestSuiteEnd')]:
+      self.assertEqual(
+          expected_count, txt.count(callback),
+          'Expected %d calls to callback %s match count on output: %s ' %
+          (expected_count, callback, txt))
+
+  def assertXmlResultCount(self, result, count, xml):
+    self.assertEqual(
+        count, xml.count('result="%s"' % result),
+        'Expected \'result="%s"\' match count of %s: %s ' %
+        (result, count, xml))
+
+  def assertXmlStatusCount(self, status, count, xml):
+    self.assertEqual(
+        count, xml.count('status="%s"' % status),
+        'Expected \'status="%s"\' match count of %s: %s ' %
+        (status, count, xml))
+
+  def assertFailFastXmlAndTxtOutput(self,
+                                    fail_fast,
+                                    test_suite,
+                                    passed_count,
+                                    failure_count,
+                                    skipped_count,
+                                    suppressed_count,
+                                    run_disabled=False):
+    """Assert XML and text output of a test execution."""
+
+    txt, xml = RunAndReturnOutput(test_suite, fail_fast, run_disabled)
+    if failure_count > 0:
+      self.assertIn('%s FAILED TEST' % failure_count, txt)
+    if suppressed_count > 0:
+      self.assertIn('%s DISABLED TEST' % suppressed_count, txt)
+    if skipped_count > 0:
+      self.assertIn('[  SKIPPED ] %s tests' % skipped_count, txt)
+    self.assertXmlStatusCount('run',
+                              passed_count + failure_count + skipped_count, xml)
+    self.assertXmlStatusCount('notrun', suppressed_count, xml)
+    self.assertXmlResultCount('completed', passed_count + failure_count, xml)
+    self.assertXmlResultCount('skipped', skipped_count, xml)
+    self.assertXmlResultCount('suppressed', suppressed_count, xml)
+
+  def assertFailFastBehavior(self,
+                             test_suite,
+                             passed_count,
+                             failure_count,
+                             skipped_count,
+                             suppressed_count,
+                             run_disabled=False):
+    """Assert --fail_fast via flag."""
+
+    for fail_fast in ('true', '1', 't', True):
+      self.assertFailFastXmlAndTxtOutput(fail_fast, test_suite, passed_count,
+                                         failure_count, skipped_count,
+                                         suppressed_count, run_disabled)
+
+  def assertNotFailFastBehavior(self,
+                                test_suite,
+                                passed_count,
+                                failure_count,
+                                skipped_count,
+                                suppressed_count,
+                                run_disabled=False):
+    """Assert --nofail_fast via flag."""
+
+    for fail_fast in ('false', '0', 'f', False):
+      self.assertFailFastXmlAndTxtOutput(fail_fast, test_suite, passed_count,
+                                         failure_count, skipped_count,
+                                         suppressed_count, run_disabled)
+
+  def testFlag_HasFixtureTest(self):
+    """Tests the behavior of fail_fast and TEST_F."""
+    self.assertFailFastBehavior(
+        test_suite='HasFixtureTest',
+        passed_count=1,
+        failure_count=1,
+        skipped_count=3,
+        suppressed_count=0)
+    self.assertNotFailFastBehavior(
+        test_suite='HasFixtureTest',
+        passed_count=1,
+        failure_count=4,
+        skipped_count=0,
+        suppressed_count=0)
+
+  def testFlag_HasSimpleTest(self):
+    """Tests the behavior of fail_fast and TEST."""
+    self.assertFailFastBehavior(
+        test_suite='HasSimpleTest',
+        passed_count=1,
+        failure_count=1,
+        skipped_count=3,
+        suppressed_count=0)
+    self.assertNotFailFastBehavior(
+        test_suite='HasSimpleTest',
+        passed_count=1,
+        failure_count=4,
+        skipped_count=0,
+        suppressed_count=0)
+
+  def testFlag_HasParametersTest(self):
+    """Tests the behavior of fail_fast and TEST_P."""
+    self.assertFailFastBehavior(
+        test_suite='HasParametersSuite/HasParametersTest',
+        passed_count=0,
+        failure_count=1,
+        skipped_count=3,
+        suppressed_count=0)
+    self.assertNotFailFastBehavior(
+        test_suite='HasParametersSuite/HasParametersTest',
+        passed_count=0,
+        failure_count=4,
+        skipped_count=0,
+        suppressed_count=0)
+
+  def testFlag_HasDisabledTest(self):
+    """Tests the behavior of fail_fast and Disabled test cases."""
+    self.assertFailFastBehavior(
+        test_suite='HasDisabledTest',
+        passed_count=1,
+        failure_count=1,
+        skipped_count=2,
+        suppressed_count=1,
+        run_disabled=False)
+    self.assertNotFailFastBehavior(
+        test_suite='HasDisabledTest',
+        passed_count=1,
+        failure_count=3,
+        skipped_count=0,
+        suppressed_count=1,
+        run_disabled=False)
+
+  def testFlag_HasDisabledRunDisabledTest(self):
+    """Tests the behavior of fail_fast and Disabled test cases enabled."""
+    self.assertFailFastBehavior(
+        test_suite='HasDisabledTest',
+        passed_count=1,
+        failure_count=1,
+        skipped_count=3,
+        suppressed_count=0,
+        run_disabled=True)
+    self.assertNotFailFastBehavior(
+        test_suite='HasDisabledTest',
+        passed_count=1,
+        failure_count=4,
+        skipped_count=0,
+        suppressed_count=0,
+        run_disabled=True)
+
+  def testFlag_HasDisabledSuiteTest(self):
+    """Tests the behavior of fail_fast and Disabled test suites."""
+    self.assertFailFastBehavior(
+        test_suite='DISABLED_HasDisabledSuite',
+        passed_count=0,
+        failure_count=0,
+        skipped_count=0,
+        suppressed_count=5,
+        run_disabled=False)
+    self.assertNotFailFastBehavior(
+        test_suite='DISABLED_HasDisabledSuite',
+        passed_count=0,
+        failure_count=0,
+        skipped_count=0,
+        suppressed_count=5,
+        run_disabled=False)
+
+  def testFlag_HasDisabledSuiteRunDisabledTest(self):
+    """Tests the behavior of fail_fast and Disabled test suites enabled."""
+    self.assertFailFastBehavior(
+        test_suite='DISABLED_HasDisabledSuite',
+        passed_count=1,
+        failure_count=1,
+        skipped_count=3,
+        suppressed_count=0,
+        run_disabled=True)
+    self.assertNotFailFastBehavior(
+        test_suite='DISABLED_HasDisabledSuite',
+        passed_count=1,
+        failure_count=4,
+        skipped_count=0,
+        suppressed_count=0,
+        run_disabled=True)
+
+  if SUPPORTS_DEATH_TESTS:
+
+    def testFlag_HasDeathTest(self):
+      """Tests the behavior of fail_fast and death tests."""
+      self.assertFailFastBehavior(
+          test_suite='HasDeathTest',
+          passed_count=1,
+          failure_count=1,
+          skipped_count=3,
+          suppressed_count=0)
+      self.assertNotFailFastBehavior(
+          test_suite='HasDeathTest',
+          passed_count=1,
+          failure_count=4,
+          skipped_count=0,
+          suppressed_count=0)
+
+
+if __name__ == '__main__':
+  gtest_test_utils.Main()
diff --git a/ext/googletest/googletest/test/googletest-failfast-unittest_.cc b/ext/googletest/googletest/test/googletest-failfast-unittest_.cc
new file mode 100644
index 0000000..0b2c951
--- /dev/null
+++ b/ext/googletest/googletest/test/googletest-failfast-unittest_.cc
@@ -0,0 +1,167 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Unit test for Google Test test filters.
+//
+// A user can specify which test(s) in a Google Test program to run via
+// either the GTEST_FILTER environment variable or the --gtest_filter
+// flag.  This is used for testing such functionality.
+//
+// The program will be invoked from a Python unit test.  Don't run it
+// directly.
+
+#include "gtest/gtest.h"
+
+namespace {
+
+// Test HasFixtureTest.
+
+class HasFixtureTest : public testing::Test {};
+
+TEST_F(HasFixtureTest, Test0) {}
+
+TEST_F(HasFixtureTest, Test1) { FAIL() << "Expected failure."; }
+
+TEST_F(HasFixtureTest, Test2) { FAIL() << "Expected failure."; }
+
+TEST_F(HasFixtureTest, Test3) { FAIL() << "Expected failure."; }
+
+TEST_F(HasFixtureTest, Test4) { FAIL() << "Expected failure."; }
+
+// Test HasSimpleTest.
+
+TEST(HasSimpleTest, Test0) {}
+
+TEST(HasSimpleTest, Test1) { FAIL() << "Expected failure."; }
+
+TEST(HasSimpleTest, Test2) { FAIL() << "Expected failure."; }
+
+TEST(HasSimpleTest, Test3) { FAIL() << "Expected failure."; }
+
+TEST(HasSimpleTest, Test4) { FAIL() << "Expected failure."; }
+
+// Test HasDisabledTest.
+
+TEST(HasDisabledTest, Test0) {}
+
+TEST(HasDisabledTest, DISABLED_Test1) { FAIL() << "Expected failure."; }
+
+TEST(HasDisabledTest, Test2) { FAIL() << "Expected failure."; }
+
+TEST(HasDisabledTest, Test3) { FAIL() << "Expected failure."; }
+
+TEST(HasDisabledTest, Test4) { FAIL() << "Expected failure."; }
+
+// Test HasDeathTest
+
+TEST(HasDeathTest, Test0) { EXPECT_DEATH_IF_SUPPORTED(exit(1), ".*"); }
+
+TEST(HasDeathTest, Test1) {
+  EXPECT_DEATH_IF_SUPPORTED(FAIL() << "Expected failure.", ".*");
+}
+
+TEST(HasDeathTest, Test2) {
+  EXPECT_DEATH_IF_SUPPORTED(FAIL() << "Expected failure.", ".*");
+}
+
+TEST(HasDeathTest, Test3) {
+  EXPECT_DEATH_IF_SUPPORTED(FAIL() << "Expected failure.", ".*");
+}
+
+TEST(HasDeathTest, Test4) {
+  EXPECT_DEATH_IF_SUPPORTED(FAIL() << "Expected failure.", ".*");
+}
+
+// Test DISABLED_HasDisabledSuite
+
+TEST(DISABLED_HasDisabledSuite, Test0) {}
+
+TEST(DISABLED_HasDisabledSuite, Test1) { FAIL() << "Expected failure."; }
+
+TEST(DISABLED_HasDisabledSuite, Test2) { FAIL() << "Expected failure."; }
+
+TEST(DISABLED_HasDisabledSuite, Test3) { FAIL() << "Expected failure."; }
+
+TEST(DISABLED_HasDisabledSuite, Test4) { FAIL() << "Expected failure."; }
+
+// Test HasParametersTest
+
+class HasParametersTest : public testing::TestWithParam<int> {};
+
+TEST_P(HasParametersTest, Test1) { FAIL() << "Expected failure."; }
+
+TEST_P(HasParametersTest, Test2) { FAIL() << "Expected failure."; }
+
+INSTANTIATE_TEST_SUITE_P(HasParametersSuite, HasParametersTest,
+                         testing::Values(1, 2));
+
+class MyTestListener : public ::testing::EmptyTestEventListener {
+  void OnTestSuiteStart(const ::testing::TestSuite& test_suite) override {
+    printf("We are in OnTestSuiteStart of %s.\n", test_suite.name());
+  }
+
+  void OnTestStart(const ::testing::TestInfo& test_info) override {
+    printf("We are in OnTestStart of %s.%s.\n", test_info.test_suite_name(),
+           test_info.name());
+  }
+
+  void OnTestPartResult(
+      const ::testing::TestPartResult& test_part_result) override {
+    printf("We are in OnTestPartResult %s:%d.\n", test_part_result.file_name(),
+           test_part_result.line_number());
+  }
+
+  void OnTestEnd(const ::testing::TestInfo& test_info) override {
+    printf("We are in OnTestEnd of %s.%s.\n", test_info.test_suite_name(),
+           test_info.name());
+  }
+
+  void OnTestSuiteEnd(const ::testing::TestSuite& test_suite) override {
+    printf("We are in OnTestSuiteEnd of %s.\n", test_suite.name());
+  }
+};
+
+TEST(HasSkipTest, Test0) { SUCCEED() << "Expected success."; }
+
+TEST(HasSkipTest, Test1) { GTEST_SKIP() << "Expected skip."; }
+
+TEST(HasSkipTest, Test2) { FAIL() << "Expected failure."; }
+
+TEST(HasSkipTest, Test3) { FAIL() << "Expected failure."; }
+
+TEST(HasSkipTest, Test4) { FAIL() << "Expected failure."; }
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  ::testing::UnitTest::GetInstance()->listeners().Append(new MyTestListener());
+  return RUN_ALL_TESTS();
+}
diff --git a/ext/googletest/googletest/test/googletest-global-environment-unittest.py b/ext/googletest/googletest/test/googletest-global-environment-unittest.py
new file mode 100644
index 0000000..f347559
--- /dev/null
+++ b/ext/googletest/googletest/test/googletest-global-environment-unittest.py
@@ -0,0 +1,128 @@
+# Copyright 2021 Google Inc. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Unit test for Google Test's global test environment behavior.
+
+A user can specify a global test environment via
+testing::AddGlobalTestEnvironment. Failures in the global environment should
+result in all unit tests being skipped.
+
+This script tests such functionality by invoking
+googletest-global-environment-unittest_ (a program written with Google Test).
+"""
+
+import re
+import gtest_test_utils
+
+
+def RunAndReturnOutput(args=None):
+  """Runs the test program and returns its output."""
+
+  return gtest_test_utils.Subprocess([
+      gtest_test_utils.GetTestExecutablePath(
+          'googletest-global-environment-unittest_')
+  ] + (args or [])).output
+
+
+class GTestGlobalEnvironmentUnitTest(gtest_test_utils.TestCase):
+  """Tests global test environment failures."""
+
+  def testEnvironmentSetUpFails(self):
+    """Tests the behavior of not specifying the fail_fast."""
+
+    # Run the test.
+    txt = RunAndReturnOutput()
+
+    # We should see the text of the global environment setup error.
+    self.assertIn('Canned environment setup error', txt)
+
+    # Our test should have been skipped due to the error, and not treated as a
+    # pass.
+    self.assertIn('[  SKIPPED ] 1 test', txt)
+    self.assertIn('[  PASSED  ] 0 tests', txt)
+
+    # The test case shouldn't have been run.
+    self.assertNotIn('Unexpected call', txt)
+
+  def testEnvironmentSetUpAndTornDownForEachRepeat(self):
+    """Tests the behavior of test environments and gtest_repeat."""
+
+    txt = RunAndReturnOutput(['--gtest_repeat=2'])
+
+    # By default, with gtest_repeat=2, the global test environment should be set
+    # up and torn down for each iteration.
+    expected_pattern = ('(.|\n)*'
+                        r'Repeating all tests \(iteration 1\)'
+                        '(.|\n)*'
+                        'Global test environment set-up.'
+                        '(.|\n)*'
+                        'SomeTest.DoesFoo'
+                        '(.|\n)*'
+                        'Global test environment tear-down'
+                        '(.|\n)*'
+                        r'Repeating all tests \(iteration 2\)'
+                        '(.|\n)*'
+                        'Global test environment set-up.'
+                        '(.|\n)*'
+                        'SomeTest.DoesFoo'
+                        '(.|\n)*'
+                        'Global test environment tear-down'
+                        '(.|\n)*')
+    self.assertRegex(txt, expected_pattern)
+
+  def testEnvironmentSetUpAndTornDownOnce(self):
+    """Tests environment and --gtest_recreate_environments_when_repeating."""
+
+    txt = RunAndReturnOutput([
+        '--gtest_repeat=2', '--gtest_recreate_environments_when_repeating=false'
+    ])
+
+    # When --gtest_recreate_environments_when_repeating is false, the test
+    # environment should only be set up and torn down once, at the start and
+    # end of the test respectively.
+    expected_pattern = ('(.|\n)*'
+                        r'Repeating all tests \(iteration 1\)'
+                        '(.|\n)*'
+                        'Global test environment set-up.'
+                        '(.|\n)*'
+                        'SomeTest.DoesFoo'
+                        '(.|\n)*'
+                        r'Repeating all tests \(iteration 2\)'
+                        '(.|\n)*'
+                        'SomeTest.DoesFoo'
+                        '(.|\n)*'
+                        'Global test environment tear-down'
+                        '(.|\n)*')
+    self.assertRegex(txt, expected_pattern)
+
+    self.assertEqual(len(re.findall('Global test environment set-up', txt)), 1)
+    self.assertEqual(
+        len(re.findall('Global test environment tear-down', txt)), 1)
+
+
+if __name__ == '__main__':
+  gtest_test_utils.Main()
diff --git a/ext/googletest/googletest/test/googletest-global-environment-unittest_.cc b/ext/googletest/googletest/test/googletest-global-environment-unittest_.cc
new file mode 100644
index 0000000..f401b2f
--- /dev/null
+++ b/ext/googletest/googletest/test/googletest-global-environment-unittest_.cc
@@ -0,0 +1,58 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Unit test for Google Test global test environments.
+//
+// The program will be invoked from a Python unit test.  Don't run it
+// directly.
+
+#include "gtest/gtest.h"
+
+namespace {
+
+// An environment that always fails in its SetUp method.
+class FailingEnvironment final : public ::testing::Environment {
+ public:
+  void SetUp() override { FAIL() << "Canned environment setup error"; }
+};
+
+// Register the environment.
+auto* const g_environment_ =
+    ::testing::AddGlobalTestEnvironment(new FailingEnvironment);
+
+// A test that doesn't actually run.
+TEST(SomeTest, DoesFoo) { FAIL() << "Unexpected call"; }
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+
+  return RUN_ALL_TESTS();
+}
diff --git a/ext/googletest/googletest/test/googletest-json-output-unittest.py b/ext/googletest/googletest/test/googletest-json-output-unittest.py
index 15861f7..41c8565 100644
--- a/ext/googletest/googletest/test/googletest-json-output-unittest.py
+++ b/ext/googletest/googletest/test/googletest-json-output-unittest.py
@@ -58,9 +58,9 @@
 
 EXPECTED_NON_EMPTY = {
     u'tests':
-        24,
+        26,
     u'failures':
-        4,
+        5,
     u'disabled':
         2,
     u'errors':
@@ -158,9 +158,9 @@
         u'name':
             u'SkippedTest',
         u'tests':
-            1,
+            3,
         u'failures':
-            0,
+            1,
         u'disabled':
             0,
         u'errors':
@@ -176,6 +176,32 @@
             u'time': u'*',
             u'timestamp': u'*',
             u'classname': u'SkippedTest'
+        }, {
+            u'name': u'SkippedWithMessage',
+            u'status': u'RUN',
+            u'result': u'SKIPPED',
+            u'time': u'*',
+            u'timestamp': u'*',
+            u'classname': u'SkippedTest'
+        }, {
+            u'name':
+                u'SkippedAfterFailure',
+            u'status':
+                u'RUN',
+            u'result':
+                u'COMPLETED',
+            u'time':
+                u'*',
+            u'timestamp':
+                u'*',
+            u'classname':
+                u'SkippedTest',
+            u'failures': [{
+                u'failure': u'gtest_xml_output_unittest_.cc:*\n'
+                            u'Expected equality of these values:\n'
+                            u'  1\n  2' + STACK_TRACE_TEMPLATE,
+                u'type': u''
+            }]
         }]
     }, {
         u'name':
@@ -586,15 +612,59 @@
     }],
 }
 
-EXPECTED_EMPTY = {
-    u'tests': 0,
-    u'failures': 0,
-    u'disabled': 0,
-    u'errors': 0,
-    u'time': u'*',
-    u'timestamp': u'*',
-    u'name': u'AllTests',
-    u'testsuites': [],
+EXPECTED_NO_TEST = {
+    u'tests':
+        0,
+    u'failures':
+        0,
+    u'disabled':
+        0,
+    u'errors':
+        0,
+    u'time':
+        u'*',
+    u'timestamp':
+        u'*',
+    u'name':
+        u'AllTests',
+    u'testsuites': [{
+        u'name':
+            u'NonTestSuiteFailure',
+        u'tests':
+            1,
+        u'failures':
+            1,
+        u'disabled':
+            0,
+        u'skipped':
+            0,
+        u'errors':
+            0,
+        u'time':
+            u'*',
+        u'timestamp':
+            u'*',
+        u'testsuite': [{
+            u'name':
+                u'',
+            u'status':
+                u'RUN',
+            u'result':
+                u'COMPLETED',
+            u'time':
+                u'*',
+            u'timestamp':
+                u'*',
+            u'classname':
+                u'',
+            u'failures': [{
+                u'failure': u'gtest_no_test_unittest.cc:*\n'
+                            u'Expected equality of these values:\n'
+                            u'  1\n  2' + STACK_TRACE_TEMPLATE,
+                u'type': u'',
+            }]
+        }]
+    }],
 }
 
 GTEST_PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath(GTEST_PROGRAM_NAME)
@@ -619,14 +689,14 @@
       """
       self._TestJsonOutput(GTEST_PROGRAM_NAME, EXPECTED_NON_EMPTY, 1)
 
-  def testEmptyJsonOutput(self):
+  def testNoTestJsonOutput(self):
     """Verifies JSON output for a Google Test binary without actual tests.
 
-    Runs a test program that generates an empty JSON output, and
-    tests that the JSON output is expected.
+    Runs a test program that generates an JSON output for a binary with no
+    tests, and tests that the JSON output is expected.
     """
 
-    self._TestJsonOutput('gtest_no_test_unittest', EXPECTED_EMPTY, 0)
+    self._TestJsonOutput('gtest_no_test_unittest', EXPECTED_NO_TEST, 0)
 
   def testTimestampValue(self):
     """Checks whether the timestamp attribute in the JSON output is valid.
diff --git a/ext/googletest/googletest/test/googletest-listener-test.cc b/ext/googletest/googletest/test/googletest-listener-test.cc
index 10457af..9d6c9ca 100644
--- a/ext/googletest/googletest/test/googletest-listener-test.cc
+++ b/ext/googletest/googletest/test/googletest-listener-test.cc
@@ -284,7 +284,7 @@
   GTEST_CHECK_(events.size() == 0)
       << "AddGlobalTestEnvironment should not generate any events itself.";
 
-  ::testing::GTEST_FLAG(repeat) = 2;
+  GTEST_FLAG_SET(repeat, 2);
   int ret_val = RUN_ALL_TESTS();
 
 #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
diff --git a/ext/googletest/googletest/test/googletest-options-test.cc b/ext/googletest/googletest/test/googletest-options-test.cc
index f07b316..cd386ff 100644
--- a/ext/googletest/googletest/test/googletest-options-test.cc
+++ b/ext/googletest/googletest/test/googletest-options-test.cc
@@ -42,6 +42,9 @@
 # include <windows.h>
 #elif GTEST_OS_WINDOWS
 # include <direct.h>
+#elif GTEST_OS_OS2
+// For strcasecmp on OS/2
+#include <strings.h>
 #endif  // GTEST_OS_WINDOWS_MOBILE
 
 #include "src/gtest-internal-inl.h"
@@ -58,29 +61,29 @@
 // Testing UnitTestOptions::GetOutputFormat/GetOutputFile.
 
 TEST(XmlOutputTest, GetOutputFormatDefault) {
-  GTEST_FLAG(output) = "";
+  GTEST_FLAG_SET(output, "");
   EXPECT_STREQ("", UnitTestOptions::GetOutputFormat().c_str());
 }
 
 TEST(XmlOutputTest, GetOutputFormat) {
-  GTEST_FLAG(output) = "xml:filename";
+  GTEST_FLAG_SET(output, "xml:filename");
   EXPECT_STREQ("xml", UnitTestOptions::GetOutputFormat().c_str());
 }
 
 TEST(XmlOutputTest, GetOutputFileDefault) {
-  GTEST_FLAG(output) = "";
+  GTEST_FLAG_SET(output, "");
   EXPECT_EQ(GetAbsolutePathOf(FilePath("test_detail.xml")).string(),
             UnitTestOptions::GetAbsolutePathToOutputFile());
 }
 
 TEST(XmlOutputTest, GetOutputFileSingleFile) {
-  GTEST_FLAG(output) = "xml:filename.abc";
+  GTEST_FLAG_SET(output, "xml:filename.abc");
   EXPECT_EQ(GetAbsolutePathOf(FilePath("filename.abc")).string(),
             UnitTestOptions::GetAbsolutePathToOutputFile());
 }
 
 TEST(XmlOutputTest, GetOutputFileFromDirectoryPath) {
-  GTEST_FLAG(output) = "xml:path" GTEST_PATH_SEP_;
+  GTEST_FLAG_SET(output, "xml:path" GTEST_PATH_SEP_);
   const std::string expected_output_file =
       GetAbsolutePathOf(
           FilePath(std::string("path") + GTEST_PATH_SEP_ +
@@ -141,28 +144,28 @@
 };
 
 TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithDefault) {
-  GTEST_FLAG(output) = "";
+  GTEST_FLAG_SET(output, "");
   EXPECT_EQ(FilePath::ConcatPaths(original_working_dir_,
                                   FilePath("test_detail.xml")).string(),
             UnitTestOptions::GetAbsolutePathToOutputFile());
 }
 
 TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithDefaultXML) {
-  GTEST_FLAG(output) = "xml";
+  GTEST_FLAG_SET(output, "xml");
   EXPECT_EQ(FilePath::ConcatPaths(original_working_dir_,
                                   FilePath("test_detail.xml")).string(),
             UnitTestOptions::GetAbsolutePathToOutputFile());
 }
 
 TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithRelativeFile) {
-  GTEST_FLAG(output) = "xml:filename.abc";
+  GTEST_FLAG_SET(output, "xml:filename.abc");
   EXPECT_EQ(FilePath::ConcatPaths(original_working_dir_,
                                   FilePath("filename.abc")).string(),
             UnitTestOptions::GetAbsolutePathToOutputFile());
 }
 
 TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithRelativePath) {
-  GTEST_FLAG(output) = "xml:path" GTEST_PATH_SEP_;
+  GTEST_FLAG_SET(output, "xml:path" GTEST_PATH_SEP_);
   const std::string expected_output_file =
       FilePath::ConcatPaths(
           original_working_dir_,
@@ -179,11 +182,11 @@
 
 TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithAbsoluteFile) {
 #if GTEST_OS_WINDOWS
-  GTEST_FLAG(output) = "xml:c:\\tmp\\filename.abc";
+  GTEST_FLAG_SET(output, "xml:c:\\tmp\\filename.abc");
   EXPECT_EQ(FilePath("c:\\tmp\\filename.abc").string(),
             UnitTestOptions::GetAbsolutePathToOutputFile());
 #else
-  GTEST_FLAG(output) ="xml:/tmp/filename.abc";
+  GTEST_FLAG_SET(output, "xml:/tmp/filename.abc");
   EXPECT_EQ(FilePath("/tmp/filename.abc").string(),
             UnitTestOptions::GetAbsolutePathToOutputFile());
 #endif
@@ -196,7 +199,7 @@
   const std::string path = "/tmp/";
 #endif
 
-  GTEST_FLAG(output) = "xml:" + path;
+  GTEST_FLAG_SET(output, "xml:" + path);
   const std::string expected_output_file =
       path + GetCurrentExecutableName().string() + ".xml";
   const std::string& output_file =
diff --git a/ext/googletest/googletest/test/googletest-output-test-golden-lin.txt b/ext/googletest/googletest/test/googletest-output-test-golden-lin.txt
index 038de92..3fab3b9 100644
--- a/ext/googletest/googletest/test/googletest-output-test-golden-lin.txt
+++ b/ext/googletest/googletest/test/googletest-output-test-golden-lin.txt
@@ -12,7 +12,7 @@
   3
 Stack trace: (omitted)
 
-[==========] Running 85 tests from 40 test suites.
+[==========] Running 88 tests from 41 test suites.
 [----------] Global test environment set-up.
 FooEnvironment::SetUp() called.
 BarEnvironment::SetUp() called.
@@ -982,6 +982,43 @@
 Stack trace: (omitted)
 
 [  FAILED  ] PrintingStrings/ParamTest.Failure/a, where GetParam() = "a"
+[----------] 3 tests from GoogleTestVerification
+[ RUN      ] GoogleTestVerification.UninstantiatedParameterizedTestSuite<NoTests>
+googletest-output-test_.cc:#: Failure
+Parameterized test suite NoTests is instantiated via INSTANTIATE_TEST_SUITE_P, but no tests are defined via TEST_P . No test cases will run.
+
+Ideally, INSTANTIATE_TEST_SUITE_P should only ever be invoked from code that always depend on code that provides TEST_P. Failing to do so is often an indication of dead code, e.g. the last TEST_P was removed but the rest got left behind.
+
+To suppress this error for this test suite, insert the following line (in a non-header) in the namespace it is defined in:
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NoTests);
+Stack trace: (omitted)
+
+[  FAILED  ] GoogleTestVerification.UninstantiatedParameterizedTestSuite<NoTests>
+[ RUN      ] GoogleTestVerification.UninstantiatedParameterizedTestSuite<DetectNotInstantiatedTest>
+googletest-output-test_.cc:#: Failure
+Parameterized test suite DetectNotInstantiatedTest is defined via TEST_P, but never instantiated. None of the test cases will run. Either no INSTANTIATE_TEST_SUITE_P is provided or the only ones provided expand to nothing.
+
+Ideally, TEST_P definitions should only ever be included as part of binaries that intend to use them. (As opposed to, for example, being placed in a library that may be linked in to get other utilities.)
+
+To suppress this error for this test suite, insert the following line (in a non-header) in the namespace it is defined in:
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DetectNotInstantiatedTest);
+Stack trace: (omitted)
+
+[  FAILED  ] GoogleTestVerification.UninstantiatedParameterizedTestSuite<DetectNotInstantiatedTest>
+[ RUN      ] GoogleTestVerification.UninstantiatedTypeParameterizedTestSuite<DetectNotInstantiatedTypesTest>
+googletest-output-test_.cc:#: Failure
+Type parameterized test suite DetectNotInstantiatedTypesTest is defined via REGISTER_TYPED_TEST_SUITE_P, but never instantiated via INSTANTIATE_TYPED_TEST_SUITE_P. None of the test cases will run.
+
+Ideally, TYPED_TEST_P definitions should only ever be included as part of binaries that intend to use them. (As opposed to, for example, being placed in a library that may be linked in to get other utilities.)
+
+To suppress this error for this test suite, insert the following line (in a non-header) in the namespace it is defined in:
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DetectNotInstantiatedTypesTest);
+Stack trace: (omitted)
+
+[  FAILED  ] GoogleTestVerification.UninstantiatedTypeParameterizedTestSuite<DetectNotInstantiatedTypesTest>
 [----------] Global test environment tear-down
 BarEnvironment::TearDown() called.
 googletest-output-test_.cc:#: Failure
@@ -995,9 +1032,9 @@
 Expected fatal failure.
 Stack trace: (omitted)
 
-[==========] 85 tests from 40 test suites ran.
+[==========] 88 tests from 41 test suites ran.
 [  PASSED  ] 31 tests.
-[  FAILED  ] 54 tests, listed below:
+[  FAILED  ] 57 tests, listed below:
 [  FAILED  ] NonfatalFailureTest.EscapesStringOperands
 [  FAILED  ] NonfatalFailureTest.DiffForLongStrings
 [  FAILED  ] FatalFailureTest.FatalFailureInSubroutine
@@ -1052,8 +1089,11 @@
 [  FAILED  ] BadDynamicFixture2.Derived
 [  FAILED  ] PrintingFailingParams/FailingParamTest.Fails/0, where GetParam() = 2
 [  FAILED  ] PrintingStrings/ParamTest.Failure/a, where GetParam() = "a"
+[  FAILED  ] GoogleTestVerification.UninstantiatedParameterizedTestSuite<NoTests>
+[  FAILED  ] GoogleTestVerification.UninstantiatedParameterizedTestSuite<DetectNotInstantiatedTest>
+[  FAILED  ] GoogleTestVerification.UninstantiatedTypeParameterizedTestSuite<DetectNotInstantiatedTypesTest>
 
-54 FAILED TESTS
+57 FAILED TESTS
   YOU HAVE 1 DISABLED TEST
 
 Note: Google Test filter = FatalFailureTest.*:LoggingTest.*
diff --git a/ext/googletest/googletest/test/googletest-output-test.py b/ext/googletest/googletest/test/googletest-output-test.py
index c727f17..09028f6 100755
--- a/ext/googletest/googletest/test/googletest-output-test.py
+++ b/ext/googletest/googletest/test/googletest-output-test.py
@@ -29,7 +29,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-"""Tests the text output of Google C++ Testing and Mocking Framework.
+r"""Tests the text output of Google C++ Testing and Mocking Framework.
 
 To update the golden file:
 googletest_output_test.py --build_dir=BUILD/DIR --gengolden
@@ -331,7 +331,7 @@
     if CAN_GENERATE_GOLDEN_FILE:
       output = GetOutputOfAllCommands()
       golden_file = open(GOLDEN_PATH, 'wb')
-      golden_file.write(output)
+      golden_file.write(output.encode())
       golden_file.close()
     else:
       message = (
diff --git a/ext/googletest/googletest/test/googletest-output-test_.cc b/ext/googletest/googletest/test/googletest-output-test_.cc
index 4f716d8..9e5465c 100644
--- a/ext/googletest/googletest/test/googletest-output-test_.cc
+++ b/ext/googletest/googletest/test/googletest-output-test_.cc
@@ -476,63 +476,6 @@
   GTEST_FAIL_AT("foo.cc", 42) << "Expected fatal failure in foo.cc";
 }
 
-#if GTEST_IS_THREADSAFE
-
-// A unary function that may die.
-void DieIf(bool should_die) {
-  GTEST_CHECK_(!should_die) << " - death inside DieIf().";
-}
-
-// Tests running death tests in a multi-threaded context.
-
-// Used for coordination between the main and the spawn thread.
-struct SpawnThreadNotifications {
-  SpawnThreadNotifications() {}
-
-  Notification spawn_thread_started;
-  Notification spawn_thread_ok_to_terminate;
-
- private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(SpawnThreadNotifications);
-};
-
-// The function to be executed in the thread spawn by the
-// MultipleThreads test (below).
-static void ThreadRoutine(SpawnThreadNotifications* notifications) {
-  // Signals the main thread that this thread has started.
-  notifications->spawn_thread_started.Notify();
-
-  // Waits for permission to finish from the main thread.
-  notifications->spawn_thread_ok_to_terminate.WaitForNotification();
-}
-
-// This is a death-test test, but it's not named with a DeathTest
-// suffix.  It starts threads which might interfere with later
-// death tests, so it must run after all other death tests.
-class DeathTestAndMultiThreadsTest : public testing::Test {
- protected:
-  // Starts a thread and waits for it to begin.
-  void SetUp() override {
-    thread_.reset(new ThreadWithParam<SpawnThreadNotifications*>(
-        &ThreadRoutine, &notifications_, nullptr));
-    notifications_.spawn_thread_started.WaitForNotification();
-  }
-  // Tells the thread to finish, and reaps it.
-  // Depending on the version of the thread library in use,
-  // a manager thread might still be left running that will interfere
-  // with later death tests.  This is unfortunate, but this class
-  // cleans up after itself as best it can.
-  void TearDown() override {
-    notifications_.spawn_thread_ok_to_terminate.Notify();
-  }
-
- private:
-  SpawnThreadNotifications notifications_;
-  std::unique_ptr<ThreadWithParam<SpawnThreadNotifications*> > thread_;
-};
-
-#endif  // GTEST_IS_THREADSAFE
-
 // The MixedUpTestSuiteTest test case verifies that Google Test will fail a
 // test if it uses a different fixture class than what other tests in
 // the same test case use.  It deliberately contains two fixture
@@ -790,8 +733,16 @@
                          testing::Values(std::string("a")),
                          ParamNameFunc);
 
-// This #ifdef block tests the output of typed tests.
-#if GTEST_HAS_TYPED_TEST
+// The case where a suite has INSTANTIATE_TEST_SUITE_P but not TEST_P.
+using NoTests = ParamTest;
+INSTANTIATE_TEST_SUITE_P(ThisIsOdd, NoTests, ::testing::Values("Hello"));
+
+// fails under kErrorOnUninstantiatedParameterizedTest=true
+class DetectNotInstantiatedTest : public testing::TestWithParam<int> {};
+TEST_P(DetectNotInstantiatedTest, Used) { }
+
+// This would make the test failure from the above go away.
+// INSTANTIATE_TEST_SUITE_P(Fix, DetectNotInstantiatedTest, testing::Values(1));
 
 template <typename T>
 class TypedTest : public testing::Test {
@@ -829,11 +780,6 @@
 
 TYPED_TEST(TypedTestWithNames, Failure) { FAIL(); }
 
-#endif  // GTEST_HAS_TYPED_TEST
-
-// This #ifdef block tests the output of type-parameterized tests.
-#if GTEST_HAS_TYPED_TEST_P
-
 template <typename T>
 class TypedTestP : public testing::Test {
 };
@@ -869,7 +815,20 @@
 INSTANTIATE_TYPED_TEST_SUITE_P(UnsignedCustomName, TypedTestP, UnsignedTypes,
                               TypedTestPNames);
 
-#endif  // GTEST_HAS_TYPED_TEST_P
+template <typename T>
+class DetectNotInstantiatedTypesTest : public testing::Test {};
+TYPED_TEST_SUITE_P(DetectNotInstantiatedTypesTest);
+TYPED_TEST_P(DetectNotInstantiatedTypesTest, Used) {
+  TypeParam instantiate;
+  (void)instantiate;
+}
+REGISTER_TYPED_TEST_SUITE_P(DetectNotInstantiatedTypesTest, Used);
+
+// kErrorOnUninstantiatedTypeParameterizedTest=true would make the above fail.
+// Adding the following would make that test failure go away.
+//
+// typedef ::testing::Types<char, int, unsigned int> MyTypes;
+// INSTANTIATE_TYPED_TEST_SUITE_P(All, DetectNotInstantiatedTypesTest, MyTypes);
 
 #if GTEST_HAS_DEATH_TEST
 
@@ -879,8 +838,6 @@
 TEST(ADeathTest, ShouldRunFirst) {
 }
 
-# if GTEST_HAS_TYPED_TEST
-
 // We rely on the golden file to verify that typed tests whose test
 // case name ends with DeathTest are run first.
 
@@ -894,10 +851,6 @@
 TYPED_TEST(ATypedDeathTest, ShouldRunFirst) {
 }
 
-# endif  // GTEST_HAS_TYPED_TEST
-
-# if GTEST_HAS_TYPED_TEST_P
-
 
 // We rely on the golden file to verify that type-parameterized tests
 // whose test case name ends with DeathTest are run first.
@@ -915,8 +868,6 @@
 
 INSTANTIATE_TYPED_TEST_SUITE_P(My, ATypeParamDeathTest, NumericTypes);
 
-# endif  // GTEST_HAS_TYPED_TEST_P
-
 #endif  // GTEST_HAS_DEATH_TEST
 
 // Tests various failure conditions of
@@ -1078,7 +1029,7 @@
         "BadDynamicFixture1", "TestBase", nullptr, nullptr, __FILE__, __LINE__,
         []() -> testing::Test* { return new DynamicTest<true>; }),
 
-    // Register two tests with the same fixture incorrectly by ommiting the
+    // Register two tests with the same fixture incorrectly by omitting the
     // return type.
     testing::RegisterTest(
         "BadDynamicFixture2", "FixtureBase", nullptr, nullptr, __FILE__,
@@ -1115,7 +1066,7 @@
 // of them are intended to fail), and then compare the test results
 // with the "golden" file.
 int main(int argc, char **argv) {
-  testing::GTEST_FLAG(print_time) = false;
+  GTEST_FLAG_SET(print_time, false);
 
   // We just run the tests, knowing some of them are intended to fail.
   // We will use a separate Python script to compare the output of
@@ -1130,7 +1081,7 @@
                  std::string("internal_skip_environment_and_ad_hoc_tests")) > 0;
 
 #if GTEST_HAS_DEATH_TEST
-  if (testing::internal::GTEST_FLAG(internal_run_death_test) != "") {
+  if (GTEST_FLAG_GET(internal_run_death_test) != "") {
     // Skip the usual output capturing if we're running as the child
     // process of an threadsafe-style death test.
 # if GTEST_OS_WINDOWS
diff --git a/ext/googletest/googletest/test/googletest-param-test-test.cc b/ext/googletest/googletest/test/googletest-param-test-test.cc
index 6c187df..023aa46 100644
--- a/ext/googletest/googletest/test/googletest-param-test-test.cc
+++ b/ext/googletest/googletest/test/googletest-param-test-test.cc
@@ -37,6 +37,7 @@
 # include <algorithm>
 # include <iostream>
 # include <list>
+# include <set>
 # include <sstream>
 # include <string>
 # include <vector>
@@ -489,16 +490,17 @@
 class NonDefaultConstructAssignString {
  public:
   NonDefaultConstructAssignString(const std::string& s) : str_(s) {}
+  NonDefaultConstructAssignString() = delete;
+  NonDefaultConstructAssignString(const NonDefaultConstructAssignString&) =
+      default;
+  NonDefaultConstructAssignString& operator=(
+      const NonDefaultConstructAssignString&) = delete;
+  ~NonDefaultConstructAssignString() = default;
 
   const std::string& str() const { return str_; }
 
  private:
   std::string str_;
-
-  // Not default constructible
-  NonDefaultConstructAssignString();
-  // Not assignable
-  void operator=(const NonDefaultConstructAssignString&);
 };
 
 TEST(CombineTest, NonDefaultConstructAssign) {
@@ -802,7 +804,7 @@
      ::testing::UnitTest::GetInstance()->current_test_info();
 
   EXPECT_STREQ("FortyTwo/MacroNamingTest", test_info->test_suite_name());
-  EXPECT_STREQ("FooSomeTestName", test_info->name());
+  EXPECT_STREQ("FooSomeTestName/0", test_info->name());
 }
 
 INSTANTIATE_TEST_SUITE_P(FortyTwo, MacroNamingTest, Values(42));
@@ -819,6 +821,36 @@
   EXPECT_STREQ("FooSomeTestName", test_info->name());
 }
 
+TEST(MacroNameing, LookupNames) {
+  std::set<std::string> know_suite_names, know_test_names;
+
+  auto ins = testing::UnitTest::GetInstance();
+  int ts = 0;
+  while (const testing::TestSuite* suite = ins->GetTestSuite(ts++)) {
+    know_suite_names.insert(suite->name());
+
+    int ti = 0;
+    while (const testing::TestInfo* info = suite->GetTestInfo(ti++)) {
+      know_test_names.insert(std::string(suite->name()) + "." + info->name());
+    }
+  }
+
+  // Check that the expected form of the test suit name actually exists.
+  EXPECT_NE(  //
+      know_suite_names.find("FortyTwo/MacroNamingTest"),
+      know_suite_names.end());
+  EXPECT_NE(
+      know_suite_names.find("MacroNamingTestNonParametrized"),
+      know_suite_names.end());
+  // Check that the expected form of the test name actually exists.
+  EXPECT_NE(  //
+      know_test_names.find("FortyTwo/MacroNamingTest.FooSomeTestName/0"),
+      know_test_names.end());
+  EXPECT_NE(
+      know_test_names.find("MacroNamingTestNonParametrized.FooSomeTestName"),
+      know_test_names.end());
+}
+
 // Tests that user supplied custom parameter names are working correctly.
 // Runs the test with a builtin helper method which uses PrintToString,
 // as well as a custom function and custom functor to ensure all possible
@@ -1037,6 +1069,38 @@
 INSTANTIATE_TEST_SUITE_P(MyEnumTests, MyEnumTest,
                          ::testing::Values(ENUM1, ENUM2, 0));
 
+namespace works_here {
+// Never used not instantiated, this should work.
+class NotUsedTest : public testing::TestWithParam<int> {};
+
+///////
+// Never used not instantiated, this should work.
+template <typename T>
+class NotUsedTypeTest : public testing::Test {};
+TYPED_TEST_SUITE_P(NotUsedTypeTest);
+
+// Used but not instantiated, this would fail. but...
+class NotInstantiatedTest : public testing::TestWithParam<int> {};
+// ... we mark is as allowed.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NotInstantiatedTest);
+
+TEST_P(NotInstantiatedTest, Used) { }
+
+using OtherName = NotInstantiatedTest;
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OtherName);
+TEST_P(OtherName, Used) { }
+
+// Used but not instantiated, this would fail. but...
+template <typename T>
+class NotInstantiatedTypeTest : public testing::Test {};
+TYPED_TEST_SUITE_P(NotInstantiatedTypeTest);
+// ... we mark is as allowed.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NotInstantiatedTypeTest);
+
+TYPED_TEST_P(NotInstantiatedTypeTest, Used) { }
+REGISTER_TYPED_TEST_SUITE_P(NotInstantiatedTypeTest, Used);
+}  // namespace works_here
+
 int main(int argc, char **argv) {
   // Used in TestGenerationTest test suite.
   AddGlobalTestEnvironment(TestGenerationTest::Environment::Instance());
diff --git a/ext/googletest/googletest/test/googletest-param-test-test.h b/ext/googletest/googletest/test/googletest-param-test-test.h
index 6480570..8919375 100644
--- a/ext/googletest/googletest/test/googletest-param-test-test.h
+++ b/ext/googletest/googletest/test/googletest-param-test-test.h
@@ -32,8 +32,8 @@
 // This header file provides classes and functions used internally
 // for testing Google Test itself.
 
-#ifndef GTEST_TEST_GTEST_PARAM_TEST_TEST_H_
-#define GTEST_TEST_GTEST_PARAM_TEST_TEST_H_
+#ifndef GOOGLETEST_TEST_GOOGLETEST_PARAM_TEST_TEST_H_
+#define GOOGLETEST_TEST_GOOGLETEST_PARAM_TEST_TEST_H_
 
 #include "gtest/gtest.h"
 
@@ -48,4 +48,4 @@
     : public ::testing::TestWithParam<int> {
 };
 
-#endif  // GTEST_TEST_GTEST_PARAM_TEST_TEST_H_
+#endif  // GOOGLETEST_TEST_GOOGLETEST_PARAM_TEST_TEST_H_
diff --git a/ext/googletest/googletest/test/googletest-port-test.cc b/ext/googletest/googletest/test/googletest-port-test.cc
index 60d637c..16d30c4 100644
--- a/ext/googletest/googletest/test/googletest-port-test.cc
+++ b/ext/googletest/googletest/test/googletest-port-test.cc
@@ -90,10 +90,10 @@
 
 class Base {
  public:
-  // Copy constructor and assignment operator do exactly what we need, so we
-  // use them.
   Base() : member_(0) {}
   explicit Base(int n) : member_(n) {}
+  Base(const Base&) = default;
+  Base& operator=(const Base&) = default;
   virtual ~Base() {}
   int member() { return member_; }
 
@@ -201,6 +201,13 @@
   EXPECT_TRUE(converted);
 }
 
+// The following code intentionally tests a suboptimal syntax.
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-else"
+#pragma GCC diagnostic ignored "-Wempty-body"
+#pragma GCC diagnostic ignored "-Wpragmas"
+#endif
 TEST(GtestCheckSyntaxTest, BehavesLikeASingleStatement) {
   if (AlwaysFalse())
     GTEST_CHECK_(false) << "This should never be executed; "
@@ -216,6 +223,9 @@
   else
     GTEST_CHECK_(true) << "";
 }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
 
 TEST(GtestCheckSyntaxTest, WorksWithSwitch) {
   switch (0) {
@@ -270,7 +280,7 @@
 
 #if GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_QNX || GTEST_OS_FUCHSIA || \
     GTEST_OS_DRAGONFLY || GTEST_OS_FREEBSD || GTEST_OS_GNU_KFREEBSD || \
-    GTEST_OS_NETBSD || GTEST_OS_OPENBSD
+    GTEST_OS_NETBSD || GTEST_OS_OPENBSD || GTEST_OS_GNU_HURD
 void* ThreadFunc(void* data) {
   internal::Mutex* mutex = static_cast<internal::Mutex*>(data);
   mutex->Lock();
@@ -279,36 +289,61 @@
 }
 
 TEST(GetThreadCountTest, ReturnsCorrectValue) {
-  const size_t starting_count = GetThreadCount();
-  pthread_t       thread_id;
+  size_t starting_count;
+  size_t thread_count_after_create;
+  size_t thread_count_after_join;
 
-  internal::Mutex mutex;
-  {
-    internal::MutexLock lock(&mutex);
-    pthread_attr_t  attr;
-    ASSERT_EQ(0, pthread_attr_init(&attr));
-    ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE));
+  // We can't guarantee that no other thread was created or destroyed between
+  // any two calls to GetThreadCount(). We make multiple attempts, hoping that
+  // background noise is not constant and we would see the "right" values at
+  // some point.
+  for (int attempt = 0; attempt < 20; ++attempt) {
+    starting_count = GetThreadCount();
+    pthread_t thread_id;
 
-    const int status = pthread_create(&thread_id, &attr, &ThreadFunc, &mutex);
-    ASSERT_EQ(0, pthread_attr_destroy(&attr));
-    ASSERT_EQ(0, status);
-    EXPECT_EQ(starting_count + 1, GetThreadCount());
+    internal::Mutex mutex;
+    {
+      internal::MutexLock lock(&mutex);
+      pthread_attr_t attr;
+      ASSERT_EQ(0, pthread_attr_init(&attr));
+      ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE));
+
+      const int status = pthread_create(&thread_id, &attr, &ThreadFunc, &mutex);
+      ASSERT_EQ(0, pthread_attr_destroy(&attr));
+      ASSERT_EQ(0, status);
+    }
+
+    thread_count_after_create = GetThreadCount();
+
+    void* dummy;
+    ASSERT_EQ(0, pthread_join(thread_id, &dummy));
+
+    // Join before we decide whether we need to retry the test. Retry if an
+    // arbitrary other thread was created or destroyed in the meantime.
+    if (thread_count_after_create != starting_count + 1) continue;
+
+    // The OS may not immediately report the updated thread count after
+    // joining a thread, causing flakiness in this test. To counter that, we
+    // wait for up to .5 seconds for the OS to report the correct value.
+    bool thread_count_matches = false;
+    for (int i = 0; i < 5; ++i) {
+      thread_count_after_join = GetThreadCount();
+      if (thread_count_after_join == starting_count) {
+        thread_count_matches = true;
+        break;
+      }
+
+      SleepMilliseconds(100);
+    }
+
+    // Retry if an arbitrary other thread was created or destroyed.
+    if (!thread_count_matches) continue;
+
+    break;
   }
 
-  void* dummy;
-  ASSERT_EQ(0, pthread_join(thread_id, &dummy));
-
-  // The OS may not immediately report the updated thread count after
-  // joining a thread, causing flakiness in this test. To counter that, we
-  // wait for up to .5 seconds for the OS to report the correct value.
-  for (int i = 0; i < 5; ++i) {
-    if (GetThreadCount() == starting_count)
-      break;
-
-    SleepMilliseconds(100);
-  }
-
-  EXPECT_EQ(starting_count, GetThreadCount());
+  EXPECT_EQ(thread_count_after_create, starting_count + 1);
+  EXPECT_EQ(thread_count_after_join, starting_count);
 }
 #else
 TEST(GetThreadCountTest, ReturnsZeroWhenUnableToCountThreads) {
@@ -363,8 +398,6 @@
 
 #if GTEST_USES_POSIX_RE
 
-# if GTEST_HAS_TYPED_TEST
-
 template <typename Str>
 class RETest : public ::testing::Test {};
 
@@ -420,8 +453,6 @@
   EXPECT_FALSE(RE::PartialMatch(TypeParam("zza"), re));
 }
 
-# endif  // GTEST_HAS_TYPED_TEST
-
 #elif GTEST_USES_SIMPLE_RE
 
 TEST(IsInSetTest, NulCharIsNotInAnySet) {
@@ -1180,8 +1211,6 @@
     return DestructorCall::List().size() - 1;
   }
   const size_t index_;
-
-  GTEST_DISALLOW_ASSIGN_(DestructorTracker);
 };
 
 typedef ThreadLocal<DestructorTracker>* ThreadParam;
diff --git a/ext/googletest/googletest/test/googletest-printers-test.cc b/ext/googletest/googletest/test/googletest-printers-test.cc
index 4bdc9ad..e1e8e1c 100644
--- a/ext/googletest/googletest/test/googletest-printers-test.cc
+++ b/ext/googletest/googletest/test/googletest-printers-test.cc
@@ -32,14 +32,16 @@
 //
 // This file tests the universal value printer.
 
-#include <ctype.h>
-#include <limits.h>
-#include <string.h>
 #include <algorithm>
+#include <cctype>
+#include <cstdint>
+#include <cstring>
 #include <deque>
 #include <forward_list>
+#include <limits>
 #include <list>
 #include <map>
+#include <memory>
 #include <set>
 #include <sstream>
 #include <string>
@@ -89,6 +91,18 @@
   operator ::testing::internal::BiggestInt() const { return 42; }
 };
 
+// A parent class with two child classes. The parent and one of the kids have
+// stream operators.
+class ParentClass {};
+class ChildClassWithStreamOperator : public ParentClass {};
+class ChildClassWithoutStreamOperator : public ParentClass {};
+static void operator<<(std::ostream& os, const ParentClass&) {
+  os << "ParentClass";
+}
+static void operator<<(std::ostream& os, const ChildClassWithStreamOperator&) {
+  os << "ChildClassWithStreamOperator";
+}
+
 // A user-defined unprintable class template in the global namespace.
 template <typename T>
 class UnprintableTemplateInGlobal {
@@ -176,7 +190,18 @@
   return os << "StreamableTemplateInFoo: " << x.value();
 }
 
-// A user-defined streamable but recursivly-defined container type in
+// A user-defined streamable type in a user namespace whose operator<< is
+// templated on the type of the output stream.
+struct TemplatedStreamableInFoo {};
+
+template <typename OutputStream>
+OutputStream& operator<<(OutputStream& os,
+                         const TemplatedStreamableInFoo& /*ts*/) {
+  os << "TemplatedStreamableInFoo";
+  return os;
+}
+
+// A user-defined streamable but recursively-defined container type in
 // a user namespace, it mimics therefore std::filesystem::path or
 // boost::filesystem::path.
 class PathLike {
@@ -204,6 +229,33 @@
 }  // namespace foo
 
 namespace testing {
+namespace {
+template <typename T>
+class Wrapper {
+ public:
+  explicit Wrapper(T&& value) : value_(std::forward<T>(value)) {}
+
+  const T& value() const { return value_; }
+
+ private:
+  T value_;
+};
+
+}  // namespace
+
+namespace internal {
+template <typename T>
+class UniversalPrinter<Wrapper<T>> {
+ public:
+  static void Print(const Wrapper<T>& w, ::std::ostream* os) {
+    *os << "Wrapper(";
+    UniversalPrint(w.value(), os);
+    *os << ')';
+  }
+};
+}  // namespace internal
+
+
 namespace gtest_printers_test {
 
 using ::std::deque;
@@ -219,7 +271,6 @@
 using ::testing::internal::FormatForComparisonFailureMessage;
 using ::testing::internal::ImplicitCast_;
 using ::testing::internal::NativeArray;
-using ::testing::internal::RE;
 using ::testing::internal::RelationToSourceReference;
 using ::testing::internal::Strings;
 using ::testing::internal::UniversalPrint;
@@ -310,6 +361,20 @@
             Print(static_cast<unsigned char>('b')));
 }
 
+TEST(PrintCharTest, Char16) {
+  EXPECT_EQ("U+0041", Print(u'A'));
+}
+
+TEST(PrintCharTest, Char32) {
+  EXPECT_EQ("U+0041", Print(U'A'));
+}
+
+#ifdef __cpp_char8_t
+TEST(PrintCharTest, Char8) {
+  EXPECT_EQ("U+0041", Print(u8'A'));
+}
+#endif
+
 // Tests printing other simple, built-in types.
 
 // bool.
@@ -340,23 +405,39 @@
   EXPECT_EQ("L'\\xC74D' (51021)", Print(static_cast<wchar_t>(0xC74D)));
 }
 
-// Test that Int64 provides more storage than wchar_t.
+// Test that int64_t provides more storage than wchar_t.
 TEST(PrintTypeSizeTest, Wchar_t) {
-  EXPECT_LT(sizeof(wchar_t), sizeof(testing::internal::Int64));
+  EXPECT_LT(sizeof(wchar_t), sizeof(int64_t));
 }
 
 // Various integer types.
 TEST(PrintBuiltInTypeTest, Integer) {
   EXPECT_EQ("'\\xFF' (255)", Print(static_cast<unsigned char>(255)));  // uint8
   EXPECT_EQ("'\\x80' (-128)", Print(static_cast<signed char>(-128)));  // int8
-  EXPECT_EQ("65535", Print(USHRT_MAX));  // uint16
-  EXPECT_EQ("-32768", Print(SHRT_MIN));  // int16
-  EXPECT_EQ("4294967295", Print(UINT_MAX));  // uint32
-  EXPECT_EQ("-2147483648", Print(INT_MIN));  // int32
+  EXPECT_EQ("65535", Print(std::numeric_limits<uint16_t>::max()));  // uint16
+  EXPECT_EQ("-32768", Print(std::numeric_limits<int16_t>::min()));  // int16
+  EXPECT_EQ("4294967295",
+            Print(std::numeric_limits<uint32_t>::max()));  // uint32
+  EXPECT_EQ("-2147483648",
+            Print(std::numeric_limits<int32_t>::min()));  // int32
   EXPECT_EQ("18446744073709551615",
-            Print(static_cast<testing::internal::UInt64>(-1)));  // uint64
+            Print(std::numeric_limits<uint64_t>::max()));  // uint64
   EXPECT_EQ("-9223372036854775808",
-            Print(static_cast<testing::internal::Int64>(1) << 63));  // int64
+            Print(std::numeric_limits<int64_t>::min()));  // int64
+#ifdef __cpp_char8_t
+  EXPECT_EQ("U+0000",
+            Print(std::numeric_limits<char8_t>::min()));  // char8_t
+  EXPECT_EQ("U+00FF",
+            Print(std::numeric_limits<char8_t>::max()));  // char8_t
+#endif
+  EXPECT_EQ("U+0000",
+            Print(std::numeric_limits<char16_t>::min()));  // char16_t
+  EXPECT_EQ("U+FFFF",
+            Print(std::numeric_limits<char16_t>::max()));  // char16_t
+  EXPECT_EQ("U+0000",
+            Print(std::numeric_limits<char32_t>::min()));  // char32_t
+  EXPECT_EQ("U+FFFFFFFF",
+            Print(std::numeric_limits<char32_t>::max()));  // char32_t
 }
 
 // Size types.
@@ -412,6 +493,92 @@
             Print(p));
 }
 
+#ifdef __cpp_char8_t
+// const char8_t*.
+TEST(PrintU8StringTest, Const) {
+  const char8_t* p = u8"界";
+  EXPECT_EQ(PrintPointer(p) + " pointing to u8\"\\xE7\\x95\\x8C\"", Print(p));
+}
+
+// char8_t*.
+TEST(PrintU8StringTest, NonConst) {
+  char8_t p[] = u8"世";
+  EXPECT_EQ(PrintPointer(p) + " pointing to u8\"\\xE4\\xB8\\x96\"",
+            Print(static_cast<char8_t*>(p)));
+}
+
+// NULL u8 string.
+TEST(PrintU8StringTest, Null) {
+  const char8_t* p = nullptr;
+  EXPECT_EQ("NULL", Print(p));
+}
+
+// Tests that u8 strings are escaped properly.
+TEST(PrintU8StringTest, EscapesProperly) {
+  const char8_t* p = u8"'\"?\\\a\b\f\n\r\t\v\x7F\xFF hello 世界";
+  EXPECT_EQ(PrintPointer(p) +
+                " pointing to u8\"'\\\"?\\\\\\a\\b\\f\\n\\r\\t\\v\\x7F\\xFF "
+                "hello \\xE4\\xB8\\x96\\xE7\\x95\\x8C\"",
+            Print(p));
+}
+#endif
+
+// const char16_t*.
+TEST(PrintU16StringTest, Const) {
+  const char16_t* p = u"界";
+  EXPECT_EQ(PrintPointer(p) + " pointing to u\"\\x754C\"", Print(p));
+}
+
+// char16_t*.
+TEST(PrintU16StringTest, NonConst) {
+  char16_t p[] = u"世";
+  EXPECT_EQ(PrintPointer(p) + " pointing to u\"\\x4E16\"",
+            Print(static_cast<char16_t*>(p)));
+}
+
+// NULL u16 string.
+TEST(PrintU16StringTest, Null) {
+  const char16_t* p = nullptr;
+  EXPECT_EQ("NULL", Print(p));
+}
+
+// Tests that u16 strings are escaped properly.
+TEST(PrintU16StringTest, EscapesProperly) {
+  const char16_t* p = u"'\"?\\\a\b\f\n\r\t\v\x7F\xFF hello 世界";
+  EXPECT_EQ(PrintPointer(p) +
+                " pointing to u\"'\\\"?\\\\\\a\\b\\f\\n\\r\\t\\v\\x7F\\xFF "
+                "hello \\x4E16\\x754C\"",
+            Print(p));
+}
+
+// const char32_t*.
+TEST(PrintU32StringTest, Const) {
+  const char32_t* p = U"🗺️";
+  EXPECT_EQ(PrintPointer(p) + " pointing to U\"\\x1F5FA\\xFE0F\"", Print(p));
+}
+
+// char32_t*.
+TEST(PrintU32StringTest, NonConst) {
+  char32_t p[] = U"🌌";
+  EXPECT_EQ(PrintPointer(p) + " pointing to U\"\\x1F30C\"",
+            Print(static_cast<char32_t*>(p)));
+}
+
+// NULL u32 string.
+TEST(PrintU32StringTest, Null) {
+  const char32_t* p = nullptr;
+  EXPECT_EQ("NULL", Print(p));
+}
+
+// Tests that u32 strings are escaped properly.
+TEST(PrintU32StringTest, EscapesProperly) {
+  const char32_t* p = U"'\"?\\\a\b\f\n\r\t\v\x7F\xFF hello 🗺️";
+  EXPECT_EQ(PrintPointer(p) +
+                " pointing to U\"'\\\"?\\\\\\a\\b\\f\\n\\r\\t\\v\\x7F\\xFF "
+                "hello \\x1F5FA\\xFE0F\"",
+            Print(p));
+}
+
 // MSVC compiler can be configured to define whar_t as a typedef
 // of unsigned short. Defining an overload for const wchar_t* in that case
 // would cause pointers to unsigned shorts be printed as wide strings,
@@ -622,21 +789,66 @@
   EXPECT_EQ("\"H\\0i\" (no terminating NUL)", PrintArrayHelper(a));
 }
 
-// const char array with terminating NUL.
-TEST(PrintArrayTest, ConstCharArrayWithTerminatingNul) {
+// char array with terminating NUL.
+TEST(PrintArrayTest, CharArrayWithTerminatingNul) {
   const char a[] = "\0Hi";
   EXPECT_EQ("\"\\0Hi\"", PrintArrayHelper(a));
 }
 
-// const wchar_t array without terminating NUL.
+#ifdef __cpp_char8_t
+// char_t array without terminating NUL.
+TEST(PrintArrayTest, Char8ArrayWithNoTerminatingNul) {
+  // Array a contains '\0' in the middle and doesn't end with '\0'.
+  const char8_t a[] = {u8'H', u8'\0', u8'i'};
+  EXPECT_EQ("u8\"H\\0i\" (no terminating NUL)", PrintArrayHelper(a));
+}
+
+// char8_t array with terminating NUL.
+TEST(PrintArrayTest, Char8ArrayWithTerminatingNul) {
+  const char8_t a[] = u8"\0世界";
+  EXPECT_EQ(
+      "u8\"\\0\\xE4\\xB8\\x96\\xE7\\x95\\x8C\"",
+      PrintArrayHelper(a));
+}
+#endif
+
+// const char16_t array without terminating NUL.
+TEST(PrintArrayTest, Char16ArrayWithNoTerminatingNul) {
+  // Array a contains '\0' in the middle and doesn't end with '\0'.
+  const char16_t a[] = {u'こ', u'\0', u'ん', u'に', u'ち', u'は'};
+  EXPECT_EQ("u\"\\x3053\\0\\x3093\\x306B\\x3061\\x306F\" (no terminating NUL)",
+            PrintArrayHelper(a));
+}
+
+// char16_t array with terminating NUL.
+TEST(PrintArrayTest, Char16ArrayWithTerminatingNul) {
+  const char16_t a[] = u"\0こんにちは";
+  EXPECT_EQ("u\"\\0\\x3053\\x3093\\x306B\\x3061\\x306F\"", PrintArrayHelper(a));
+}
+
+// char32_t array without terminating NUL.
+TEST(PrintArrayTest, Char32ArrayWithNoTerminatingNul) {
+  // Array a contains '\0' in the middle and doesn't end with '\0'.
+  const char32_t a[] = {U'👋', U'\0', U'🌌'};
+  EXPECT_EQ("U\"\\x1F44B\\0\\x1F30C\" (no terminating NUL)",
+            PrintArrayHelper(a));
+}
+
+// char32_t array with terminating NUL.
+TEST(PrintArrayTest, Char32ArrayWithTerminatingNul) {
+  const char32_t a[] = U"\0👋🌌";
+  EXPECT_EQ("U\"\\0\\x1F44B\\x1F30C\"", PrintArrayHelper(a));
+}
+
+// wchar_t array without terminating NUL.
 TEST(PrintArrayTest, WCharArrayWithNoTerminatingNul) {
   // Array a contains '\0' in the middle and doesn't end with '\0'.
-  const wchar_t a[] = { L'H', L'\0', L'i' };
+  const wchar_t a[] = {L'H', L'\0', L'i'};
   EXPECT_EQ("L\"H\\0i\" (no terminating NUL)", PrintArrayHelper(a));
 }
 
 // wchar_t array with terminating NUL.
-TEST(PrintArrayTest, WConstCharArrayWithTerminatingNul) {
+TEST(PrintArrayTest, WCharArrayWithTerminatingNul) {
   const wchar_t a[] = L"\0Hi";
   EXPECT_EQ("L\"\\0Hi\"", PrintArrayHelper(a));
 }
@@ -700,6 +912,26 @@
 }
 #endif  // GTEST_HAS_STD_WSTRING
 
+#ifdef __cpp_char8_t
+TEST(PrintStringTest, U8String) {
+  std::u8string str = u8"Hello, 世界";
+  EXPECT_EQ(str, str);  // Verify EXPECT_EQ compiles with this type.
+  EXPECT_EQ("u8\"Hello, \\xE4\\xB8\\x96\\xE7\\x95\\x8C\"", Print(str));
+}
+#endif
+
+TEST(PrintStringTest, U16String) {
+  std::u16string str = u"Hello, 世界";
+  EXPECT_EQ(str, str);  // Verify EXPECT_EQ compiles with this type.
+  EXPECT_EQ("u\"Hello, \\x4E16\\x754C\"", Print(str));
+}
+
+TEST(PrintStringTest, U32String) {
+  std::u32string str = U"Hello, 🗺️";
+  EXPECT_EQ(str, str);  // Verify EXPECT_EQ compiles with this type
+  EXPECT_EQ("U\"Hello, \\x1F5FA\\xFE0F\"", Print(str));
+}
+
 // Tests printing types that support generic streaming (i.e. streaming
 // to std::basic_ostream<Char, CharTraits> for any valid Char and
 // CharTraits types).
@@ -758,22 +990,22 @@
   EXPECT_EQ("AllowsGenericStreamingAndImplicitConversionTemplate", Print(a));
 }
 
-#if GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_STRING_VIEW
 
-// Tests printing ::absl::string_view.
+// Tests printing internal::StringView.
 
 TEST(PrintStringViewTest, SimpleStringView) {
-  const ::absl::string_view sp = "Hello";
+  const internal::StringView sp = "Hello";
   EXPECT_EQ("\"Hello\"", Print(sp));
 }
 
 TEST(PrintStringViewTest, UnprintableCharacters) {
   const char str[] = "NUL (\0) and \r\t";
-  const ::absl::string_view sp(str, sizeof(str) - 1);
+  const internal::StringView sp(str, sizeof(str) - 1);
   EXPECT_EQ("\"NUL (\\0) and \\r\\t\"", Print(sp));
 }
 
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
 
 // Tests printing STL containers.
 
@@ -978,9 +1210,8 @@
   EXPECT_EQ("(false, 2, 3, 4)", Print(t4));
 
   const char* const str = "8";
-  ::std::tuple<bool, char, short, testing::internal::Int32,  // NOLINT
-               testing::internal::Int64, float, double, const char*, void*,
-               std::string>
+  ::std::tuple<bool, char, short, int32_t, int64_t, float, double,  // NOLINT
+               const char*, void*, std::string>
       t10(false, 'a', static_cast<short>(3), 4, 5, 1.5F, -2.5, str,  // NOLINT
           nullptr, "10");
   EXPECT_EQ("(false, 'a' (97, 0x61), 3, 4, 5, 1.5, -2.5, " + PrintPointer(str) +
@@ -1064,6 +1295,20 @@
             Print(::foo::StreamableTemplateInFoo<int>()));
 }
 
+TEST(PrintStreamableTypeTest, TypeInUserNamespaceWithTemplatedStreamOperator) {
+  EXPECT_EQ("TemplatedStreamableInFoo",
+            Print(::foo::TemplatedStreamableInFoo()));
+}
+
+TEST(PrintStreamableTypeTest, SubclassUsesSuperclassStreamOperator) {
+  ParentClass parent;
+  ChildClassWithStreamOperator child_stream;
+  ChildClassWithoutStreamOperator child_no_stream;
+  EXPECT_EQ("ParentClass", Print(parent));
+  EXPECT_EQ("ChildClassWithStreamOperator", Print(child_stream));
+  EXPECT_EQ("ParentClass", Print(child_no_stream));
+}
+
 // Tests printing a user-defined recursive container type that has a <<
 // operator.
 TEST(PrintStreamableTypeTest, PathLikeInUserNamespace) {
@@ -1472,6 +1717,13 @@
   EXPECT_EQ("123", ss.str());
 }
 
+TEST(UniversalPrintTest, WorksForPairWithConst) {
+  std::pair<const Wrapper<std::string>, int> p(Wrapper<std::string>("abc"), 1);
+  ::std::stringstream ss;
+  UniversalPrint(p, &ss);
+  EXPECT_EQ("(Wrapper(\"abc\"), 1)", ss.str());
+}
+
 TEST(UniversalPrintTest, WorksForCString) {
   const char* s1 = "abc";
   ::std::stringstream ss1;
@@ -1501,6 +1753,63 @@
   EXPECT_EQ("\"\\\"Line\\0 1\\\"\\nLine 2\"", ss2.str());
 }
 
+TEST(UniversalPrintTest, IncompleteType) {
+  struct Incomplete;
+  char some_object = 0;
+  EXPECT_EQ("(incomplete type)",
+            PrintToString(reinterpret_cast<Incomplete&>(some_object)));
+}
+
+TEST(UniversalPrintTest, SmartPointers) {
+  EXPECT_EQ("(nullptr)", PrintToString(std::unique_ptr<int>()));
+  std::unique_ptr<int> p(new int(17));
+  EXPECT_EQ("(ptr = " + PrintPointer(p.get()) + ", value = 17)",
+            PrintToString(p));
+  std::unique_ptr<int[]> p2(new int[2]);
+  EXPECT_EQ("(" + PrintPointer(p2.get()) + ")", PrintToString(p2));
+
+  EXPECT_EQ("(nullptr)", PrintToString(std::shared_ptr<int>()));
+  std::shared_ptr<int> p3(new int(1979));
+  EXPECT_EQ("(ptr = " + PrintPointer(p3.get()) + ", value = 1979)",
+            PrintToString(p3));
+#if __cpp_lib_shared_ptr_arrays >= 201611L
+  std::shared_ptr<int[]> p4(new int[2]);
+  EXPECT_EQ("(" + PrintPointer(p4.get()) + ")", PrintToString(p4));
+#endif
+
+  // modifiers
+  EXPECT_EQ("(nullptr)", PrintToString(std::unique_ptr<int>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::unique_ptr<const int>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::unique_ptr<volatile int>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::unique_ptr<volatile const int>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::unique_ptr<int[]>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::unique_ptr<const int[]>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::unique_ptr<volatile int[]>()));
+  EXPECT_EQ("(nullptr)",
+            PrintToString(std::unique_ptr<volatile const int[]>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::shared_ptr<int>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::shared_ptr<const int>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::shared_ptr<volatile int>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::shared_ptr<volatile const int>()));
+#if __cpp_lib_shared_ptr_arrays >= 201611L
+  EXPECT_EQ("(nullptr)", PrintToString(std::shared_ptr<int[]>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::shared_ptr<const int[]>()));
+  EXPECT_EQ("(nullptr)", PrintToString(std::shared_ptr<volatile int[]>()));
+  EXPECT_EQ("(nullptr)",
+            PrintToString(std::shared_ptr<volatile const int[]>()));
+#endif
+
+  // void
+  EXPECT_EQ("(nullptr)", PrintToString(std::unique_ptr<void, void (*)(void*)>(
+                             nullptr, nullptr)));
+  EXPECT_EQ("(" + PrintPointer(p.get()) + ")",
+            PrintToString(
+                std::unique_ptr<void, void (*)(void*)>(p.get(), [](void*) {})));
+  EXPECT_EQ("(nullptr)", PrintToString(std::shared_ptr<void>()));
+  EXPECT_EQ("(" + PrintPointer(p.get()) + ")",
+            PrintToString(std::shared_ptr<void>(p.get(), [](void*) {})));
+}
+
 TEST(UniversalTersePrintTupleFieldsToStringsTestWithStd, PrintsEmptyTuple) {
   Strings result = UniversalTersePrintTupleFieldsToStrings(::std::make_tuple());
   EXPECT_EQ(0u, result.size());
@@ -1530,32 +1839,65 @@
   EXPECT_EQ("\"a\"", result[1]);
 }
 
-#if GTEST_HAS_ABSL
+#if GTEST_INTERNAL_HAS_ANY
+class PrintAnyTest : public ::testing::Test {
+ protected:
+  template <typename T>
+  static std::string ExpectedTypeName() {
+#if GTEST_HAS_RTTI
+    return internal::GetTypeName<T>();
+#else
+    return "<unknown_type>";
+#endif  // GTEST_HAS_RTTI
+  }
+};
 
+TEST_F(PrintAnyTest, Empty) {
+  internal::Any any;
+  EXPECT_EQ("no value", PrintToString(any));
+}
+
+TEST_F(PrintAnyTest, NonEmpty) {
+  internal::Any any;
+  constexpr int val1 = 10;
+  const std::string val2 = "content";
+
+  any = val1;
+  EXPECT_EQ("value of type " + ExpectedTypeName<int>(), PrintToString(any));
+
+  any = val2;
+  EXPECT_EQ("value of type " + ExpectedTypeName<std::string>(),
+            PrintToString(any));
+}
+#endif  // GTEST_INTERNAL_HAS_ANY
+
+#if GTEST_INTERNAL_HAS_OPTIONAL
 TEST(PrintOptionalTest, Basic) {
-  absl::optional<int> value;
+  internal::Optional<int> value;
   EXPECT_EQ("(nullopt)", PrintToString(value));
   value = {7};
   EXPECT_EQ("(7)", PrintToString(value));
-  EXPECT_EQ("(1.1)", PrintToString(absl::optional<double>{1.1}));
-  EXPECT_EQ("(\"A\")", PrintToString(absl::optional<std::string>{"A"}));
+  EXPECT_EQ("(1.1)", PrintToString(internal::Optional<double>{1.1}));
+  EXPECT_EQ("(\"A\")", PrintToString(internal::Optional<std::string>{"A"}));
 }
+#endif  // GTEST_INTERNAL_HAS_OPTIONAL
 
+#if GTEST_INTERNAL_HAS_VARIANT
 struct NonPrintable {
   unsigned char contents = 17;
 };
 
 TEST(PrintOneofTest, Basic) {
-  using Type = absl::variant<int, StreamableInGlobal, NonPrintable>;
-  EXPECT_EQ("('int' with value 7)", PrintToString(Type(7)));
-  EXPECT_EQ("('StreamableInGlobal' with value StreamableInGlobal)",
+  using Type = internal::Variant<int, StreamableInGlobal, NonPrintable>;
+  EXPECT_EQ("('int(index = 0)' with value 7)", PrintToString(Type(7)));
+  EXPECT_EQ("('StreamableInGlobal(index = 1)' with value StreamableInGlobal)",
             PrintToString(Type(StreamableInGlobal{})));
   EXPECT_EQ(
-      "('testing::gtest_printers_test::NonPrintable' with value 1-byte object "
-      "<11>)",
+      "('testing::gtest_printers_test::NonPrintable(index = 2)' with value "
+      "1-byte object <11>)",
       PrintToString(Type(NonPrintable{})));
 }
-#endif  // GTEST_HAS_ABSL
+#endif  // GTEST_INTERNAL_HAS_VARIANT
 namespace {
 class string_ref;
 
diff --git a/ext/googletest/ci/get-nprocessors.sh b/ext/googletest/googletest/test/googletest-setuptestsuite-test.py
similarity index 65%
rename from ext/googletest/ci/get-nprocessors.sh
rename to ext/googletest/googletest/test/googletest-setuptestsuite-test.py
index 43635e7..c82162f 100755
--- a/ext/googletest/ci/get-nprocessors.sh
+++ b/ext/googletest/googletest/test/googletest-setuptestsuite-test.py
@@ -1,7 +1,7 @@
-#!/usr/bin/env bash
-# Copyright 2017 Google Inc.
-# All Rights Reserved.
+#!/usr/bin/env python
 #
+# Copyright 2019, Google Inc.
+# All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -29,20 +29,26 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-# This file is typically sourced by another script.
-# if possible, ask for the precise number of processors,
-# otherwise take 2 processors as reasonable default; see
-# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
-if [ -x /usr/bin/getconf ]; then
-    NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
-else
-    NPROCESSORS=2
-fi
+"""Verifies that SetUpTestSuite and TearDownTestSuite errors are noticed."""
 
-# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
-# crashes if parallelized too much (maybe memory consumption problem),
-# so limit to 4 processors for the time being.
-if [ $NPROCESSORS -gt 4 ] ; then
-	echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
-	NPROCESSORS=4
-fi
+import gtest_test_utils
+
+COMMAND = gtest_test_utils.GetTestExecutablePath(
+    'googletest-setuptestsuite-test_')
+
+
+class GTestSetUpTestSuiteTest(gtest_test_utils.TestCase):
+
+  def testSetupErrorAndTearDownError(self):
+    p = gtest_test_utils.Subprocess(COMMAND)
+    self.assertNotEqual(p.exit_code, 0, msg=p.output)
+
+    self.assertIn(
+        '[  FAILED  ] SetupFailTest: SetUpTestSuite or TearDownTestSuite\n'
+        '[  FAILED  ] TearDownFailTest: SetUpTestSuite or TearDownTestSuite\n'
+        '\n'
+        ' 2 FAILED TEST SUITES\n',
+        p.output)
+
+if __name__ == '__main__':
+  gtest_test_utils.Main()
diff --git a/ext/googletest/googletest/test/googletest-setuptestsuite-test_.cc b/ext/googletest/googletest/test/googletest-setuptestsuite-test_.cc
new file mode 100644
index 0000000..a4bc4ef
--- /dev/null
+++ b/ext/googletest/googletest/test/googletest-setuptestsuite-test_.cc
@@ -0,0 +1,49 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#include "gtest/gtest.h"
+
+class SetupFailTest : public ::testing::Test {
+ protected:
+  static void SetUpTestSuite() {
+    ASSERT_EQ("", "SET_UP_FAIL");
+  }
+};
+
+TEST_F(SetupFailTest, NoopPassingTest) {}
+
+class TearDownFailTest : public ::testing::Test {
+ protected:
+  static void TearDownTestSuite() {
+    ASSERT_EQ("", "TEAR_DOWN_FAIL");
+  }
+};
+
+TEST_F(TearDownFailTest, NoopPassingTest) {}
diff --git a/ext/googletest/googletest/test/googletest-shuffle-test_.cc b/ext/googletest/googletest/test/googletest-shuffle-test_.cc
index c1fc106..4505663 100644
--- a/ext/googletest/googletest/test/googletest-shuffle-test_.cc
+++ b/ext/googletest/googletest/test/googletest-shuffle-test_.cc
@@ -82,7 +82,7 @@
   }
 
   void OnTestStart(const TestInfo& test_info) override {
-    printf("%s.%s\n", test_info.test_case_name(), test_info.name());
+    printf("%s.%s\n", test_info.test_suite_name(), test_info.name());
   }
 };
 
diff --git a/ext/googletest/googletest/test/googletest-test2_test.cc b/ext/googletest/googletest/test/googletest-test2_test.cc
deleted file mode 100644
index 2e425da..0000000
--- a/ext/googletest/googletest/test/googletest-test2_test.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2008, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-//
-// Tests for Google Test itself.  This verifies that the basic constructs of
-// Google Test work.
-
-#include "gtest/gtest.h"
-#include "googletest-param-test-test.h"
-
-using ::testing::Values;
-using ::testing::internal::ParamGenerator;
-
-// Tests that generators defined in a different translation unit
-// are functional. The test using extern_gen_2 is defined
-// in googletest-param-test-test.cc.
-ParamGenerator<int> extern_gen_2 = Values(33);
-
-// Tests that a parameterized test case can be defined in one translation unit
-// and instantiated in another. The test is defined in
-// googletest-param-test-test.cc and ExternalInstantiationTest fixture class is
-// defined in gtest-param-test_test.h.
-INSTANTIATE_TEST_SUITE_P(MultiplesOf33,
-                         ExternalInstantiationTest,
-                         Values(33, 66));
-
-// Tests that a parameterized test case can be instantiated
-// in multiple translation units. Another instantiation is defined
-// in googletest-param-test-test.cc and
-// InstantiationInMultipleTranslationUnitsTest fixture is defined in
-// gtest-param-test_test.h
-INSTANTIATE_TEST_SUITE_P(Sequence2,
-                         InstantiationInMultipleTranslationUnitsTest,
-                         Values(42*3, 42*4, 42*5));
-
diff --git a/ext/googletest/googletest/test/gtest-typed-test2_test.cc b/ext/googletest/googletest/test/gtest-typed-test2_test.cc
index 7000160..e83ca2e 100644
--- a/ext/googletest/googletest/test/gtest-typed-test2_test.cc
+++ b/ext/googletest/googletest/test/gtest-typed-test2_test.cc
@@ -33,12 +33,8 @@
 #include "test/gtest-typed-test_test.h"
 #include "gtest/gtest.h"
 
-#if GTEST_HAS_TYPED_TEST_P
-
 // Tests that the same type-parameterized test case can be
 // instantiated in different translation units linked together.
 // (ContainerTest is also instantiated in gtest-typed-test_test.cc.)
 INSTANTIATE_TYPED_TEST_SUITE_P(Vector, ContainerTest,
                                testing::Types<std::vector<int> >);
-
-#endif  // GTEST_HAS_TYPED_TEST_P
diff --git a/ext/googletest/googletest/test/gtest-typed-test_test.cc b/ext/googletest/googletest/test/gtest-typed-test_test.cc
index 5411832..5fc678c 100644
--- a/ext/googletest/googletest/test/gtest-typed-test_test.cc
+++ b/ext/googletest/googletest/test/gtest-typed-test_test.cc
@@ -88,9 +88,6 @@
 template <typename T>
 T* CommonTest<T>::shared_ = nullptr;
 
-// This #ifdef block tests typed tests.
-#if GTEST_HAS_TYPED_TEST
-
 using testing::Types;
 
 // Tests that SetUpTestSuite()/TearDownTestSuite(), fixture ctor/dtor,
@@ -193,22 +190,17 @@
   if (std::is_same<TypeParam, char>::value) {
     EXPECT_STREQ(::testing::UnitTest::GetInstance()
                      ->current_test_info()
-                     ->test_case_name(),
+                     ->test_suite_name(),
                  "TypedTestWithNames/char0");
   }
   if (std::is_same<TypeParam, int>::value) {
     EXPECT_STREQ(::testing::UnitTest::GetInstance()
                      ->current_test_info()
-                     ->test_case_name(),
+                     ->test_suite_name(),
                  "TypedTestWithNames/int1");
   }
 }
 
-#endif  // GTEST_HAS_TYPED_TEST
-
-// This #ifdef block tests type-parameterized tests.
-#if GTEST_HAS_TYPED_TEST_P
-
 using testing::Types;
 using testing::internal::TypedTestSuitePState;
 
@@ -228,7 +220,7 @@
 TEST_F(TypedTestSuitePStateTest, SucceedsForMatchingList) {
   const char* tests = "A, B, C";
   EXPECT_EQ(tests,
-            state_.VerifyRegisteredTestNames("foo.cc", 1, tests));
+            state_.VerifyRegisteredTestNames("Suite", "foo.cc", 1, tests));
 }
 
 // Makes sure that the order of the tests and spaces around the names
@@ -236,33 +228,33 @@
 TEST_F(TypedTestSuitePStateTest, IgnoresOrderAndSpaces) {
   const char* tests = "A,C,   B";
   EXPECT_EQ(tests,
-            state_.VerifyRegisteredTestNames("foo.cc", 1, tests));
+            state_.VerifyRegisteredTestNames("Suite", "foo.cc", 1, tests));
 }
 
 using TypedTestSuitePStateDeathTest = TypedTestSuitePStateTest;
 
 TEST_F(TypedTestSuitePStateDeathTest, DetectsDuplicates) {
   EXPECT_DEATH_IF_SUPPORTED(
-      state_.VerifyRegisteredTestNames("foo.cc", 1, "A, B, A, C"),
+      state_.VerifyRegisteredTestNames("Suite", "foo.cc", 1, "A, B, A, C"),
       "foo\\.cc.1.?: Test A is listed more than once\\.");
 }
 
 TEST_F(TypedTestSuitePStateDeathTest, DetectsExtraTest) {
   EXPECT_DEATH_IF_SUPPORTED(
-      state_.VerifyRegisteredTestNames("foo.cc", 1, "A, B, C, D"),
+      state_.VerifyRegisteredTestNames("Suite", "foo.cc", 1, "A, B, C, D"),
       "foo\\.cc.1.?: No test named D can be found in this test suite\\.");
 }
 
 TEST_F(TypedTestSuitePStateDeathTest, DetectsMissedTest) {
   EXPECT_DEATH_IF_SUPPORTED(
-      state_.VerifyRegisteredTestNames("foo.cc", 1, "A, C"),
+      state_.VerifyRegisteredTestNames("Suite", "foo.cc", 1, "A, C"),
       "foo\\.cc.1.?: You forgot to list test B\\.");
 }
 
 // Tests that defining a test for a parameterized test case generates
 // a run-time error if the test case has been registered.
 TEST_F(TypedTestSuitePStateDeathTest, DetectsTestAfterRegistration) {
-  state_.VerifyRegisteredTestNames("foo.cc", 1, "A, B, C");
+  state_.VerifyRegisteredTestNames("Suite", "foo.cc", 1, "A, B, C");
   EXPECT_DEATH_IF_SUPPORTED(
       state_.AddTestName("foo.cc", 2, "FooTest", "D"),
       "foo\\.cc.2.?: Test D must be defined before REGISTER_TYPED_TEST_SUITE_P"
@@ -315,13 +307,13 @@
   if (std::is_same<TypeParam, char>::value) {
     EXPECT_STREQ(::testing::UnitTest::GetInstance()
                      ->current_test_info()
-                     ->test_case_name(),
+                     ->test_suite_name(),
                  "CustomName/TypeParametrizedTestWithNames/parChar0");
   }
   if (std::is_same<TypeParam, int>::value) {
     EXPECT_STREQ(::testing::UnitTest::GetInstance()
                      ->current_test_info()
-                     ->test_case_name(),
+                     ->test_suite_name(),
                  "CustomName/TypeParametrizedTestWithNames/parInt1");
   }
 }
@@ -443,20 +435,3 @@
 
 }  // namespace library2
 
-#endif  // GTEST_HAS_TYPED_TEST_P
-
-#if !defined(GTEST_HAS_TYPED_TEST) && !defined(GTEST_HAS_TYPED_TEST_P)
-
-// Google Test may not support type-parameterized tests with some
-// compilers. If we use conditional compilation to compile out all
-// code referring to the gtest_main library, MSVC linker will not link
-// that library at all and consequently complain about missing entry
-// point defined in that library (fatal error LNK1561: entry point
-// must be defined). This dummy test keeps gtest_main linked in.
-TEST(DummyTest, TypedTestsAreNotSupportedOnThisPlatform) {}
-
-#if _MSC_VER
-GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4127
-#endif                             //  _MSC_VER
-
-#endif  // #if !defined(GTEST_HAS_TYPED_TEST) && !defined(GTEST_HAS_TYPED_TEST_P)
diff --git a/ext/googletest/googletest/test/gtest-typed-test_test.h b/ext/googletest/googletest/test/gtest-typed-test_test.h
index 23137b7..8ce559c 100644
--- a/ext/googletest/googletest/test/gtest-typed-test_test.h
+++ b/ext/googletest/googletest/test/gtest-typed-test_test.h
@@ -27,14 +27,11 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-
-#ifndef GTEST_TEST_GTEST_TYPED_TEST_TEST_H_
-#define GTEST_TEST_GTEST_TYPED_TEST_TEST_H_
+#ifndef GOOGLETEST_TEST_GTEST_TYPED_TEST_TEST_H_
+#define GOOGLETEST_TEST_GTEST_TYPED_TEST_TEST_H_
 
 #include "gtest/gtest.h"
 
-#if GTEST_HAS_TYPED_TEST_P
-
 using testing::Test;
 
 // For testing that the same type-parameterized test case can be
@@ -60,6 +57,4 @@
 REGISTER_TYPED_TEST_SUITE_P(ContainerTest,
                             CanBeDefaultConstructed, InitialSizeIsZero);
 
-#endif  // GTEST_HAS_TYPED_TEST_P
-
-#endif  // GTEST_TEST_GTEST_TYPED_TEST_TEST_H_
+#endif  // GOOGLETEST_TEST_GTEST_TYPED_TEST_TEST_H_
diff --git a/ext/googletest/googletest/test/gtest-unittest-api_test.cc b/ext/googletest/googletest/test/gtest-unittest-api_test.cc
index 480a41f..8ef5058 100644
--- a/ext/googletest/googletest/test/gtest-unittest-api_test.cc
+++ b/ext/googletest/googletest/test/gtest-unittest-api_test.cc
@@ -55,8 +55,8 @@
   // name.  The caller is responsible for deleting the array.
   static TestSuite const** GetSortedTestSuites() {
     UnitTest& unit_test = *UnitTest::GetInstance();
-    auto const** const test_suites =
-        new const TestSuite*[unit_test.total_test_suite_count()];
+    auto const** const test_suites = new const TestSuite*[static_cast<size_t>(
+      unit_test.total_test_suite_count())];
 
     for (int i = 0; i < unit_test.total_test_suite_count(); ++i)
       test_suites[i] = unit_test.GetTestSuite(i);
@@ -83,8 +83,8 @@
   // sorted by the test name.  The caller is responsible for deleting the
   // array.
   static TestInfo const** GetSortedTests(const TestSuite* test_suite) {
-    TestInfo const** const tests =
-        new const TestInfo*[test_suite->total_test_count()];
+    TestInfo const** const tests = new const TestInfo*[static_cast<size_t>(
+      test_suite->total_test_count())];
 
     for (int i = 0; i < test_suite->total_test_count(); ++i)
       tests[i] = test_suite->GetTestInfo(i);
@@ -95,17 +95,12 @@
   }
 };
 
-#if GTEST_HAS_TYPED_TEST
 template <typename T> class TestSuiteWithCommentTest : public Test {};
 TYPED_TEST_SUITE(TestSuiteWithCommentTest, Types<int>);
 TYPED_TEST(TestSuiteWithCommentTest, Dummy) {}
 
 const int kTypedTestSuites = 1;
 const int kTypedTests = 1;
-#else
-const int kTypedTestSuites = 0;
-const int kTypedTests = 0;
-#endif  // GTEST_HAS_TYPED_TEST
 
 // We can only test the accessors that do not change value while tests run.
 // Since tests can be run in any order, the values the accessors that track
@@ -123,9 +118,7 @@
 
   EXPECT_STREQ("ApiTest", test_suites[0]->name());
   EXPECT_STREQ("DISABLED_Test", test_suites[1]->name());
-#if GTEST_HAS_TYPED_TEST
   EXPECT_STREQ("TestSuiteWithCommentTest/0", test_suites[2]->name());
-#endif  // GTEST_HAS_TYPED_TEST
 
   delete[] test_suites;
 
@@ -183,12 +176,11 @@
   delete[] tests;
   tests = nullptr;
 
-#if GTEST_HAS_TYPED_TEST
   test_suite = UnitTestHelper::FindTestSuite("TestSuiteWithCommentTest/0");
   ASSERT_TRUE(test_suite != nullptr);
 
   EXPECT_STREQ("TestSuiteWithCommentTest/0", test_suite->name());
-  EXPECT_STREQ(GetTypeName<int>().c_str(), test_suite->type_param());
+  EXPECT_STREQ(GetTypeName<Types<int>>().c_str(), test_suite->type_param());
   EXPECT_TRUE(test_suite->should_run());
   EXPECT_EQ(0, test_suite->disabled_test_count());
   EXPECT_EQ(1, test_suite->test_to_run_count());
@@ -199,11 +191,10 @@
   EXPECT_STREQ("Dummy", tests[0]->name());
   EXPECT_STREQ("TestSuiteWithCommentTest/0", tests[0]->test_suite_name());
   EXPECT_TRUE(IsNull(tests[0]->value_param()));
-  EXPECT_STREQ(GetTypeName<int>().c_str(), tests[0]->type_param());
+  EXPECT_STREQ(GetTypeName<Types<int>>().c_str(), tests[0]->type_param());
   EXPECT_TRUE(tests[0]->should_run());
 
   delete[] tests;
-#endif  // GTEST_HAS_TYPED_TEST
 }
 
 TEST(ApiTest, TestSuiteDisabledAccessorsWork) {
@@ -263,9 +254,9 @@
     EXPECT_EQ(0, test_suites[1]->successful_test_count());
     EXPECT_EQ(0, test_suites[1]->failed_test_count());
 
-#if GTEST_HAS_TYPED_TEST
     EXPECT_STREQ("TestSuiteWithCommentTest/0", test_suites[2]->name());
-    EXPECT_STREQ(GetTypeName<int>().c_str(), test_suites[2]->type_param());
+    EXPECT_STREQ(GetTypeName<Types<int>>().c_str(),
+                 test_suites[2]->type_param());
     EXPECT_TRUE(test_suites[2]->should_run());
     EXPECT_EQ(0, test_suites[2]->disabled_test_count());
     ASSERT_EQ(1, test_suites[2]->total_test_count());
@@ -273,7 +264,6 @@
     EXPECT_EQ(0, test_suites[2]->failed_test_count());
     EXPECT_TRUE(test_suites[2]->Passed());
     EXPECT_FALSE(test_suites[2]->Failed());
-#endif  // GTEST_HAS_TYPED_TEST
 
     const TestSuite* test_suite = UnitTestHelper::FindTestSuite("ApiTest");
     const TestInfo** tests = UnitTestHelper::GetSortedTests(test_suite);
@@ -310,20 +300,18 @@
 
     delete[] tests;
 
-#if GTEST_HAS_TYPED_TEST
     test_suite = UnitTestHelper::FindTestSuite("TestSuiteWithCommentTest/0");
     tests = UnitTestHelper::GetSortedTests(test_suite);
 
     EXPECT_STREQ("Dummy", tests[0]->name());
     EXPECT_STREQ("TestSuiteWithCommentTest/0", tests[0]->test_suite_name());
     EXPECT_TRUE(IsNull(tests[0]->value_param()));
-    EXPECT_STREQ(GetTypeName<int>().c_str(), tests[0]->type_param());
+    EXPECT_STREQ(GetTypeName<Types<int>>().c_str(), tests[0]->type_param());
     EXPECT_TRUE(tests[0]->should_run());
     EXPECT_TRUE(tests[0]->result()->Passed());
     EXPECT_EQ(0, tests[0]->result()->test_property_count());
 
     delete[] tests;
-#endif  // GTEST_HAS_TYPED_TEST
     delete[] test_suites;
   }
 };
diff --git a/ext/googletest/googletest/test/gtest_environment_test.cc b/ext/googletest/googletest/test/gtest_environment_test.cc
index 064bfc5..c7facf5 100644
--- a/ext/googletest/googletest/test/gtest_environment_test.cc
+++ b/ext/googletest/googletest/test/gtest_environment_test.cc
@@ -35,10 +35,6 @@
 #include "gtest/gtest.h"
 #include "src/gtest-internal-inl.h"
 
-namespace testing {
-GTEST_DECLARE_string_(filter);
-}
-
 namespace {
 
 enum FailureType {
@@ -174,7 +170,7 @@
 
   // Verifies that RUN_ALL_TESTS() doesn't do global set-up or
   // tear-down when there is no test to run.
-  testing::GTEST_FLAG(filter) = "-*";
+  GTEST_FLAG_SET(filter, "-*");
   Check(RunAllTests(env, NO_FAILURE) == 0,
         "RUN_ALL_TESTS() should return zero, as there is no test to run.");
   Check(!env->set_up_was_run(),
diff --git a/ext/googletest/googletest/test/gtest_help_test.py b/ext/googletest/googletest/test/gtest_help_test.py
index 582d24c..54d4504 100755
--- a/ext/googletest/googletest/test/gtest_help_test.py
+++ b/ext/googletest/googletest/test/gtest_help_test.py
@@ -43,6 +43,8 @@
 
 
 IS_LINUX = os.name == 'posix' and os.uname()[0] == 'Linux'
+IS_GNUHURD = os.name == 'posix' and os.uname()[0] == 'GNU'
+IS_GNUKFREEBSD = os.name == 'posix' and os.uname()[0] == 'GNU/kFreeBSD'
 IS_WINDOWS = os.name == 'nt'
 
 PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath('gtest_help_test_')
@@ -68,6 +70,7 @@
     FLAG_PREFIX + r'shuffle.*' +
     FLAG_PREFIX + r'random_seed=.*' +
     FLAG_PREFIX + r'color=.*' +
+    FLAG_PREFIX + r'brief.*' +
     FLAG_PREFIX + r'print_time.*' +
     FLAG_PREFIX + r'output=.*' +
     FLAG_PREFIX + r'break_on_failure.*' +
@@ -110,7 +113,7 @@
     self.assertEquals(0, exit_code)
     self.assert_(HELP_REGEX.search(output), output)
 
-    if IS_LINUX:
+    if IS_LINUX or IS_GNUHURD or IS_GNUKFREEBSD:
       self.assert_(STREAM_RESULT_TO_FLAG in output, output)
     else:
       self.assert_(STREAM_RESULT_TO_FLAG not in output, output)
diff --git a/ext/googletest/googletest/test/gtest_list_output_unittest.py b/ext/googletest/googletest/test/gtest_list_output_unittest.py
index 3bba7ea..a442fc1 100644
--- a/ext/googletest/googletest/test/gtest_list_output_unittest.py
+++ b/ext/googletest/googletest/test/gtest_list_output_unittest.py
@@ -46,16 +46,42 @@
 GTEST_OUTPUT_FLAG = '--gtest_output'
 
 EXPECTED_XML = """<\?xml version="1.0" encoding="UTF-8"\?>
-<testsuites tests="2" name="AllTests">
+<testsuites tests="16" name="AllTests">
   <testsuite name="FooTest" tests="2">
     <testcase name="Test1" file=".*gtest_list_output_unittest_.cc" line="43" />
     <testcase name="Test2" file=".*gtest_list_output_unittest_.cc" line="45" />
   </testsuite>
+  <testsuite name="FooTestFixture" tests="2">
+    <testcase name="Test3" file=".*gtest_list_output_unittest_.cc" line="48" />
+    <testcase name="Test4" file=".*gtest_list_output_unittest_.cc" line="49" />
+  </testsuite>
+  <testsuite name="TypedTest/0" tests="2">
+    <testcase name="Test7" type_param="int" file=".*gtest_list_output_unittest_.cc" line="60" />
+    <testcase name="Test8" type_param="int" file=".*gtest_list_output_unittest_.cc" line="61" />
+  </testsuite>
+  <testsuite name="TypedTest/1" tests="2">
+    <testcase name="Test7" type_param="bool" file=".*gtest_list_output_unittest_.cc" line="60" />
+    <testcase name="Test8" type_param="bool" file=".*gtest_list_output_unittest_.cc" line="61" />
+  </testsuite>
+  <testsuite name="Single/TypeParameterizedTestSuite/0" tests="2">
+    <testcase name="Test9" type_param="int" file=".*gtest_list_output_unittest_.cc" line="66" />
+    <testcase name="Test10" type_param="int" file=".*gtest_list_output_unittest_.cc" line="67" />
+  </testsuite>
+  <testsuite name="Single/TypeParameterizedTestSuite/1" tests="2">
+    <testcase name="Test9" type_param="bool" file=".*gtest_list_output_unittest_.cc" line="66" />
+    <testcase name="Test10" type_param="bool" file=".*gtest_list_output_unittest_.cc" line="67" />
+  </testsuite>
+  <testsuite name="ValueParam/ValueParamTest" tests="4">
+    <testcase name="Test5/0" value_param="33" file=".*gtest_list_output_unittest_.cc" line="52" />
+    <testcase name="Test5/1" value_param="42" file=".*gtest_list_output_unittest_.cc" line="52" />
+    <testcase name="Test6/0" value_param="33" file=".*gtest_list_output_unittest_.cc" line="53" />
+    <testcase name="Test6/1" value_param="42" file=".*gtest_list_output_unittest_.cc" line="53" />
+  </testsuite>
 </testsuites>
 """
 
 EXPECTED_JSON = """{
-  "tests": 2,
+  "tests": 16,
   "name": "AllTests",
   "testsuites": \[
     {
@@ -73,6 +99,124 @@
           "line": 45
         }
       \]
+    },
+    {
+      "name": "FooTestFixture",
+      "tests": 2,
+      "testsuite": \[
+        {
+          "name": "Test3",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 48
+        },
+        {
+          "name": "Test4",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 49
+        }
+      \]
+    },
+    {
+      "name": "TypedTest\\\\/0",
+      "tests": 2,
+      "testsuite": \[
+        {
+          "name": "Test7",
+          "type_param": "int",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 60
+        },
+        {
+          "name": "Test8",
+          "type_param": "int",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 61
+        }
+      \]
+    },
+    {
+      "name": "TypedTest\\\\/1",
+      "tests": 2,
+      "testsuite": \[
+        {
+          "name": "Test7",
+          "type_param": "bool",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 60
+        },
+        {
+          "name": "Test8",
+          "type_param": "bool",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 61
+        }
+      \]
+    },
+    {
+      "name": "Single\\\\/TypeParameterizedTestSuite\\\\/0",
+      "tests": 2,
+      "testsuite": \[
+        {
+          "name": "Test9",
+          "type_param": "int",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 66
+        },
+        {
+          "name": "Test10",
+          "type_param": "int",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 67
+        }
+      \]
+    },
+    {
+      "name": "Single\\\\/TypeParameterizedTestSuite\\\\/1",
+      "tests": 2,
+      "testsuite": \[
+        {
+          "name": "Test9",
+          "type_param": "bool",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 66
+        },
+        {
+          "name": "Test10",
+          "type_param": "bool",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 67
+        }
+      \]
+    },
+    {
+      "name": "ValueParam\\\\/ValueParamTest",
+      "tests": 4,
+      "testsuite": \[
+        {
+          "name": "Test5\\\\/0",
+          "value_param": "33",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 52
+        },
+        {
+          "name": "Test5\\\\/1",
+          "value_param": "42",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 52
+        },
+        {
+          "name": "Test6\\\\/0",
+          "value_param": "33",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 53
+        },
+        {
+          "name": "Test6\\\\/1",
+          "value_param": "42",
+          "file": ".*gtest_list_output_unittest_.cc",
+          "line": 53
+        }
+      \]
     }
   \]
 }
@@ -114,8 +258,9 @@
     p = gtest_test_utils.Subprocess(
         command, env=environ_copy, working_dir=gtest_test_utils.GetTempDir())
 
-    self.assert_(p.exited)
-    self.assertEquals(0, p.exit_code)
+    self.assertTrue(p.exited)
+    self.assertEqual(0, p.exit_code)
+    self.assertTrue(os.path.isfile(file_path))
     with open(file_path) as f:
       result = f.read()
     return result
@@ -128,7 +273,7 @@
     for actual_line in actual_lines:
       expected_line = expected_lines[line_count]
       expected_line_re = re.compile(expected_line.strip())
-      self.assert_(
+      self.assertTrue(
           expected_line_re.match(actual_line.strip()),
           ('actual output of "%s",\n'
            'which does not match expected regex of "%s"\n'
diff --git a/ext/googletest/googletest/test/gtest_list_output_unittest_.cc b/ext/googletest/googletest/test/gtest_list_output_unittest_.cc
index b1c7b4d..92b9d4f 100644
--- a/ext/googletest/googletest/test/gtest_list_output_unittest_.cc
+++ b/ext/googletest/googletest/test/gtest_list_output_unittest_.cc
@@ -44,6 +44,32 @@
 
 TEST(FooTest, Test2) {}
 
+class FooTestFixture : public ::testing::Test {};
+TEST_F(FooTestFixture, Test3) {}
+TEST_F(FooTestFixture, Test4) {}
+
+class ValueParamTest : public ::testing::TestWithParam<int> {};
+TEST_P(ValueParamTest, Test5) {}
+TEST_P(ValueParamTest, Test6) {}
+INSTANTIATE_TEST_SUITE_P(ValueParam, ValueParamTest, ::testing::Values(33, 42));
+
+template <typename T>
+class TypedTest : public ::testing::Test {};
+typedef testing::Types<int, bool> TypedTestTypes;
+TYPED_TEST_SUITE(TypedTest, TypedTestTypes);
+TYPED_TEST(TypedTest, Test7) {}
+TYPED_TEST(TypedTest, Test8) {}
+
+template <typename T>
+class TypeParameterizedTestSuite : public ::testing::Test {};
+TYPED_TEST_SUITE_P(TypeParameterizedTestSuite);
+TYPED_TEST_P(TypeParameterizedTestSuite, Test9) {}
+TYPED_TEST_P(TypeParameterizedTestSuite, Test10) {}
+REGISTER_TYPED_TEST_SUITE_P(TypeParameterizedTestSuite, Test9, Test10);
+typedef testing::Types<int, bool> TypeParameterizedTestSuiteTypes;  // NOLINT
+INSTANTIATE_TYPED_TEST_SUITE_P(Single, TypeParameterizedTestSuite,
+                               TypeParameterizedTestSuiteTypes);
+
 int main(int argc, char **argv) {
   ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/ext/googletest/googletest/test/gtest_pred_impl_unittest.cc b/ext/googletest/googletest/test/gtest_pred_impl_unittest.cc
index 1afe5e2..bbef994 100644
--- a/ext/googletest/googletest/test/gtest_pred_impl_unittest.cc
+++ b/ext/googletest/googletest/test/gtest_pred_impl_unittest.cc
@@ -27,7 +27,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-// This file is AUTOMATICALLY GENERATED on 01/02/2019 by command
+// This file is AUTOMATICALLY GENERATED on 11/05/2019 by command
 // 'gen_gtest_pred_impl.py 5'.  DO NOT EDIT BY HAND!
 
 // Regression test for gtest_pred_impl.h
@@ -78,9 +78,8 @@
   return v1 > 0;
 }
 
-// The following two functions are needed to circumvent a bug in
-// gcc 2.95.3, which sometimes has problem with the above template
-// function.
+// The following two functions are needed because a compiler doesn't have
+// a context yet to know which template function must be instantiated.
 bool PredFunction1Int(int v1) {
   return v1 > 0;
 }
@@ -465,9 +464,8 @@
   return v1 + v2 > 0;
 }
 
-// The following two functions are needed to circumvent a bug in
-// gcc 2.95.3, which sometimes has problem with the above template
-// function.
+// The following two functions are needed because a compiler doesn't have
+// a context yet to know which template function must be instantiated.
 bool PredFunction2Int(int v1, int v2) {
   return v1 + v2 > 0;
 }
@@ -894,9 +892,8 @@
   return v1 + v2 + v3 > 0;
 }
 
-// The following two functions are needed to circumvent a bug in
-// gcc 2.95.3, which sometimes has problem with the above template
-// function.
+// The following two functions are needed because a compiler doesn't have
+// a context yet to know which template function must be instantiated.
 bool PredFunction3Int(int v1, int v2, int v3) {
   return v1 + v2 + v3 > 0;
 }
@@ -1365,9 +1362,8 @@
   return v1 + v2 + v3 + v4 > 0;
 }
 
-// The following two functions are needed to circumvent a bug in
-// gcc 2.95.3, which sometimes has problem with the above template
-// function.
+// The following two functions are needed because a compiler doesn't have
+// a context yet to know which template function must be instantiated.
 bool PredFunction4Int(int v1, int v2, int v3, int v4) {
   return v1 + v2 + v3 + v4 > 0;
 }
@@ -1878,9 +1874,8 @@
   return v1 + v2 + v3 + v4 + v5 > 0;
 }
 
-// The following two functions are needed to circumvent a bug in
-// gcc 2.95.3, which sometimes has problem with the above template
-// function.
+// The following two functions are needed because a compiler doesn't have
+// a context yet to know which template function must be instantiated.
 bool PredFunction5Int(int v1, int v2, int v3, int v4, int v5) {
   return v1 + v2 + v3 + v4 + v5 > 0;
 }
diff --git a/ext/googletest/googletest/test/gtest_repeat_test.cc b/ext/googletest/googletest/test/gtest_repeat_test.cc
index 7da4a15..c7af3ef 100644
--- a/ext/googletest/googletest/test/gtest_repeat_test.cc
+++ b/ext/googletest/googletest/test/gtest_repeat_test.cc
@@ -35,18 +35,6 @@
 #include "gtest/gtest.h"
 #include "src/gtest-internal-inl.h"
 
-namespace testing {
-
-GTEST_DECLARE_string_(death_test_style);
-GTEST_DECLARE_string_(filter);
-GTEST_DECLARE_int32_(repeat);
-
-}  // namespace testing
-
-using testing::GTEST_FLAG(death_test_style);
-using testing::GTEST_FLAG(filter);
-using testing::GTEST_FLAG(repeat);
-
 namespace {
 
 // We need this when we are testing Google Test itself and therefore
@@ -103,10 +91,10 @@
 TEST(BarDeathTest, ThreadSafeAndFast) {
   g_death_test_count++;
 
-  GTEST_FLAG(death_test_style) = "threadsafe";
+  GTEST_FLAG_SET(death_test_style, "threadsafe");
   EXPECT_DEATH_IF_SUPPORTED(::testing::internal::posix::Abort(), "");
 
-  GTEST_FLAG(death_test_style) = "fast";
+  GTEST_FLAG_SET(death_test_style, "fast");
   EXPECT_DEATH_IF_SUPPORTED(::testing::internal::posix::Abort(), "");
 }
 
@@ -153,7 +141,7 @@
 
 // Tests the behavior of Google Test when --gtest_repeat has the given value.
 void TestRepeat(int repeat) {
-  GTEST_FLAG(repeat) = repeat;
+  GTEST_FLAG_SET(repeat, repeat);
 
   ResetCounts();
   GTEST_CHECK_INT_EQ_(repeat > 0 ? 1 : 0, RUN_ALL_TESTS());
@@ -163,8 +151,8 @@
 // Tests using --gtest_repeat when --gtest_filter specifies an empty
 // set of tests.
 void TestRepeatWithEmptyFilter(int repeat) {
-  GTEST_FLAG(repeat) = repeat;
-  GTEST_FLAG(filter) = "None";
+  GTEST_FLAG_SET(repeat, repeat);
+  GTEST_FLAG_SET(filter, "None");
 
   ResetCounts();
   GTEST_CHECK_INT_EQ_(0, RUN_ALL_TESTS());
@@ -174,8 +162,8 @@
 // Tests using --gtest_repeat when --gtest_filter specifies a set of
 // successful tests.
 void TestRepeatWithFilterForSuccessfulTests(int repeat) {
-  GTEST_FLAG(repeat) = repeat;
-  GTEST_FLAG(filter) = "*-*ShouldFail";
+  GTEST_FLAG_SET(repeat, repeat);
+  GTEST_FLAG_SET(filter, "*-*ShouldFail");
 
   ResetCounts();
   GTEST_CHECK_INT_EQ_(0, RUN_ALL_TESTS());
@@ -190,8 +178,8 @@
 // Tests using --gtest_repeat when --gtest_filter specifies a set of
 // failed tests.
 void TestRepeatWithFilterForFailedTests(int repeat) {
-  GTEST_FLAG(repeat) = repeat;
-  GTEST_FLAG(filter) = "*ShouldFail";
+  GTEST_FLAG_SET(repeat, repeat);
+  GTEST_FLAG_SET(filter, "*ShouldFail");
 
   ResetCounts();
   GTEST_CHECK_INT_EQ_(1, RUN_ALL_TESTS());
diff --git a/ext/googletest/ci/get-nprocessors.sh b/ext/googletest/googletest/test/gtest_skip_check_output_test.py
similarity index 61%
copy from ext/googletest/ci/get-nprocessors.sh
copy to ext/googletest/googletest/test/gtest_skip_check_output_test.py
index 43635e7..14e63ab 100755
--- a/ext/googletest/ci/get-nprocessors.sh
+++ b/ext/googletest/googletest/test/gtest_skip_check_output_test.py
@@ -1,7 +1,6 @@
-#!/usr/bin/env bash
-# Copyright 2017 Google Inc.
-# All Rights Reserved.
+#!/usr/bin/env python
 #
+# Copyright 2019 Google LLC.  All Rights Reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -28,21 +27,33 @@
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Tests Google Test's gtest skip in environment setup  behavior.
 
-# This file is typically sourced by another script.
-# if possible, ask for the precise number of processors,
-# otherwise take 2 processors as reasonable default; see
-# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
-if [ -x /usr/bin/getconf ]; then
-    NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
-else
-    NPROCESSORS=2
-fi
+This script invokes gtest_skip_in_environment_setup_test_ and verifies its
+output.
+"""
 
-# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
-# crashes if parallelized too much (maybe memory consumption problem),
-# so limit to 4 processors for the time being.
-if [ $NPROCESSORS -gt 4 ] ; then
-	echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
-	NPROCESSORS=4
-fi
+import re
+
+import gtest_test_utils
+
+# Path to the gtest_skip_in_environment_setup_test binary
+EXE_PATH = gtest_test_utils.GetTestExecutablePath('gtest_skip_test')
+
+OUTPUT = gtest_test_utils.Subprocess([EXE_PATH]).output
+
+
+# Test.
+class SkipEntireEnvironmentTest(gtest_test_utils.TestCase):
+
+  def testSkipEntireEnvironmentTest(self):
+    self.assertIn('Skipped\nskipping single test\n', OUTPUT)
+    skip_fixture = 'Skipped\nskipping all tests for this fixture\n'
+    self.assertIsNotNone(
+        re.search(skip_fixture + '.*' + skip_fixture, OUTPUT, flags=re.DOTALL),
+        repr(OUTPUT))
+    self.assertNotIn('FAILED', OUTPUT)
+
+
+if __name__ == '__main__':
+  gtest_test_utils.Main()
diff --git a/ext/googletest/googletest/test/gtest_skip_test.cc b/ext/googletest/googletest/test/gtest_skip_test.cc
index 717e105..4a23004 100644
--- a/ext/googletest/googletest/test/gtest_skip_test.cc
+++ b/ext/googletest/googletest/test/gtest_skip_test.cc
@@ -35,7 +35,7 @@
 using ::testing::Test;
 
 TEST(SkipTest, DoesSkip) {
-  GTEST_SKIP();
+  GTEST_SKIP() << "skipping single test";
   EXPECT_EQ(0, 1);
 }
 
diff --git a/ext/googletest/googletest/test/gtest_test_utils.py b/ext/googletest/googletest/test/gtest_test_utils.py
index ef9363c..d0c2446 100755
--- a/ext/googletest/googletest/test/gtest_test_utils.py
+++ b/ext/googletest/googletest/test/gtest_test_utils.py
@@ -217,7 +217,6 @@
       following attributes:
         terminated_by_signal   True if and only if the child process has been
                                terminated by a signal.
-        signal                 Sygnal that terminated the child process.
         exited                 True if and only if the child process exited
                                normally.
         exit_code              The code with which the child process exited.
@@ -289,10 +288,9 @@
       else:  # os.WIFEXITED(ret_code) should return True here.
         self._return_code = os.WEXITSTATUS(ret_code)
 
-    if self._return_code < 0:
+    if bool(self._return_code & 0x80000000):
       self.terminated_by_signal = True
       self.exited = False
-      self.signal = -self._return_code
     else:
       self.terminated_by_signal = False
       self.exited = True
diff --git a/ext/googletest/googletest/test/gtest_throw_on_failure_ex_test.cc b/ext/googletest/googletest/test/gtest_throw_on_failure_ex_test.cc
index 1d95adb..aeead13 100644
--- a/ext/googletest/googletest/test/gtest_throw_on_failure_ex_test.cc
+++ b/ext/googletest/googletest/test/gtest_throw_on_failure_ex_test.cc
@@ -50,7 +50,7 @@
 // Tests that an assertion failure throws a subclass of
 // std::runtime_error.
 void TestFailureThrowsRuntimeError() {
-  testing::GTEST_FLAG(throw_on_failure) = true;
+  GTEST_FLAG_SET(throw_on_failure, true);
 
   // A successful assertion shouldn't throw.
   try {
diff --git a/ext/googletest/googletest/test/gtest_unittest.cc b/ext/googletest/googletest/test/gtest_unittest.cc
index 39749b7..3f2f082 100644
--- a/ext/googletest/googletest/test/gtest_unittest.cc
+++ b/ext/googletest/googletest/test/gtest_unittest.cc
@@ -37,21 +37,19 @@
 // code once "gtest.h" has been #included.
 // Do not move it after other gtest #includes.
 TEST(CommandLineFlagsTest, CanBeAccessedInCodeOnceGTestHIsIncluded) {
-  bool dummy = testing::GTEST_FLAG(also_run_disabled_tests)
-      || testing::GTEST_FLAG(break_on_failure)
-      || testing::GTEST_FLAG(catch_exceptions)
-      || testing::GTEST_FLAG(color) != "unknown"
-      || testing::GTEST_FLAG(filter) != "unknown"
-      || testing::GTEST_FLAG(list_tests)
-      || testing::GTEST_FLAG(output) != "unknown"
-      || testing::GTEST_FLAG(print_time)
-      || testing::GTEST_FLAG(random_seed)
-      || testing::GTEST_FLAG(repeat) > 0
-      || testing::GTEST_FLAG(show_internal_stack_frames)
-      || testing::GTEST_FLAG(shuffle)
-      || testing::GTEST_FLAG(stack_trace_depth) > 0
-      || testing::GTEST_FLAG(stream_result_to) != "unknown"
-      || testing::GTEST_FLAG(throw_on_failure);
+  bool dummy =
+      GTEST_FLAG_GET(also_run_disabled_tests) ||
+      GTEST_FLAG_GET(break_on_failure) || GTEST_FLAG_GET(catch_exceptions) ||
+      GTEST_FLAG_GET(color) != "unknown" || GTEST_FLAG_GET(fail_fast) ||
+      GTEST_FLAG_GET(filter) != "unknown" || GTEST_FLAG_GET(list_tests) ||
+      GTEST_FLAG_GET(output) != "unknown" || GTEST_FLAG_GET(brief) ||
+      GTEST_FLAG_GET(print_time) || GTEST_FLAG_GET(random_seed) ||
+      GTEST_FLAG_GET(repeat) > 0 ||
+      GTEST_FLAG_GET(recreate_environments_when_repeating) ||
+      GTEST_FLAG_GET(show_internal_stack_frames) || GTEST_FLAG_GET(shuffle) ||
+      GTEST_FLAG_GET(stack_trace_depth) > 0 ||
+      GTEST_FLAG_GET(stream_result_to) != "unknown" ||
+      GTEST_FLAG_GET(throw_on_failure);
   EXPECT_TRUE(dummy || !dummy);  // Suppresses warning that dummy is unused.
 }
 
@@ -60,8 +58,10 @@
 #include <string.h>
 #include <time.h>
 
+#include <cstdint>
 #include <map>
 #include <ostream>
+#include <string>
 #include <type_traits>
 #include <unordered_set>
 #include <vector>
@@ -196,35 +196,20 @@
 using testing::EmptyTestEventListener;
 using testing::Environment;
 using testing::FloatLE;
-using testing::GTEST_FLAG(also_run_disabled_tests);
-using testing::GTEST_FLAG(break_on_failure);
-using testing::GTEST_FLAG(catch_exceptions);
-using testing::GTEST_FLAG(color);
-using testing::GTEST_FLAG(death_test_use_fork);
-using testing::GTEST_FLAG(filter);
-using testing::GTEST_FLAG(list_tests);
-using testing::GTEST_FLAG(output);
-using testing::GTEST_FLAG(print_time);
-using testing::GTEST_FLAG(random_seed);
-using testing::GTEST_FLAG(repeat);
-using testing::GTEST_FLAG(show_internal_stack_frames);
-using testing::GTEST_FLAG(shuffle);
-using testing::GTEST_FLAG(stack_trace_depth);
-using testing::GTEST_FLAG(stream_result_to);
-using testing::GTEST_FLAG(throw_on_failure);
 using testing::IsNotSubstring;
 using testing::IsSubstring;
+using testing::kMaxStackTraceDepth;
 using testing::Message;
 using testing::ScopedFakeTestPartResultReporter;
 using testing::StaticAssertTypeEq;
 using testing::Test;
-using testing::TestCase;
 using testing::TestEventListeners;
 using testing::TestInfo;
 using testing::TestPartResult;
 using testing::TestPartResultArray;
 using testing::TestProperty;
 using testing::TestResult;
+using testing::TestSuite;
 using testing::TimeInMillis;
 using testing::UnitTest;
 using testing::internal::AlwaysFalse;
@@ -240,7 +225,6 @@
 using testing::internal::ForEach;
 using testing::internal::FormatEpochTimeInMillisAsIso8601;
 using testing::internal::FormatTimeInMillisAsSeconds;
-using testing::internal::GTestFlagSaver;
 using testing::internal::GetCurrentOsStackTraceExceptTop;
 using testing::internal::GetElementOr;
 using testing::internal::GetNextRandomSeed;
@@ -249,16 +233,18 @@
 using testing::internal::GetTimeInMillis;
 using testing::internal::GetTypeId;
 using testing::internal::GetUnitTestImpl;
-using testing::internal::Int32;
+using testing::internal::GTestFlagSaver;
+using testing::internal::HasDebugStringAndShortDebugString;
 using testing::internal::Int32FromEnvOrDie;
-using testing::internal::IsAProtocolMessage;
 using testing::internal::IsContainer;
 using testing::internal::IsContainerTest;
 using testing::internal::IsNotContainer;
+using testing::internal::kMaxRandomSeed;
+using testing::internal::kTestTypeIdInGoogleTest;
 using testing::internal::NativeArray;
 using testing::internal::OsStackTraceGetter;
 using testing::internal::OsStackTraceGetterInterface;
-using testing::internal::ParseInt32Flag;
+using testing::internal::ParseFlag;
 using testing::internal::RelationToSourceCopy;
 using testing::internal::RelationToSourceReference;
 using testing::internal::ShouldRunTestOnShard;
@@ -271,15 +257,11 @@
 using testing::internal::String;
 using testing::internal::TestEventListenersAccessor;
 using testing::internal::TestResultAccessor;
-using testing::internal::UInt32;
 using testing::internal::UnitTestImpl;
 using testing::internal::WideStringToUtf8;
 using testing::internal::edit_distance::CalculateOptimalEdits;
 using testing::internal::edit_distance::CreateUnifiedDiff;
 using testing::internal::edit_distance::EditType;
-using testing::internal::kMaxRandomSeed;
-using testing::internal::kTestTypeIdInGoogleTest;
-using testing::kMaxStackTraceDepth;
 
 #if GTEST_HAS_STREAM_REDIRECTION
 using testing::internal::CaptureStdout;
@@ -483,28 +465,28 @@
 const TimeInMillis FormatEpochTimeInMillisAsIso8601Test::kMillisPerSec;
 
 TEST_F(FormatEpochTimeInMillisAsIso8601Test, PrintsTwoDigitSegments) {
-  EXPECT_EQ("2011-10-31T18:52:42",
+  EXPECT_EQ("2011-10-31T18:52:42.000",
             FormatEpochTimeInMillisAsIso8601(1320087162 * kMillisPerSec));
 }
 
-TEST_F(FormatEpochTimeInMillisAsIso8601Test, MillisecondsDoNotAffectResult) {
+TEST_F(FormatEpochTimeInMillisAsIso8601Test, IncludesMillisecondsAfterDot) {
   EXPECT_EQ(
-      "2011-10-31T18:52:42",
+      "2011-10-31T18:52:42.234",
       FormatEpochTimeInMillisAsIso8601(1320087162 * kMillisPerSec + 234));
 }
 
 TEST_F(FormatEpochTimeInMillisAsIso8601Test, PrintsLeadingZeroes) {
-  EXPECT_EQ("2011-09-03T05:07:02",
+  EXPECT_EQ("2011-09-03T05:07:02.000",
             FormatEpochTimeInMillisAsIso8601(1315026422 * kMillisPerSec));
 }
 
 TEST_F(FormatEpochTimeInMillisAsIso8601Test, Prints24HourTime) {
-  EXPECT_EQ("2011-09-28T17:08:22",
+  EXPECT_EQ("2011-09-28T17:08:22.000",
             FormatEpochTimeInMillisAsIso8601(1317229702 * kMillisPerSec));
 }
 
 TEST_F(FormatEpochTimeInMillisAsIso8601Test, PrintsEpochStart) {
-  EXPECT_EQ("1970-01-01T00:00:00", FormatEpochTimeInMillisAsIso8601(0));
+  EXPECT_EQ("1970-01-01T00:00:00.000", FormatEpochTimeInMillisAsIso8601(0));
 }
 
 # ifdef __BORLANDC__
@@ -788,7 +770,7 @@
 }
 
 TEST(RandomTest, GeneratesNumbersWithinRange) {
-  const UInt32 kRange = 10000;
+  constexpr uint32_t kRange = 10000;
   testing::internal::Random random(12345);
   for (int i = 0; i < 10; i++) {
     EXPECT_LT(random.Generate(kRange), kRange) << " for iteration " << i;
@@ -801,10 +783,10 @@
 }
 
 TEST(RandomTest, RepeatsWhenReseeded) {
-  const int kSeed = 123;
-  const int kArraySize = 10;
-  const UInt32 kRange = 10000;
-  UInt32 values[kArraySize];
+  constexpr int kSeed = 123;
+  constexpr int kArraySize = 10;
+  constexpr uint32_t kRange = 10000;
+  uint32_t values[kArraySize];
 
   testing::internal::Random random(kSeed);
   for (int i = 0; i < kArraySize; i++) {
@@ -1594,21 +1576,24 @@
   static void SetUpTestSuite() {
     saver_ = new GTestFlagSaver;
 
-    GTEST_FLAG(also_run_disabled_tests) = false;
-    GTEST_FLAG(break_on_failure) = false;
-    GTEST_FLAG(catch_exceptions) = false;
-    GTEST_FLAG(death_test_use_fork) = false;
-    GTEST_FLAG(color) = "auto";
-    GTEST_FLAG(filter) = "";
-    GTEST_FLAG(list_tests) = false;
-    GTEST_FLAG(output) = "";
-    GTEST_FLAG(print_time) = true;
-    GTEST_FLAG(random_seed) = 0;
-    GTEST_FLAG(repeat) = 1;
-    GTEST_FLAG(shuffle) = false;
-    GTEST_FLAG(stack_trace_depth) = kMaxStackTraceDepth;
-    GTEST_FLAG(stream_result_to) = "";
-    GTEST_FLAG(throw_on_failure) = false;
+    GTEST_FLAG_SET(also_run_disabled_tests, false);
+    GTEST_FLAG_SET(break_on_failure, false);
+    GTEST_FLAG_SET(catch_exceptions, false);
+    GTEST_FLAG_SET(death_test_use_fork, false);
+    GTEST_FLAG_SET(color, "auto");
+    GTEST_FLAG_SET(fail_fast, false);
+    GTEST_FLAG_SET(filter, "");
+    GTEST_FLAG_SET(list_tests, false);
+    GTEST_FLAG_SET(output, "");
+    GTEST_FLAG_SET(brief, false);
+    GTEST_FLAG_SET(print_time, true);
+    GTEST_FLAG_SET(random_seed, 0);
+    GTEST_FLAG_SET(repeat, 1);
+    GTEST_FLAG_SET(recreate_environments_when_repeating, true);
+    GTEST_FLAG_SET(shuffle, false);
+    GTEST_FLAG_SET(stack_trace_depth, kMaxStackTraceDepth);
+    GTEST_FLAG_SET(stream_result_to, "");
+    GTEST_FLAG_SET(throw_on_failure, false);
   }
 
   // Restores the Google Test flags that the tests have modified.  This will
@@ -1621,37 +1606,43 @@
   // Verifies that the Google Test flags have their default values, and then
   // modifies each of them.
   void VerifyAndModifyFlags() {
-    EXPECT_FALSE(GTEST_FLAG(also_run_disabled_tests));
-    EXPECT_FALSE(GTEST_FLAG(break_on_failure));
-    EXPECT_FALSE(GTEST_FLAG(catch_exceptions));
-    EXPECT_STREQ("auto", GTEST_FLAG(color).c_str());
-    EXPECT_FALSE(GTEST_FLAG(death_test_use_fork));
-    EXPECT_STREQ("", GTEST_FLAG(filter).c_str());
-    EXPECT_FALSE(GTEST_FLAG(list_tests));
-    EXPECT_STREQ("", GTEST_FLAG(output).c_str());
-    EXPECT_TRUE(GTEST_FLAG(print_time));
-    EXPECT_EQ(0, GTEST_FLAG(random_seed));
-    EXPECT_EQ(1, GTEST_FLAG(repeat));
-    EXPECT_FALSE(GTEST_FLAG(shuffle));
-    EXPECT_EQ(kMaxStackTraceDepth, GTEST_FLAG(stack_trace_depth));
-    EXPECT_STREQ("", GTEST_FLAG(stream_result_to).c_str());
-    EXPECT_FALSE(GTEST_FLAG(throw_on_failure));
+    EXPECT_FALSE(GTEST_FLAG_GET(also_run_disabled_tests));
+    EXPECT_FALSE(GTEST_FLAG_GET(break_on_failure));
+    EXPECT_FALSE(GTEST_FLAG_GET(catch_exceptions));
+    EXPECT_STREQ("auto", GTEST_FLAG_GET(color).c_str());
+    EXPECT_FALSE(GTEST_FLAG_GET(death_test_use_fork));
+    EXPECT_FALSE(GTEST_FLAG_GET(fail_fast));
+    EXPECT_STREQ("", GTEST_FLAG_GET(filter).c_str());
+    EXPECT_FALSE(GTEST_FLAG_GET(list_tests));
+    EXPECT_STREQ("", GTEST_FLAG_GET(output).c_str());
+    EXPECT_FALSE(GTEST_FLAG_GET(brief));
+    EXPECT_TRUE(GTEST_FLAG_GET(print_time));
+    EXPECT_EQ(0, GTEST_FLAG_GET(random_seed));
+    EXPECT_EQ(1, GTEST_FLAG_GET(repeat));
+    EXPECT_TRUE(GTEST_FLAG_GET(recreate_environments_when_repeating));
+    EXPECT_FALSE(GTEST_FLAG_GET(shuffle));
+    EXPECT_EQ(kMaxStackTraceDepth, GTEST_FLAG_GET(stack_trace_depth));
+    EXPECT_STREQ("", GTEST_FLAG_GET(stream_result_to).c_str());
+    EXPECT_FALSE(GTEST_FLAG_GET(throw_on_failure));
 
-    GTEST_FLAG(also_run_disabled_tests) = true;
-    GTEST_FLAG(break_on_failure) = true;
-    GTEST_FLAG(catch_exceptions) = true;
-    GTEST_FLAG(color) = "no";
-    GTEST_FLAG(death_test_use_fork) = true;
-    GTEST_FLAG(filter) = "abc";
-    GTEST_FLAG(list_tests) = true;
-    GTEST_FLAG(output) = "xml:foo.xml";
-    GTEST_FLAG(print_time) = false;
-    GTEST_FLAG(random_seed) = 1;
-    GTEST_FLAG(repeat) = 100;
-    GTEST_FLAG(shuffle) = true;
-    GTEST_FLAG(stack_trace_depth) = 1;
-    GTEST_FLAG(stream_result_to) = "localhost:1234";
-    GTEST_FLAG(throw_on_failure) = true;
+    GTEST_FLAG_SET(also_run_disabled_tests, true);
+    GTEST_FLAG_SET(break_on_failure, true);
+    GTEST_FLAG_SET(catch_exceptions, true);
+    GTEST_FLAG_SET(color, "no");
+    GTEST_FLAG_SET(death_test_use_fork, true);
+    GTEST_FLAG_SET(fail_fast, true);
+    GTEST_FLAG_SET(filter, "abc");
+    GTEST_FLAG_SET(list_tests, true);
+    GTEST_FLAG_SET(output, "xml:foo.xml");
+    GTEST_FLAG_SET(brief, true);
+    GTEST_FLAG_SET(print_time, false);
+    GTEST_FLAG_SET(random_seed, 1);
+    GTEST_FLAG_SET(repeat, 100);
+    GTEST_FLAG_SET(recreate_environments_when_repeating, false);
+    GTEST_FLAG_SET(shuffle, true);
+    GTEST_FLAG_SET(stack_trace_depth, 1);
+    GTEST_FLAG_SET(stream_result_to, "localhost:1234");
+    GTEST_FLAG_SET(throw_on_failure, true);
   }
 
  private:
@@ -1767,29 +1758,29 @@
 }
 #endif  // !GTEST_OS_WINDOWS_MOBILE
 
-// Tests ParseInt32Flag().
+// Tests ParseFlag().
 
 // Tests that ParseInt32Flag() returns false and doesn't change the
 // output value when the flag has wrong format
 TEST(ParseInt32FlagTest, ReturnsFalseForInvalidFlag) {
-  Int32 value = 123;
-  EXPECT_FALSE(ParseInt32Flag("--a=100", "b", &value));
+  int32_t value = 123;
+  EXPECT_FALSE(ParseFlag("--a=100", "b", &value));
   EXPECT_EQ(123, value);
 
-  EXPECT_FALSE(ParseInt32Flag("a=100", "a", &value));
+  EXPECT_FALSE(ParseFlag("a=100", "a", &value));
   EXPECT_EQ(123, value);
 }
 
-// Tests that ParseInt32Flag() returns false and doesn't change the
+// Tests that ParseFlag() returns false and doesn't change the
 // output value when the flag overflows as an Int32.
 TEST(ParseInt32FlagTest, ReturnsDefaultWhenValueOverflows) {
   printf("(expecting 2 warnings)\n");
 
-  Int32 value = 123;
-  EXPECT_FALSE(ParseInt32Flag("--abc=12345678987654321", "abc", &value));
+  int32_t value = 123;
+  EXPECT_FALSE(ParseFlag("--abc=12345678987654321", "abc", &value));
   EXPECT_EQ(123, value);
 
-  EXPECT_FALSE(ParseInt32Flag("--abc=-12345678987654321", "abc", &value));
+  EXPECT_FALSE(ParseFlag("--abc=-12345678987654321", "abc", &value));
   EXPECT_EQ(123, value);
 }
 
@@ -1799,11 +1790,11 @@
 TEST(ParseInt32FlagTest, ReturnsDefaultWhenValueIsInvalid) {
   printf("(expecting 2 warnings)\n");
 
-  Int32 value = 123;
-  EXPECT_FALSE(ParseInt32Flag("--abc=A1", "abc", &value));
+  int32_t value = 123;
+  EXPECT_FALSE(ParseFlag("--abc=A1", "abc", &value));
   EXPECT_EQ(123, value);
 
-  EXPECT_FALSE(ParseInt32Flag("--abc=12X", "abc", &value));
+  EXPECT_FALSE(ParseFlag("--abc=12X", "abc", &value));
   EXPECT_EQ(123, value);
 }
 
@@ -1811,12 +1802,11 @@
 // returns true when the flag represents a valid decimal integer in
 // the range of an Int32.
 TEST(ParseInt32FlagTest, ParsesAndReturnsValidValue) {
-  Int32 value = 123;
-  EXPECT_TRUE(ParseInt32Flag("--" GTEST_FLAG_PREFIX_ "abc=456", "abc", &value));
+  int32_t value = 123;
+  EXPECT_TRUE(ParseFlag("--" GTEST_FLAG_PREFIX_ "abc=456", "abc", &value));
   EXPECT_EQ(456, value);
 
-  EXPECT_TRUE(ParseInt32Flag("--" GTEST_FLAG_PREFIX_ "abc=-789",
-                             "abc", &value));
+  EXPECT_TRUE(ParseFlag("--" GTEST_FLAG_PREFIX_ "abc=-789", "abc", &value));
   EXPECT_EQ(-789, value);
 }
 
@@ -1834,7 +1824,7 @@
 #endif  // !GTEST_OS_WINDOWS_MOBILE
 
 // Tests that Int32FromEnvOrDie() aborts with an error message
-// if the variable is not an Int32.
+// if the variable is not an int32_t.
 TEST(Int32FromEnvOrDieDeathTest, AbortsOnFailure) {
   SetEnv(GTEST_FLAG_PREFIX_UPPER_ "VAR", "xxx");
   EXPECT_DEATH_IF_SUPPORTED(
@@ -1843,7 +1833,7 @@
 }
 
 // Tests that Int32FromEnvOrDie() aborts with an error message
-// if the variable cannot be represented by an Int32.
+// if the variable cannot be represented by an int32_t.
 TEST(Int32FromEnvOrDieDeathTest, AbortsOnInt32Overflow) {
   SetEnv(GTEST_FLAG_PREFIX_UPPER_ "VAR", "1234567891234567891234");
   EXPECT_DEATH_IF_SUPPORTED(
@@ -2759,7 +2749,7 @@
   typedef typename Floating::Bits Bits;
 
   void SetUp() override {
-    const size_t max_ulps = Floating::kMaxUlps;
+    const uint32_t max_ulps = Floating::kMaxUlps;
 
     // The bits that represent 0.0.
     const Bits zero_bits = Floating(0).bits();
@@ -2924,22 +2914,18 @@
 TEST_F(FloatTest, EXPECT_NEAR) {
   EXPECT_NEAR(-1.0f, -1.1f, 0.2f);
   EXPECT_NEAR(2.0f, 3.0f, 1.0f);
-  EXPECT_NONFATAL_FAILURE(EXPECT_NEAR(1.0f,1.5f, 0.25f),  // NOLINT
+  EXPECT_NONFATAL_FAILURE(EXPECT_NEAR(1.0f, 1.5f, 0.25f),  // NOLINT
                           "The difference between 1.0f and 1.5f is 0.5, "
                           "which exceeds 0.25f");
-  // To work around a bug in gcc 2.95.0, there is intentionally no
-  // space after the first comma in the previous line.
 }
 
 // Tests ASSERT_NEAR.
 TEST_F(FloatTest, ASSERT_NEAR) {
   ASSERT_NEAR(-1.0f, -1.1f, 0.2f);
   ASSERT_NEAR(2.0f, 3.0f, 1.0f);
-  EXPECT_FATAL_FAILURE(ASSERT_NEAR(1.0f,1.5f, 0.25f),  // NOLINT
+  EXPECT_FATAL_FAILURE(ASSERT_NEAR(1.0f, 1.5f, 0.25f),  // NOLINT
                        "The difference between 1.0f and 1.5f is 0.5, "
                        "which exceeds 0.25f");
-  // To work around a bug in gcc 2.95.0, there is intentionally no
-  // space after the first comma in the previous line.
 }
 
 // Tests the cases where FloatLE() should succeed.
@@ -3080,8 +3066,13 @@
   EXPECT_NONFATAL_FAILURE(EXPECT_NEAR(1.0, 1.5, 0.25),  // NOLINT
                           "The difference between 1.0 and 1.5 is 0.5, "
                           "which exceeds 0.25");
-  // To work around a bug in gcc 2.95.0, there is intentionally no
-  // space after the first comma in the previous statement.
+  // At this magnitude adjacent doubles are 512.0 apart, so this triggers a
+  // slightly different failure reporting path.
+  EXPECT_NONFATAL_FAILURE(
+      EXPECT_NEAR(4.2934311416234112e+18, 4.2934311416234107e+18, 1.0),
+      "The abs_error parameter 1.0 evaluates to 1 which is smaller than the "
+      "minimum distance between doubles for numbers of this magnitude which is "
+      "512");
 }
 
 // Tests ASSERT_NEAR.
@@ -3091,8 +3082,6 @@
   EXPECT_FATAL_FAILURE(ASSERT_NEAR(1.0, 1.5, 0.25),  // NOLINT
                        "The difference between 1.0 and 1.5 is 0.5, "
                        "which exceeds 0.25");
-  // To work around a bug in gcc 2.95.0, there is intentionally no
-  // space after the first comma in the previous statement.
 }
 
 // Tests the cases where DoubleLE() should succeed.
@@ -3179,8 +3168,6 @@
 
 // Tests that disabled typed tests aren't run.
 
-#if GTEST_HAS_TYPED_TEST
-
 template <typename T>
 class TypedTest : public Test {
 };
@@ -3202,12 +3189,8 @@
   FAIL() << "Unexpected failure: Disabled typed test should not run.";
 }
 
-#endif  // GTEST_HAS_TYPED_TEST
-
 // Tests that disabled type-parameterized tests aren't run.
 
-#if GTEST_HAS_TYPED_TEST_P
-
 template <typename T>
 class TypedTestP : public Test {
 };
@@ -3238,8 +3221,6 @@
 
 INSTANTIATE_TYPED_TEST_SUITE_P(My, DISABLED_TypedTestP, NumericTypes);
 
-#endif  // GTEST_HAS_TYPED_TEST_P
-
 // Tests that assertion macros evaluate their arguments exactly once.
 
 class SingleEvaluationTest : public Test {
@@ -3345,9 +3326,26 @@
 
 #if GTEST_HAS_EXCEPTIONS
 
+#if GTEST_HAS_RTTI
+
+#ifdef _MSC_VER
+#define ERROR_DESC "class std::runtime_error"
+#else
+#define ERROR_DESC "std::runtime_error"
+#endif
+
+#else  // GTEST_HAS_RTTI
+
+#define ERROR_DESC "an std::exception-derived error"
+
+#endif  // GTEST_HAS_RTTI
+
 void ThrowAnInteger() {
   throw 1;
 }
+void ThrowRuntimeError(const char* what) {
+  throw std::runtime_error(what);
+}
 
 // Tests that assertion arguments are evaluated exactly once.
 TEST_F(SingleEvaluationTest, ExceptionTests) {
@@ -3365,31 +3363,38 @@
   }, bool), "throws a different type");
   EXPECT_EQ(2, a_);
 
+  // failed EXPECT_THROW, throws runtime error
+  EXPECT_NONFATAL_FAILURE(EXPECT_THROW({  // NOLINT
+    a_++;
+    ThrowRuntimeError("A description");
+  }, bool), "throws " ERROR_DESC " with description \"A description\"");
+  EXPECT_EQ(3, a_);
+
   // failed EXPECT_THROW, throws nothing
   EXPECT_NONFATAL_FAILURE(EXPECT_THROW(a_++, bool), "throws nothing");
-  EXPECT_EQ(3, a_);
+  EXPECT_EQ(4, a_);
 
   // successful EXPECT_NO_THROW
   EXPECT_NO_THROW(a_++);
-  EXPECT_EQ(4, a_);
+  EXPECT_EQ(5, a_);
 
   // failed EXPECT_NO_THROW
   EXPECT_NONFATAL_FAILURE(EXPECT_NO_THROW({  // NOLINT
     a_++;
     ThrowAnInteger();
   }), "it throws");
-  EXPECT_EQ(5, a_);
+  EXPECT_EQ(6, a_);
 
   // successful EXPECT_ANY_THROW
   EXPECT_ANY_THROW({  // NOLINT
     a_++;
     ThrowAnInteger();
   });
-  EXPECT_EQ(6, a_);
+  EXPECT_EQ(7, a_);
 
   // failed EXPECT_ANY_THROW
   EXPECT_NONFATAL_FAILURE(EXPECT_ANY_THROW(a_++), "it doesn't");
-  EXPECT_EQ(7, a_);
+  EXPECT_EQ(8, a_);
 }
 
 #endif  // GTEST_HAS_EXCEPTIONS
@@ -3734,10 +3739,6 @@
 TEST(AssertionTest, ASSERT_EQ_NULL) {
   // A success.
   const char* p = nullptr;
-  // Some older GCC versions may issue a spurious warning in this or the next
-  // assertion statement. This warning should not be suppressed with
-  // static_cast since the test verifies the ability to use bare NULL as the
-  // expected parameter to the macro.
   ASSERT_EQ(nullptr, p);
 
   // A failure.
@@ -3813,6 +3814,12 @@
       ASSERT_THROW(ThrowAnInteger(), bool),
       "Expected: ThrowAnInteger() throws an exception of type bool.\n"
       "  Actual: it throws a different type.");
+  EXPECT_FATAL_FAILURE(
+      ASSERT_THROW(ThrowRuntimeError("A description"), std::logic_error),
+      "Expected: ThrowRuntimeError(\"A description\") "
+      "throws an exception of type std::logic_error.\n  "
+      "Actual: it throws " ERROR_DESC " "
+      "with description \"A description\".");
 # endif
 
   EXPECT_FATAL_FAILURE(
@@ -3827,6 +3834,11 @@
   EXPECT_FATAL_FAILURE(ASSERT_NO_THROW(ThrowAnInteger()),
                        "Expected: ThrowAnInteger() doesn't throw an exception."
                        "\n  Actual: it throws.");
+  EXPECT_FATAL_FAILURE(ASSERT_NO_THROW(ThrowRuntimeError("A description")),
+                       "Expected: ThrowRuntimeError(\"A description\") "
+                       "doesn't throw an exception.\n  "
+                       "Actual: it throws " ERROR_DESC " "
+                       "with description \"A description\".");
 }
 
 // Tests ASSERT_ANY_THROW.
@@ -4096,11 +4108,13 @@
 
 #endif  // GTEST_OS_WINDOWS
 
-#ifdef __BORLANDC__
-// Silences warnings: "Condition is always true", "Unreachable code"
-# pragma option push -w-ccc -w-rch
+// The following code intentionally tests a suboptimal syntax.
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-else"
+#pragma GCC diagnostic ignored "-Wempty-body"
+#pragma GCC diagnostic ignored "-Wpragmas"
 #endif
-
 // Tests that the assertion macros behave like single statements.
 TEST(AssertionSyntaxTest, BasicAssertionsBehavesLikeSingleStatement) {
   if (AlwaysFalse())
@@ -4120,6 +4134,9 @@
   else
     EXPECT_GT(3, 2) << "";
 }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
 
 #if GTEST_HAS_EXCEPTIONS
 // Tests that the compiler will not complain about unreachable code in the
@@ -4136,6 +4153,17 @@
   EXPECT_NONFATAL_FAILURE(EXPECT_ANY_THROW(n++), "");
 }
 
+TEST(ExpectThrowTest, DoesNotGenerateDuplicateCatchClauseWarning) {
+  EXPECT_THROW(throw std::exception(), std::exception);
+}
+
+// The following code intentionally tests a suboptimal syntax.
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-else"
+#pragma GCC diagnostic ignored "-Wempty-body"
+#pragma GCC diagnostic ignored "-Wpragmas"
+#endif
 TEST(AssertionSyntaxTest, ExceptionAssertionsBehavesLikeSingleStatement) {
   if (AlwaysFalse())
     EXPECT_THROW(ThrowNothing(), bool);
@@ -4161,8 +4189,19 @@
   else
     ;  // NOLINT
 }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
 #endif  // GTEST_HAS_EXCEPTIONS
 
+// The following code intentionally tests a suboptimal syntax.
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-else"
+#pragma GCC diagnostic ignored "-Wempty-body"
+#pragma GCC diagnostic ignored "-Wpragmas"
+#endif
 TEST(AssertionSyntaxTest, NoFatalFailureAssertionsBehavesLikeSingleStatement) {
   if (AlwaysFalse())
     EXPECT_NO_FATAL_FAILURE(FAIL()) << "This should never be executed. "
@@ -4185,6 +4224,9 @@
   else
     ASSERT_NO_FATAL_FAILURE(SUCCEED());
 }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
 
 // Tests that the assertion macros work well with switch statements.
 TEST(AssertionSyntaxTest, WorksWithSwitch) {
@@ -4314,10 +4356,8 @@
 TEST(AssertionWithMessageTest, ASSERT_FLOATING) {
   ASSERT_FLOAT_EQ(1, 1) << "This should succeed.";
   ASSERT_DOUBLE_EQ(1, 1) << "This should succeed.";
-  EXPECT_FATAL_FAILURE(ASSERT_NEAR(1,1.2, 0.1) << "Expect failure.",  // NOLINT
+  EXPECT_FATAL_FAILURE(ASSERT_NEAR(1, 1.2, 0.1) << "Expect failure.",  // NOLINT
                        "Expect failure.");
-  // To work around a bug in gcc 2.95.0, there is intentionally no
-  // space after the first comma in the previous statement.
 }
 
 // Tests using ASSERT_FALSE with a streamed message.
@@ -4458,10 +4498,6 @@
 TEST(ExpectTest, EXPECT_EQ_NULL) {
   // A success.
   const char* p = nullptr;
-  // Some older GCC versions may issue a spurious warning in this or the next
-  // assertion statement. This warning should not be suppressed with
-  // static_cast since the test verifies the ability to use bare NULL as the
-  // expected parameter to the macro.
   EXPECT_EQ(nullptr, p);
 
   // A failure.
@@ -4552,6 +4588,12 @@
   EXPECT_NONFATAL_FAILURE(EXPECT_THROW(ThrowAnInteger(), bool),
                           "Expected: ThrowAnInteger() throws an exception of "
                           "type bool.\n  Actual: it throws a different type.");
+  EXPECT_NONFATAL_FAILURE(EXPECT_THROW(ThrowRuntimeError("A description"),
+                                       std::logic_error),
+                          "Expected: ThrowRuntimeError(\"A description\") "
+                          "throws an exception of type std::logic_error.\n  "
+                          "Actual: it throws " ERROR_DESC " "
+                          "with description \"A description\".");
   EXPECT_NONFATAL_FAILURE(
       EXPECT_THROW(ThrowNothing(), bool),
       "Expected: ThrowNothing() throws an exception of type bool.\n"
@@ -4564,6 +4606,11 @@
   EXPECT_NONFATAL_FAILURE(EXPECT_NO_THROW(ThrowAnInteger()),
                           "Expected: ThrowAnInteger() doesn't throw an "
                           "exception.\n  Actual: it throws.");
+  EXPECT_NONFATAL_FAILURE(EXPECT_NO_THROW(ThrowRuntimeError("A description")),
+                          "Expected: ThrowRuntimeError(\"A description\") "
+                          "doesn't throw an exception.\n  "
+                          "Actual: it throws " ERROR_DESC " "
+                          "with description \"A description\".");
 }
 
 // Tests EXPECT_ANY_THROW.
@@ -5303,7 +5350,7 @@
 TEST_F(TestInfoTest, Names) {
   const TestInfo* const test_info = GetTestInfo("Names");
 
-  ASSERT_STREQ("TestInfoTest", test_info->test_case_name());
+  ASSERT_STREQ("TestInfoTest", test_info->test_suite_name());
   ASSERT_STREQ("Names", test_info->name());
 }
 
@@ -5373,7 +5420,7 @@
 
 // Tests setting up and tearing down a test case.
 // Legacy API is deprecated but still available
-#ifndef REMOVE_LEGACY_TEST_CASEAPI
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 class SetUpTestCaseTest : public Test {
  protected:
   // This will be called once before the first test in this test case
@@ -5432,7 +5479,7 @@
 TEST_F(SetUpTestCaseTest, Test2) {
   EXPECT_STREQ("123", shared_resource_);
 }
-#endif  //  REMOVE_LEGACY_TEST_CASEAPI
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
 
 // Tests SetupTestSuite/TearDown TestSuite
 class SetUpTestSuiteTest : public Test {
@@ -5501,20 +5548,24 @@
 // The Flags struct stores a copy of all Google Test flags.
 struct Flags {
   // Constructs a Flags struct where each flag has its default value.
-  Flags() : also_run_disabled_tests(false),
-            break_on_failure(false),
-            catch_exceptions(false),
-            death_test_use_fork(false),
-            filter(""),
-            list_tests(false),
-            output(""),
-            print_time(true),
-            random_seed(0),
-            repeat(1),
-            shuffle(false),
-            stack_trace_depth(kMaxStackTraceDepth),
-            stream_result_to(""),
-            throw_on_failure(false) {}
+  Flags()
+      : also_run_disabled_tests(false),
+        break_on_failure(false),
+        catch_exceptions(false),
+        death_test_use_fork(false),
+        fail_fast(false),
+        filter(""),
+        list_tests(false),
+        output(""),
+        brief(false),
+        print_time(true),
+        random_seed(0),
+        repeat(1),
+        recreate_environments_when_repeating(true),
+        shuffle(false),
+        stack_trace_depth(kMaxStackTraceDepth),
+        stream_result_to(""),
+        throw_on_failure(false) {}
 
   // Factory methods.
 
@@ -5550,6 +5601,14 @@
     return flags;
   }
 
+  // Creates a Flags struct where the gtest_fail_fast flag has
+  // the given value.
+  static Flags FailFast(bool fail_fast) {
+    Flags flags;
+    flags.fail_fast = fail_fast;
+    return flags;
+  }
+
   // Creates a Flags struct where the gtest_filter flag has the given
   // value.
   static Flags Filter(const char* filter) {
@@ -5574,6 +5633,14 @@
     return flags;
   }
 
+  // Creates a Flags struct where the gtest_brief flag has the given
+  // value.
+  static Flags Brief(bool brief) {
+    Flags flags;
+    flags.brief = brief;
+    return flags;
+  }
+
   // Creates a Flags struct where the gtest_print_time flag has the given
   // value.
   static Flags PrintTime(bool print_time) {
@@ -5584,7 +5651,7 @@
 
   // Creates a Flags struct where the gtest_random_seed flag has the given
   // value.
-  static Flags RandomSeed(Int32 random_seed) {
+  static Flags RandomSeed(int32_t random_seed) {
     Flags flags;
     flags.random_seed = random_seed;
     return flags;
@@ -5592,12 +5659,22 @@
 
   // Creates a Flags struct where the gtest_repeat flag has the given
   // value.
-  static Flags Repeat(Int32 repeat) {
+  static Flags Repeat(int32_t repeat) {
     Flags flags;
     flags.repeat = repeat;
     return flags;
   }
 
+  // Creates a Flags struct where the gtest_recreate_environments_when_repeating
+  // flag has the given value.
+  static Flags RecreateEnvironmentsWhenRepeating(
+      bool recreate_environments_when_repeating) {
+    Flags flags;
+    flags.recreate_environments_when_repeating =
+        recreate_environments_when_repeating;
+    return flags;
+  }
+
   // Creates a Flags struct where the gtest_shuffle flag has the given
   // value.
   static Flags Shuffle(bool shuffle) {
@@ -5608,7 +5685,7 @@
 
   // Creates a Flags struct where the GTEST_FLAG(stack_trace_depth) flag has
   // the given value.
-  static Flags StackTraceDepth(Int32 stack_trace_depth) {
+  static Flags StackTraceDepth(int32_t stack_trace_depth) {
     Flags flags;
     flags.stack_trace_depth = stack_trace_depth;
     return flags;
@@ -5635,14 +5712,17 @@
   bool break_on_failure;
   bool catch_exceptions;
   bool death_test_use_fork;
+  bool fail_fast;
   const char* filter;
   bool list_tests;
   const char* output;
+  bool brief;
   bool print_time;
-  Int32 random_seed;
-  Int32 repeat;
+  int32_t random_seed;
+  int32_t repeat;
+  bool recreate_environments_when_repeating;
   bool shuffle;
-  Int32 stack_trace_depth;
+  int32_t stack_trace_depth;
   const char* stream_result_to;
   bool throw_on_failure;
 };
@@ -5652,20 +5732,23 @@
  protected:
   // Clears the flags before each test.
   void SetUp() override {
-    GTEST_FLAG(also_run_disabled_tests) = false;
-    GTEST_FLAG(break_on_failure) = false;
-    GTEST_FLAG(catch_exceptions) = false;
-    GTEST_FLAG(death_test_use_fork) = false;
-    GTEST_FLAG(filter) = "";
-    GTEST_FLAG(list_tests) = false;
-    GTEST_FLAG(output) = "";
-    GTEST_FLAG(print_time) = true;
-    GTEST_FLAG(random_seed) = 0;
-    GTEST_FLAG(repeat) = 1;
-    GTEST_FLAG(shuffle) = false;
-    GTEST_FLAG(stack_trace_depth) = kMaxStackTraceDepth;
-    GTEST_FLAG(stream_result_to) = "";
-    GTEST_FLAG(throw_on_failure) = false;
+    GTEST_FLAG_SET(also_run_disabled_tests, false);
+    GTEST_FLAG_SET(break_on_failure, false);
+    GTEST_FLAG_SET(catch_exceptions, false);
+    GTEST_FLAG_SET(death_test_use_fork, false);
+    GTEST_FLAG_SET(fail_fast, false);
+    GTEST_FLAG_SET(filter, "");
+    GTEST_FLAG_SET(list_tests, false);
+    GTEST_FLAG_SET(output, "");
+    GTEST_FLAG_SET(brief, false);
+    GTEST_FLAG_SET(print_time, true);
+    GTEST_FLAG_SET(random_seed, 0);
+    GTEST_FLAG_SET(repeat, 1);
+    GTEST_FLAG_SET(recreate_environments_when_repeating, true);
+    GTEST_FLAG_SET(shuffle, false);
+    GTEST_FLAG_SET(stack_trace_depth, kMaxStackTraceDepth);
+    GTEST_FLAG_SET(stream_result_to, "");
+    GTEST_FLAG_SET(throw_on_failure, false);
   }
 
   // Asserts that two narrow or wide string arrays are equal.
@@ -5682,21 +5765,26 @@
   // Verifies that the flag values match the expected values.
   static void CheckFlags(const Flags& expected) {
     EXPECT_EQ(expected.also_run_disabled_tests,
-              GTEST_FLAG(also_run_disabled_tests));
-    EXPECT_EQ(expected.break_on_failure, GTEST_FLAG(break_on_failure));
-    EXPECT_EQ(expected.catch_exceptions, GTEST_FLAG(catch_exceptions));
-    EXPECT_EQ(expected.death_test_use_fork, GTEST_FLAG(death_test_use_fork));
-    EXPECT_STREQ(expected.filter, GTEST_FLAG(filter).c_str());
-    EXPECT_EQ(expected.list_tests, GTEST_FLAG(list_tests));
-    EXPECT_STREQ(expected.output, GTEST_FLAG(output).c_str());
-    EXPECT_EQ(expected.print_time, GTEST_FLAG(print_time));
-    EXPECT_EQ(expected.random_seed, GTEST_FLAG(random_seed));
-    EXPECT_EQ(expected.repeat, GTEST_FLAG(repeat));
-    EXPECT_EQ(expected.shuffle, GTEST_FLAG(shuffle));
-    EXPECT_EQ(expected.stack_trace_depth, GTEST_FLAG(stack_trace_depth));
+              GTEST_FLAG_GET(also_run_disabled_tests));
+    EXPECT_EQ(expected.break_on_failure, GTEST_FLAG_GET(break_on_failure));
+    EXPECT_EQ(expected.catch_exceptions, GTEST_FLAG_GET(catch_exceptions));
+    EXPECT_EQ(expected.death_test_use_fork,
+              GTEST_FLAG_GET(death_test_use_fork));
+    EXPECT_EQ(expected.fail_fast, GTEST_FLAG_GET(fail_fast));
+    EXPECT_STREQ(expected.filter, GTEST_FLAG_GET(filter).c_str());
+    EXPECT_EQ(expected.list_tests, GTEST_FLAG_GET(list_tests));
+    EXPECT_STREQ(expected.output, GTEST_FLAG_GET(output).c_str());
+    EXPECT_EQ(expected.brief, GTEST_FLAG_GET(brief));
+    EXPECT_EQ(expected.print_time, GTEST_FLAG_GET(print_time));
+    EXPECT_EQ(expected.random_seed, GTEST_FLAG_GET(random_seed));
+    EXPECT_EQ(expected.repeat, GTEST_FLAG_GET(repeat));
+    EXPECT_EQ(expected.recreate_environments_when_repeating,
+              GTEST_FLAG_GET(recreate_environments_when_repeating));
+    EXPECT_EQ(expected.shuffle, GTEST_FLAG_GET(shuffle));
+    EXPECT_EQ(expected.stack_trace_depth, GTEST_FLAG_GET(stack_trace_depth));
     EXPECT_STREQ(expected.stream_result_to,
-                 GTEST_FLAG(stream_result_to).c_str());
-    EXPECT_EQ(expected.throw_on_failure, GTEST_FLAG(throw_on_failure));
+                 GTEST_FLAG_GET(stream_result_to).c_str());
+    EXPECT_EQ(expected.throw_on_failure, GTEST_FLAG_GET(throw_on_failure));
   }
 
   // Parses a command line (specified by argc1 and argv1), then
@@ -5772,6 +5860,15 @@
   GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags(), false);
 }
 
+// Tests parsing --gtest_fail_fast.
+TEST_F(ParseFlagsTest, FailFast) {
+  const char* argv[] = {"foo.exe", "--gtest_fail_fast", nullptr};
+
+  const char* argv2[] = {"foo.exe", nullptr};
+
+  GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::FailFast(true), false);
+}
+
 // Tests parsing a bad --gtest_filter flag.
 TEST_F(ParseFlagsTest, FilterBad) {
   const char* argv[] = {"foo.exe", "--gtest_filter", nullptr};
@@ -5971,6 +6068,33 @@
                             Flags::Output("xml:directory/path/"), false);
 }
 
+// Tests having a --gtest_brief flag
+TEST_F(ParseFlagsTest, BriefFlag) {
+  const char* argv[] = {"foo.exe", "--gtest_brief", nullptr};
+
+  const char* argv2[] = {"foo.exe", nullptr};
+
+  GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Brief(true), false);
+}
+
+// Tests having a --gtest_brief flag with a "true" value
+TEST_F(ParseFlagsTest, BriefFlagTrue) {
+  const char* argv[] = {"foo.exe", "--gtest_brief=1", nullptr};
+
+  const char* argv2[] = {"foo.exe", nullptr};
+
+  GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Brief(true), false);
+}
+
+// Tests having a --gtest_brief flag with a "false" value
+TEST_F(ParseFlagsTest, BriefFlagFalse) {
+  const char* argv[] = {"foo.exe", "--gtest_brief=0", nullptr};
+
+  const char* argv2[] = {"foo.exe", nullptr};
+
+  GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Brief(false), false);
+}
+
 // Tests having a --gtest_print_time flag
 TEST_F(ParseFlagsTest, PrintTimeFlag) {
   const char* argv[] = {"foo.exe", "--gtest_print_time", nullptr};
@@ -6034,6 +6158,20 @@
   GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Repeat(1000), false);
 }
 
+// Tests parsing --gtest_recreate_environments_when_repeating
+TEST_F(ParseFlagsTest, RecreateEnvironmentsWhenRepeating) {
+  const char* argv[] = {
+      "foo.exe",
+      "--gtest_recreate_environments_when_repeating=0",
+      nullptr,
+  };
+
+  const char* argv2[] = {"foo.exe", nullptr};
+
+  GTEST_TEST_PARSING_FLAGS_(
+      argv, argv2, Flags::RecreateEnvironmentsWhenRepeating(false), false);
+}
+
 // Tests having a --gtest_also_run_disabled_tests flag
 TEST_F(ParseFlagsTest, AlsoRunDisabledTestsFlag) {
   const char* argv[] = {"foo.exe", "--gtest_also_run_disabled_tests", nullptr};
@@ -6170,7 +6308,7 @@
 #if GTEST_USE_OWN_FLAGFILE_FLAG_
 class FlagfileTest : public ParseFlagsTest {
  public:
-  virtual void SetUp() {
+  void SetUp() override {
     ParseFlagsTest::SetUp();
 
     testdata_path_.Set(internal::FilePath(
@@ -6180,7 +6318,7 @@
     EXPECT_TRUE(testdata_path_.CreateFolder());
   }
 
-  virtual void TearDown() {
+  void TearDown() override {
     testing::internal::posix::RmDir(testdata_path_.c_str());
     ParseFlagsTest::TearDown();
   }
@@ -6277,8 +6415,8 @@
     UnitTest::GetInstance()->current_test_info();
   ASSERT_TRUE(nullptr != test_info)
       << "There is a test running so we should have a valid TestInfo.";
-  EXPECT_STREQ("CurrentTestInfoTest", test_info->test_case_name())
-      << "Expected the name of the currently running test case.";
+  EXPECT_STREQ("CurrentTestInfoTest", test_info->test_suite_name())
+      << "Expected the name of the currently running test suite.";
   EXPECT_STREQ("WorksForFirstTestInATestSuite", test_info->name())
       << "Expected the name of the currently running test.";
 }
@@ -6292,8 +6430,8 @@
     UnitTest::GetInstance()->current_test_info();
   ASSERT_TRUE(nullptr != test_info)
       << "There is a test running so we should have a valid TestInfo.";
-  EXPECT_STREQ("CurrentTestInfoTest", test_info->test_case_name())
-      << "Expected the name of the currently running test case.";
+  EXPECT_STREQ("CurrentTestInfoTest", test_info->test_suite_name())
+      << "Expected the name of the currently running test suite.";
   EXPECT_STREQ("WorksForSecondTestInATestSuite", test_info->name())
       << "Expected the name of the currently running test.";
 }
@@ -6478,7 +6616,7 @@
 // Tests that Google Test correctly decides whether to use colors in the output.
 
 TEST(ColoredOutputTest, UsesColorsWhenGTestColorFlagIsYes) {
-  GTEST_FLAG(color) = "yes";
+  GTEST_FLAG_SET(color, "yes");
 
   SetEnv("TERM", "xterm");  // TERM supports colors.
   EXPECT_TRUE(ShouldUseColor(true));  // Stdout is a TTY.
@@ -6492,18 +6630,18 @@
 TEST(ColoredOutputTest, UsesColorsWhenGTestColorFlagIsAliasOfYes) {
   SetEnv("TERM", "dumb");  // TERM doesn't support colors.
 
-  GTEST_FLAG(color) = "True";
+  GTEST_FLAG_SET(color, "True");
   EXPECT_TRUE(ShouldUseColor(false));  // Stdout is not a TTY.
 
-  GTEST_FLAG(color) = "t";
+  GTEST_FLAG_SET(color, "t");
   EXPECT_TRUE(ShouldUseColor(false));  // Stdout is not a TTY.
 
-  GTEST_FLAG(color) = "1";
+  GTEST_FLAG_SET(color, "1");
   EXPECT_TRUE(ShouldUseColor(false));  // Stdout is not a TTY.
 }
 
 TEST(ColoredOutputTest, UsesNoColorWhenGTestColorFlagIsNo) {
-  GTEST_FLAG(color) = "no";
+  GTEST_FLAG_SET(color, "no");
 
   SetEnv("TERM", "xterm");  // TERM supports colors.
   EXPECT_FALSE(ShouldUseColor(true));  // Stdout is a TTY.
@@ -6517,18 +6655,18 @@
 TEST(ColoredOutputTest, UsesNoColorWhenGTestColorFlagIsInvalid) {
   SetEnv("TERM", "xterm");  // TERM supports colors.
 
-  GTEST_FLAG(color) = "F";
+  GTEST_FLAG_SET(color, "F");
   EXPECT_FALSE(ShouldUseColor(true));  // Stdout is a TTY.
 
-  GTEST_FLAG(color) = "0";
+  GTEST_FLAG_SET(color, "0");
   EXPECT_FALSE(ShouldUseColor(true));  // Stdout is a TTY.
 
-  GTEST_FLAG(color) = "unknown";
+  GTEST_FLAG_SET(color, "unknown");
   EXPECT_FALSE(ShouldUseColor(true));  // Stdout is a TTY.
 }
 
 TEST(ColoredOutputTest, UsesColorsWhenStdoutIsTty) {
-  GTEST_FLAG(color) = "auto";
+  GTEST_FLAG_SET(color, "auto");
 
   SetEnv("TERM", "xterm");  // TERM supports colors.
   EXPECT_FALSE(ShouldUseColor(false));  // Stdout is not a TTY.
@@ -6536,7 +6674,7 @@
 }
 
 TEST(ColoredOutputTest, UsesColorsWhenTermSupportsColors) {
-  GTEST_FLAG(color) = "auto";
+  GTEST_FLAG_SET(color, "auto");
 
 #if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
   // On Windows, we ignore the TERM variable as it's usually not set.
@@ -7081,24 +7219,71 @@
 class ConversionHelperBase {};
 class ConversionHelperDerived : public ConversionHelperBase {};
 
-// Tests that IsAProtocolMessage<T>::value is a compile-time constant.
-TEST(IsAProtocolMessageTest, ValueIsCompileTimeConstant) {
-  GTEST_COMPILE_ASSERT_(IsAProtocolMessage<::proto2::Message>::value,
+struct HasDebugStringMethods {
+  std::string DebugString() const { return ""; }
+  std::string ShortDebugString() const { return ""; }
+};
+
+struct InheritsDebugStringMethods : public HasDebugStringMethods {};
+
+struct WrongTypeDebugStringMethod {
+  std::string DebugString() const { return ""; }
+  int ShortDebugString() const { return 1; }
+};
+
+struct NotConstDebugStringMethod {
+  std::string DebugString() { return ""; }
+  std::string ShortDebugString() const { return ""; }
+};
+
+struct MissingDebugStringMethod {
+  std::string DebugString() { return ""; }
+};
+
+struct IncompleteType;
+
+// Tests that HasDebugStringAndShortDebugString<T>::value is a compile-time
+// constant.
+TEST(HasDebugStringAndShortDebugStringTest, ValueIsCompileTimeConstant) {
+  GTEST_COMPILE_ASSERT_(
+      HasDebugStringAndShortDebugString<HasDebugStringMethods>::value,
+      const_true);
+  GTEST_COMPILE_ASSERT_(
+      HasDebugStringAndShortDebugString<InheritsDebugStringMethods>::value,
+      const_true);
+  GTEST_COMPILE_ASSERT_(HasDebugStringAndShortDebugString<
+                            const InheritsDebugStringMethods>::value,
                         const_true);
-  GTEST_COMPILE_ASSERT_(!IsAProtocolMessage<int>::value, const_false);
+  GTEST_COMPILE_ASSERT_(
+      !HasDebugStringAndShortDebugString<WrongTypeDebugStringMethod>::value,
+      const_false);
+  GTEST_COMPILE_ASSERT_(
+      !HasDebugStringAndShortDebugString<NotConstDebugStringMethod>::value,
+      const_false);
+  GTEST_COMPILE_ASSERT_(
+      !HasDebugStringAndShortDebugString<MissingDebugStringMethod>::value,
+      const_false);
+  GTEST_COMPILE_ASSERT_(
+      !HasDebugStringAndShortDebugString<IncompleteType>::value, const_false);
+  GTEST_COMPILE_ASSERT_(!HasDebugStringAndShortDebugString<int>::value,
+                        const_false);
 }
 
-// Tests that IsAProtocolMessage<T>::value is true when T is
-// proto2::Message or a sub-class of it.
-TEST(IsAProtocolMessageTest, ValueIsTrueWhenTypeIsAProtocolMessage) {
-  EXPECT_TRUE(IsAProtocolMessage< ::proto2::Message>::value);
+// Tests that HasDebugStringAndShortDebugString<T>::value is true when T has
+// needed methods.
+TEST(HasDebugStringAndShortDebugStringTest,
+     ValueIsTrueWhenTypeHasDebugStringAndShortDebugString) {
+  EXPECT_TRUE(
+      HasDebugStringAndShortDebugString<InheritsDebugStringMethods>::value);
 }
 
-// Tests that IsAProtocolMessage<T>::value is false when T is neither
-// ::proto2::Message nor a sub-class of it.
-TEST(IsAProtocolMessageTest, ValueIsFalseWhenTypeIsNotAProtocolMessage) {
-  EXPECT_FALSE(IsAProtocolMessage<int>::value);
-  EXPECT_FALSE(IsAProtocolMessage<const ConversionHelperBase>::value);
+// Tests that HasDebugStringAndShortDebugString<T>::value is false when T
+// doesn't have needed methods.
+TEST(HasDebugStringAndShortDebugStringTest,
+     ValueIsFalseWhenTypeIsNotAProtocolMessage) {
+  EXPECT_FALSE(HasDebugStringAndShortDebugString<int>::value);
+  EXPECT_FALSE(
+      HasDebugStringAndShortDebugString<const ConversionHelperBase>::value);
 }
 
 // Tests GTEST_REMOVE_REFERENCE_AND_CONST_.
@@ -7353,20 +7538,15 @@
 // ElemFromList
 TEST(ElemFromList, Basic) {
   using testing::internal::ElemFromList;
-  using Idx = testing::internal::MakeIndexSequence<3>::type;
+  EXPECT_TRUE(
+      (std::is_same<int, ElemFromList<0, int, double, char>::type>::value));
+  EXPECT_TRUE(
+      (std::is_same<double, ElemFromList<1, int, double, char>::type>::value));
+  EXPECT_TRUE(
+      (std::is_same<char, ElemFromList<2, int, double, char>::type>::value));
   EXPECT_TRUE((
-      std::is_same<int, ElemFromList<0, Idx, int, double, char>::type>::value));
-  EXPECT_TRUE(
-      (std::is_same<double,
-                    ElemFromList<1, Idx, int, double, char>::type>::value));
-  EXPECT_TRUE(
-      (std::is_same<char,
-                    ElemFromList<2, Idx, int, double, char>::type>::value));
-  EXPECT_TRUE(
-      (std::is_same<
-          char, ElemFromList<7, testing::internal::MakeIndexSequence<12>::type,
-                             int, int, int, int, int, int, int, char, int, int,
-                             int, int>::type>::value));
+      std::is_same<char, ElemFromList<7, int, int, int, int, int, int, int,
+                                      char, int, int, int, int>::type>::value));
 }
 
 // FlatTuple
@@ -7378,7 +7558,8 @@
   EXPECT_EQ(0.0, tuple.Get<1>());
   EXPECT_EQ(nullptr, tuple.Get<2>());
 
-  tuple = FlatTuple<int, double, const char*>(7, 3.2, "Foo");
+  tuple = FlatTuple<int, double, const char*>(
+      testing::internal::FlatTupleConstructTag{}, 7, 3.2, "Foo");
   EXPECT_EQ(7, tuple.Get<0>());
   EXPECT_EQ(3.2, tuple.Get<1>());
   EXPECT_EQ(std::string("Foo"), tuple.Get<2>());
@@ -7387,6 +7568,147 @@
   EXPECT_EQ(5.1, tuple.Get<1>());
 }
 
+namespace {
+std::string AddIntToString(int i, const std::string& s) {
+  return s + std::to_string(i);
+}
+}  // namespace
+
+TEST(FlatTuple, Apply) {
+  using testing::internal::FlatTuple;
+
+  FlatTuple<int, std::string> tuple{testing::internal::FlatTupleConstructTag{},
+                                    5, "Hello"};
+
+  // Lambda.
+  EXPECT_TRUE(tuple.Apply([](int i, const std::string& s) -> bool {
+    return i == static_cast<int>(s.size());
+  }));
+
+  // Function.
+  EXPECT_EQ(tuple.Apply(AddIntToString), "Hello5");
+
+  // Mutating operations.
+  tuple.Apply([](int& i, std::string& s) {
+    ++i;
+    s += s;
+  });
+  EXPECT_EQ(tuple.Get<0>(), 6);
+  EXPECT_EQ(tuple.Get<1>(), "HelloHello");
+}
+
+struct ConstructionCounting {
+  ConstructionCounting() { ++default_ctor_calls; }
+  ~ConstructionCounting() { ++dtor_calls; }
+  ConstructionCounting(const ConstructionCounting&) { ++copy_ctor_calls; }
+  ConstructionCounting(ConstructionCounting&&) noexcept { ++move_ctor_calls; }
+  ConstructionCounting& operator=(const ConstructionCounting&) {
+    ++copy_assignment_calls;
+    return *this;
+  }
+  ConstructionCounting& operator=(ConstructionCounting&&) noexcept {
+    ++move_assignment_calls;
+    return *this;
+  }
+
+  static void Reset() {
+    default_ctor_calls = 0;
+    dtor_calls = 0;
+    copy_ctor_calls = 0;
+    move_ctor_calls = 0;
+    copy_assignment_calls = 0;
+    move_assignment_calls = 0;
+  }
+
+  static int default_ctor_calls;
+  static int dtor_calls;
+  static int copy_ctor_calls;
+  static int move_ctor_calls;
+  static int copy_assignment_calls;
+  static int move_assignment_calls;
+};
+
+int ConstructionCounting::default_ctor_calls = 0;
+int ConstructionCounting::dtor_calls = 0;
+int ConstructionCounting::copy_ctor_calls = 0;
+int ConstructionCounting::move_ctor_calls = 0;
+int ConstructionCounting::copy_assignment_calls = 0;
+int ConstructionCounting::move_assignment_calls = 0;
+
+TEST(FlatTuple, ConstructorCalls) {
+  using testing::internal::FlatTuple;
+
+  // Default construction.
+  ConstructionCounting::Reset();
+  { FlatTuple<ConstructionCounting> tuple; }
+  EXPECT_EQ(ConstructionCounting::default_ctor_calls, 1);
+  EXPECT_EQ(ConstructionCounting::dtor_calls, 1);
+  EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 0);
+  EXPECT_EQ(ConstructionCounting::move_ctor_calls, 0);
+  EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 0);
+  EXPECT_EQ(ConstructionCounting::move_assignment_calls, 0);
+
+  // Copy construction.
+  ConstructionCounting::Reset();
+  {
+    ConstructionCounting elem;
+    FlatTuple<ConstructionCounting> tuple{
+        testing::internal::FlatTupleConstructTag{}, elem};
+  }
+  EXPECT_EQ(ConstructionCounting::default_ctor_calls, 1);
+  EXPECT_EQ(ConstructionCounting::dtor_calls, 2);
+  EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 1);
+  EXPECT_EQ(ConstructionCounting::move_ctor_calls, 0);
+  EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 0);
+  EXPECT_EQ(ConstructionCounting::move_assignment_calls, 0);
+
+  // Move construction.
+  ConstructionCounting::Reset();
+  {
+    FlatTuple<ConstructionCounting> tuple{
+        testing::internal::FlatTupleConstructTag{}, ConstructionCounting{}};
+  }
+  EXPECT_EQ(ConstructionCounting::default_ctor_calls, 1);
+  EXPECT_EQ(ConstructionCounting::dtor_calls, 2);
+  EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 0);
+  EXPECT_EQ(ConstructionCounting::move_ctor_calls, 1);
+  EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 0);
+  EXPECT_EQ(ConstructionCounting::move_assignment_calls, 0);
+
+  // Copy assignment.
+  // TODO(ofats): it should be testing assignment operator of FlatTuple, not its
+  // elements
+  ConstructionCounting::Reset();
+  {
+    FlatTuple<ConstructionCounting> tuple;
+    ConstructionCounting elem;
+    tuple.Get<0>() = elem;
+  }
+  EXPECT_EQ(ConstructionCounting::default_ctor_calls, 2);
+  EXPECT_EQ(ConstructionCounting::dtor_calls, 2);
+  EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 0);
+  EXPECT_EQ(ConstructionCounting::move_ctor_calls, 0);
+  EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 1);
+  EXPECT_EQ(ConstructionCounting::move_assignment_calls, 0);
+
+  // Move assignment.
+  // TODO(ofats): it should be testing assignment operator of FlatTuple, not its
+  // elements
+  ConstructionCounting::Reset();
+  {
+    FlatTuple<ConstructionCounting> tuple;
+    tuple.Get<0>() = ConstructionCounting{};
+  }
+  EXPECT_EQ(ConstructionCounting::default_ctor_calls, 2);
+  EXPECT_EQ(ConstructionCounting::dtor_calls, 2);
+  EXPECT_EQ(ConstructionCounting::copy_ctor_calls, 0);
+  EXPECT_EQ(ConstructionCounting::move_ctor_calls, 0);
+  EXPECT_EQ(ConstructionCounting::copy_assignment_calls, 0);
+  EXPECT_EQ(ConstructionCounting::move_assignment_calls, 1);
+
+  ConstructionCounting::Reset();
+}
+
 TEST(FlatTuple, ManyTypes) {
   using testing::internal::FlatTuple;
 
@@ -7439,22 +7761,7 @@
 }
 
 // Tests ad_hoc_test_result().
-
-class AdHocTestResultTest : public testing::Test {
- protected:
-  static void SetUpTestSuite() {
-    FAIL() << "A failure happened inside SetUpTestSuite().";
-  }
-};
-
-TEST_F(AdHocTestResultTest, AdHocTestResultForTestSuiteShowsFailure) {
-  const testing::TestResult& test_result = testing::UnitTest::GetInstance()
-                                               ->current_test_suite()
-                                               ->ad_hoc_test_result();
-  EXPECT_TRUE(test_result.Failed());
-}
-
-TEST_F(AdHocTestResultTest, AdHocTestResultTestForUnitTestDoesNotShowFailure) {
+TEST(AdHocTestResultTest, AdHocTestResultForUnitTestDoesNotShowFailure) {
   const testing::TestResult& test_result =
       testing::UnitTest::GetInstance()->ad_hoc_test_result();
   EXPECT_FALSE(test_result.Failed());
diff --git a/ext/googletest/googletest/test/gtest_xml_outfiles_test.py b/ext/googletest/googletest/test/gtest_xml_outfiles_test.py
index e093f6f..ac66feb 100755
--- a/ext/googletest/googletest/test/gtest_xml_outfiles_test.py
+++ b/ext/googletest/googletest/test/gtest_xml_outfiles_test.py
@@ -42,7 +42,7 @@
 
 EXPECTED_XML_1 = """<?xml version="1.0" encoding="UTF-8"?>
 <testsuites tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*" name="AllTests">
-  <testsuite name="PropertyOne" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="PropertyOne" tests="1" failures="0" skipped="0" disabled="0" errors="0" time="*" timestamp="*">
     <testcase name="TestSomeProperties" status="run" result="completed" time="*" timestamp="*" classname="PropertyOne">
       <properties>
         <property name="SetUpProp" value="1"/>
@@ -56,7 +56,7 @@
 
 EXPECTED_XML_2 = """<?xml version="1.0" encoding="UTF-8"?>
 <testsuites tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*" name="AllTests">
-  <testsuite name="PropertyTwo" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="PropertyTwo" tests="1" failures="0" skipped="0" disabled="0" errors="0" time="*" timestamp="*">
     <testcase name="TestSomeProperties" status="run" result="completed" time="*" timestamp="*" classname="PropertyTwo">
       <properties>
         <property name="SetUpProp" value="2"/>
diff --git a/ext/googletest/googletest/test/gtest_xml_output_unittest.py b/ext/googletest/googletest/test/gtest_xml_output_unittest.py
index 63b1af0..eade7aa 100755
--- a/ext/googletest/googletest/test/gtest_xml_output_unittest.py
+++ b/ext/googletest/googletest/test/gtest_xml_output_unittest.py
@@ -65,11 +65,11 @@
   sys.argv.remove(NO_STACKTRACE_SUPPORT_FLAG)
 
 EXPECTED_NON_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
-<testsuites tests="24" failures="4" disabled="2" errors="0" time="*" timestamp="*" name="AllTests" ad_hoc_property="42">
-  <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+<testsuites tests="26" failures="5" disabled="2" errors="0" time="*" timestamp="*" name="AllTests" ad_hoc_property="42">
+  <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="Succeeds" status="run" result="completed" time="*" timestamp="*" classname="SuccessfulTest"/>
   </testsuite>
-  <testsuite name="FailedTest" tests="1" failures="1" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="FailedTest" tests="1" failures="1" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="Fails" status="run" result="completed" time="*" timestamp="*" classname="FailedTest">
       <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Expected equality of these values:&#x0A;  1&#x0A;  2" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
 Expected equality of these values:
@@ -77,7 +77,7 @@
   2%(stack)s]]></failure>
     </testcase>
   </testsuite>
-  <testsuite name="MixedResultTest" tests="3" failures="1" disabled="1" errors="0" time="*" timestamp="*">
+  <testsuite name="MixedResultTest" tests="3" failures="1" disabled="1" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="Succeeds" status="run" result="completed" time="*" timestamp="*" classname="MixedResultTest"/>
     <testcase name="Fails" status="run" result="completed" time="*" timestamp="*" classname="MixedResultTest">
       <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Expected equality of these values:&#x0A;  1&#x0A;  2" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
@@ -91,27 +91,43 @@
     </testcase>
     <testcase name="DISABLED_test" status="notrun" result="suppressed" time="*" timestamp="*" classname="MixedResultTest"/>
   </testsuite>
-  <testsuite name="XmlQuotingTest" tests="1" failures="1" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="XmlQuotingTest" tests="1" failures="1" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="OutputsCData" status="run" result="completed" time="*" timestamp="*" classname="XmlQuotingTest">
       <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Failed&#x0A;XML output: &lt;?xml encoding=&quot;utf-8&quot;&gt;&lt;top&gt;&lt;![CDATA[cdata text]]&gt;&lt;/top&gt;" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
 Failed
 XML output: <?xml encoding="utf-8"><top><![CDATA[cdata text]]>]]&gt;<![CDATA[</top>%(stack)s]]></failure>
     </testcase>
   </testsuite>
-  <testsuite name="InvalidCharactersTest" tests="1" failures="1" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="InvalidCharactersTest" tests="1" failures="1" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="InvalidCharactersInMessage" status="run" result="completed" time="*" timestamp="*" classname="InvalidCharactersTest">
       <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Failed&#x0A;Invalid characters in brackets []" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
 Failed
 Invalid characters in brackets []%(stack)s]]></failure>
     </testcase>
   </testsuite>
-  <testsuite name="DisabledTest" tests="1" failures="0" disabled="1" errors="0" time="*" timestamp="*">
+  <testsuite name="DisabledTest" tests="1" failures="0" disabled="1" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="DISABLED_test_not_run" status="notrun" result="suppressed" time="*" timestamp="*" classname="DisabledTest"/>
   </testsuite>
-  <testsuite name="SkippedTest" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
-    <testcase name="Skipped" status="run" result="skipped" time="*" timestamp="*" classname="SkippedTest"/>
+  <testsuite name="SkippedTest" tests="3" failures="1" disabled="0" skipped="2" errors="0" time="*" timestamp="*">
+    <testcase name="Skipped" status="run" result="skipped" time="*" timestamp="*" classname="SkippedTest">
+      <skipped message="gtest_xml_output_unittest_.cc:*&#x0A;"><![CDATA[gtest_xml_output_unittest_.cc:*
+%(stack)s]]></skipped>
+    </testcase>
+    <testcase name="SkippedWithMessage" status="run" result="skipped" time="*" timestamp="*" classname="SkippedTest">
+      <skipped message="gtest_xml_output_unittest_.cc:*&#x0A;It is good practice to tell why you skip a test."><![CDATA[gtest_xml_output_unittest_.cc:*
+It is good practice to tell why you skip a test.%(stack)s]]></skipped>
+    </testcase>
+    <testcase name="SkippedAfterFailure" status="run" result="completed" time="*" timestamp="*" classname="SkippedTest">
+      <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Expected equality of these values:&#x0A;  1&#x0A;  2" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
+Expected equality of these values:
+  1
+  2%(stack)s]]></failure>
+      <skipped message="gtest_xml_output_unittest_.cc:*&#x0A;It is good practice to tell why you skip a test."><![CDATA[gtest_xml_output_unittest_.cc:*
+It is good practice to tell why you skip a test.%(stack)s]]></skipped>
+    </testcase>
+
   </testsuite>
-  <testsuite name="PropertyRecordingTest" tests="4" failures="0" disabled="0" errors="0" time="*" timestamp="*" SetUpTestSuite="yes" TearDownTestSuite="aye">
+  <testsuite name="PropertyRecordingTest" tests="4" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*" SetUpTestSuite="yes" TearDownTestSuite="aye">
     <testcase name="OneProperty" status="run" result="completed" time="*" timestamp="*" classname="PropertyRecordingTest">
       <properties>
         <property name="key_1" value="1"/>
@@ -135,7 +151,7 @@
       </properties>
     </testcase>
   </testsuite>
-  <testsuite name="NoFixtureTest" tests="3" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="NoFixtureTest" tests="3" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
      <testcase name="RecordProperty" status="run" result="completed" time="*" timestamp="*" classname="NoFixtureTest">
        <properties>
          <property name="key" value="1"/>
@@ -152,22 +168,22 @@
        </properties>
      </testcase>
   </testsuite>
-  <testsuite name="Single/ValueParamTest" tests="4" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="Single/ValueParamTest" tests="4" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="HasValueParamAttribute/0" value_param="33" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
     <testcase name="HasValueParamAttribute/1" value_param="42" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
     <testcase name="AnotherTestThatHasValueParamAttribute/0" value_param="33" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
     <testcase name="AnotherTestThatHasValueParamAttribute/1" value_param="42" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
   </testsuite>
-  <testsuite name="TypedTest/0" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="TypedTest/0" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="HasTypeParamAttribute" type_param="*" status="run" result="completed" time="*" timestamp="*" classname="TypedTest/0" />
   </testsuite>
-  <testsuite name="TypedTest/1" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="TypedTest/1" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="HasTypeParamAttribute" type_param="*" status="run" result="completed" time="*" timestamp="*" classname="TypedTest/1" />
   </testsuite>
-  <testsuite name="Single/TypeParameterizedTestSuite/0" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="Single/TypeParameterizedTestSuite/0" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="HasTypeParamAttribute" type_param="*" status="run" result="completed" time="*" timestamp="*" classname="Single/TypeParameterizedTestSuite/0" />
   </testsuite>
-  <testsuite name="Single/TypeParameterizedTestSuite/1" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="Single/TypeParameterizedTestSuite/1" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="HasTypeParamAttribute" type_param="*" status="run" result="completed" time="*" timestamp="*" classname="Single/TypeParameterizedTestSuite/1" />
   </testsuite>
 </testsuites>""" % {
@@ -177,7 +193,7 @@
 EXPECTED_FILTERED_TEST_XML = """<?xml version="1.0" encoding="UTF-8"?>
 <testsuites tests="1" failures="0" disabled="0" errors="0" time="*"
             timestamp="*" name="AllTests" ad_hoc_property="42">
-  <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0"
+  <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" skipped="0"
              errors="0" time="*" timestamp="*">
     <testcase name="Succeeds" status="run" result="completed" time="*" timestamp="*" classname="SuccessfulTest"/>
   </testsuite>
@@ -185,25 +201,35 @@
 
 EXPECTED_SHARDED_TEST_XML = """<?xml version="1.0" encoding="UTF-8"?>
 <testsuites tests="3" failures="0" disabled="0" errors="0" time="*" timestamp="*" name="AllTests" ad_hoc_property="42">
-  <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
+  <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
     <testcase name="Succeeds" status="run" result="completed" time="*" timestamp="*" classname="SuccessfulTest"/>
   </testsuite>
-  <testsuite name="PropertyRecordingTest" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*" SetUpTestSuite="yes" TearDownTestSuite="aye">
-    <testcase name="TwoValuesForOneKeyUsesLastValue" status="run" result="completed" time="*" timestamp="*" classname="PropertyRecordingTest">
+  <testsuite name="PropertyRecordingTest" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*" SetUpTestSuite="yes" TearDownTestSuite="aye">
+    <testcase name="IntValuedProperty" status="run" result="completed" time="*" timestamp="*" classname="PropertyRecordingTest">
       <properties>
-        <property name="key_1" value="2"/>
+        <property name="key_int" value="1"/>
       </properties>
     </testcase>
   </testsuite>
-  <testsuite name="Single/ValueParamTest" tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*">
-    <testcase name="AnotherTestThatHasValueParamAttribute/0" value_param="33" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
+  <testsuite name="Single/ValueParamTest" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
+    <testcase name="HasValueParamAttribute/0" value_param="33" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
   </testsuite>
 </testsuites>"""
 
-EXPECTED_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
+EXPECTED_NO_TEST_XML = """<?xml version="1.0" encoding="UTF-8"?>
 <testsuites tests="0" failures="0" disabled="0" errors="0" time="*"
             timestamp="*" name="AllTests">
-</testsuites>"""
+  <testsuite name="NonTestSuiteFailure" tests="1" failures="1" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
+    <testcase name="" status="run" result="completed" time="*" timestamp="*" classname="">
+      <failure message="gtest_no_test_unittest.cc:*&#x0A;Expected equality of these values:&#x0A;  1&#x0A;  2" type=""><![CDATA[gtest_no_test_unittest.cc:*
+Expected equality of these values:
+  1
+  2%(stack)s]]></failure>
+    </testcase>
+  </testsuite>
+</testsuites>""" % {
+    'stack': STACK_TRACE_TEMPLATE
+}
 
 GTEST_PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath(GTEST_PROGRAM_NAME)
 
@@ -226,14 +252,14 @@
       """
       self._TestXmlOutput(GTEST_PROGRAM_NAME, EXPECTED_NON_EMPTY_XML, 1)
 
-  def testEmptyXmlOutput(self):
+  def testNoTestXmlOutput(self):
     """Verifies XML output for a Google Test binary without actual tests.
 
-    Runs a test program that generates an empty XML output, and
-    tests that the XML output is expected.
+    Runs a test program that generates an XML output for a binary without tests,
+    and tests that the XML output is expected.
     """
 
-    self._TestXmlOutput('gtest_no_test_unittest', EXPECTED_EMPTY_XML, 0)
+    self._TestXmlOutput('gtest_no_test_unittest', EXPECTED_NO_TEST_XML, 0)
 
   def testTimestampValue(self):
     """Checks whether the timestamp attribute in the XML output is valid.
diff --git a/ext/googletest/googletest/test/gtest_xml_output_unittest_.cc b/ext/googletest/googletest/test/gtest_xml_output_unittest_.cc
index c95fd66..c0036aa 100644
--- a/ext/googletest/googletest/test/gtest_xml_output_unittest_.cc
+++ b/ext/googletest/googletest/test/gtest_xml_output_unittest_.cc
@@ -74,6 +74,15 @@
   GTEST_SKIP();
 }
 
+TEST_F(SkippedTest, SkippedWithMessage) {
+  GTEST_SKIP() << "It is good practice to tell why you skip a test.";
+}
+
+TEST_F(SkippedTest, SkippedAfterFailure) {
+  EXPECT_EQ(1, 2);
+  GTEST_SKIP() << "It is good practice to tell why you skip a test.";
+}
+
 TEST(MixedResultTest, Succeeds) {
   EXPECT_EQ(1, 1);
   ASSERT_EQ(1, 1);
@@ -154,16 +163,13 @@
 TEST_P(ValueParamTest, AnotherTestThatHasValueParamAttribute) {}
 INSTANTIATE_TEST_SUITE_P(Single, ValueParamTest, Values(33, 42));
 
-#if GTEST_HAS_TYPED_TEST
 // Verifies that the type parameter name is output in the 'type_param'
 // XML attribute for typed tests.
 template <typename T> class TypedTest : public Test {};
 typedef testing::Types<int, long> TypedTestTypes;
 TYPED_TEST_SUITE(TypedTest, TypedTestTypes);
 TYPED_TEST(TypedTest, HasTypeParamAttribute) {}
-#endif
 
-#if GTEST_HAS_TYPED_TEST_P
 // Verifies that the type parameter name is output in the 'type_param'
 // XML attribute for type-parameterized tests.
 template <typename T>
@@ -174,7 +180,6 @@
 typedef testing::Types<int, long> TypeParameterizedTestSuiteTypes;  // NOLINT
 INSTANTIATE_TYPED_TEST_SUITE_P(Single, TypeParameterizedTestSuite,
                                TypeParameterizedTestSuiteTypes);
-#endif
 
 int main(int argc, char** argv) {
   InitGoogleTest(&argc, argv);
diff --git a/ext/googletest/googletest/test/gtest_xml_test_utils.py b/ext/googletest/googletest/test/gtest_xml_test_utils.py
index 9914a49..ec42c62 100755
--- a/ext/googletest/googletest/test/gtest_xml_test_utils.py
+++ b/ext/googletest/googletest/test/gtest_xml_test_utils.py
@@ -70,7 +70,7 @@
     self.assertEquals(expected_node.tagName, actual_node.tagName)
 
     expected_attributes = expected_node.attributes
-    actual_attributes   = actual_node  .attributes
+    actual_attributes = actual_node.attributes
     self.assertEquals(
         expected_attributes.length, actual_attributes.length,
         'attribute numbers differ in element %s:\nExpected: %r\nActual: %r' % (
@@ -78,7 +78,7 @@
             actual_attributes.keys()))
     for i in range(expected_attributes.length):
       expected_attr = expected_attributes.item(i)
-      actual_attr   = actual_attributes.get(expected_attr.name)
+      actual_attr = actual_attributes.get(expected_attr.name)
       self.assert_(
           actual_attr is not None,
           'expected attribute %s not found in element %s' %
@@ -105,6 +105,7 @@
       'testsuite': 'name',
       'testcase': 'name',
       'failure': 'message',
+      'skipped': 'message',
       'property': 'name',
   }
 
@@ -171,7 +172,7 @@
 
     if element.tagName in ('testsuites', 'testsuite', 'testcase'):
       timestamp = element.getAttributeNode('timestamp')
-      timestamp.value = re.sub(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d$',
+      timestamp.value = re.sub(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\d$',
                                '*', timestamp.value)
     if element.tagName in ('testsuites', 'testsuite', 'testcase'):
       time = element.getAttributeNode('time')
@@ -179,7 +180,7 @@
       type_param = element.getAttributeNode('type_param')
       if type_param and type_param.value:
         type_param.value = '*'
-    elif element.tagName == 'failure':
+    elif element.tagName == 'failure' or element.tagName == 'skipped':
       source_line_pat = r'^.*[/\\](.*:)\d+\n'
       # Replaces the source line information with a normalized form.
       message = element.getAttributeNode('message')
diff --git a/ext/googletest/googletest/test/production.h b/ext/googletest/googletest/test/production.h
index 542723b..41a5472 100644
--- a/ext/googletest/googletest/test/production.h
+++ b/ext/googletest/googletest/test/production.h
@@ -30,8 +30,8 @@
 //
 // This is part of the unit test for gtest_prod.h.
 
-#ifndef GTEST_TEST_PRODUCTION_H_
-#define GTEST_TEST_PRODUCTION_H_
+#ifndef GOOGLETEST_TEST_PRODUCTION_H_
+#define GOOGLETEST_TEST_PRODUCTION_H_
 
 #include "gtest/gtest_prod.h"
 
@@ -51,4 +51,4 @@
   int x_;
 };
 
-#endif  // GTEST_TEST_PRODUCTION_H_
+#endif  // GOOGLETEST_TEST_PRODUCTION_H_
diff --git a/ext/googletest/library.json b/ext/googletest/library.json
deleted file mode 100644
index e46fcbd..0000000
--- a/ext/googletest/library.json
+++ /dev/null
@@ -1,59 +0,0 @@
-{
-  "name": "googletest",
-  "keywords": "unittest, unit, test, gtest, gmock",
-  "description": "googletest is a testing framework developed by the Testing Technology team with Google's specific requirements and constraints in mind. No matter whether you work on Linux, Windows, or a Mac, if you write C++ code, googletest can help you. And it supports any kind of tests, not just unit tests.",
-   "license": "BSD-3-Clause",
-  "homepage": "https://github.com/google/googletest/blob/master/README.md",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/google/googletest.git"
-  },
-  "version": "1.8.1",
-  "frameworks": "arduino",
-  "platforms": [
-        "espressif32"
-  ],
-  "export": {
-        "include": [
-            "googlemock/include/*",
-            "googlemock/src/*",
-            "googletest/include/*",
-            "googletest/src/*"
-        ],
-        "exclude": [
-            "ci",
-            "googlemock/build-aux",
-            "googlemock/cmake",
-            "googlemock/make",
-            "googlemock/msvc",
-            "googlemock/scripts",
-            "googlemock/src/gmock-all.cc",
-            "googlemock/src/gmock_main.cc",
-            "googlemock/test",
-            "googlemock/CMakeLists.txt",
-            "googlemock/Makefile.am",
-            "googlemock/configure.ac",
-            "googletest/cmake",
-            "googletest/codegear",
-            "googletest/m4",
-            "googletest/make",
-            "googletest/msvc",
-            "googletest/scripts",
-            "googletest/src/gtest-all.cc",
-            "googletest/src/gtest_main.cc",
-            "googletest/test",
-            "googletest/xcode",
-            "googletest/CMakeLists.txt",
-            "googletest/Makefile.am",
-            "googletest/configure.ac"
-          ]
-  },
-  "build": {
-        "flags": [
-            "-Igooglemock/include",
-            "-Igooglemock",
-            "-Igoogletest/include",
-            "-Igoogletest"
-        ]
-  }
-}
diff --git a/ext/googletest/platformio.ini b/ext/googletest/platformio.ini
deleted file mode 100644
index 3910026..0000000
--- a/ext/googletest/platformio.ini
+++ /dev/null
@@ -1,31 +0,0 @@
-; PlatformIO Project Configuration File
-;
-;   Build options: build flags, source filter
-;   Upload options: custom upload port, speed and extra flags
-;   Library options: dependencies, extra library storages
-;   Advanced options: extra scripting
-;
-; Please visit documentation for the other options and examples
-; http://docs.platformio.org/page/projectconf.html
-
-
-[platformio]
-#src_dir = ./googlemock
-#src_dir = ./googletest
-src_dir = .
-
-[env:googletest_esp32]
-platform = espressif32
-board = esp32dev
-framework = arduino
-build_flags = -I./googletest/include -I./googletest
-src_filter = +<*> -<.git/> -<googlemock> -<googletest/codegear/> -<googletest/samples> -<googletest/test/> -<googletest/xcode> -<googletest/src> +<googletest/src/gtest-all.cc> +<googletest/src/gtest_main.cc>
-upload_speed = 921600
-
-[env:googlemock_esp32]
-platform = espressif32
-board = esp32dev
-framework = arduino
-build_flags = -I./googlemock/include -I./googletest/include -I./googletest -I./googlemock
-src_filter = +<*> -<.git/> -<googletest> -<googlemock/test/> -<googlemock/src> +<googlemock/src/gmock-all.cc> +<googlemock/src/gmock_main.cc> +<googletest/src/gtest-all.cc>
-upload_speed = 921600
diff --git a/ext/iostream3/SConscript b/ext/iostream3/SConscript
index f8b5461..df0b213 100644
--- a/ext/iostream3/SConscript
+++ b/ext/iostream3/SConscript
@@ -37,10 +37,10 @@
 #
 # Authors: Andreas Hansson
 
-Import('main')
+Import('env')
 
-main.Library('iostream3', [main.SharedObject('zfstream.cc')])
+env.Library('iostream3', [env.SharedObject('zfstream.cc')])
 
-main.Prepend(CPPPATH=Dir('.'))
-main.Append(LIBS=['iostream3'])
-main.Prepend(LIBPATH=[Dir('.')])
+env.Prepend(CPPPATH=Dir('.'))
+env.Append(LIBS=['iostream3'])
+env.Prepend(LIBPATH=[Dir('.')])
diff --git a/ext/libelf/SConscript b/ext/libelf/SConscript
index ccc1f64..535e216 100644
--- a/ext/libelf/SConscript
+++ b/ext/libelf/SConscript
@@ -30,7 +30,7 @@
 
 import os, subprocess
 
-Import('main')
+Import('env')
 
 elf_files = []
 def ElfFile(filename):
@@ -97,7 +97,7 @@
 ElfFile('libelf_fsize.c')
 ElfFile('libelf_msize.c')
 
-m4env = main.Clone()
+m4env = env.Clone()
 if m4env['GCC']:
     m4env.Append(CCFLAGS=['-Wno-pointer-sign',
                           '-Wno-unused-but-set-variable',
@@ -146,6 +146,6 @@
 m4env.Command(File('native-elf-format.h'), File('native-elf-format'),
               '${SOURCE} > ${TARGET}')
 
-main.Prepend(CPPPATH=Dir('.'))
-main.Append(LIBS=[File('libelf.a')])
-main.Prepend(LIBPATH=[Dir('.')])
+env.Prepend(CPPPATH=Dir('.'))
+env.Append(LIBS=[File('libelf.a')])
+env.Prepend(LIBPATH=[Dir('.')])
diff --git a/ext/libfdt/SConscript b/ext/libfdt/SConscript
index fb44115..64573b7 100644
--- a/ext/libfdt/SConscript
+++ b/ext/libfdt/SConscript
@@ -28,7 +28,7 @@
 #
 # Authors: Anthony Gutierrez
 
-Import('main')
+Import('env')
 
 fdt_files = []
 
@@ -43,7 +43,7 @@
 FdtFile('fdt_empty_tree.c')
 FdtFile('fdt_strerror.c')
 
-main.Library('fdt', [main.SharedObject(f) for f in fdt_files])
-main.Prepend(CPPPATH=Dir('.'))
-main.Append(LIBS=['fdt'])
-main.Prepend(LIBPATH=[Dir('.')])
+env.Library('fdt', [env.SharedObject(f) for f in fdt_files])
+env.Prepend(CPPPATH=Dir('.'))
+env.Append(LIBS=['fdt'])
+env.Prepend(LIBPATH=[Dir('.')])
diff --git a/ext/nomali/SConscript b/ext/nomali/SConscript
index 2810f6d..b156ab0 100644
--- a/ext/nomali/SConscript
+++ b/ext/nomali/SConscript
@@ -37,11 +37,11 @@
 #
 # Authors: Andreas Sandberg
 
-Import('main')
+Import('env')
 
-main.Prepend(CPPPATH=Dir('./include'))
+env.Prepend(CPPPATH=Dir('./include'))
 
-nomali = main.Clone()
+nomali = env.Clone()
 nomali.Append(CCFLAGS=['-Wno-ignored-qualifiers'])
 
 nomali_sources = [
@@ -60,6 +60,6 @@
 
 nomali.Library('nomali', [  nomali.SharedObject(f) for f in nomali_sources ])
 
-main.Append(LIBS=['nomali'])
-main.Prepend(LIBPATH=[Dir('.')])
+env.Append(LIBS=['nomali'])
+env.Prepend(LIBPATH=[Dir('.')])
 
diff --git a/ext/ply/ANNOUNCE b/ext/ply/ANNOUNCE
index 0a155ce..3e58250 100644
--- a/ext/ply/ANNOUNCE
+++ b/ext/ply/ANNOUNCE
@@ -1,13 +1,12 @@
-March 24, 2009
+February 15, 2018
 
-                  Announcing :  PLY-3.2 (Python Lex-Yacc)
+                  Announcing :  PLY-3.11 (Python Lex-Yacc)
 
                         http://www.dabeaz.com/ply
 
-I'm pleased to announce a significant new update to PLY---a 100% Python
-implementation of the common parsing tools lex and yacc.  PLY-3.2 adds
-compatibility for Python 2.6 and 3.0, provides some new customization
-options, and cleans up a lot of internal implementation details.
+I'm pleased to announce PLY-3.11--a pure Python implementation of the
+common parsing tools lex and yacc.  PLY-3.11 is a minor bug fix
+release.  It supports both Python 2 and Python 3.
 
 If you are new to PLY, here are a few highlights:
 
diff --git a/ext/ply/CHANGES b/ext/ply/CHANGES
index 9d8b25d..4405007 100644
--- a/ext/ply/CHANGES
+++ b/ext/ply/CHANGES
@@ -1,3 +1,341 @@
+Version 3.11
+---------------------
+02/15/18  beazley
+          Fixed some minor bugs related to re flags and token order.  
+          Github pull requests #151 and #153.
+
+02/15/18  beazley
+          Added a set_lexpos() method to grammar symbols.  Github issue #148.
+
+
+04/13/17  beazley
+          Mostly minor bug fixes and small code cleanups.
+
+Version 3.10
+---------------------
+01/31/17: beazley
+          Changed grammar signature computation to not involve hashing
+          functions. Parts are just combined into a big string.
+
+10/07/16: beazley
+          Fixed Issue #101: Incorrect shift-reduce conflict resolution with
+          precedence specifier.
+
+          PLY was incorrectly resolving shift-reduce conflicts in certain
+          cases.  For example, in the example/calc/calc.py example, you 
+          could trigger it doing this:
+
+          calc > -3 - 4            
+          1                         (correct answer should be -7)
+          calc >
+
+          Issue and suggested patch contributed by https://github.com/RomaVis
+           
+Version 3.9
+---------------------
+08/30/16: beazley
+          Exposed the parser state number as the parser.state attribute
+          in productions and error functions. For example:
+
+          def p_somerule(p):
+              '''
+              rule : A B C
+              '''
+              print('State:', p.parser.state)
+
+          May address issue #65 (publish current state in error callback).
+
+08/30/16: beazley 
+          Fixed Issue #88. Python3 compatibility with ply/cpp.
+
+08/30/16: beazley
+          Fixed Issue #93. Ply can crash if SyntaxError is raised inside
+          a production.   Not actually sure if the original implementation
+          worked as documented at all.  Yacc has been modified to follow
+          the spec as outlined in the CHANGES noted for 11/27/07 below.
+
+08/30/16: beazley
+          Fixed Issue #97. Failure with code validation when the original
+          source files aren't present.   Validation step now ignores
+          the missing file.
+
+08/30/16: beazley
+          Minor fixes to version numbers.
+
+Version 3.8
+---------------------
+10/02/15: beazley
+          Fixed issues related to Python 3.5. Patch contributed by Barry Warsaw.
+
+Version 3.7
+---------------------
+08/25/15: beazley
+          Fixed problems when reading table files from pickled data.
+
+05/07/15: beazley
+          Fixed regression in handling of table modules if specified as module
+          objects.   See https://github.com/dabeaz/ply/issues/63
+
+Version 3.6
+---------------------
+04/25/15: beazley
+          If PLY is unable to create the 'parser.out' or 'parsetab.py' files due
+          to permission issues, it now just issues a warning message and
+          continues to operate. This could happen if a module using PLY
+	  is installed in a funny way where tables have to be regenerated, but
+          for whatever reason, the user doesn't have write permission on
+          the directory where PLY wants to put them.  
+
+04/24/15: beazley
+          Fixed some issues related to use of packages and table file
+          modules.  Just to emphasize, PLY now generates its special
+          files such as 'parsetab.py' and 'lextab.py' in the *SAME*
+          directory as the source file that uses lex() and yacc().
+
+	  If for some reason, you want to change the name of the table
+          module, use the tabmodule and lextab options:
+
+             lexer = lex.lex(lextab='spamlextab')
+             parser = yacc.yacc(tabmodule='spamparsetab')
+
+          If you specify a simple name as shown, the module will still be
+          created in the same directory as the file invoking lex() or yacc().
+          If you want the table files to be placed into a different package,
+          then give a fully qualified package name.  For example:
+
+             lexer = lex.lex(lextab='pkgname.files.lextab')
+             parser = yacc.yacc(tabmodule='pkgname.files.parsetab')
+
+          For this to work, 'pkgname.files' must already exist as a valid 
+          Python package (i.e., the directories must already exist and be
+          set up with the proper __init__.py files, etc.).
+
+Version 3.5
+---------------------
+04/21/15: beazley
+          Added support for defaulted_states in the parser.  A
+          defaulted_state is a state where the only legal action is a
+          reduction of a single grammar rule across all valid input
+          tokens.  For such states, the rule is reduced and the
+          reading of the next lookahead token is delayed until it is
+          actually needed at a later point in time.
+
+	  This delay in consuming the next lookahead token is a
+	  potentially important feature in advanced parsing
+	  applications that require tight interaction between the
+	  lexer and the parser.  For example, a grammar rule change
+	  modify the lexer state upon reduction and have such changes
+	  take effect before the next input token is read.
+
+	  *** POTENTIAL INCOMPATIBILITY ***
+	  One potential danger of defaulted_states is that syntax
+	  errors might be deferred to a a later point of processing
+	  than where they were detected in past versions of PLY.
+	  Thus, it's possible that your error handling could change
+	  slightly on the same inputs.  defaulted_states do not change
+	  the overall parsing of the input (i.e., the same grammar is
+	  accepted).
+
+	  If for some reason, you need to disable defaulted states,
+	  you can do this:
+
+              parser = yacc.yacc()
+              parser.defaulted_states = {}
+
+04/21/15: beazley
+          Fixed debug logging in the parser.  It wasn't properly reporting goto states
+          on grammar rule reductions.
+
+04/20/15: beazley
+          Added actions to be defined to character literals (Issue #32).  For example:
+
+              literals = [ '{', '}' ]
+
+              def t_lbrace(t):
+                  r'\{'
+                  # Some action
+                  t.type = '{'
+                  return t
+
+              def t_rbrace(t):
+                  r'\}'
+                  # Some action
+                  t.type = '}'
+                  return t
+
+04/19/15: beazley
+          Import of the 'parsetab.py' file is now constrained to only consider the
+          directory specified by the outputdir argument to yacc().  If not supplied,
+          the import will only consider the directory in which the grammar is defined.
+          This should greatly reduce problems with the wrong parsetab.py file being
+          imported by mistake. For example, if it's found somewhere else on the path
+          by accident.
+
+	  *** POTENTIAL INCOMPATIBILITY ***  It's possible that this might break some
+          packaging/deployment setup if PLY was instructed to place its parsetab.py
+          in a different location.  You'll have to specify a proper outputdir= argument
+          to yacc() to fix this if needed.
+
+04/19/15: beazley
+          Changed default output directory to be the same as that in which the
+          yacc grammar is defined.  If your grammar is in a file 'calc.py',
+          then the parsetab.py and parser.out files should be generated in the
+          same directory as that file.  The destination directory can be changed
+          using the outputdir= argument to yacc().
+
+04/19/15: beazley
+          Changed the parsetab.py file signature slightly so that the parsetab won't
+          regenerate if created on a different major version of Python (ie., a 
+          parsetab created on Python 2 will work with Python 3).
+
+04/16/15: beazley
+          Fixed Issue #44 call_errorfunc() should return the result of errorfunc()
+
+04/16/15: beazley
+          Support for versions of Python <2.7 is officially dropped.  PLY may work, but
+          the unit tests requires Python 2.7 or newer.
+
+04/16/15: beazley
+          Fixed bug related to calling yacc(start=...).   PLY wasn't regenerating the
+          table file correctly for this case.
+
+04/16/15: beazley
+          Added skipped tests for PyPy and Java.  Related to use of Python's -O option.
+
+05/29/13: beazley
+          Added filter to make unit tests pass under 'python -3'.
+          Reported by Neil Muller.
+       
+05/29/13: beazley
+          Fixed CPP_INTEGER regex in ply/cpp.py (Issue 21).
+	  Reported by @vbraun.
+ 
+05/29/13: beazley
+          Fixed yacc validation bugs when from __future__ import unicode_literals
+          is being used.  Reported by Kenn Knowles.
+
+05/29/13: beazley
+          Added support for Travis-CI.  Contributed by Kenn Knowles.
+
+05/29/13: beazley
+          Added a .gitignore file.  Suggested by Kenn Knowles.
+
+05/29/13: beazley
+	  Fixed validation problems for source files that include a 
+          different source code encoding specifier.  Fix relies on
+          the inspect module.  Should work on Python 2.6 and newer.
+          Not sure about older versions of Python.
+          Contributed by Michael Droettboom
+
+05/21/13: beazley
+          Fixed unit tests for yacc to eliminate random failures due to dict hash value
+	  randomization in Python 3.3
+	  Reported by Arfrever
+
+10/15/12: beazley
+          Fixed comment whitespace processing bugs in ply/cpp.py.
+          Reported by Alexei Pososin.
+
+10/15/12: beazley
+          Fixed token names in ply/ctokens.py to match rule names.
+          Reported by Alexei Pososin.
+
+04/26/12: beazley
+          Changes to functions available in panic mode error recover.  In previous versions
+          of PLY, the following global functions were available for use in the p_error() rule:
+
+                 yacc.errok()       # Reset error state
+                 yacc.token()       # Get the next token
+                 yacc.restart()     # Reset the parsing stack
+
+          The use of global variables was problematic for code involving multiple parsers
+          and frankly was a poor design overall.   These functions have been moved to methods
+          of the parser instance created by the yacc() function.   You should write code like
+          this:
+
+                def p_error(p):
+                    ...
+                    parser.errok()
+
+                parser = yacc.yacc()
+
+          *** POTENTIAL INCOMPATIBILITY ***  The original global functions now issue a
+          DeprecationWarning.
+         
+04/19/12: beazley
+          Fixed some problems with line and position tracking and the use of error
+          symbols.   If you have a grammar rule involving an error rule like this:
+
+               def p_assignment_bad(p):
+                   '''assignment : location EQUALS error SEMI'''
+                   ...
+
+          You can now do line and position tracking on the error token.  For example:
+
+               def p_assignment_bad(p):
+                   '''assignment : location EQUALS error SEMI'''
+                   start_line = p.lineno(3)
+                   start_pos  = p.lexpos(3)
+
+          If the trackng=True option is supplied to parse(), you can additionally get
+          spans:
+
+               def p_assignment_bad(p):
+                   '''assignment : location EQUALS error SEMI'''
+                   start_line, end_line = p.linespan(3)
+                   start_pos, end_pos = p.lexspan(3)
+
+          Note that error handling is still a hairy thing in PLY. This won't work
+          unless your lexer is providing accurate information.   Please report bugs.
+          Suggested by a bug reported by Davis Herring.
+              
+04/18/12: beazley
+          Change to doc string handling in lex module.  Regex patterns are now first
+          pulled from a function's .regex attribute.  If that doesn't exist, then
+	  .doc is checked as a fallback.   The @TOKEN decorator now sets the .regex
+	  attribute of a function instead of its doc string.
+	  Changed suggested by Kristoffer Ellersgaard Koch.
+
+04/18/12: beazley
+          Fixed issue #1: Fixed _tabversion. It should use __tabversion__ instead of __version__
+          Reported by Daniele Tricoli
+
+04/18/12: beazley
+          Fixed issue #8: Literals empty list causes IndexError
+          Reported by Walter Nissen.
+
+04/18/12: beazley
+          Fixed issue #12: Typo in code snippet in documentation
+          Reported by florianschanda.
+
+04/18/12: beazley
+          Fixed issue #10: Correctly escape t_XOREQUAL pattern.
+          Reported by Andy Kittner.
+
+Version 3.4
+---------------------
+02/17/11: beazley
+          Minor patch to make cpp.py compatible with Python 3.  Note: This
+          is an experimental file not currently used by the rest of PLY.
+
+02/17/11: beazley
+          Fixed setup.py trove classifiers to properly list PLY as
+          Python 3 compatible.
+
+01/02/11: beazley
+          Migration of repository to github.
+
+Version 3.3
+-----------------------------
+08/25/09: beazley
+          Fixed issue 15 related to the set_lineno() method in yacc.  Reported by
+	  mdsherry.
+
+08/25/09: beazley
+          Fixed a bug related to regular expression compilation flags not being
+          properly stored in lextab.py files created by the lexer when running
+          in optimize mode.  Reported by Bruce Frederiksen.
+
 
 Version 3.2
 -----------------------------
diff --git a/ext/ply/MANIFEST.in b/ext/ply/MANIFEST.in
new file mode 100644
index 0000000..0d37431
--- /dev/null
+++ b/ext/ply/MANIFEST.in
@@ -0,0 +1,8 @@
+recursive-include example *
+recursive-include doc *
+recursive-include test *
+include ANNOUNCE
+include README.md
+include CHANGES
+include TODO
+global-exclude *.pyc
diff --git a/ext/ply/PKG-INFO b/ext/ply/PKG-INFO
new file mode 100644
index 0000000..f2d8c8a
--- /dev/null
+++ b/ext/ply/PKG-INFO
@@ -0,0 +1,23 @@
+Metadata-Version: 1.1
+Name: ply
+Version: 3.11
+Summary: Python Lex & Yacc
+Home-page: http://www.dabeaz.com/ply/
+Author: David Beazley
+Author-email: dave@dabeaz.com
+License: BSD
+Description-Content-Type: UNKNOWN
+Description: 
+        PLY is yet another implementation of lex and yacc for Python. Some notable
+        features include the fact that its implemented entirely in Python and it
+        uses LALR(1) parsing which is efficient and well suited for larger grammars.
+        
+        PLY provides most of the standard lex/yacc features including support for empty 
+        productions, precedence rules, error recovery, and support for ambiguous grammars. 
+        
+        PLY is extremely easy to use and provides very extensive error checking. 
+        It is compatible with both Python 2 and Python 3.
+        
+Platform: UNKNOWN
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 2
diff --git a/ext/ply/README b/ext/ply/README.md
similarity index 64%
rename from ext/ply/README
rename to ext/ply/README.md
index d3b785f..05df32a 100644
--- a/ext/ply/README
+++ b/ext/ply/README.md
@@ -1,6 +1,8 @@
-PLY (Python Lex-Yacc)                   Version 3.2
+# PLY (Python Lex-Yacc)                   Version 3.11
 
-Copyright (C) 2001-2009,
+[![Build Status](https://travis-ci.org/dabeaz/ply.svg?branch=master)](https://travis-ci.org/dabeaz/ply)
+
+Copyright (C) 2001-2018
 David M. Beazley (Dabeaz LLC)
 All rights reserved.
 
@@ -96,7 +98,7 @@
 
 Requirements
 ============
-PLY requires the use of Python 2.2 or greater.  However, you should
+PLY requires the use of Python 2.6 or greater.  However, you should
 use the latest Python release if possible.  It should work on just
 about any platform.  PLY has been tested with both CPython and Jython.
 It also seems to work with IronPython.
@@ -112,7 +114,11 @@
 Ullman.  The topics found in "Lex & Yacc" by Levine, Mason, and Brown
 may also be useful.
 
-A Google group for PLY can be found at
+The GitHub page for PLY can be found at:
+
+     https://github.com/dabeaz/ply
+
+An old and relatively inactive discussion group for PLY is found at:
 
      http://groups.google.com/group/ply-hack
 
@@ -130,7 +136,7 @@
 Special Note for PLY-3.0
 ========================
 PLY-3.0 the first PLY release to support Python 3. However, backwards
-compatibility with Python 2.2 is still preserved. PLY provides dual
+compatibility with Python 2.6 is still preserved. PLY provides dual
 Python 2/3 compatibility by restricting its implementation to a common
 subset of basic language features. You should not convert PLY using
 2to3--it is not necessary and may in fact break the implementation.
@@ -141,109 +147,109 @@
 Here is a simple example showing a PLY implementation of a calculator
 with variables.
 
-# -----------------------------------------------------------------------------
-# calc.py
-#
-# A simple calculator with variables.
-# -----------------------------------------------------------------------------
+    # -----------------------------------------------------------------------------
+    # calc.py
+    #
+    # A simple calculator with variables.
+    # -----------------------------------------------------------------------------
 
-tokens = (
-    'NAME','NUMBER',
-    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
-    'LPAREN','RPAREN',
-    )
+    tokens = (
+        'NAME','NUMBER',
+        'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
+        'LPAREN','RPAREN',
+        )
 
-# Tokens
+    # Tokens
 
-t_PLUS    = r'\+'
-t_MINUS   = r'-'
-t_TIMES   = r'\*'
-t_DIVIDE  = r'/'
-t_EQUALS  = r'='
-t_LPAREN  = r'\('
-t_RPAREN  = r'\)'
-t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+    t_PLUS    = r'\+'
+    t_MINUS   = r'-'
+    t_TIMES   = r'\*'
+    t_DIVIDE  = r'/'
+    t_EQUALS  = r'='
+    t_LPAREN  = r'\('
+    t_RPAREN  = r'\)'
+    t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
 
-def t_NUMBER(t):
-    r'\d+'
-    t.value = int(t.value)
-    return t
+    def t_NUMBER(t):
+        r'\d+'
+        t.value = int(t.value)
+        return t
 
-# Ignored characters
-t_ignore = " \t"
+    # Ignored characters
+    t_ignore = " \t"
 
-def t_newline(t):
-    r'\n+'
-    t.lexer.lineno += t.value.count("\n")
-    
-def t_error(t):
-    print "Illegal character '%s'" % t.value[0]
-    t.lexer.skip(1)
-    
-# Build the lexer
-import ply.lex as lex
-lex.lex()
+    def t_newline(t):
+        r'\n+'
+        t.lexer.lineno += t.value.count("\n")
 
-# Precedence rules for the arithmetic operators
-precedence = (
-    ('left','PLUS','MINUS'),
-    ('left','TIMES','DIVIDE'),
-    ('right','UMINUS'),
-    )
+    def t_error(t):
+        print("Illegal character '%s'" % t.value[0])
+        t.lexer.skip(1)
 
-# dictionary of names (for storing variables)
-names = { }
+    # Build the lexer
+    import ply.lex as lex
+    lex.lex()
 
-def p_statement_assign(p):
-    'statement : NAME EQUALS expression'
-    names[p[1]] = p[3]
+    # Precedence rules for the arithmetic operators
+    precedence = (
+        ('left','PLUS','MINUS'),
+        ('left','TIMES','DIVIDE'),
+        ('right','UMINUS'),
+        )
 
-def p_statement_expr(p):
-    'statement : expression'
-    print p[1]
+    # dictionary of names (for storing variables)
+    names = { }
 
-def p_expression_binop(p):
-    '''expression : expression PLUS expression
-                  | expression MINUS expression
-                  | expression TIMES expression
-                  | expression DIVIDE expression'''
-    if p[2] == '+'  : p[0] = p[1] + p[3]
-    elif p[2] == '-': p[0] = p[1] - p[3]
-    elif p[2] == '*': p[0] = p[1] * p[3]
-    elif p[2] == '/': p[0] = p[1] / p[3]
+    def p_statement_assign(p):
+        'statement : NAME EQUALS expression'
+        names[p[1]] = p[3]
 
-def p_expression_uminus(p):
-    'expression : MINUS expression %prec UMINUS'
-    p[0] = -p[2]
+    def p_statement_expr(p):
+        'statement : expression'
+        print(p[1])
 
-def p_expression_group(p):
-    'expression : LPAREN expression RPAREN'
-    p[0] = p[2]
+    def p_expression_binop(p):
+        '''expression : expression PLUS expression
+                      | expression MINUS expression
+                      | expression TIMES expression
+                      | expression DIVIDE expression'''
+        if p[2] == '+'  : p[0] = p[1] + p[3]
+        elif p[2] == '-': p[0] = p[1] - p[3]
+        elif p[2] == '*': p[0] = p[1] * p[3]
+        elif p[2] == '/': p[0] = p[1] / p[3]
 
-def p_expression_number(p):
-    'expression : NUMBER'
-    p[0] = p[1]
+    def p_expression_uminus(p):
+        'expression : MINUS expression %prec UMINUS'
+        p[0] = -p[2]
 
-def p_expression_name(p):
-    'expression : NAME'
-    try:
-        p[0] = names[p[1]]
-    except LookupError:
-        print "Undefined name '%s'" % p[1]
-        p[0] = 0
+    def p_expression_group(p):
+        'expression : LPAREN expression RPAREN'
+        p[0] = p[2]
 
-def p_error(p):
-    print "Syntax error at '%s'" % p.value
+    def p_expression_number(p):
+        'expression : NUMBER'
+        p[0] = p[1]
 
-import ply.yacc as yacc
-yacc.yacc()
+    def p_expression_name(p):
+        'expression : NAME'
+        try:
+            p[0] = names[p[1]]
+        except LookupError:
+            print("Undefined name '%s'" % p[1])
+            p[0] = 0
 
-while 1:
-    try:
-        s = raw_input('calc > ')
-    except EOFError:
-        break
-    yacc.parse(s)
+    def p_error(p):
+        print("Syntax error at '%s'" % p.value)
+
+    import ply.yacc as yacc
+    yacc.yacc()
+
+    while True:
+        try:
+            s = raw_input('calc > ')   # use input() on Python 3
+        except EOFError:
+            break
+        yacc.parse(s)
 
 
 Bug Reports and Patches
@@ -252,12 +258,10 @@
 for Python.  As a general rule, I don't spend huge amounts of time
 working on it unless I receive very specific bug reports and/or
 patches to fix problems. I also try to incorporate submitted feature
-requests and enhancements into each new version.  To contact me about
-bugs and/or new features, please send email to dave@dabeaz.com.
-
-In addition there is a Google group for discussing PLY related issues at
-
-    http://groups.google.com/group/ply-hack
+requests and enhancements into each new version.  Please visit the PLY
+github page at https://github.com/dabeaz/ply to submit issues and pull
+requests.  To contact me about bugs and/or new features, please send
+email to dave@dabeaz.com.
  
 -- Dave
 
diff --git a/ext/ply/doc/internal.html b/ext/ply/doc/internal.html
index 3fabfe2..57e87df 100644
--- a/ext/ply/doc/internal.html
+++ b/ext/ply/doc/internal.html
@@ -12,7 +12,7 @@
 </b>
 
 <p>
-<b>PLY Version: 3.0</b>
+<b>PLY Version: 3.11</b>
 <p>
 
 <!-- INDEX -->
diff --git a/ext/ply/doc/ply.html b/ext/ply/doc/ply.html
index 3345e79..b35ba44 100644
--- a/ext/ply/doc/ply.html
+++ b/ext/ply/doc/ply.html
@@ -12,13 +12,13 @@
 </b>
 
 <p>
-<b>PLY Version: 3.0</b>
+<b>PLY Version: 3.11</b>
 <p>
 
 <!-- INDEX -->
 <div class="sectiontoc">
 <ul>
-<li><a href="#ply_nn1">Preface and Requirements</a>
+<li><a href="#ply_nn0">Preface and Requirements</a>
 <li><a href="#ply_nn1">Introduction</a>
 <li><a href="#ply_nn2">PLY Overview</a>
 <li><a href="#ply_nn3">Lex</a>
@@ -32,8 +32,9 @@
 <li><a href="#ply_nn10">Ignored characters</a>
 <li><a href="#ply_nn11">Literal characters</a>
 <li><a href="#ply_nn12">Error handling</a>
+<li><a href="#ply_nn14">EOF Handling</a>
 <li><a href="#ply_nn13">Building and using the lexer</a>
-<li><a href="#ply_nn14">The @TOKEN decorator</a>
+<li><a href="#ply_nn14b">The @TOKEN decorator</a>
 <li><a href="#ply_nn15">Optimized mode</a>
 <li><a href="#ply_nn16">Debugging</a>
 <li><a href="#ply_nn17">Alternative specification of lexers</a>
@@ -41,7 +42,7 @@
 <li><a href="#ply_nn19">Lexer cloning</a>
 <li><a href="#ply_nn20">Internal lexer state</a>
 <li><a href="#ply_nn21">Conditional lexing and start conditions</a>
-<li><a href="#ply_nn21">Miscellaneous Issues</a>
+<li><a href="#ply_nn21b">Miscellaneous Issues</a>
 </ul>
 <li><a href="#ply_nn22">Parsing basics</a>
 <li><a href="#ply_nn23">Yacc</a>
@@ -49,29 +50,31 @@
 <li><a href="#ply_nn24">An example</a>
 <li><a href="#ply_nn25">Combining Grammar Rule Functions</a>
 <li><a href="#ply_nn26">Character Literals</a>
-<li><a href="#ply_nn26">Empty Productions</a>
+<li><a href="#ply_nn26b">Empty Productions</a>
 <li><a href="#ply_nn28">Changing the starting symbol</a>
 <li><a href="#ply_nn27">Dealing With Ambiguous Grammars</a>
-<li><a href="#ply_nn28">The parser.out file</a>
+<li><a href="#ply_nn28b">The parser.out file</a>
 <li><a href="#ply_nn29">Syntax Error Handling</a>
 <ul>
 <li><a href="#ply_nn30">Recovery and resynchronization with error rules</a>
 <li><a href="#ply_nn31">Panic mode recovery</a>
-<li><a href="#ply_nn35">Signaling an error from a production</a>
+<li><a href="#ply_nn35">Signalling an error from a production</a>
+<li><a href="#ply_nn38">When Do Syntax Errors Get Reported</a>
 <li><a href="#ply_nn32">General comments on error handling</a>
 </ul>
 <li><a href="#ply_nn33">Line Number and Position Tracking</a>
 <li><a href="#ply_nn34">AST Construction</a>
-<li><a href="#ply_nn35">Embedded Actions</a>
+<li><a href="#ply_nn35b">Embedded Actions</a>
 <li><a href="#ply_nn36">Miscellaneous Yacc Notes</a>
 </ul>
 <li><a href="#ply_nn37">Multiple Parsers and Lexers</a>
-<li><a href="#ply_nn38">Using Python's Optimized Mode</a>
+<li><a href="#ply_nn38b">Using Python's Optimized Mode</a>
 <li><a href="#ply_nn44">Advanced Debugging</a>
 <ul>
 <li><a href="#ply_nn45">Debugging the lex() and yacc() commands</a>
 <li><a href="#ply_nn46">Run-time Debugging</a>
 </ul>
+<li><a href="#ply_nn49">Packaging Advice</a>
 <li><a href="#ply_nn39">Where to go from here?</a>
 </ul>
 </div>
@@ -79,7 +82,10 @@
 
 
 
-<H2><a name="ply_nn1"></a>1. Preface and Requirements</H2>
+
+
+
+<H2><a name="ply_nn0"></a>1. Preface and Requirements</H2>
 
 
 <p>
@@ -90,12 +96,8 @@
 </p>
 
 <p>
-PLY-3.0 is compatible with both Python 2 and Python 3.  Be aware that
-Python 3 support is new and has not been extensively tested (although
-all of the examples and unit tests pass under Python 3.0).   If you are
-using Python 2, you should try to use Python 2.4 or newer.  Although PLY
-works with versions as far back as Python 2.2, some of its optional features
-require more modern library modules.
+PLY-3.5 is compatible with both Python 2 and Python 3.  If you are using
+Python 2, you have to use Python 2.6 or newer.
 </p>
 
 <H2><a name="ply_nn1"></a>2. Introduction</H2>
@@ -111,19 +113,7 @@
 
 <p>
 Early versions of PLY were developed to support an Introduction to
-Compilers Course I taught in 2001 at the University of Chicago.  In this course,
-students built a fully functional compiler for a simple Pascal-like
-language.  Their compiler, implemented entirely in Python, had to
-include lexical analysis, parsing, type checking, type inference,
-nested scoping, and code generation for the SPARC processor.
-Approximately 30 different compiler implementations were completed in
-this course.  Most of PLY's interface and operation has been influenced by common
-usability problems encountered by students.   Since 2001, PLY has
-continued to be improved as feedback has been received from users.
-PLY-3.0 represents a major refactoring of the original implementation
-with an eye towards future enhancements.
-
-<p>
+Compilers Course I taught in 2001 at the University of Chicago. 
 Since PLY was primarily developed as an instructional tool, you will
 find it to be fairly picky about token and grammar rule
 specification. In part, this
@@ -137,10 +127,10 @@
 fully capable lex/yacc implementation written entirely in Python.
 
 <p>
-The rest of this document assumes that you are somewhat familar with
+The rest of this document assumes that you are somewhat familiar with
 parsing theory, syntax directed translation, and the use of compiler
 construction tools such as lex and yacc in other programming
-languages. If you are unfamilar with these topics, you will probably
+languages. If you are unfamiliar with these topics, you will probably
 want to consult an introductory text such as "Compilers: Principles,
 Techniques, and Tools", by Aho, Sethi, and Ullman.  O'Reilly's "Lex
 and Yacc" by John Levine may also be handy.  In fact, the O'Reilly book can be
@@ -149,13 +139,14 @@
 <H2><a name="ply_nn2"></a>3. PLY Overview</H2>
 
 
+<p>
 PLY consists of two separate modules; <tt>lex.py</tt> and
 <tt>yacc.py</tt>, both of which are found in a Python package
 called <tt>ply</tt>. The <tt>lex.py</tt> module is used to break input text into a
 collection of tokens specified by a collection of regular expression
 rules.  <tt>yacc.py</tt> is used to recognize language syntax that has
-been specified in the form of a context free grammar. <tt>yacc.py</tt> uses LR parsing and generates its parsing tables
-using either the LALR(1) (the default) or SLR table generation algorithms.
+been specified in the form of a context free grammar.
+</p>
 
 <p>
 The two tools are meant to work together.  Specifically,
@@ -171,7 +162,7 @@
 Like its Unix counterpart, <tt>yacc.py</tt> provides most of the
 features you expect including extensive error checking, grammar
 validation, support for empty productions, error tokens, and ambiguity
-resolution via precedence rules.  In fact, everything that is possible in traditional yacc 
+resolution via precedence rules.  In fact, almost everything that is possible in traditional yacc 
 should be supported in PLY.
 
 <p>
@@ -282,7 +273,7 @@
 
 # Error handling rule
 def t_error(t):
-    print "Illegal character '%s'" % t.value[0]
+    print("Illegal character '%s'" % t.value[0])
     t.lexer.skip(1)
 
 # Build the lexer
@@ -310,8 +301,9 @@
 # Tokenize
 while True:
     tok = lexer.token()
-    if not tok: break      # No more input
-    print tok
+    if not tok: 
+        break      # No more input
+    print(tok)
 </pre>
 </blockquote>
 
@@ -338,7 +330,7 @@
 <blockquote>
 <pre>
 for tok in lexer:
-    print tok
+    print(tok)
 </pre>
 </blockquote>
 
@@ -353,8 +345,9 @@
 # Tokenize
 while True:
     tok = lexer.token()
-    if not tok: break      # No more input
-    print tok.type, tok.value, tok.line, tok.lexpos
+    if not tok: 
+        break      # No more input
+    print(tok.type, tok.value, tok.lineno, tok.lexpos)
 </pre>
 </blockquote>
 
@@ -367,10 +360,12 @@
 <H3><a name="ply_nn5"></a>4.2 The tokens list</H3>
 
 
+<p>
 All lexers must provide a list <tt>tokens</tt> that defines all of the possible token
 names that can be produced by the lexer.  This list is always required
 and is used to perform a variety of validation checks.  The tokens list is also used by the
 <tt>yacc.py</tt> module to identify terminals.
+</p>
 
 <p>
 In the example, the following code specified the token names:
@@ -392,7 +387,7 @@
 <H3><a name="ply_nn6"></a>4.3 Specification of tokens</H3>
 
 
-Each token is specified by writing a regular expression rule.  Each of these rules are
+Each token is specified by writing a regular expression rule compatible with Python's <tt>re</tt> module.  Each of these rules
 are defined by  making declarations with a special prefix <tt>t_</tt> to indicate that it
 defines a token.  For simple tokens, the regular expression can
 be specified as strings such as this (note: Python raw strings are used since they are the
@@ -429,8 +424,17 @@
 function, the token is simply discarded and the next token read.
 
 <p>
-Internally, <tt>lex.py</tt> uses the <tt>re</tt> module to do its patten matching.  When building the master regular expression,
+Internally, <tt>lex.py</tt> uses the <tt>re</tt> module to do its pattern matching.  Patterns are compiled
+using the <tt>re.VERBOSE</tt> flag which can be used to help readability.  However, be aware that unescaped
+whitespace is ignored and comments are allowed in this mode.  If your pattern involves whitespace, make sure you
+use <tt>\s</tt>.  If you need to match the <tt>#</tt> character, use <tt>[#]</tt>.
+</p>
+
+<p>
+When building the master regular expression,
 rules are added in the following order:
+</p>
+
 <p>
 <ol>
 <li>All tokens defined by functions are added in the same order as they appear in the lexer file.
@@ -548,21 +552,18 @@
 After the line number is updated, the token is simply discarded since nothing is returned.
 
 <p>
-<tt>lex.py</tt> does not perform and kind of automatic column tracking.  However, it does record positional
+<tt>lex.py</tt> does not perform any kind of automatic column tracking.  However, it does record positional
 information related to each token in the <tt>lexpos</tt> attribute.   Using this, it is usually possible to compute 
 column information as a separate step.   For instance, just count backwards until you reach a newline.
 
 <blockquote>
 <pre>
-# Compute column. 
+# Compute column.
 #     input is the input text string
 #     token is a token instance
-def find_column(input,token):
-    last_cr = input.rfind('\n',0,token.lexpos)
-    if last_cr < 0:
-	last_cr = 0
-    column = (token.lexpos - last_cr) + 1
-    return column
+def find_column(input, token):
+    line_start = input.rfind('\n', 0, token.lexpos) + 1
+    return (token.lexpos - line_start) + 1
 </pre>
 </blockquote>
 
@@ -580,6 +581,15 @@
 similar to <tt>t_newline()</tt>, the use of <tt>t_ignore</tt> provides substantially better
 lexing performance because it is handled as a special case and is checked in a much
 more efficient manner than the normal regular expression rules.
+</p>
+
+<p>
+The characters given in <tt>t_ignore</tt> are not ignored when such characters are part of
+other regular expression patterns.  For example, if you had a rule to capture quoted text,
+that pattern can include the ignored characters (which will be captured in the normal way).  The
+main purpose of <tt>t_ignore</tt> is to ignore whitespace and other padding between the
+tokens that you actually want to parse.
+</p>
 
 <H3><a name="ply_nn11"></a>4.8 Literal characters</H3>
 
@@ -604,14 +614,38 @@
 A literal character is simply a single character that is returned "as is" when encountered by the lexer.  Literals are checked
 after all of the defined regular expression rules.  Thus, if a rule starts with one of the literal characters, it will always 
 take precedence.
+
 <p>
 When a literal token is returned, both its <tt>type</tt> and <tt>value</tt> attributes are set to the character itself. For example, <tt>'+'</tt>.
+</p>
+
+<p>
+It's possible to write token functions that perform additional actions
+when literals are matched.  However, you'll need to set the token type
+appropriately. For example:
+</p>
+
+<blockquote>
+<pre>
+literals = [ '{', '}' ]
+
+def t_lbrace(t):
+    r'\{'
+    t.type = '{'      # Set token type to the expected literal
+    return t
+
+def t_rbrace(t):
+    r'\}'
+    t.type = '}'      # Set token type to the expected literal
+    return t
+</pre>
+</blockquote>
 
 <H3><a name="ply_nn12"></a>4.9 Error handling</H3>
 
 
 <p>
-Finally, the <tt>t_error()</tt>
+The <tt>t_error()</tt>
 function is used to handle lexing errors that occur when illegal
 characters are detected.  In this case, the <tt>t.value</tt> attribute contains the
 rest of the input string that has not been tokenized.  In the example, the error function
@@ -621,49 +655,67 @@
 <pre>
 # Error handling rule
 def t_error(t):
-    print "Illegal character '%s'" % t.value[0]
+    print("Illegal character '%s'" % t.value[0])
     t.lexer.skip(1)
 </pre>
 </blockquote>
 
 In this case, we simply print the offending character and skip ahead one character by calling <tt>t.lexer.skip(1)</tt>.
 
-<H3><a name="ply_nn13"></a>4.10 Building and using the lexer</H3>
+<H3><a name="ply_nn14"></a>4.10 EOF Handling</H3>
 
 
 <p>
-To build the lexer, the function <tt>lex.lex()</tt> is used.  This function
-uses Python reflection (or introspection) to read the the regular expression rules
+The <tt>t_eof()</tt> function is used to handle an end-of-file (EOF) condition in the input.   As input, it
+receives a token type <tt>'eof'</tt> with the <tt>lineno</tt> and <tt>lexpos</tt> attributes set appropriately.
+The main use of this function is provide more input to the lexer so that it can continue to parse.  Here is an
+example of how this works:
+</p>
+
+<blockquote>
+<pre>
+# EOF handling rule
+def t_eof(t):
+    # Get more input (Example)
+    more = raw_input('... ')
+    if more:
+        self.lexer.input(more)
+        return self.lexer.token()
+    return None
+</pre>
+</blockquote>
+
+<p>
+The EOF function should return the next available token (by calling <tt>self.lexer.token())</tt> or <tt>None</tt> to
+indicate no more data.   Be aware that setting more input with the <tt>self.lexer.input()</tt> method does
+NOT reset the lexer state or the <tt>lineno</tt> attribute used for position tracking.   The <tt>lexpos</tt> 
+attribute is reset so be aware of that if you're using it in error reporting.
+</p>
+
+<H3><a name="ply_nn13"></a>4.11 Building and using the lexer</H3>
+
+
+<p>
+To build the lexer, the function <tt>lex.lex()</tt> is used.  For example:</p>
+
+<blockquote>
+<pre>
+lexer = lex.lex()
+</pre>
+</blockquote>
+
+<p>This function
+uses Python reflection (or introspection) to read the regular expression rules
 out of the calling context and build the lexer. Once the lexer has been built, two methods can
 be used to control the lexer.
-
+</p>
 <ul>
 <li><tt>lexer.input(data)</tt>.   Reset the lexer and store a new input string.
 <li><tt>lexer.token()</tt>.  Return the next token.  Returns a special <tt>LexToken</tt> instance on success or
 None if the end of the input text has been reached.
 </ul>
 
-The preferred way to use PLY is to invoke the above methods directly on the lexer object returned by the
-<tt>lex()</tt> function.   The legacy interface to PLY involves module-level functions <tt>lex.input()</tt> and <tt>lex.token()</tt>.
-For example:
-
-<blockquote>
-<pre>
-lex.lex()
-lex.input(sometext)
-while 1:
-    tok = lex.token()
-    if not tok: break
-    print tok
-</pre>
-</blockquote>
-
-<p>
-In this example, the module-level functions <tt>lex.input()</tt> and <tt>lex.token()</tt> are bound to the <tt>input()</tt> 
-and <tt>token()</tt> methods of the last lexer created by the lex module.    This interface may go away at some point so
-it's probably best not to use it.
-
-<H3><a name="ply_nn14"></a>4.11 The @TOKEN decorator</H3>
+<H3><a name="ply_nn14b"></a>4.12 The @TOKEN decorator</H3>
 
 
 In some applications, you may want to define build tokens from as a series of
@@ -695,22 +747,11 @@
 </pre>
 </blockquote>
 
-This will attach <tt>identifier</tt> to the docstring for <tt>t_ID()</tt> allowing <tt>lex.py</tt> to work normally.  An alternative
-approach this problem is to set the docstring directly like this:
+<p>
+This will attach <tt>identifier</tt> to the docstring for <tt>t_ID()</tt> allowing <tt>lex.py</tt> to work normally. 
+</p>
 
-<blockquote>
-<pre>
-def t_ID(t):
-    ...
-
-t_ID.__doc__ = identifier
-</pre>
-</blockquote>
-
-<b>NOTE:</b> Use of <tt>@TOKEN</tt> requires Python-2.4 or newer.  If you're concerned about backwards compatibility with older
-versions of Python, use the alternative approach of setting the docstring directly.
-
-<H3><a name="ply_nn15"></a>4.12 Optimized mode</H3>
+<H3><a name="ply_nn15"></a>4.13 Optimized mode</H3>
 
 
 For improved performance, it may be desirable to use Python's
@@ -727,8 +768,9 @@
 </blockquote>
 
 Next, run Python in its normal operating mode.  When you do
-this, <tt>lex.py</tt> will write a file called <tt>lextab.py</tt> to
-the current directory.  This file contains all of the regular
+this, <tt>lex.py</tt> will write a file called <tt>lextab.py</tt> in
+the same directory as the module containing the lexer specification.
+This file contains all of the regular
 expression rules and tables used during lexing.  On subsequent
 executions,
 <tt>lextab.py</tt> will simply be imported to build the lexer.  This
@@ -736,7 +778,8 @@
 works in Python's optimized mode.
 
 <p>
-To change the name of the lexer-generated file, use the <tt>lextab</tt> keyword argument.  For example:
+To change the name of the lexer-generated module, use the <tt>lextab</tt> keyword argument.  For example:
+</p>
 
 <blockquote>
 <pre>
@@ -747,7 +790,7 @@
 When running in optimized mode, it is important to note that lex disables most error checking.  Thus, this is really only recommended
 if you're sure everything is working correctly and you're ready to start releasing production code.
 
-<H3><a name="ply_nn16"></a>4.13 Debugging</H3>
+<H3><a name="ply_nn16"></a>4.14 Debugging</H3>
 
 
 For the purpose of debugging, you can run <tt>lex()</tt> in a debugging mode as follows:
@@ -779,7 +822,7 @@
 Please refer to the "Debugging" section near the end for some more advanced details 
 of debugging.
 
-<H3><a name="ply_nn17"></a>4.14 Alternative specification of lexers</H3>
+<H3><a name="ply_nn17"></a>4.15 Alternative specification of lexers</H3>
 
 
 As shown in the example, lexers are specified all within one Python module.   If you want to
@@ -830,7 +873,7 @@
 
 # Error handling rule
 def t_error(t):
-    print "Illegal character '%s'" % t.value[0]
+    print("Illegal character '%s'" % t.value[0])
     t.lexer.skip(1)
 </pre>
 </blockquote>
@@ -860,7 +903,7 @@
 <pre>
 import ply.lex as lex
 
-class MyLexer:
+class MyLexer(object):
     # List of token names.   This is always required
     tokens = (
        'NUMBER',
@@ -897,7 +940,7 @@
 
     # Error handling rule
     def t_error(self,t):
-        print "Illegal character '%s'" % t.value[0]
+        print("Illegal character '%s'" % t.value[0])
         t.lexer.skip(1)
 
     <b># Build the lexer
@@ -908,9 +951,10 @@
     def test(self,data):
         self.lexer.input(data)
         while True:
-             tok = lexer.token()
-             if not tok: break
-             print tok
+             tok = self.lexer.token()
+             if not tok: 
+                 break
+             print(tok)
 
 # Build the lexer and try it out
 m = MyLexer()
@@ -928,7 +972,7 @@
 When using the <tt>module</tt> option to <tt>lex()</tt>, PLY collects symbols
 from the underlying object using the <tt>dir()</tt> function. There is no
 direct access to the <tt>__dict__</tt> attribute of the object supplied as a 
-module value.
+module value. </p>
 
 <P>
 Finally, if you want to keep things nicely encapsulated, but don't want to use a 
@@ -974,7 +1018,7 @@
 
     # Error handling rule
     def t_error(t):
-        print "Illegal character '%s'" % t.value[0]
+        print("Illegal character '%s'" % t.value[0])
         t.lexer.skip(1)
 
     # Build the lexer from my environment and return it    
@@ -982,8 +1026,13 @@
 </pre>
 </blockquote>
 
+<p>
+<b>Important note:</b> If you are defining a lexer using a class or closure, be aware that PLY still requires you to only
+define a single lexer per module (source file).   There are extensive validation/error checking parts of the PLY that 
+may falsely report error messages if you don't follow this rule.
+</p>
 
-<H3><a name="ply_nn18"></a>4.15 Maintaining state</H3>
+<H3><a name="ply_nn18"></a>4.16 Maintaining state</H3>
 
 
 In your lexer, you may want to maintain a variety of state
@@ -1033,7 +1082,7 @@
 internal attributes of the lexer (with the exception of <tt>lineno</tt>) have names that are prefixed
 by <tt>lex</tt> (e.g., <tt>lexdata</tt>,<tt>lexpos</tt>, etc.).  Thus,
 it is perfectly safe to store attributes in the lexer that
-don't have names starting with that prefix or a name that conlicts with one of the
+don't have names starting with that prefix or a name that conflicts with one of the
 predefined methods (e.g., <tt>input()</tt>, <tt>token()</tt>, etc.).
 
 <p>
@@ -1080,7 +1129,7 @@
 </pre>
 </blockquote>
 
-<H3><a name="ply_nn19"></a>4.16 Lexer cloning</H3>
+<H3><a name="ply_nn19"></a>4.17 Lexer cloning</H3>
 
 
 <p>
@@ -1105,7 +1154,7 @@
 
 <p>
 Creating a clone is different than calling <tt>lex.lex()</tt> in that
-PLY doesn't regenerate any of the internal tables or regular expressions.  So,
+PLY doesn't regenerate any of the internal tables or regular expressions.
 
 <p>
 Special considerations need to be made when cloning lexers that also
@@ -1129,7 +1178,7 @@
 that reuses the regular expressions and environment of another lexer.  If you
 need to make a totally new copy of a lexer, then call <tt>lex()</tt> again.
 
-<H3><a name="ply_nn20"></a>4.17 Internal lexer state</H3>
+<H3><a name="ply_nn20"></a>4.18 Internal lexer state</H3>
 
 
 A Lexer object <tt>lexer</tt> has a number of internal attributes that may be useful in certain
@@ -1167,7 +1216,7 @@
 Note: This attribute is only updated when tokens are defined and processed by functions.  
 </blockquote>
 
-<H3><a name="ply_nn21"></a>4.18 Conditional lexing and start conditions</H3>
+<H3><a name="ply_nn21"></a>4.19 Conditional lexing and start conditions</H3>
 
 
 In advanced parsing applications, it may be useful to have different
@@ -1178,7 +1227,7 @@
 lexing rules, and so forth.  The implementation is based largely on
 the "start condition" feature of GNU flex.  Details of this can be found
 at <a
-href="http://www.gnu.org/software/flex/manual/html_chapter/flex_11.html">http://www.gnu.org/software/flex/manual/html_chapter/flex_11.html.</a>.
+href="http://flex.sourceforge.net/manual/Start-Conditions.html">http://flex.sourceforge.net/manual/Start-Conditions.html</a>.
 
 <p>
 To define a new lexing state, it must first be declared.  This is done by including a "states" declaration in your
@@ -1244,8 +1293,8 @@
 </blockquote>
 
 <p>
-States are also associated with the special <tt>t_ignore</tt> and <tt>t_error()</tt> declarations.  For example, if a state treats
-these differently, you can declare:
+States are also associated with the special <tt>t_ignore</tt>, <tt>t_error()</tt>, and <tt>t_eof()</tt> declarations.  For example, if a state treats
+these differently, you can declare:</p>
 
 <blockquote>
 <pre>
@@ -1336,7 +1385,7 @@
 
 # C or C++ comment (ignore)    
 def t_ccode_comment(t):
-    r'(/\*(.|\n)*?*/)|(//.*)'
+    r'(/\*(.|\n)*?\*/)|(//.*)'
     pass
 
 # C string
@@ -1366,13 +1415,16 @@
 position), stores it, and returns a token 'CCODE' containing all of that text.  When returning the token, the lexing state is restored back to its
 initial state.
 
-<H3><a name="ply_nn21"></a>4.19 Miscellaneous Issues</H3>
+<H3><a name="ply_nn21b"></a>4.20 Miscellaneous Issues</H3>
 
 
 <P>
 <li>The lexer requires input to be supplied as a single input string.  Since most machines have more than enough memory, this 
 rarely presents a performance concern.  However, it means that the lexer currently can't be used with streaming data
-such as open files or sockets.  This limitation is primarily a side-effect of using the <tt>re</tt> module.
+such as open files or sockets.  This limitation is primarily a side-effect of using the <tt>re</tt> module.  You might be
+able to work around this by implementing an appropriate <tt>def t_eof()</tt> end-of-file handling rule. The main complication
+here is that you'll probably need to ensure that data is fed to the lexer in a way so that it doesn't split in in the middle
+of a token.</p>
 
 <p>
 <li>The lexer should work properly with both Unicode strings given as token and pattern matching rules as
@@ -1383,10 +1435,13 @@
 
 <blockquote>
 <pre>
-lex.lex(reflags=re.UNICODE)
+lex.lex(reflags=re.UNICODE | re.VERBOSE)
 </pre>
 </blockquote>
 
+Note: by default, <tt>reflags</tt> is set to <tt>re.VERBOSE</tt>.  If you provide
+your own flags, you may need to include this for PLY to preserve its normal behavior.
+
 <p>
 <li>Since the lexer is written entirely in Python, its performance is
 largely determined by that of the Python <tt>re</tt> module.  Although
@@ -1403,7 +1458,8 @@
 <ul>
 <li>It must provide a <tt>token()</tt> method that returns the next token or <tt>None</tt> if no more
 tokens are available.
-<li>The <tt>token()</tt> method must return an object <tt>tok</tt> that has <tt>type</tt> and <tt>value</tt> attributes.
+<li>The <tt>token()</tt> method must return an object <tt>tok</tt> that has <tt>type</tt> and <tt>value</tt> attributes.  If 
+line number tracking is being used, then the token should also define a <tt>lineno</tt> attribute.
 </ul>
 
 <H2><a name="ply_nn22"></a>5. Parsing basics</H2>
@@ -1595,7 +1651,7 @@
 
 # Error rule for syntax errors
 def p_error(p):
-    print "Syntax error in input!"
+    print("Syntax error in input!")
 
 # Build the parser
 parser = yacc.yacc()
@@ -1607,7 +1663,7 @@
        break
    if not s: continue
    result = parser.parse(s)
-   print result
+   print(result)
 </pre>
 </blockquote>
 
@@ -1677,15 +1733,25 @@
 </pre>
 </blockquote>
 
+<p>
 Since table construction is relatively expensive (especially for large
-grammars), the resulting parsing table is written to the current
-directory in a file called <tt>parsetab.py</tt>.  In addition, a
+grammars), the resulting parsing table is written to 
+a file called <tt>parsetab.py</tt>.  In addition, a
 debugging file called <tt>parser.out</tt> is created.  On subsequent
 executions, <tt>yacc</tt> will reload the table from
 <tt>parsetab.py</tt> unless it has detected a change in the underlying
 grammar (in which case the tables and <tt>parsetab.py</tt> file are
-regenerated).  Note: The names of parser output files can be changed
-if necessary.  See the <a href="reference.html">PLY Reference</a> for details.
+regenerated).  Both of these files are written to the same directory
+as the module in which the parser is specified.  
+The name of the <tt>parsetab</tt> module can be changed using the
+<tt>tabmodule</tt> keyword argument to <tt>yacc()</tt>.  For example:
+</p>
+
+<blockquote>
+<pre>
+parser = yacc.yacc(tabmodule='fooparsetab')
+</pre>
+</blockquote>
 
 <p>
 If any errors are detected in your grammar specification, <tt>yacc.py</tt> will produce
@@ -1824,7 +1890,7 @@
 <b>Character literals are limited to a single character</b>.  Thus, it is not legal to specify literals such as <tt>'&lt;='</tt> or <tt>'=='</tt>.  For this, use
 the normal lexing rules (e.g., define a rule such as <tt>t_EQ = r'=='</tt>).
 
-<H3><a name="ply_nn26"></a>6.4 Empty Productions</H3>
+<H3><a name="ply_nn26b"></a>6.4 Empty Productions</H3>
 
 
 <tt>yacc.py</tt> can handle empty productions by defining a rule like this:
@@ -1880,7 +1946,7 @@
 
 <blockquote>
 <pre>
-yacc.yacc(start='foo')
+parser = yacc.yacc(start='foo')
 </pre>
 </blockquote>
 
@@ -2060,7 +2126,7 @@
 
 <p>
 At first, the use of UMINUS in this example may appear very confusing.
-UMINUS is not an input token or a grammer rule.  Instead, you should
+UMINUS is not an input token or a grammar rule.  Instead, you should
 think of it as the name of a special marker in the precedence table.   When you use the <tt>%prec</tt> qualifier, you're simply
 telling yacc that you want the precedence of the expression to be the same as for this special marker instead of the usual precedence.
 
@@ -2123,7 +2189,7 @@
 
 <p>
 It should be noted that reduce/reduce conflicts are notoriously
-difficult to spot simply looking at the input grammer.  When a
+difficult to spot simply looking at the input grammar.  When a
 reduce/reduce conflict occurs, <tt>yacc()</tt> will try to help by
 printing a warning message such as this:
 
@@ -2142,7 +2208,7 @@
 <tt>parser.out</tt> debugging file with an appropriately high level of
 caffeination.
 
-<H3><a name="ply_nn28"></a>6.7 The parser.out file</H3>
+<H3><a name="ply_nn28b"></a>6.7 The parser.out file</H3>
 
 
 Tracking down shift/reduce and reduce/reduce conflicts is one of the finer pleasures of using an LR
@@ -2448,8 +2514,9 @@
 
 <ol>
 <li>On the first occurrence of an error, the user-defined <tt>p_error()</tt> function
-is called with the offending token as an argument.  However, if the syntax error is due to
-reaching the end-of-file, <tt>p_error()</tt> is called with an argument of <tt>None</tt>.
+is called with the offending token as an argument. However, if the syntax error is due to
+reaching the end-of-file, <tt>p_error()</tt> is called with an
+  argument of <tt>None</tt>.
 Afterwards, the parser enters
 an "error-recovery" mode in which it will not make future calls to <tt>p_error()</tt> until it
 has successfully shifted at least 3 tokens onto the parsing stack.
@@ -2495,7 +2562,7 @@
 <pre>
 def p_statement_print_error(p):
      'statement : PRINT error SEMI'
-     print "Syntax error in print statement. Bad expression"
+     print("Syntax error in print statement. Bad expression")
 
 </pre>
 </blockquote>
@@ -2519,7 +2586,7 @@
 <pre>
 def p_statement_print_error(p):
     'statement : PRINT error'
-    print "Syntax error in print statement. Bad expression"
+    print("Syntax error in print statement. Bad expression")
 </pre>
 </blockquote>
 
@@ -2541,12 +2608,17 @@
 <blockquote>
 <pre>
 def p_error(p):
-    print "Whoa. You are seriously hosed."
+    print("Whoa. You are seriously hosed.")
+    if not p:
+        print("End of File!")
+        return
+
     # Read ahead looking for a closing '}'
-    while 1:
-        tok = yacc.token()             # Get the next token
-        if not tok or tok.type == 'RBRACE': break
-    yacc.restart()
+    while True:
+        tok = parser.token()             # Get the next token
+        if not tok or tok.type == 'RBRACE': 
+            break
+    parser.restart()
 </pre>
 </blockquote>
 
@@ -2556,32 +2628,33 @@
 <blockquote>
 <pre>
 def p_error(p):
-    print "Syntax error at token", p.type
-    # Just discard the token and tell the parser it's okay.
-    yacc.errok()
+    if p:
+         print("Syntax error at token", p.type)
+         # Just discard the token and tell the parser it's okay.
+         parser.errok()
+    else:
+         print("Syntax error at EOF")
 </pre>
 </blockquote>
 
 <P>
-Within the <tt>p_error()</tt> function, three functions are available to control the behavior
-of the parser:
+More information on these methods is as follows:
+</p>
+
 <p>
 <ul>
-<li><tt>yacc.errok()</tt>.  This resets the parser state so it doesn't think it's in error-recovery
+<li><tt>parser.errok()</tt>.  This resets the parser state so it doesn't think it's in error-recovery
 mode.   This will prevent an <tt>error</tt> token from being generated and will reset the internal
 error counters so that the next syntax error will call <tt>p_error()</tt> again.
 
 <p>
-<li><tt>yacc.token()</tt>.  This returns the next token on the input stream.
+<li><tt>parser.token()</tt>.  This returns the next token on the input stream.
 
 <p>
-<li><tt>yacc.restart()</tt>.  This discards the entire parsing stack and resets the parser
+<li><tt>parser.restart()</tt>.  This discards the entire parsing stack and resets the parser
 to its initial state. 
 </ul>
 
-Note: these functions are only available when invoking <tt>p_error()</tt> and are not available
-at any other time.
-
 <p>
 To supply the next lookahead token to the parser, <tt>p_error()</tt> can return a token.  This might be
 useful if trying to synchronize on special characters.  For example:
@@ -2590,17 +2663,24 @@
 <pre>
 def p_error(p):
     # Read ahead looking for a terminating ";"
-    while 1:
-        tok = yacc.token()             # Get the next token
+    while True:
+        tok = parser.token()             # Get the next token
         if not tok or tok.type == 'SEMI': break
-    yacc.errok()
+    parser.errok()
 
     # Return SEMI to the parser as the next lookahead token
     return tok  
 </pre>
 </blockquote>
 
-<H4><a name="ply_nn35"></a>6.8.3 Signaling an error from a production</H4>
+<p>
+Keep in mind in that the above error handling functions,
+<tt>parser</tt> is an instance of the parser created by
+<tt>yacc()</tt>.   You'll need to save this instance someplace in your
+code so that you can refer to it during error handling.
+</p>
+
+<H4><a name="ply_nn35"></a>6.8.3 Signalling an error from a production</H4>
 
 
 If necessary, a production rule can manually force the parser to enter error recovery.  This
@@ -2629,8 +2709,44 @@
 <P>
 Note: This feature of PLY is meant to mimic the behavior of the YYERROR macro in yacc.
 
+<H4><a name="ply_nn38"></a>6.8.4 When Do Syntax Errors Get Reported</H4>
 
-<H4><a name="ply_nn32"></a>6.8.4 General comments on error handling</H4>
+
+<p>
+In most cases, yacc will handle errors as soon as a bad input token is
+detected on the input.  However, be aware that yacc may choose to
+delay error handling until after it has reduced one or more grammar
+rules first.  This behavior might be unexpected, but it's related to
+special states in the underlying parsing table known as "defaulted
+states."  A defaulted state is parsing condition where the same
+grammar rule will be reduced regardless of what <em>valid</em> token
+comes next on the input.  For such states, yacc chooses to go ahead
+and reduce the grammar rule <em>without reading the next input
+token</em>.  If the next token is bad, yacc will eventually get around to reading it and 
+report a syntax error.  It's just a little unusual in that you might
+see some of your grammar rules firing immediately prior to the syntax 
+error.
+</p>
+
+<p>
+Usually, the delayed error reporting with defaulted states is harmless
+(and there are other reasons for wanting PLY to behave in this way).
+However, if you need to turn this behavior off for some reason.  You
+can clear the defaulted states table like this:
+</p>
+
+<blockquote>
+<pre>
+parser = yacc.yacc()
+parser.defaulted_states = {}
+</pre>
+</blockquote>
+
+<p>
+Disabling defaulted states is not recommended if your grammar makes use
+of embedded actions as described in Section 6.11.</p>
+
+<H4><a name="ply_nn32"></a>6.8.5 General comments on error handling</H4>
 
 
 For normal types of languages, error recovery with error rules and resynchronization characters is probably the most reliable
@@ -2713,7 +2829,7 @@
 def p_bad_func(p):
     'funccall : fname LPAREN error RPAREN'
     # Line number reported from LPAREN token
-    print "Bad function call at line", p.lineno(2)
+    print("Bad function call at line", p.lineno(2))
 </pre>
 </blockquote>
 
@@ -2834,7 +2950,7 @@
 </pre>
 </blockquote>
 
-<H3><a name="ply_nn35"></a>6.11 Embedded Actions</H3>
+<H3><a name="ply_nn35b"></a>6.11 Embedded Actions</H3>
 
 
 The parsing technique used by yacc only allows actions to be executed at the end of a rule.  For example,
@@ -2844,7 +2960,7 @@
 <pre>
 def p_foo(p):
     "foo : A B C D"
-    print "Parsed a foo", p[1],p[2],p[3],p[4]
+    print("Parsed a foo", p[1],p[2],p[3],p[4])
 </pre>
 </blockquote>
 
@@ -2860,12 +2976,12 @@
 <pre>
 def p_foo(p):
     "foo : A seen_A B C D"
-    print "Parsed a foo", p[1],p[3],p[4],p[5]
-    print "seen_A returned", p[2]
+    print("Parsed a foo", p[1],p[3],p[4],p[5])
+    print("seen_A returned", p[2])
 
 def p_seen_A(p):
     "seen_A :"
-    print "Saw an A = ", p[-1]   # Access grammar symbol to left
+    print("Saw an A = ", p[-1])   # Access grammar symbol to left
     p[0] = some_value            # Assign value to seen_A
 
 </pre>
@@ -2956,25 +3072,13 @@
 
 
 <ul>
-<li>The default parsing method is LALR. To use SLR instead, run yacc() as follows:
-
-<blockquote>
-<pre>
-yacc.yacc(method="SLR")
-</pre>
-</blockquote>
-Note: LALR table generation takes approximately twice as long as SLR table generation.   There is no
-difference in actual parsing performance---the same code is used in both cases.   LALR is preferred when working
-with more complicated grammars since it is more powerful.
-
-<p>
 
 <li>By default, <tt>yacc.py</tt> relies on <tt>lex.py</tt> for tokenizing.  However, an alternative tokenizer
 can be supplied as follows:
 
 <blockquote>
 <pre>
-yacc.parse(lexer=x)
+parser = yacc.parse(lexer=x)
 </pre>
 </blockquote>
 in this case, <tt>x</tt> must be a Lexer object that minimally has a <tt>x.token()</tt> method for retrieving the next
@@ -2986,7 +3090,7 @@
 
 <blockquote>
 <pre>
-yacc.yacc(debug=0)
+parser = yacc.yacc(debug=False)
 </pre>
 </blockquote>
 
@@ -2995,23 +3099,36 @@
 
 <blockquote>
 <pre>
-yacc.yacc(tabmodule="foo")
+parser = yacc.yacc(tabmodule="foo")
 </pre>
 </blockquote>
 
+<P>
+Normally, the <tt>parsetab.py</tt> file is placed into the same directory as
+the module where the parser is defined. If you want it to go somewhere else, you can
+given an absolute package name for <tt>tabmodule</tt> instead.  In that case, the 
+tables will be written there.
+</p>
+
 <p>
 <li>To change the directory in which the <tt>parsetab.py</tt> file (and other output files) are written, use:
 <blockquote>
 <pre>
-yacc.yacc(tabmodule="foo",outputdir="somedirectory")
+parser = yacc.yacc(tabmodule="foo",outputdir="somedirectory")
 </pre>
 </blockquote>
 
 <p>
+Note: Be aware that unless the directory specified is also on Python's path (<tt>sys.path</tt>), subsequent
+imports of the table file will fail.   As a general rule, it's better to specify a destination using the
+<tt>tabmodule</tt> argument instead of directly specifying a directory using the <tt>outputdir</tt> argument.
+</p>
+
+<p>
 <li>To prevent yacc from generating any kind of parser table file, use:
 <blockquote>
 <pre>
-yacc.yacc(write_tables=0)
+parser = yacc.yacc(write_tables=False)
 </pre>
 </blockquote>
 
@@ -3023,38 +3140,73 @@
 
 <blockquote>
 <pre>
-yacc.parse(debug=1)     
+parser.parse(input_text, debug=True)     
 </pre>
 </blockquote>
 
 <p>
-<li>The <tt>yacc.yacc()</tt> function really returns a parser object.  If you want to support multiple
-parsers in the same application, do this:
-
-<blockquote>
-<pre>
-p = yacc.yacc()
-...
-p.parse()
-</pre>
-</blockquote>
-
-Note: The function <tt>yacc.parse()</tt> is bound to the last parser that was generated.
-
-<p>
 <li>Since the generation of the LALR tables is relatively expensive, previously generated tables are
 cached and reused if possible.  The decision to regenerate the tables is determined by taking an MD5
 checksum of all grammar rules and precedence rules.  Only in the event of a mismatch are the tables regenerated.
 
 <p>
 It should be noted that table generation is reasonably efficient, even for grammars that involve around a 100 rules
-and several hundred states.  For more complex languages such as C, table generation may take 30-60 seconds on a slow
-machine.  Please be patient.
+and several hundred states. </li>
+
 
 <p>
 <li>Since LR parsing is driven by tables, the performance of the parser is largely independent of the
 size of the grammar.   The biggest bottlenecks will be the lexer and the complexity of the code in your grammar rules.
+</li>
+</p>
+
+<p>
+<li><tt>yacc()</tt> also allows parsers to be defined as classes and as closures (see the section on alternative specification of
+lexers).  However, be aware that only one parser may be defined in a single module (source file).  There are various 
+error checks and validation steps that may issue confusing error messages if you try to define multiple parsers
+in the same source file.
+</li>
+</p>
+
+<p>
+<li>Decorators of production rules have to update the wrapped function's line number.  <tt>wrapper.co_firstlineno = func.__code__.co_firstlineno</tt>:
+
+<blockquote>
+<pre>
+from functools import wraps
+from nodes import Collection
+
+
+def strict(*types):
+    def decorate(func):
+        @wraps(func)
+        def wrapper(p):
+            func(p)
+            if not isinstance(p[0], types):
+                raise TypeError
+
+        wrapper.co_firstlineno = func.__code__.co_firstlineno
+        return wrapper
+
+    return decorate
+
+@strict(Collection)
+def p_collection(p):
+    """
+    collection  : sequence
+                | map
+    """
+    p[0] = p[1]
+</pre>
+</blockquote>
+
+</li>
+</p>
+
+
 </ul>
+</p>
+
 
 <H2><a name="ply_nn37"></a>7. Multiple Parsers and Lexers</H2>
 
@@ -3097,7 +3249,7 @@
 def t_NUMBER(t):
    r'\d+'
    ...
-   print t.lexer           # Show lexer object
+   print(t.lexer)           # Show lexer object
 </pre>
 </blockquote>
 
@@ -3109,8 +3261,8 @@
 def p_expr_plus(p):
    'expr : expr PLUS expr'
    ...
-   print p.parser          # Show parser object
-   print p.lexer           # Show lexer object
+   print(p.parser)          # Show parser object
+   print(p.lexer)           # Show lexer object
 </pre>
 </blockquote>
 
@@ -3118,7 +3270,7 @@
 For example, if you wanted to have different parsing modes, you could attach a mode
 attribute to the parser object and look at it later.
 
-<H2><a name="ply_nn38"></a>8. Using Python's Optimized Mode</H2>
+<H2><a name="ply_nn38b"></a>8. Using Python's Optimized Mode</H2>
 
 
 Because PLY uses information from doc-strings, parsing and lexing
@@ -3151,7 +3303,7 @@
 
 <p>
 Debugging a compiler is typically not an easy task. PLY provides some
-advanced diagonistic capabilities through the use of Python's
+advanced diagostic capabilities through the use of Python's
 <tt>logging</tt> module.   The next two sections describe this:
 
 <H3><a name="ply_nn45"></a>9.1 Debugging the lex() and yacc() commands</H3>
@@ -3244,7 +3396,90 @@
 redirects to a file where you can more easily inspect the output after
 execution.
 
-<H2><a name="ply_nn39"></a>10. Where to go from here?</H2>
+<H2><a name="ply_nn49"></a>10. Packaging Advice</H2>
+
+
+<p>
+If you are distributing a package that makes use of PLY, you should
+spend a few moments thinking about how you want to handle the files
+that are automatically generated.  For example, the <tt>parsetab.py</tt>
+file generated by the <tt>yacc()</tt> function.</p>
+
+<p>
+Starting in PLY-3.6, the table files are created in the same directory
+as the file where a parser is defined.   This means that the
+<tt>parsetab.py</tt> file will live side-by-side with your parser
+specification.  In terms of packaging, this is probably the easiest and
+most sane approach to manage.  You don't need to give <tt>yacc()</tt>
+any extra arguments and it should just "work."</p>
+
+<p>
+One concern is the management of the <tt>parsetab.py</tt> file itself.
+For example, should you have this file checked into version control (e.g., GitHub),
+should it be included in a package distribution as a normal file, or should you
+just let PLY generate it automatically for the user when they install your package?
+</p>
+
+<p>
+As of PLY-3.6, the <tt>parsetab.py</tt> file should be compatible across all versions
+of Python including Python 2 and 3.  Thus, a table file generated in Python 2 should
+work fine if it's used on Python 3.  Because of this, it should be relatively harmless 
+to distribute the <tt>parsetab.py</tt> file yourself if you need to. However, be aware
+that older/newer versions of PLY may try to regenerate the file if there are future 
+enhancements or changes to its format.
+</p>
+
+<p>
+To make the generation of table files easier for the purposes of installation, you might
+way to make your parser files executable using the <tt>-m</tt> option or similar.  For
+example:
+</p>
+
+<blockquote>
+<pre>
+# calc.py
+...
+...
+def make_parser():
+    parser = yacc.yacc()
+    return parser
+
+if __name__ == '__main__':
+    make_parser()
+</pre>
+</blockquote>
+
+<p>
+You can then use a command such as <tt>python -m calc.py</tt> to generate the tables. Alternatively,
+a <tt>setup.py</tt> script, can import the module and use <tt>make_parser()</tt> to create the
+parsing tables.
+</p>
+
+<p>
+If you're willing to sacrifice a little startup time, you can also instruct PLY to never write the
+tables using <tt>yacc.yacc(write_tables=False, debug=False)</tt>.   In this mode, PLY will regenerate
+the parsing tables from scratch each time.  For a small grammar, you probably won't notice.  For a 
+large grammar, you should probably reconsider--the parsing tables are meant to dramatically speed up this process.
+</p>
+
+<p>
+During operation, is is normal for PLY to produce diagnostic error
+messages (usually printed to standard error).  These are generated
+entirely using the <tt>logging</tt> module.  If you want to redirect
+these messages or silence them, you can provide your own logging
+object to <tt>yacc()</tt>.  For example:
+</p>
+
+<blockquote>
+<pre>
+import logging
+log = logging.getLogger('ply')
+...
+parser = yacc.yacc(errorlog=log)
+</pre>
+</blockquote>
+
+<H2><a name="ply_nn39"></a>11. Where to go from here?</H2>
 
 
 The <tt>examples</tt> directory of the PLY distribution contains several simple examples.   Please consult a
diff --git a/ext/ply/example/BASIC/basic.py b/ext/ply/example/BASIC/basic.py
index b14483d..70ac9e7 100644
--- a/ext/ply/example/BASIC/basic.py
+++ b/ext/ply/example/BASIC/basic.py
@@ -2,7 +2,7 @@
 #
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 if sys.version_info[0] >= 3:
     raw_input = input
@@ -17,7 +17,8 @@
 if len(sys.argv) == 2:
     data = open(sys.argv[1]).read()
     prog = basparse.parse(data)
-    if not prog: raise SystemExit
+    if not prog:
+        raise SystemExit
     b = basinterp.BasicInterpreter(prog)
     try:
         b.run()
@@ -39,33 +40,26 @@
         line = raw_input("[BASIC] ")
     except EOFError:
         raise SystemExit
-    if not line: continue
+    if not line:
+        continue
     line += "\n"
     prog = basparse.parse(line)
-    if not prog: continue
+    if not prog:
+        continue
 
     keys = list(prog)
     if keys[0] > 0:
-         b.add_statements(prog)
+        b.add_statements(prog)
     else:
-         stat = prog[keys[0]]
-         if stat[0] == 'RUN':
-             try:
-                 b.run()
-             except RuntimeError:
-                 pass
-         elif stat[0] == 'LIST':
-             b.list()
-         elif stat[0] == 'BLANK':
-             b.del_line(stat[1])
-         elif stat[0] == 'NEW':
-             b.new()
-
-  
-            
-
-
-
-
-
-
+        stat = prog[keys[0]]
+        if stat[0] == 'RUN':
+            try:
+                b.run()
+            except RuntimeError:
+                pass
+        elif stat[0] == 'LIST':
+            b.list()
+        elif stat[0] == 'BLANK':
+            b.del_line(stat[1])
+        elif stat[0] == 'NEW':
+            b.new()
diff --git a/ext/ply/example/BASIC/basiclex.py b/ext/ply/example/BASIC/basiclex.py
index 3d27cde..4151f4c 100644
--- a/ext/ply/example/BASIC/basiclex.py
+++ b/ext/ply/example/BASIC/basiclex.py
@@ -3,72 +3,59 @@
 from ply import *
 
 keywords = (
-    'LET','READ','DATA','PRINT','GOTO','IF','THEN','FOR','NEXT','TO','STEP',
-    'END','STOP','DEF','GOSUB','DIM','REM','RETURN','RUN','LIST','NEW',
+    'LET', 'READ', 'DATA', 'PRINT', 'GOTO', 'IF', 'THEN', 'FOR', 'NEXT', 'TO', 'STEP',
+    'END', 'STOP', 'DEF', 'GOSUB', 'DIM', 'REM', 'RETURN', 'RUN', 'LIST', 'NEW',
 )
 
 tokens = keywords + (
-     'EQUALS','PLUS','MINUS','TIMES','DIVIDE','POWER',
-     'LPAREN','RPAREN','LT','LE','GT','GE','NE',
-     'COMMA','SEMI', 'INTEGER','FLOAT', 'STRING',
-     'ID','NEWLINE'
+    'EQUALS', 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'POWER',
+    'LPAREN', 'RPAREN', 'LT', 'LE', 'GT', 'GE', 'NE',
+    'COMMA', 'SEMI', 'INTEGER', 'FLOAT', 'STRING',
+    'ID', 'NEWLINE'
 )
 
 t_ignore = ' \t'
 
+
 def t_REM(t):
     r'REM .*'
     return t
 
+
 def t_ID(t):
     r'[A-Z][A-Z0-9]*'
     if t.value in keywords:
         t.type = t.value
     return t
-    
-t_EQUALS  = r'='
-t_PLUS    = r'\+'
-t_MINUS   = r'-'
-t_TIMES   = r'\*'
-t_POWER   = r'\^'
-t_DIVIDE  = r'/'
-t_LPAREN  = r'\('
-t_RPAREN  = r'\)'
-t_LT      = r'<'
-t_LE      = r'<='
-t_GT      = r'>'
-t_GE      = r'>='
-t_NE      = r'<>'
-t_COMMA   = r'\,'
-t_SEMI    = r';'
-t_INTEGER = r'\d+'    
-t_FLOAT   = r'((\d*\.\d+)(E[\+-]?\d+)?|([1-9]\d*E[\+-]?\d+))'
-t_STRING  = r'\".*?\"'
+
+t_EQUALS = r'='
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_TIMES = r'\*'
+t_POWER = r'\^'
+t_DIVIDE = r'/'
+t_LPAREN = r'\('
+t_RPAREN = r'\)'
+t_LT = r'<'
+t_LE = r'<='
+t_GT = r'>'
+t_GE = r'>='
+t_NE = r'<>'
+t_COMMA = r'\,'
+t_SEMI = r';'
+t_INTEGER = r'\d+'
+t_FLOAT = r'((\d*\.\d+)(E[\+-]?\d+)?|([1-9]\d*E[\+-]?\d+))'
+t_STRING = r'\".*?\"'
+
 
 def t_NEWLINE(t):
     r'\n'
     t.lexer.lineno += 1
     return t
 
+
 def t_error(t):
     print("Illegal character %s" % t.value[0])
     t.lexer.skip(1)
 
 lex.lex(debug=0)
-
-
-
-
-
-
-
-       
-   
-  
-            
-
-
-
-
-
-
diff --git a/ext/ply/example/BASIC/basiclog.py b/ext/ply/example/BASIC/basiclog.py
index ccfd7b9..9dcc7fe 100644
--- a/ext/ply/example/BASIC/basiclog.py
+++ b/ext/ply/example/BASIC/basiclog.py
@@ -2,16 +2,16 @@
 #
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 if sys.version_info[0] >= 3:
     raw_input = input
 
 import logging
 logging.basicConfig(
-    level = logging.INFO,
-    filename = "parselog.txt",
-    filemode = "w"
+    level=logging.INFO,
+    filename="parselog.txt",
+    filemode="w"
 )
 log = logging.getLogger()
 
@@ -24,8 +24,9 @@
 # interactive mode below
 if len(sys.argv) == 2:
     data = open(sys.argv[1]).read()
-    prog = basparse.parse(data,debug=log)
-    if not prog: raise SystemExit
+    prog = basparse.parse(data, debug=log)
+    if not prog:
+        raise SystemExit
     b = basinterp.BasicInterpreter(prog)
     try:
         b.run()
@@ -47,33 +48,26 @@
         line = raw_input("[BASIC] ")
     except EOFError:
         raise SystemExit
-    if not line: continue
+    if not line:
+        continue
     line += "\n"
-    prog = basparse.parse(line,debug=log)
-    if not prog: continue
+    prog = basparse.parse(line, debug=log)
+    if not prog:
+        continue
 
     keys = list(prog)
     if keys[0] > 0:
-         b.add_statements(prog)
+        b.add_statements(prog)
     else:
-         stat = prog[keys[0]]
-         if stat[0] == 'RUN':
-             try:
-                 b.run()
-             except RuntimeError:
-                 pass
-         elif stat[0] == 'LIST':
-             b.list()
-         elif stat[0] == 'BLANK':
-             b.del_line(stat[1])
-         elif stat[0] == 'NEW':
-             b.new()
-
-  
-            
-
-
-
-
-
-
+        stat = prog[keys[0]]
+        if stat[0] == 'RUN':
+            try:
+                b.run()
+            except RuntimeError:
+                pass
+        elif stat[0] == 'LIST':
+            b.list()
+        elif stat[0] == 'BLANK':
+            b.del_line(stat[1])
+        elif stat[0] == 'NEW':
+            b.new()
diff --git a/ext/ply/example/BASIC/basinterp.py b/ext/ply/example/BASIC/basinterp.py
index 3e8a777..67762c7 100644
--- a/ext/ply/example/BASIC/basinterp.py
+++ b/ext/ply/example/BASIC/basinterp.py
@@ -5,141 +5,167 @@
 import math
 import random
 
+
 class BasicInterpreter:
 
     # Initialize the interpreter. prog is a dictionary
     # containing (line,statement) mappings
-    def __init__(self,prog):
-         self.prog = prog
+    def __init__(self, prog):
+        self.prog = prog
 
-         self.functions = {           # Built-in function table
-             'SIN' : lambda z: math.sin(self.eval(z)),
-             'COS' : lambda z: math.cos(self.eval(z)),
-             'TAN' : lambda z: math.tan(self.eval(z)),
-             'ATN' : lambda z: math.atan(self.eval(z)),
-             'EXP' : lambda z: math.exp(self.eval(z)),
-             'ABS' : lambda z: abs(self.eval(z)),
-             'LOG' : lambda z: math.log(self.eval(z)),
-             'SQR' : lambda z: math.sqrt(self.eval(z)),
-             'INT' : lambda z: int(self.eval(z)),
-             'RND' : lambda z: random.random()
-         }
+        self.functions = {           # Built-in function table
+            'SIN': lambda z: math.sin(self.eval(z)),
+            'COS': lambda z: math.cos(self.eval(z)),
+            'TAN': lambda z: math.tan(self.eval(z)),
+            'ATN': lambda z: math.atan(self.eval(z)),
+            'EXP': lambda z: math.exp(self.eval(z)),
+            'ABS': lambda z: abs(self.eval(z)),
+            'LOG': lambda z: math.log(self.eval(z)),
+            'SQR': lambda z: math.sqrt(self.eval(z)),
+            'INT': lambda z: int(self.eval(z)),
+            'RND': lambda z: random.random()
+        }
 
     # Collect all data statements
     def collect_data(self):
-         self.data = []
-         for lineno in self.stat:
-              if self.prog[lineno][0] == 'DATA':
-                  self.data = self.data + self.prog[lineno][1]
-         self.dc = 0                  # Initialize the data counter
+        self.data = []
+        for lineno in self.stat:
+            if self.prog[lineno][0] == 'DATA':
+                self.data = self.data + self.prog[lineno][1]
+        self.dc = 0                  # Initialize the data counter
 
     # Check for end statements
     def check_end(self):
-         has_end = 0
-         for lineno in self.stat:
-             if self.prog[lineno][0] == 'END' and not has_end:
-                  has_end = lineno
-         if not has_end:
-             print("NO END INSTRUCTION")
-             self.error = 1
-             return
-         if has_end != lineno:
-             print("END IS NOT LAST")
-             self.error = 1
+        has_end = 0
+        for lineno in self.stat:
+            if self.prog[lineno][0] == 'END' and not has_end:
+                has_end = lineno
+        if not has_end:
+            print("NO END INSTRUCTION")
+            self.error = 1
+            return
+        if has_end != lineno:
+            print("END IS NOT LAST")
+            self.error = 1
 
     # Check loops
     def check_loops(self):
-         for pc in range(len(self.stat)):
-             lineno = self.stat[pc]
-             if self.prog[lineno][0] == 'FOR':
-                  forinst = self.prog[lineno]
-                  loopvar = forinst[1]
-                  for i in range(pc+1,len(self.stat)):
-                       if self.prog[self.stat[i]][0] == 'NEXT':
-                            nextvar = self.prog[self.stat[i]][1]
-                            if nextvar != loopvar: continue
-                            self.loopend[pc] = i
-                            break
-                  else:
-                       print("FOR WITHOUT NEXT AT LINE %s" % self.stat[pc])
-                       self.error = 1
-                  
-    # Evaluate an expression
-    def eval(self,expr):
-        etype = expr[0]
-        if etype == 'NUM': return expr[1]
-        elif etype == 'GROUP': return self.eval(expr[1])
-        elif etype == 'UNARY':
-             if expr[1] == '-': return -self.eval(expr[2])
-        elif etype == 'BINOP':
-             if expr[1] == '+': return self.eval(expr[2])+self.eval(expr[3])
-             elif expr[1] == '-': return self.eval(expr[2])-self.eval(expr[3])
-             elif expr[1] == '*': return self.eval(expr[2])*self.eval(expr[3])
-             elif expr[1] == '/': return float(self.eval(expr[2]))/self.eval(expr[3])
-             elif expr[1] == '^': return abs(self.eval(expr[2]))**self.eval(expr[3])
-        elif etype == 'VAR':
-             var,dim1,dim2 = expr[1]
-             if not dim1 and not dim2:
-                  if var in self.vars:
-                       return self.vars[var]
-                  else:
-                       print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))
-                       raise RuntimeError
-             # May be a list lookup or a function evaluation
-             if dim1 and not dim2:
-                if var in self.functions:
-                      # A function
-                      return self.functions[var](dim1)
+        for pc in range(len(self.stat)):
+            lineno = self.stat[pc]
+            if self.prog[lineno][0] == 'FOR':
+                forinst = self.prog[lineno]
+                loopvar = forinst[1]
+                for i in range(pc + 1, len(self.stat)):
+                    if self.prog[self.stat[i]][0] == 'NEXT':
+                        nextvar = self.prog[self.stat[i]][1]
+                        if nextvar != loopvar:
+                            continue
+                        self.loopend[pc] = i
+                        break
                 else:
-                      # A list evaluation
-                      if var in self.lists:
-                            dim1val = self.eval(dim1)
-                            if dim1val < 1 or dim1val > len(self.lists[var]):
-                                 print("LIST INDEX OUT OF BOUNDS AT LINE %s" % self.stat[self.pc])
-                                 raise RuntimeError
-                            return self.lists[var][dim1val-1]
-             if dim1 and dim2:
-                 if var in self.tables:
-                      dim1val = self.eval(dim1)
-                      dim2val = self.eval(dim2)
-                      if dim1val < 1 or dim1val > len(self.tables[var]) or dim2val < 1 or dim2val > len(self.tables[var][0]):
-                           print("TABLE INDEX OUT OUT BOUNDS AT LINE %s" % self.stat[self.pc])
-                           raise RuntimeError
-                      return self.tables[var][dim1val-1][dim2val-1]
-             print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))
-             raise RuntimeError
+                    print("FOR WITHOUT NEXT AT LINE %s" % self.stat[pc])
+                    self.error = 1
+
+    # Evaluate an expression
+    def eval(self, expr):
+        etype = expr[0]
+        if etype == 'NUM':
+            return expr[1]
+        elif etype == 'GROUP':
+            return self.eval(expr[1])
+        elif etype == 'UNARY':
+            if expr[1] == '-':
+                return -self.eval(expr[2])
+        elif etype == 'BINOP':
+            if expr[1] == '+':
+                return self.eval(expr[2]) + self.eval(expr[3])
+            elif expr[1] == '-':
+                return self.eval(expr[2]) - self.eval(expr[3])
+            elif expr[1] == '*':
+                return self.eval(expr[2]) * self.eval(expr[3])
+            elif expr[1] == '/':
+                return float(self.eval(expr[2])) / self.eval(expr[3])
+            elif expr[1] == '^':
+                return abs(self.eval(expr[2]))**self.eval(expr[3])
+        elif etype == 'VAR':
+            var, dim1, dim2 = expr[1]
+            if not dim1 and not dim2:
+                if var in self.vars:
+                    return self.vars[var]
+                else:
+                    print("UNDEFINED VARIABLE %s AT LINE %s" %
+                          (var, self.stat[self.pc]))
+                    raise RuntimeError
+            # May be a list lookup or a function evaluation
+            if dim1 and not dim2:
+                if var in self.functions:
+                    # A function
+                    return self.functions[var](dim1)
+                else:
+                    # A list evaluation
+                    if var in self.lists:
+                        dim1val = self.eval(dim1)
+                        if dim1val < 1 or dim1val > len(self.lists[var]):
+                            print("LIST INDEX OUT OF BOUNDS AT LINE %s" %
+                                  self.stat[self.pc])
+                            raise RuntimeError
+                        return self.lists[var][dim1val - 1]
+            if dim1 and dim2:
+                if var in self.tables:
+                    dim1val = self.eval(dim1)
+                    dim2val = self.eval(dim2)
+                    if dim1val < 1 or dim1val > len(self.tables[var]) or dim2val < 1 or dim2val > len(self.tables[var][0]):
+                        print("TABLE INDEX OUT OUT BOUNDS AT LINE %s" %
+                              self.stat[self.pc])
+                        raise RuntimeError
+                    return self.tables[var][dim1val - 1][dim2val - 1]
+            print("UNDEFINED VARIABLE %s AT LINE %s" %
+                  (var, self.stat[self.pc]))
+            raise RuntimeError
 
     # Evaluate a relational expression
-    def releval(self,expr):
-         etype = expr[1]
-         lhs   = self.eval(expr[2])
-         rhs   = self.eval(expr[3])
-         if etype == '<':
-             if lhs < rhs: return 1
-             else: return 0
+    def releval(self, expr):
+        etype = expr[1]
+        lhs = self.eval(expr[2])
+        rhs = self.eval(expr[3])
+        if etype == '<':
+            if lhs < rhs:
+                return 1
+            else:
+                return 0
 
-         elif etype == '<=':
-             if lhs <= rhs: return 1
-             else: return 0
+        elif etype == '<=':
+            if lhs <= rhs:
+                return 1
+            else:
+                return 0
 
-         elif etype == '>':
-             if lhs > rhs: return 1
-             else: return 0
+        elif etype == '>':
+            if lhs > rhs:
+                return 1
+            else:
+                return 0
 
-         elif etype == '>=':
-             if lhs >= rhs: return 1
-             else: return 0
+        elif etype == '>=':
+            if lhs >= rhs:
+                return 1
+            else:
+                return 0
 
-         elif etype == '=':
-             if lhs == rhs: return 1
-             else: return 0
+        elif etype == '=':
+            if lhs == rhs:
+                return 1
+            else:
+                return 0
 
-         elif etype == '<>':
-             if lhs != rhs: return 1
-             else: return 0
+        elif etype == '<>':
+            if lhs != rhs:
+                return 1
+            else:
+                return 0
 
     # Assignment
-    def assign(self,target,value):
+    def assign(self, target, value):
         var, dim1, dim2 = target
         if not dim1 and not dim2:
             self.vars[var] = self.eval(value)
@@ -147,42 +173,44 @@
             # List assignment
             dim1val = self.eval(dim1)
             if not var in self.lists:
-                 self.lists[var] = [0]*10
+                self.lists[var] = [0] * 10
 
             if dim1val > len(self.lists[var]):
-                 print ("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
-                 raise RuntimeError
-            self.lists[var][dim1val-1] = self.eval(value)
+                print ("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
+                raise RuntimeError
+            self.lists[var][dim1val - 1] = self.eval(value)
         elif dim1 and dim2:
             dim1val = self.eval(dim1)
             dim2val = self.eval(dim2)
             if not var in self.tables:
-                 temp = [0]*10
-                 v = []
-                 for i in range(10): v.append(temp[:])
-                 self.tables[var] = v
+                temp = [0] * 10
+                v = []
+                for i in range(10):
+                    v.append(temp[:])
+                self.tables[var] = v
             # Variable already exists
             if dim1val > len(self.tables[var]) or dim2val > len(self.tables[var][0]):
-                 print("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
-                 raise RuntimeError
-            self.tables[var][dim1val-1][dim2val-1] = self.eval(value)
+                print("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
+                raise RuntimeError
+            self.tables[var][dim1val - 1][dim2val - 1] = self.eval(value)
 
     # Change the current line number
-    def goto(self,linenum):
-         if not linenum in self.prog:
-              print("UNDEFINED LINE NUMBER %d AT LINE %d" % (linenum, self.stat[self.pc]))
-              raise RuntimeError
-         self.pc = self.stat.index(linenum)
+    def goto(self, linenum):
+        if not linenum in self.prog:
+            print("UNDEFINED LINE NUMBER %d AT LINE %d" %
+                  (linenum, self.stat[self.pc]))
+            raise RuntimeError
+        self.pc = self.stat.index(linenum)
 
     # Run it
     def run(self):
-        self.vars   = { }            # All variables
-        self.lists  = { }            # List variables
-        self.tables = { }            # Tables
-        self.loops  = [ ]            # Currently active loops
-        self.loopend= { }            # Mapping saying where loops end
-        self.gosub  = None           # Gosub return point (if any)
-        self.error  = 0              # Indicates program error
+        self.vars = {}            # All variables
+        self.lists = {}            # List variables
+        self.tables = {}            # Tables
+        self.loops = []            # Currently active loops
+        self.loopend = {}            # Mapping saying where loops end
+        self.gosub = None           # Gosub return point (if any)
+        self.error = 0              # Indicates program error
 
         self.stat = list(self.prog)  # Ordered list of all line numbers
         self.stat.sort()
@@ -194,248 +222,275 @@
         self.check_end()
         self.check_loops()
 
-        if self.error: raise RuntimeError
+        if self.error:
+            raise RuntimeError
 
         while 1:
-            line  = self.stat[self.pc]
+            line = self.stat[self.pc]
             instr = self.prog[line]
-            
+
             op = instr[0]
 
             # END and STOP statements
             if op == 'END' or op == 'STOP':
-                 break           # We're done
+                break           # We're done
 
             # GOTO statement
             elif op == 'GOTO':
-                 newline = instr[1]
-                 self.goto(newline)
-                 continue
+                newline = instr[1]
+                self.goto(newline)
+                continue
 
             # PRINT statement
             elif op == 'PRINT':
-                 plist = instr[1]
-                 out = ""
-                 for label,val in plist:
-                     if out:
-                          out += ' '*(15 - (len(out) % 15))
-                     out += label
-                     if val:
-                          if label: out += " "
-                          eval = self.eval(val)
-                          out += str(eval)
-                 sys.stdout.write(out)
-                 end = instr[2]
-                 if not (end == ',' or end == ';'): 
-                     sys.stdout.write("\n")
-                 if end == ',': sys.stdout.write(" "*(15-(len(out) % 15)))
-                 if end == ';': sys.stdout.write(" "*(3-(len(out) % 3)))
-                     
+                plist = instr[1]
+                out = ""
+                for label, val in plist:
+                    if out:
+                        out += ' ' * (15 - (len(out) % 15))
+                    out += label
+                    if val:
+                        if label:
+                            out += " "
+                        eval = self.eval(val)
+                        out += str(eval)
+                sys.stdout.write(out)
+                end = instr[2]
+                if not (end == ',' or end == ';'):
+                    sys.stdout.write("\n")
+                if end == ',':
+                    sys.stdout.write(" " * (15 - (len(out) % 15)))
+                if end == ';':
+                    sys.stdout.write(" " * (3 - (len(out) % 3)))
+
             # LET statement
             elif op == 'LET':
-                 target = instr[1]
-                 value  = instr[2]
-                 self.assign(target,value)
+                target = instr[1]
+                value = instr[2]
+                self.assign(target, value)
 
             # READ statement
             elif op == 'READ':
-                 for target in instr[1]:
-                      if self.dc < len(self.data):
-                          value = ('NUM',self.data[self.dc])
-                          self.assign(target,value)
-                          self.dc += 1
-                      else:
-                          # No more data.  Program ends
-                          return
+                for target in instr[1]:
+                    if self.dc < len(self.data):
+                        value = ('NUM', self.data[self.dc])
+                        self.assign(target, value)
+                        self.dc += 1
+                    else:
+                        # No more data.  Program ends
+                        return
             elif op == 'IF':
-                 relop = instr[1]
-                 newline = instr[2]
-                 if (self.releval(relop)):
-                     self.goto(newline)
-                     continue
+                relop = instr[1]
+                newline = instr[2]
+                if (self.releval(relop)):
+                    self.goto(newline)
+                    continue
 
             elif op == 'FOR':
-                 loopvar = instr[1]
-                 initval = instr[2]
-                 finval  = instr[3]
-                 stepval = instr[4]
-              
-                 # Check to see if this is a new loop
-                 if not self.loops or self.loops[-1][0] != self.pc:
-                        # Looks like a new loop. Make the initial assignment
-                        newvalue = initval
-                        self.assign((loopvar,None,None),initval)
-                        if not stepval: stepval = ('NUM',1)
-                        stepval = self.eval(stepval)    # Evaluate step here
-                        self.loops.append((self.pc,stepval))
-                 else:
-                        # It's a repeat of the previous loop
-                        # Update the value of the loop variable according to the step
-                        stepval = ('NUM',self.loops[-1][1])
-                        newvalue = ('BINOP','+',('VAR',(loopvar,None,None)),stepval)
+                loopvar = instr[1]
+                initval = instr[2]
+                finval = instr[3]
+                stepval = instr[4]
 
-                 if self.loops[-1][1] < 0: relop = '>='
-                 else: relop = '<='
-                 if not self.releval(('RELOP',relop,newvalue,finval)):
-                      # Loop is done. Jump to the NEXT
-                      self.pc = self.loopend[self.pc]
-                      self.loops.pop()
-                 else:
-                      self.assign((loopvar,None,None),newvalue)
+                # Check to see if this is a new loop
+                if not self.loops or self.loops[-1][0] != self.pc:
+                    # Looks like a new loop. Make the initial assignment
+                    newvalue = initval
+                    self.assign((loopvar, None, None), initval)
+                    if not stepval:
+                        stepval = ('NUM', 1)
+                    stepval = self.eval(stepval)    # Evaluate step here
+                    self.loops.append((self.pc, stepval))
+                else:
+                    # It's a repeat of the previous loop
+                    # Update the value of the loop variable according to the
+                    # step
+                    stepval = ('NUM', self.loops[-1][1])
+                    newvalue = (
+                        'BINOP', '+', ('VAR', (loopvar, None, None)), stepval)
+
+                if self.loops[-1][1] < 0:
+                    relop = '>='
+                else:
+                    relop = '<='
+                if not self.releval(('RELOP', relop, newvalue, finval)):
+                    # Loop is done. Jump to the NEXT
+                    self.pc = self.loopend[self.pc]
+                    self.loops.pop()
+                else:
+                    self.assign((loopvar, None, None), newvalue)
 
             elif op == 'NEXT':
-                 if not self.loops:
-                       print("NEXT WITHOUT FOR AT LINE %s" % line)
-                       return
- 
-                 nextvar = instr[1]
-                 self.pc = self.loops[-1][0]
-                 loopinst = self.prog[self.stat[self.pc]]
-                 forvar = loopinst[1]
-                 if nextvar != forvar:
-                       print("NEXT DOESN'T MATCH FOR AT LINE %s" % line)
-                       return
-                 continue
+                if not self.loops:
+                    print("NEXT WITHOUT FOR AT LINE %s" % line)
+                    return
+
+                nextvar = instr[1]
+                self.pc = self.loops[-1][0]
+                loopinst = self.prog[self.stat[self.pc]]
+                forvar = loopinst[1]
+                if nextvar != forvar:
+                    print("NEXT DOESN'T MATCH FOR AT LINE %s" % line)
+                    return
+                continue
             elif op == 'GOSUB':
-                 newline = instr[1]
-                 if self.gosub:
-                       print("ALREADY IN A SUBROUTINE AT LINE %s" % line)
-                       return
-                 self.gosub = self.stat[self.pc]
-                 self.goto(newline)
-                 continue
+                newline = instr[1]
+                if self.gosub:
+                    print("ALREADY IN A SUBROUTINE AT LINE %s" % line)
+                    return
+                self.gosub = self.stat[self.pc]
+                self.goto(newline)
+                continue
 
             elif op == 'RETURN':
-                 if not self.gosub:
-                      print("RETURN WITHOUT A GOSUB AT LINE %s" % line)
-                      return
-                 self.goto(self.gosub)
-                 self.gosub = None
+                if not self.gosub:
+                    print("RETURN WITHOUT A GOSUB AT LINE %s" % line)
+                    return
+                self.goto(self.gosub)
+                self.gosub = None
 
             elif op == 'FUNC':
-                 fname = instr[1]
-                 pname = instr[2]
-                 expr  = instr[3]
-                 def eval_func(pvalue,name=pname,self=self,expr=expr):
-                      self.assign((pname,None,None),pvalue)
-                      return self.eval(expr)
-                 self.functions[fname] = eval_func
+                fname = instr[1]
+                pname = instr[2]
+                expr = instr[3]
+
+                def eval_func(pvalue, name=pname, self=self, expr=expr):
+                    self.assign((pname, None, None), pvalue)
+                    return self.eval(expr)
+                self.functions[fname] = eval_func
 
             elif op == 'DIM':
-                 for vname,x,y in instr[1]:
-                     if y == 0:
-                          # Single dimension variable
-                          self.lists[vname] = [0]*x
-                     else:
-                          # Double dimension variable
-                          temp = [0]*y
-                          v = []
-                          for i in range(x):
-                              v.append(temp[:])
-                          self.tables[vname] = v
+                for vname, x, y in instr[1]:
+                    if y == 0:
+                        # Single dimension variable
+                        self.lists[vname] = [0] * x
+                    else:
+                        # Double dimension variable
+                        temp = [0] * y
+                        v = []
+                        for i in range(x):
+                            v.append(temp[:])
+                        self.tables[vname] = v
 
-            self.pc += 1         
+            self.pc += 1
 
     # Utility functions for program listing
-    def expr_str(self,expr):
+    def expr_str(self, expr):
         etype = expr[0]
-        if etype == 'NUM': return str(expr[1])
-        elif etype == 'GROUP': return "(%s)" % self.expr_str(expr[1])
+        if etype == 'NUM':
+            return str(expr[1])
+        elif etype == 'GROUP':
+            return "(%s)" % self.expr_str(expr[1])
         elif etype == 'UNARY':
-             if expr[1] == '-': return "-"+str(expr[2])
+            if expr[1] == '-':
+                return "-" + str(expr[2])
         elif etype == 'BINOP':
-             return "%s %s %s" % (self.expr_str(expr[2]),expr[1],self.expr_str(expr[3]))
+            return "%s %s %s" % (self.expr_str(expr[2]), expr[1], self.expr_str(expr[3]))
         elif etype == 'VAR':
-              return self.var_str(expr[1])
+            return self.var_str(expr[1])
 
-    def relexpr_str(self,expr):
-         return "%s %s %s" % (self.expr_str(expr[2]),expr[1],self.expr_str(expr[3]))
+    def relexpr_str(self, expr):
+        return "%s %s %s" % (self.expr_str(expr[2]), expr[1], self.expr_str(expr[3]))
 
-    def var_str(self,var):
-         varname,dim1,dim2 = var
-         if not dim1 and not dim2: return varname
-         if dim1 and not dim2: return "%s(%s)" % (varname, self.expr_str(dim1))
-         return "%s(%s,%s)" % (varname, self.expr_str(dim1),self.expr_str(dim2))
+    def var_str(self, var):
+        varname, dim1, dim2 = var
+        if not dim1 and not dim2:
+            return varname
+        if dim1 and not dim2:
+            return "%s(%s)" % (varname, self.expr_str(dim1))
+        return "%s(%s,%s)" % (varname, self.expr_str(dim1), self.expr_str(dim2))
 
     # Create a program listing
     def list(self):
-         stat = list(self.prog)      # Ordered list of all line numbers
-         stat.sort()
-         for line in stat:
-             instr = self.prog[line]
-             op = instr[0]
-             if op in ['END','STOP','RETURN']:
-                   print("%s %s" % (line, op))
-                   continue
-             elif op == 'REM':
-                   print("%s %s" % (line, instr[1]))
-             elif op == 'PRINT':
-                   _out = "%s %s " % (line, op)
-                   first = 1
-                   for p in instr[1]:
-                         if not first: _out += ", "
-                         if p[0] and p[1]: _out += '"%s"%s' % (p[0],self.expr_str(p[1]))
-                         elif p[1]: _out += self.expr_str(p[1])
-                         else: _out += '"%s"' % (p[0],)
-                         first = 0
-                   if instr[2]: _out += instr[2]
-                   print(_out)
-             elif op == 'LET':
-                   print("%s LET %s = %s" % (line,self.var_str(instr[1]),self.expr_str(instr[2])))
-             elif op == 'READ':
-                   _out = "%s READ " % line
-                   first = 1
-                   for r in instr[1]:
-                         if not first: _out += ","
-                         _out += self.var_str(r)
-                         first = 0
-                   print(_out)
-             elif op == 'IF':
-                   print("%s IF %s THEN %d" % (line,self.relexpr_str(instr[1]),instr[2]))
-             elif op == 'GOTO' or op == 'GOSUB':
-                   print("%s %s %s" % (line, op, instr[1]))
-             elif op == 'FOR':
-                   _out = "%s FOR %s = %s TO %s" % (line,instr[1],self.expr_str(instr[2]),self.expr_str(instr[3]))
-                   if instr[4]: _out += " STEP %s" % (self.expr_str(instr[4]))
-                   print(_out)
-             elif op == 'NEXT':
-                   print("%s NEXT %s" % (line, instr[1]))
-             elif op == 'FUNC':
-                   print("%s DEF %s(%s) = %s" % (line,instr[1],instr[2],self.expr_str(instr[3])))
-             elif op == 'DIM':
-                   _out = "%s DIM " % line
-                   first = 1
-                   for vname,x,y in instr[1]:
-                         if not first: _out += ","
-                         first = 0
-                         if y == 0:
-                               _out += "%s(%d)" % (vname,x)
-                         else:
-                               _out += "%s(%d,%d)" % (vname,x,y)
-                         
-                   print(_out)
-             elif op == 'DATA':
-                   _out = "%s DATA " % line
-                   first = 1
-                   for v in instr[1]:
-                        if not first: _out += ","
-                        first = 0
-                        _out += v
-                   print(_out)
+        stat = list(self.prog)      # Ordered list of all line numbers
+        stat.sort()
+        for line in stat:
+            instr = self.prog[line]
+            op = instr[0]
+            if op in ['END', 'STOP', 'RETURN']:
+                print("%s %s" % (line, op))
+                continue
+            elif op == 'REM':
+                print("%s %s" % (line, instr[1]))
+            elif op == 'PRINT':
+                _out = "%s %s " % (line, op)
+                first = 1
+                for p in instr[1]:
+                    if not first:
+                        _out += ", "
+                    if p[0] and p[1]:
+                        _out += '"%s"%s' % (p[0], self.expr_str(p[1]))
+                    elif p[1]:
+                        _out += self.expr_str(p[1])
+                    else:
+                        _out += '"%s"' % (p[0],)
+                    first = 0
+                if instr[2]:
+                    _out += instr[2]
+                print(_out)
+            elif op == 'LET':
+                print("%s LET %s = %s" %
+                      (line, self.var_str(instr[1]), self.expr_str(instr[2])))
+            elif op == 'READ':
+                _out = "%s READ " % line
+                first = 1
+                for r in instr[1]:
+                    if not first:
+                        _out += ","
+                    _out += self.var_str(r)
+                    first = 0
+                print(_out)
+            elif op == 'IF':
+                print("%s IF %s THEN %d" %
+                      (line, self.relexpr_str(instr[1]), instr[2]))
+            elif op == 'GOTO' or op == 'GOSUB':
+                print("%s %s %s" % (line, op, instr[1]))
+            elif op == 'FOR':
+                _out = "%s FOR %s = %s TO %s" % (
+                    line, instr[1], self.expr_str(instr[2]), self.expr_str(instr[3]))
+                if instr[4]:
+                    _out += " STEP %s" % (self.expr_str(instr[4]))
+                print(_out)
+            elif op == 'NEXT':
+                print("%s NEXT %s" % (line, instr[1]))
+            elif op == 'FUNC':
+                print("%s DEF %s(%s) = %s" %
+                      (line, instr[1], instr[2], self.expr_str(instr[3])))
+            elif op == 'DIM':
+                _out = "%s DIM " % line
+                first = 1
+                for vname, x, y in instr[1]:
+                    if not first:
+                        _out += ","
+                    first = 0
+                    if y == 0:
+                        _out += "%s(%d)" % (vname, x)
+                    else:
+                        _out += "%s(%d,%d)" % (vname, x, y)
+
+                print(_out)
+            elif op == 'DATA':
+                _out = "%s DATA " % line
+                first = 1
+                for v in instr[1]:
+                    if not first:
+                        _out += ","
+                    first = 0
+                    _out += v
+                print(_out)
 
     # Erase the current program
     def new(self):
-         self.prog = {}
- 
+        self.prog = {}
+
     # Insert statements
-    def add_statements(self,prog):
-         for line,stat in prog.items():
-              self.prog[line] = stat
+    def add_statements(self, prog):
+        for line, stat in prog.items():
+            self.prog[line] = stat
 
     # Delete a statement
-    def del_line(self,lineno):
-         try:
-             del self.prog[lineno]
-         except KeyError:
-             pass
-
+    def del_line(self, lineno):
+        try:
+            del self.prog[lineno]
+        except KeyError:
+            pass
diff --git a/ext/ply/example/BASIC/basparse.py b/ext/ply/example/BASIC/basparse.py
index ccdeb16..d610c7d 100644
--- a/ext/ply/example/BASIC/basparse.py
+++ b/ext/ply/example/BASIC/basparse.py
@@ -7,64 +7,72 @@
 tokens = basiclex.tokens
 
 precedence = (
-               ('left', 'PLUS','MINUS'),
-               ('left', 'TIMES','DIVIDE'),
-               ('left', 'POWER'),
-               ('right','UMINUS')
+    ('left', 'PLUS', 'MINUS'),
+    ('left', 'TIMES', 'DIVIDE'),
+    ('left', 'POWER'),
+    ('right', 'UMINUS')
 )
 
-#### A BASIC program is a series of statements.  We represent the program as a
-#### dictionary of tuples indexed by line number.
+# A BASIC program is a series of statements.  We represent the program as a
+# dictionary of tuples indexed by line number.
+
 
 def p_program(p):
     '''program : program statement
                | statement'''
 
     if len(p) == 2 and p[1]:
-       p[0] = { }
-       line,stat = p[1]
-       p[0][line] = stat
-    elif len(p) ==3:
-       p[0] = p[1]
-       if not p[0]: p[0] = { }
-       if p[2]:
-           line,stat = p[2]
-           p[0][line] = stat
+        p[0] = {}
+        line, stat = p[1]
+        p[0][line] = stat
+    elif len(p) == 3:
+        p[0] = p[1]
+        if not p[0]:
+            p[0] = {}
+        if p[2]:
+            line, stat = p[2]
+            p[0][line] = stat
 
-#### This catch-all rule is used for any catastrophic errors.  In this case,
-#### we simply return nothing
+# This catch-all rule is used for any catastrophic errors.  In this case,
+# we simply return nothing
+
 
 def p_program_error(p):
     '''program : error'''
     p[0] = None
     p.parser.error = 1
 
-#### Format of all BASIC statements. 
+# Format of all BASIC statements.
+
 
 def p_statement(p):
     '''statement : INTEGER command NEWLINE'''
-    if isinstance(p[2],str):
-        print("%s %s %s" % (p[2],"AT LINE", p[1]))
+    if isinstance(p[2], str):
+        print("%s %s %s" % (p[2], "AT LINE", p[1]))
         p[0] = None
         p.parser.error = 1
     else:
         lineno = int(p[1])
-        p[0] = (lineno,p[2])
+        p[0] = (lineno, p[2])
 
-#### Interactive statements.
+# Interactive statements.
+
 
 def p_statement_interactive(p):
     '''statement : RUN NEWLINE
                  | LIST NEWLINE
                  | NEW NEWLINE'''
-    p[0] = (0, (p[1],0))
+    p[0] = (0, (p[1], 0))
 
-#### Blank line number
+# Blank line number
+
+
 def p_statement_blank(p):
     '''statement : INTEGER NEWLINE'''
-    p[0] = (0,('BLANK',int(p[1])))
+    p[0] = (0, ('BLANK', int(p[1])))
 
-#### Error handling for malformed statements
+# Error handling for malformed statements
+
 
 def p_statement_bad(p):
     '''statement : INTEGER error NEWLINE'''
@@ -72,191 +80,226 @@
     p[0] = None
     p.parser.error = 1
 
-#### Blank line
+# Blank line
+
 
 def p_statement_newline(p):
     '''statement : NEWLINE'''
     p[0] = None
 
-#### LET statement
+# LET statement
+
 
 def p_command_let(p):
     '''command : LET variable EQUALS expr'''
-    p[0] = ('LET',p[2],p[4])
+    p[0] = ('LET', p[2], p[4])
+
 
 def p_command_let_bad(p):
     '''command : LET variable EQUALS error'''
     p[0] = "BAD EXPRESSION IN LET"
 
-#### READ statement
+# READ statement
+
 
 def p_command_read(p):
     '''command : READ varlist'''
-    p[0] = ('READ',p[2])
+    p[0] = ('READ', p[2])
+
 
 def p_command_read_bad(p):
     '''command : READ error'''
     p[0] = "MALFORMED VARIABLE LIST IN READ"
 
-#### DATA statement
+# DATA statement
+
 
 def p_command_data(p):
     '''command : DATA numlist'''
-    p[0] = ('DATA',p[2])
+    p[0] = ('DATA', p[2])
+
 
 def p_command_data_bad(p):
     '''command : DATA error'''
     p[0] = "MALFORMED NUMBER LIST IN DATA"
 
-#### PRINT statement
+# PRINT statement
+
 
 def p_command_print(p):
     '''command : PRINT plist optend'''
-    p[0] = ('PRINT',p[2],p[3])
+    p[0] = ('PRINT', p[2], p[3])
+
 
 def p_command_print_bad(p):
     '''command : PRINT error'''
     p[0] = "MALFORMED PRINT STATEMENT"
 
-#### Optional ending on PRINT. Either a comma (,) or semicolon (;)
+# Optional ending on PRINT. Either a comma (,) or semicolon (;)
+
 
 def p_optend(p):
     '''optend : COMMA 
               | SEMI
               |'''
-    if len(p)  == 2:
-         p[0] = p[1]
+    if len(p) == 2:
+        p[0] = p[1]
     else:
-         p[0] = None
+        p[0] = None
 
-#### PRINT statement with no arguments
+# PRINT statement with no arguments
+
 
 def p_command_print_empty(p):
     '''command : PRINT'''
-    p[0] = ('PRINT',[],None)
+    p[0] = ('PRINT', [], None)
 
-#### GOTO statement
+# GOTO statement
+
 
 def p_command_goto(p):
     '''command : GOTO INTEGER'''
-    p[0] = ('GOTO',int(p[2]))
+    p[0] = ('GOTO', int(p[2]))
+
 
 def p_command_goto_bad(p):
     '''command : GOTO error'''
     p[0] = "INVALID LINE NUMBER IN GOTO"
 
-#### IF-THEN statement
+# IF-THEN statement
+
 
 def p_command_if(p):
     '''command : IF relexpr THEN INTEGER'''
-    p[0] = ('IF',p[2],int(p[4]))
+    p[0] = ('IF', p[2], int(p[4]))
+
 
 def p_command_if_bad(p):
     '''command : IF error THEN INTEGER'''
     p[0] = "BAD RELATIONAL EXPRESSION"
 
+
 def p_command_if_bad2(p):
     '''command : IF relexpr THEN error'''
     p[0] = "INVALID LINE NUMBER IN THEN"
 
-#### FOR statement
+# FOR statement
+
 
 def p_command_for(p):
     '''command : FOR ID EQUALS expr TO expr optstep'''
-    p[0] = ('FOR',p[2],p[4],p[6],p[7])
+    p[0] = ('FOR', p[2], p[4], p[6], p[7])
+
 
 def p_command_for_bad_initial(p):
     '''command : FOR ID EQUALS error TO expr optstep'''
     p[0] = "BAD INITIAL VALUE IN FOR STATEMENT"
 
+
 def p_command_for_bad_final(p):
     '''command : FOR ID EQUALS expr TO error optstep'''
     p[0] = "BAD FINAL VALUE IN FOR STATEMENT"
 
+
 def p_command_for_bad_step(p):
     '''command : FOR ID EQUALS expr TO expr STEP error'''
     p[0] = "MALFORMED STEP IN FOR STATEMENT"
 
-#### Optional STEP qualifier on FOR statement
+# Optional STEP qualifier on FOR statement
+
 
 def p_optstep(p):
     '''optstep : STEP expr
                | empty'''
     if len(p) == 3:
-       p[0] = p[2]
+        p[0] = p[2]
     else:
-       p[0] = None
+        p[0] = None
 
-#### NEXT statement
-    
+# NEXT statement
+
+
 def p_command_next(p):
     '''command : NEXT ID'''
 
-    p[0] = ('NEXT',p[2])
+    p[0] = ('NEXT', p[2])
+
 
 def p_command_next_bad(p):
     '''command : NEXT error'''
     p[0] = "MALFORMED NEXT"
 
-#### END statement
+# END statement
+
 
 def p_command_end(p):
     '''command : END'''
     p[0] = ('END',)
 
-#### REM statement
+# REM statement
+
 
 def p_command_rem(p):
     '''command : REM'''
-    p[0] = ('REM',p[1])
+    p[0] = ('REM', p[1])
 
-#### STOP statement
+# STOP statement
+
 
 def p_command_stop(p):
     '''command : STOP'''
     p[0] = ('STOP',)
 
-#### DEF statement
+# DEF statement
+
 
 def p_command_def(p):
     '''command : DEF ID LPAREN ID RPAREN EQUALS expr'''
-    p[0] = ('FUNC',p[2],p[4],p[7])
+    p[0] = ('FUNC', p[2], p[4], p[7])
+
 
 def p_command_def_bad_rhs(p):
     '''command : DEF ID LPAREN ID RPAREN EQUALS error'''
     p[0] = "BAD EXPRESSION IN DEF STATEMENT"
 
+
 def p_command_def_bad_arg(p):
     '''command : DEF ID LPAREN error RPAREN EQUALS expr'''
     p[0] = "BAD ARGUMENT IN DEF STATEMENT"
 
-#### GOSUB statement
+# GOSUB statement
+
 
 def p_command_gosub(p):
     '''command : GOSUB INTEGER'''
-    p[0] = ('GOSUB',int(p[2]))
+    p[0] = ('GOSUB', int(p[2]))
+
 
 def p_command_gosub_bad(p):
     '''command : GOSUB error'''
     p[0] = "INVALID LINE NUMBER IN GOSUB"
 
-#### RETURN statement
+# RETURN statement
+
 
 def p_command_return(p):
     '''command : RETURN'''
     p[0] = ('RETURN',)
 
-#### DIM statement
+# DIM statement
+
 
 def p_command_dim(p):
     '''command : DIM dimlist'''
-    p[0] = ('DIM',p[2])
+    p[0] = ('DIM', p[2])
+
 
 def p_command_dim_bad(p):
     '''command : DIM error'''
     p[0] = "MALFORMED VARIABLE LIST IN DIM"
 
-#### List of variables supplied to DIM statement
+# List of variables supplied to DIM statement
+
 
 def p_dimlist(p):
     '''dimlist : dimlist COMMA dimitem
@@ -267,17 +310,20 @@
     else:
         p[0] = [p[1]]
 
-#### DIM items
+# DIM items
+
 
 def p_dimitem_single(p):
     '''dimitem : ID LPAREN INTEGER RPAREN'''
-    p[0] = (p[1],eval(p[3]),0)
+    p[0] = (p[1], eval(p[3]), 0)
+
 
 def p_dimitem_double(p):
     '''dimitem : ID LPAREN INTEGER COMMA INTEGER RPAREN'''
-    p[0] = (p[1],eval(p[3]),eval(p[5]))
+    p[0] = (p[1], eval(p[3]), eval(p[5]))
 
-#### Arithmetic expressions
+# Arithmetic expressions
+
 
 def p_expr_binary(p):
     '''expr : expr PLUS expr
@@ -286,26 +332,31 @@
             | expr DIVIDE expr
             | expr POWER expr'''
 
-    p[0] = ('BINOP',p[2],p[1],p[3])
+    p[0] = ('BINOP', p[2], p[1], p[3])
+
 
 def p_expr_number(p):
     '''expr : INTEGER
             | FLOAT'''
-    p[0] = ('NUM',eval(p[1]))
+    p[0] = ('NUM', eval(p[1]))
+
 
 def p_expr_variable(p):
     '''expr : variable'''
-    p[0] = ('VAR',p[1])
+    p[0] = ('VAR', p[1])
+
 
 def p_expr_group(p):
     '''expr : LPAREN expr RPAREN'''
-    p[0] = ('GROUP',p[2])
+    p[0] = ('GROUP', p[2])
+
 
 def p_expr_unary(p):
     '''expr : MINUS expr %prec UMINUS'''
-    p[0] = ('UNARY','-',p[2])
+    p[0] = ('UNARY', '-', p[2])
 
-#### Relational expressions
+# Relational expressions
+
 
 def p_relexpr(p):
     '''relexpr : expr LT expr
@@ -314,111 +365,110 @@
                | expr GE expr
                | expr EQUALS expr
                | expr NE expr'''
-    p[0] = ('RELOP',p[2],p[1],p[3])
+    p[0] = ('RELOP', p[2], p[1], p[3])
 
-#### Variables
+# Variables
+
 
 def p_variable(p):
     '''variable : ID
               | ID LPAREN expr RPAREN
               | ID LPAREN expr COMMA expr RPAREN'''
     if len(p) == 2:
-       p[0] = (p[1],None,None)
+        p[0] = (p[1], None, None)
     elif len(p) == 5:
-       p[0] = (p[1],p[3],None)
+        p[0] = (p[1], p[3], None)
     else:
-       p[0] = (p[1],p[3],p[5])
+        p[0] = (p[1], p[3], p[5])
 
-#### Builds a list of variable targets as a Python list
+# Builds a list of variable targets as a Python list
+
 
 def p_varlist(p):
     '''varlist : varlist COMMA variable
                | variable'''
     if len(p) > 2:
-       p[0] = p[1]
-       p[0].append(p[3])
+        p[0] = p[1]
+        p[0].append(p[3])
     else:
-       p[0] = [p[1]]
+        p[0] = [p[1]]
 
 
-#### Builds a list of numbers as a Python list
+# Builds a list of numbers as a Python list
 
 def p_numlist(p):
     '''numlist : numlist COMMA number
                | number'''
 
     if len(p) > 2:
-       p[0] = p[1]
-       p[0].append(p[3])
+        p[0] = p[1]
+        p[0].append(p[3])
     else:
-       p[0] = [p[1]]
+        p[0] = [p[1]]
 
-#### A number. May be an integer or a float
+# A number. May be an integer or a float
+
 
 def p_number(p):
     '''number  : INTEGER
                | FLOAT'''
     p[0] = eval(p[1])
 
-#### A signed number.
+# A signed number.
+
 
 def p_number_signed(p):
     '''number  : MINUS INTEGER
                | MINUS FLOAT'''
-    p[0] = eval("-"+p[2])
+    p[0] = eval("-" + p[2])
 
-#### List of targets for a print statement
-#### Returns a list of tuples (label,expr)
+# List of targets for a print statement
+# Returns a list of tuples (label,expr)
+
 
 def p_plist(p):
     '''plist   : plist COMMA pitem
                | pitem'''
     if len(p) > 3:
-       p[0] = p[1]
-       p[0].append(p[3])
+        p[0] = p[1]
+        p[0].append(p[3])
     else:
-       p[0] = [p[1]]
+        p[0] = [p[1]]
+
 
 def p_item_string(p):
     '''pitem : STRING'''
-    p[0] = (p[1][1:-1],None)
+    p[0] = (p[1][1:-1], None)
+
 
 def p_item_string_expr(p):
     '''pitem : STRING expr'''
-    p[0] = (p[1][1:-1],p[2])
+    p[0] = (p[1][1:-1], p[2])
+
 
 def p_item_expr(p):
     '''pitem : expr'''
-    p[0] = ("",p[1])
+    p[0] = ("", p[1])
 
-#### Empty
-   
+# Empty
+
+
 def p_empty(p):
     '''empty : '''
 
-#### Catastrophic error handler
+# Catastrophic error handler
+
+
 def p_error(p):
     if not p:
         print("SYNTAX ERROR AT EOF")
 
 bparser = yacc.yacc()
 
-def parse(data,debug=0):
+
+def parse(data, debug=0):
     bparser.error = 0
-    p = bparser.parse(data,debug=debug)
-    if bparser.error: return None
+    p = bparser.parse(data, debug=debug)
+    if bparser.error:
+        return None
     return p
-
-
-
-
-       
-   
-  
-            
-
-
-
-
-
-
diff --git a/ext/ply/example/GardenSnake/GardenSnake.py b/ext/ply/example/GardenSnake/GardenSnake.py
index 2a7f45e..8b493b4 100644
--- a/ext/ply/example/GardenSnake/GardenSnake.py
+++ b/ext/ply/example/GardenSnake/GardenSnake.py
@@ -37,7 +37,7 @@
 
 # Modifications for inclusion in PLY distribution
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 from ply import *
 
 ##### Lexer ######
@@ -69,18 +69,21 @@
     'INDENT',
     'DEDENT',
     'ENDMARKER',
-    )
+)
 
 #t_NUMBER = r'\d+'
 # taken from decmial.py but without the leading sign
+
+
 def t_NUMBER(t):
     r"""(\d+(\.\d*)?|\.\d+)([eE][-+]? \d+)?"""
     t.value = decimal.Decimal(t.value)
     return t
 
+
 def t_STRING(t):
     r"'([^\\']+|\\'|\\\\)*'"  # I think this is right ...
-    t.value=t.value[1:-1].decode("string-escape") # .swapcase() # for fun
+    t.value = t.value[1:-1].decode("string-escape")  # .swapcase() # for fun
     return t
 
 t_COLON = r':'
@@ -98,10 +101,11 @@
 # Ply nicely documented how to do this.
 
 RESERVED = {
-  "def": "DEF",
-  "if": "IF",
-  "return": "RETURN",
-  }
+    "def": "DEF",
+    "if": "IF",
+    "return": "RETURN",
+}
+
 
 def t_NAME(t):
     r'[a-zA-Z_][a-zA-Z0-9_]*'
@@ -111,6 +115,8 @@
 # Putting this before t_WS let it consume lines with only comments in
 # them so the latter code never sees the WS part.  Not consuming the
 # newline.  Needed for "if 1: #comment"
+
+
 def t_comment(t):
     r"[ ]*\043[^\n]*"  # \043 is '#'
     pass
@@ -125,6 +131,8 @@
 # Don't generate newline tokens when inside of parenthesis, eg
 #   a = (1,
 #        2, 3)
+
+
 def t_newline(t):
     r'\n+'
     t.lexer.lineno += len(t.value)
@@ -132,11 +140,13 @@
     if t.lexer.paren_count == 0:
         return t
 
+
 def t_LPAR(t):
     r'\('
     t.lexer.paren_count += 1
     return t
 
+
 def t_RPAR(t):
     r'\)'
     # check for underflow?  should be the job of the parser
@@ -149,7 +159,7 @@
     print "Skipping", repr(t.value[0])
     t.lexer.skip(1)
 
-## I implemented INDENT / DEDENT generation as a post-processing filter
+# I implemented INDENT / DEDENT generation as a post-processing filter
 
 # The original lex token stream contains WS and NEWLINE characters.
 # WS will only occur before any other tokens on a line.
@@ -169,6 +179,8 @@
 MUST_INDENT = 2
 
 # only care about whitespace at the start of a line
+
+
 def track_tokens_filter(lexer, tokens):
     lexer.at_line_start = at_line_start = True
     indent = NO_INDENT
@@ -180,7 +192,7 @@
             at_line_start = False
             indent = MAY_INDENT
             token.must_indent = False
-            
+
         elif token.type == "NEWLINE":
             at_line_start = True
             if indent == MAY_INDENT:
@@ -204,6 +216,7 @@
         yield token
         lexer.at_line_start = at_line_start
 
+
 def _new_token(type, lineno):
     tok = lex.LexToken()
     tok.type = type
@@ -212,10 +225,14 @@
     return tok
 
 # Synthesize a DEDENT tag
+
+
 def DEDENT(lineno):
     return _new_token("DEDENT", lineno)
 
 # Synthesize an INDENT tag
+
+
 def INDENT(lineno):
     return _new_token("INDENT", lineno)
 
@@ -228,14 +245,14 @@
     depth = 0
     prev_was_ws = False
     for token in tokens:
-##        if 1:
-##            print "Process", token,
-##            if token.at_line_start:
-##                print "at_line_start",
-##            if token.must_indent:
-##                print "must_indent",
-##            print
-                
+        # if 1:
+        # print "Process", token,
+        # if token.at_line_start:
+        # print "at_line_start",
+        # if token.must_indent:
+        # print "must_indent",
+        # print
+
         # WS only occurs at the start of the line
         # There may be WS followed by NEWLINE so
         # only track the depth here.  Don't indent/dedent
@@ -274,14 +291,15 @@
                 # At the same level
                 pass
             elif depth > levels[-1]:
-                raise IndentationError("indentation increase but not in new block")
+                raise IndentationError(
+                    "indentation increase but not in new block")
             else:
                 # Back up; but only if it matches a previous level
                 try:
                     i = levels.index(depth)
                 except ValueError:
                     raise IndentationError("inconsistent indentation")
-                for _ in range(i+1, len(levels)):
+                for _ in range(i + 1, len(levels)):
                     yield DEDENT(token.lineno)
                     levels.pop()
 
@@ -294,11 +312,11 @@
         assert token is not None
         for _ in range(1, len(levels)):
             yield DEDENT(token.lineno)
-    
+
 
 # The top-level filter adds an ENDMARKER, if requested.
 # Python's grammar uses it.
-def filter(lexer, add_endmarker = True):
+def filter(lexer, add_endmarker=True):
     token = None
     tokens = iter(lexer.token, None)
     tokens = track_tokens_filter(lexer, tokens)
@@ -313,14 +331,19 @@
 
 # Combine Ply and my filters into a new lexer
 
+
 class IndentLexer(object):
+
     def __init__(self, debug=0, optimize=0, lextab='lextab', reflags=0):
-        self.lexer = lex.lex(debug=debug, optimize=optimize, lextab=lextab, reflags=reflags)
+        self.lexer = lex.lex(debug=debug, optimize=optimize,
+                             lextab=lextab, reflags=reflags)
         self.token_stream = None
+
     def input(self, s, add_endmarker=True):
         self.lexer.paren_count = 0
         self.lexer.input(s)
         self.token_stream = filter(self.lexer, add_endmarker)
+
     def token(self):
         try:
             return self.token_stream.next()
@@ -336,6 +359,8 @@
 from compiler import ast
 
 # Helper function
+
+
 def Assign(left, right):
     names = []
     if isinstance(left, ast.Name):
@@ -356,35 +381,39 @@
 
 # The grammar comments come from Python's Grammar/Grammar file
 
-## NB: compound_stmt in single_input is followed by extra NEWLINE!
+# NB: compound_stmt in single_input is followed by extra NEWLINE!
 # file_input: (NEWLINE | stmt)* ENDMARKER
 def p_file_input_end(p):
     """file_input_end : file_input ENDMARKER"""
     p[0] = ast.Stmt(p[1])
+
+
 def p_file_input(p):
     """file_input : file_input NEWLINE
                   | file_input stmt
                   | NEWLINE
                   | stmt"""
-    if isinstance(p[len(p)-1], basestring):
+    if isinstance(p[len(p) - 1], basestring):
         if len(p) == 3:
             p[0] = p[1]
         else:
-            p[0] = [] # p == 2 --> only a blank line
+            p[0] = []  # p == 2 --> only a blank line
     else:
         if len(p) == 3:
             p[0] = p[1] + p[2]
         else:
             p[0] = p[1]
-            
+
 
 # funcdef: [decorators] 'def' NAME parameters ':' suite
 # ignoring decorators
 def p_funcdef(p):
     "funcdef : DEF NAME parameters COLON suite"
     p[0] = ast.Function(None, p[2], tuple(p[3]), (), 0, None, p[5])
-    
+
 # parameters: '(' [varargslist] ')'
+
+
 def p_parameters(p):
     """parameters : LPAR RPAR
                   | LPAR varargslist RPAR"""
@@ -392,9 +421,9 @@
         p[0] = []
     else:
         p[0] = p[2]
-    
 
-# varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) | 
+
+# varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) |
 # highly simplified
 def p_varargslist(p):
     """varargslist : varargslist COMMA NAME
@@ -405,21 +434,27 @@
         p[0] = [p[1]]
 
 # stmt: simple_stmt | compound_stmt
+
+
 def p_stmt_simple(p):
     """stmt : simple_stmt"""
     # simple_stmt is a list
     p[0] = p[1]
-    
+
+
 def p_stmt_compound(p):
     """stmt : compound_stmt"""
     p[0] = [p[1]]
 
 # simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
+
+
 def p_simple_stmt(p):
     """simple_stmt : small_stmts NEWLINE
                    | small_stmts SEMICOLON NEWLINE"""
     p[0] = p[1]
 
+
 def p_small_stmts(p):
     """small_stmts : small_stmts SEMICOLON small_stmt
                    | small_stmt"""
@@ -430,6 +465,8 @@
 
 # small_stmt: expr_stmt | print_stmt  | del_stmt | pass_stmt | flow_stmt |
 #    import_stmt | global_stmt | exec_stmt | assert_stmt
+
+
 def p_small_stmt(p):
     """small_stmt : flow_stmt
                   | expr_stmt"""
@@ -439,6 +476,8 @@
 #                      ('=' (yield_expr|testlist))*)
 # augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
 #             '<<=' | '>>=' | '**=' | '//=')
+
+
 def p_expr_stmt(p):
     """expr_stmt : testlist ASSIGN testlist
                  | testlist """
@@ -448,11 +487,14 @@
     else:
         p[0] = Assign(p[1], p[3])
 
+
 def p_flow_stmt(p):
     "flow_stmt : return_stmt"
     p[0] = p[1]
 
 # return_stmt: 'return' [testlist]
+
+
 def p_return_stmt(p):
     "return_stmt : RETURN testlist"
     p[0] = ast.Return(p[2])
@@ -463,10 +505,12 @@
                      | funcdef"""
     p[0] = p[1]
 
+
 def p_if_stmt(p):
     'if_stmt : IF test COLON suite'
     p[0] = ast.If([(p[2], p[4])], None)
 
+
 def p_suite(p):
     """suite : simple_stmt
              | NEWLINE INDENT stmts DEDENT"""
@@ -474,7 +518,7 @@
         p[0] = ast.Stmt(p[1])
     else:
         p[0] = ast.Stmt(p[3])
-    
+
 
 def p_stmts(p):
     """stmts : stmts stmt
@@ -484,7 +528,7 @@
     else:
         p[0] = p[1]
 
-## No using Python's approach because Ply supports precedence
+# No using Python's approach because Ply supports precedence
 
 # comparison: expr (comp_op expr)*
 # arith_expr: term (('+'|'-') term)*
@@ -492,12 +536,17 @@
 # factor: ('+'|'-'|'~') factor | power
 # comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
 
+
 def make_lt_compare((left, right)):
-    return ast.Compare(left, [('<', right),])
+    return ast.Compare(left, [('<', right), ])
+
+
 def make_gt_compare((left, right)):
-    return ast.Compare(left, [('>', right),])
+    return ast.Compare(left, [('>', right), ])
+
+
 def make_eq_compare((left, right)):
-    return ast.Compare(left, [('==', right),])
+    return ast.Compare(left, [('==', right), ])
 
 
 binary_ops = {
@@ -512,12 +561,13 @@
 unary_ops = {
     "+": ast.UnaryAdd,
     "-": ast.UnarySub,
-    }
+}
 precedence = (
     ("left", "EQ", "GT", "LT"),
     ("left", "PLUS", "MINUS"),
     ("left", "MULT", "DIV"),
-    )
+)
+
 
 def p_comparison(p):
     """comparison : comparison PLUS comparison
@@ -536,10 +586,12 @@
         p[0] = unary_ops[p[1]](p[2])
     else:
         p[0] = p[1]
-                  
+
 # power: atom trailer* ['**' factor]
 # trailers enables function calls.  I only allow one level of calls
 # so this is 'trailer'
+
+
 def p_power(p):
     """power : atom
              | atom trailer"""
@@ -551,26 +603,33 @@
         else:
             raise AssertionError("not implemented")
 
+
 def p_atom_name(p):
     """atom : NAME"""
     p[0] = ast.Name(p[1])
 
+
 def p_atom_number(p):
     """atom : NUMBER
             | STRING"""
     p[0] = ast.Const(p[1])
 
+
 def p_atom_tuple(p):
     """atom : LPAR testlist RPAR"""
     p[0] = p[2]
 
 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
+
+
 def p_trailer(p):
     "trailer : LPAR arglist RPAR"
     p[0] = ("CALL", p[2])
 
 # testlist: test (',' test)* [',']
 # Contains shift/reduce error
+
+
 def p_testlist(p):
     """testlist : testlist_multi COMMA
                 | testlist_multi """
@@ -586,6 +645,7 @@
     if isinstance(p[0], list):
         p[0] = ast.Tuple(p[0])
 
+
 def p_testlist_multi(p):
     """testlist_multi : testlist_multi COMMA test
                       | test"""
@@ -605,7 +665,6 @@
 def p_test(p):
     "test : comparison"
     p[0] = p[1]
-    
 
 
 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test)
@@ -619,17 +678,21 @@
         p[0] = [p[1]]
 
 # argument: test [gen_for] | test '=' test  # Really [keyword '='] test
+
+
 def p_argument(p):
     "argument : test"
     p[0] = p[1]
 
+
 def p_error(p):
-    #print "Error!", repr(p)
+    # print "Error!", repr(p)
     raise SyntaxError(p)
 
 
 class GardenSnakeParser(object):
-    def __init__(self, lexer = None):
+
+    def __init__(self, lexer=None):
         if lexer is None:
             lexer = IndentLexer()
         self.lexer = lexer
@@ -637,20 +700,23 @@
 
     def parse(self, code):
         self.lexer.input(code)
-        result = self.parser.parse(lexer = self.lexer)
+        result = self.parser.parse(lexer=self.lexer)
         return ast.Module(None, result)
 
 
 ###### Code generation ######
-    
+
 from compiler import misc, syntax, pycodegen
 
+
 class GardenSnakeCompiler(object):
+
     def __init__(self):
         self.parser = GardenSnakeParser()
+
     def compile(self, code, filename="<string>"):
         tree = self.parser.parse(code)
-        #print  tree
+        # print  tree
         misc.set_filename(filename, tree)
         syntax.check(tree)
         gen = pycodegen.ModuleCodeGenerator(tree)
@@ -658,7 +724,7 @@
         return code
 
 ####### Test code #######
-    
+
 compile = GardenSnakeCompiler().compile
 
 code = r"""
@@ -698,8 +764,10 @@
 """
 
 # Set up the GardenSnake run-time environment
+
+
 def print_(*args):
-    print "-->", " ".join(map(str,args))
+    print "-->", " ".join(map(str, args))
 
 globals()["print"] = print_
 
diff --git a/ext/ply/example/ansic/clex.py b/ext/ply/example/ansic/clex.py
index 37fdd8e..4bde1d7 100644
--- a/ext/ply/example/ansic/clex.py
+++ b/ext/ply/example/ansic/clex.py
@@ -5,7 +5,7 @@
 # ----------------------------------------------------------------------
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 import ply.lex as lex
 
@@ -15,10 +15,11 @@
     'ELSE', 'ENUM', 'EXTERN', 'FLOAT', 'FOR', 'GOTO', 'IF', 'INT', 'LONG', 'REGISTER',
     'RETURN', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRUCT', 'SWITCH', 'TYPEDEF',
     'UNION', 'UNSIGNED', 'VOID', 'VOLATILE', 'WHILE',
-    )
+)
 
 tokens = reserved + (
-    # Literals (identifier, integer constant, float constant, string constant, char const)
+    # Literals (identifier, integer constant, float constant, string constant,
+    # char const)
     'ID', 'TYPEID', 'ICONST', 'FCONST', 'SCONST', 'CCONST',
 
     # Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=)
@@ -26,10 +27,10 @@
     'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT',
     'LOR', 'LAND', 'LNOT',
     'LT', 'LE', 'GT', 'GE', 'EQ', 'NE',
-    
+
     # Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=)
     'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL',
-    'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL',
+    'LSHIFTEQUAL', 'RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL',
 
     # Increment/decrement (++,--)
     'PLUSPLUS', 'MINUSMINUS',
@@ -39,7 +40,7 @@
 
     # Conditional operator (?)
     'CONDOP',
-    
+
     # Delimeters ( ) [ ] { } , . ; :
     'LPAREN', 'RPAREN',
     'LBRACKET', 'RBRACKET',
@@ -48,84 +49,87 @@
 
     # Ellipsis (...)
     'ELLIPSIS',
-    )
+)
 
 # Completely ignored characters
-t_ignore           = ' \t\x0c'
+t_ignore = ' \t\x0c'
 
 # Newlines
+
+
 def t_NEWLINE(t):
     r'\n+'
     t.lexer.lineno += t.value.count("\n")
-    
+
 # Operators
-t_PLUS             = r'\+'
-t_MINUS            = r'-'
-t_TIMES            = r'\*'
-t_DIVIDE           = r'/'
-t_MOD              = r'%'
-t_OR               = r'\|'
-t_AND              = r'&'
-t_NOT              = r'~'
-t_XOR              = r'\^'
-t_LSHIFT           = r'<<'
-t_RSHIFT           = r'>>'
-t_LOR              = r'\|\|'
-t_LAND             = r'&&'
-t_LNOT             = r'!'
-t_LT               = r'<'
-t_GT               = r'>'
-t_LE               = r'<='
-t_GE               = r'>='
-t_EQ               = r'=='
-t_NE               = r'!='
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_TIMES = r'\*'
+t_DIVIDE = r'/'
+t_MOD = r'%'
+t_OR = r'\|'
+t_AND = r'&'
+t_NOT = r'~'
+t_XOR = r'\^'
+t_LSHIFT = r'<<'
+t_RSHIFT = r'>>'
+t_LOR = r'\|\|'
+t_LAND = r'&&'
+t_LNOT = r'!'
+t_LT = r'<'
+t_GT = r'>'
+t_LE = r'<='
+t_GE = r'>='
+t_EQ = r'=='
+t_NE = r'!='
 
 # Assignment operators
 
-t_EQUALS           = r'='
-t_TIMESEQUAL       = r'\*='
-t_DIVEQUAL         = r'/='
-t_MODEQUAL         = r'%='
-t_PLUSEQUAL        = r'\+='
-t_MINUSEQUAL       = r'-='
-t_LSHIFTEQUAL      = r'<<='
-t_RSHIFTEQUAL      = r'>>='
-t_ANDEQUAL         = r'&='
-t_OREQUAL          = r'\|='
-t_XOREQUAL         = r'^='
+t_EQUALS = r'='
+t_TIMESEQUAL = r'\*='
+t_DIVEQUAL = r'/='
+t_MODEQUAL = r'%='
+t_PLUSEQUAL = r'\+='
+t_MINUSEQUAL = r'-='
+t_LSHIFTEQUAL = r'<<='
+t_RSHIFTEQUAL = r'>>='
+t_ANDEQUAL = r'&='
+t_OREQUAL = r'\|='
+t_XOREQUAL = r'\^='
 
 # Increment/decrement
-t_PLUSPLUS         = r'\+\+'
-t_MINUSMINUS       = r'--'
+t_PLUSPLUS = r'\+\+'
+t_MINUSMINUS = r'--'
 
 # ->
-t_ARROW            = r'->'
+t_ARROW = r'->'
 
 # ?
-t_CONDOP           = r'\?'
+t_CONDOP = r'\?'
 
 # Delimeters
-t_LPAREN           = r'\('
-t_RPAREN           = r'\)'
-t_LBRACKET         = r'\['
-t_RBRACKET         = r'\]'
-t_LBRACE           = r'\{'
-t_RBRACE           = r'\}'
-t_COMMA            = r','
-t_PERIOD           = r'\.'
-t_SEMI             = r';'
-t_COLON            = r':'
-t_ELLIPSIS         = r'\.\.\.'
+t_LPAREN = r'\('
+t_RPAREN = r'\)'
+t_LBRACKET = r'\['
+t_RBRACKET = r'\]'
+t_LBRACE = r'\{'
+t_RBRACE = r'\}'
+t_COMMA = r','
+t_PERIOD = r'\.'
+t_SEMI = r';'
+t_COLON = r':'
+t_ELLIPSIS = r'\.\.\.'
 
 # Identifiers and reserved words
 
-reserved_map = { }
+reserved_map = {}
 for r in reserved:
     reserved_map[r.lower()] = r
 
+
 def t_ID(t):
     r'[A-Za-z_][\w_]*'
-    t.type = reserved_map.get(t.value,"ID")
+    t.type = reserved_map.get(t.value, "ID")
     return t
 
 # Integer literal
@@ -141,24 +145,24 @@
 t_CCONST = r'(L)?\'([^\\\n]|(\\.))*?\''
 
 # Comments
+
+
 def t_comment(t):
     r'/\*(.|\n)*?\*/'
     t.lexer.lineno += t.value.count('\n')
 
 # Preprocessor directive (ignored)
+
+
 def t_preprocessor(t):
     r'\#(.)*?\n'
     t.lexer.lineno += 1
-    
+
+
 def t_error(t):
     print("Illegal character %s" % repr(t.value[0]))
     t.lexer.skip(1)
-    
-lexer = lex.lex(optimize=1)
+
+lexer = lex.lex()
 if __name__ == "__main__":
     lex.runmain(lexer)
-
-    
-
-
-
diff --git a/ext/ply/example/ansic/cparse.py b/ext/ply/example/ansic/cparse.py
index c9b9164..5fe9bce 100644
--- a/ext/ply/example/ansic/cparse.py
+++ b/ext/ply/example/ansic/cparse.py
@@ -13,88 +13,109 @@
 
 # translation-unit:
 
+
 def p_translation_unit_1(t):
     'translation_unit : external_declaration'
     pass
 
+
 def p_translation_unit_2(t):
     'translation_unit : translation_unit external_declaration'
     pass
 
 # external-declaration:
 
+
 def p_external_declaration_1(t):
     'external_declaration : function_definition'
     pass
 
+
 def p_external_declaration_2(t):
     'external_declaration : declaration'
     pass
 
 # function-definition:
 
+
 def p_function_definition_1(t):
     'function_definition : declaration_specifiers declarator declaration_list compound_statement'
     pass
 
+
 def p_function_definition_2(t):
     'function_definition : declarator declaration_list compound_statement'
     pass
 
+
 def p_function_definition_3(t):
     'function_definition : declarator compound_statement'
     pass
 
+
 def p_function_definition_4(t):
     'function_definition : declaration_specifiers declarator compound_statement'
     pass
 
 # declaration:
 
+
 def p_declaration_1(t):
     'declaration : declaration_specifiers init_declarator_list SEMI'
     pass
 
+
 def p_declaration_2(t):
     'declaration : declaration_specifiers SEMI'
     pass
 
 # declaration-list:
 
+
 def p_declaration_list_1(t):
     'declaration_list : declaration'
     pass
 
+
 def p_declaration_list_2(t):
     'declaration_list : declaration_list declaration '
     pass
 
 # declaration-specifiers
+
+
 def p_declaration_specifiers_1(t):
     'declaration_specifiers : storage_class_specifier declaration_specifiers'
     pass
 
+
 def p_declaration_specifiers_2(t):
     'declaration_specifiers : type_specifier declaration_specifiers'
     pass
 
+
 def p_declaration_specifiers_3(t):
     'declaration_specifiers : type_qualifier declaration_specifiers'
     pass
 
+
 def p_declaration_specifiers_4(t):
     'declaration_specifiers : storage_class_specifier'
     pass
 
+
 def p_declaration_specifiers_5(t):
     'declaration_specifiers : type_specifier'
     pass
 
+
 def p_declaration_specifiers_6(t):
     'declaration_specifiers : type_qualifier'
     pass
 
 # storage-class-specifier
+
+
 def p_storage_class_specifier(t):
     '''storage_class_specifier : AUTO
                                | REGISTER
@@ -105,6 +126,8 @@
     pass
 
 # type-specifier:
+
+
 def p_type_specifier(t):
     '''type_specifier : VOID
                       | CHAR
@@ -122,6 +145,8 @@
     pass
 
 # type-qualifier:
+
+
 def p_type_qualifier(t):
     '''type_qualifier : CONST
                       | VOLATILE'''
@@ -129,19 +154,24 @@
 
 # struct-or-union-specifier
 
+
 def p_struct_or_union_specifier_1(t):
     'struct_or_union_specifier : struct_or_union ID LBRACE struct_declaration_list RBRACE'
     pass
 
+
 def p_struct_or_union_specifier_2(t):
     'struct_or_union_specifier : struct_or_union LBRACE struct_declaration_list RBRACE'
     pass
 
+
 def p_struct_or_union_specifier_3(t):
     'struct_or_union_specifier : struct_or_union ID'
     pass
 
 # struct-or-union:
+
+
 def p_struct_or_union(t):
     '''struct_or_union : STRUCT
                        | UNION
@@ -150,221 +180,273 @@
 
 # struct-declaration-list:
 
+
 def p_struct_declaration_list_1(t):
     'struct_declaration_list : struct_declaration'
     pass
 
+
 def p_struct_declaration_list_2(t):
     'struct_declaration_list : struct_declaration_list struct_declaration'
     pass
 
 # init-declarator-list:
 
+
 def p_init_declarator_list_1(t):
     'init_declarator_list : init_declarator'
     pass
 
+
 def p_init_declarator_list_2(t):
     'init_declarator_list : init_declarator_list COMMA init_declarator'
     pass
 
 # init-declarator
 
+
 def p_init_declarator_1(t):
     'init_declarator : declarator'
     pass
 
+
 def p_init_declarator_2(t):
     'init_declarator : declarator EQUALS initializer'
     pass
 
 # struct-declaration:
 
+
 def p_struct_declaration(t):
     'struct_declaration : specifier_qualifier_list struct_declarator_list SEMI'
     pass
 
 # specifier-qualifier-list:
 
+
 def p_specifier_qualifier_list_1(t):
     'specifier_qualifier_list : type_specifier specifier_qualifier_list'
     pass
 
+
 def p_specifier_qualifier_list_2(t):
     'specifier_qualifier_list : type_specifier'
     pass
 
+
 def p_specifier_qualifier_list_3(t):
     'specifier_qualifier_list : type_qualifier specifier_qualifier_list'
     pass
 
+
 def p_specifier_qualifier_list_4(t):
     'specifier_qualifier_list : type_qualifier'
     pass
 
 # struct-declarator-list:
 
+
 def p_struct_declarator_list_1(t):
     'struct_declarator_list : struct_declarator'
     pass
 
+
 def p_struct_declarator_list_2(t):
     'struct_declarator_list : struct_declarator_list COMMA struct_declarator'
     pass
 
 # struct-declarator:
 
+
 def p_struct_declarator_1(t):
     'struct_declarator : declarator'
     pass
 
+
 def p_struct_declarator_2(t):
     'struct_declarator : declarator COLON constant_expression'
     pass
 
+
 def p_struct_declarator_3(t):
     'struct_declarator : COLON constant_expression'
     pass
 
 # enum-specifier:
 
+
 def p_enum_specifier_1(t):
     'enum_specifier : ENUM ID LBRACE enumerator_list RBRACE'
     pass
 
+
 def p_enum_specifier_2(t):
     'enum_specifier : ENUM LBRACE enumerator_list RBRACE'
     pass
 
+
 def p_enum_specifier_3(t):
     'enum_specifier : ENUM ID'
     pass
 
 # enumerator_list:
+
+
 def p_enumerator_list_1(t):
     'enumerator_list : enumerator'
     pass
 
+
 def p_enumerator_list_2(t):
     'enumerator_list : enumerator_list COMMA enumerator'
     pass
 
 # enumerator:
+
+
 def p_enumerator_1(t):
     'enumerator : ID'
     pass
 
+
 def p_enumerator_2(t):
     'enumerator : ID EQUALS constant_expression'
     pass
 
 # declarator:
 
+
 def p_declarator_1(t):
     'declarator : pointer direct_declarator'
     pass
 
+
 def p_declarator_2(t):
     'declarator : direct_declarator'
     pass
 
 # direct-declarator:
 
+
 def p_direct_declarator_1(t):
     'direct_declarator : ID'
     pass
 
+
 def p_direct_declarator_2(t):
     'direct_declarator : LPAREN declarator RPAREN'
     pass
 
+
 def p_direct_declarator_3(t):
     'direct_declarator : direct_declarator LBRACKET constant_expression_opt RBRACKET'
     pass
 
+
 def p_direct_declarator_4(t):
     'direct_declarator : direct_declarator LPAREN parameter_type_list RPAREN '
     pass
 
+
 def p_direct_declarator_5(t):
     'direct_declarator : direct_declarator LPAREN identifier_list RPAREN '
     pass
 
+
 def p_direct_declarator_6(t):
     'direct_declarator : direct_declarator LPAREN RPAREN '
     pass
 
 # pointer:
+
+
 def p_pointer_1(t):
     'pointer : TIMES type_qualifier_list'
     pass
 
+
 def p_pointer_2(t):
     'pointer : TIMES'
     pass
 
+
 def p_pointer_3(t):
     'pointer : TIMES type_qualifier_list pointer'
     pass
 
+
 def p_pointer_4(t):
     'pointer : TIMES pointer'
     pass
 
 # type-qualifier-list:
 
+
 def p_type_qualifier_list_1(t):
     'type_qualifier_list : type_qualifier'
     pass
 
+
 def p_type_qualifier_list_2(t):
     'type_qualifier_list : type_qualifier_list type_qualifier'
     pass
 
 # parameter-type-list:
 
+
 def p_parameter_type_list_1(t):
     'parameter_type_list : parameter_list'
     pass
 
+
 def p_parameter_type_list_2(t):
     'parameter_type_list : parameter_list COMMA ELLIPSIS'
     pass
 
 # parameter-list:
 
+
 def p_parameter_list_1(t):
     'parameter_list : parameter_declaration'
     pass
 
+
 def p_parameter_list_2(t):
     'parameter_list : parameter_list COMMA parameter_declaration'
     pass
 
 # parameter-declaration:
+
+
 def p_parameter_declaration_1(t):
     'parameter_declaration : declaration_specifiers declarator'
     pass
 
+
 def p_parameter_declaration_2(t):
     'parameter_declaration : declaration_specifiers abstract_declarator_opt'
     pass
 
 # identifier-list:
+
+
 def p_identifier_list_1(t):
     'identifier_list : ID'
     pass
 
+
 def p_identifier_list_2(t):
     'identifier_list : identifier_list COMMA ID'
     pass
 
 # initializer:
 
+
 def p_initializer_1(t):
     'initializer : assignment_expression'
     pass
 
+
 def p_initializer_2(t):
     '''initializer : LBRACE initializer_list RBRACE
                    | LBRACE initializer_list COMMA RBRACE'''
@@ -372,84 +454,102 @@
 
 # initializer-list:
 
+
 def p_initializer_list_1(t):
     'initializer_list : initializer'
     pass
 
+
 def p_initializer_list_2(t):
     'initializer_list : initializer_list COMMA initializer'
     pass
 
 # type-name:
 
+
 def p_type_name(t):
     'type_name : specifier_qualifier_list abstract_declarator_opt'
     pass
 
+
 def p_abstract_declarator_opt_1(t):
     'abstract_declarator_opt : empty'
     pass
 
+
 def p_abstract_declarator_opt_2(t):
     'abstract_declarator_opt : abstract_declarator'
     pass
 
 # abstract-declarator:
 
+
 def p_abstract_declarator_1(t):
     'abstract_declarator : pointer '
     pass
 
+
 def p_abstract_declarator_2(t):
     'abstract_declarator : pointer direct_abstract_declarator'
     pass
 
+
 def p_abstract_declarator_3(t):
     'abstract_declarator : direct_abstract_declarator'
     pass
 
 # direct-abstract-declarator:
 
+
 def p_direct_abstract_declarator_1(t):
     'direct_abstract_declarator : LPAREN abstract_declarator RPAREN'
     pass
 
+
 def p_direct_abstract_declarator_2(t):
     'direct_abstract_declarator : direct_abstract_declarator LBRACKET constant_expression_opt RBRACKET'
     pass
 
+
 def p_direct_abstract_declarator_3(t):
     'direct_abstract_declarator : LBRACKET constant_expression_opt RBRACKET'
     pass
 
+
 def p_direct_abstract_declarator_4(t):
     'direct_abstract_declarator : direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN'
     pass
 
+
 def p_direct_abstract_declarator_5(t):
     'direct_abstract_declarator : LPAREN parameter_type_list_opt RPAREN'
     pass
 
 # Optional fields in abstract declarators
 
+
 def p_constant_expression_opt_1(t):
     'constant_expression_opt : empty'
     pass
 
+
 def p_constant_expression_opt_2(t):
     'constant_expression_opt : constant_expression'
     pass
 
+
 def p_parameter_type_list_opt_1(t):
     'parameter_type_list_opt : empty'
     pass
 
+
 def p_parameter_type_list_opt_2(t):
     'parameter_type_list_opt : parameter_type_list'
     pass
 
 # statement:
 
+
 def p_statement(t):
     '''
     statement : labeled_statement
@@ -463,124 +563,155 @@
 
 # labeled-statement:
 
+
 def p_labeled_statement_1(t):
     'labeled_statement : ID COLON statement'
     pass
 
+
 def p_labeled_statement_2(t):
     'labeled_statement : CASE constant_expression COLON statement'
     pass
 
+
 def p_labeled_statement_3(t):
     'labeled_statement : DEFAULT COLON statement'
     pass
 
 # expression-statement:
+
+
 def p_expression_statement(t):
     'expression_statement : expression_opt SEMI'
     pass
 
 # compound-statement:
 
+
 def p_compound_statement_1(t):
     'compound_statement : LBRACE declaration_list statement_list RBRACE'
     pass
 
+
 def p_compound_statement_2(t):
     'compound_statement : LBRACE statement_list RBRACE'
     pass
 
+
 def p_compound_statement_3(t):
     'compound_statement : LBRACE declaration_list RBRACE'
     pass
 
+
 def p_compound_statement_4(t):
     'compound_statement : LBRACE RBRACE'
     pass
 
 # statement-list:
 
+
 def p_statement_list_1(t):
     'statement_list : statement'
     pass
 
+
 def p_statement_list_2(t):
     'statement_list : statement_list statement'
     pass
 
 # selection-statement
 
+
 def p_selection_statement_1(t):
     'selection_statement : IF LPAREN expression RPAREN statement'
     pass
 
+
 def p_selection_statement_2(t):
     'selection_statement : IF LPAREN expression RPAREN statement ELSE statement '
     pass
 
+
 def p_selection_statement_3(t):
     'selection_statement : SWITCH LPAREN expression RPAREN statement '
     pass
 
 # iteration_statement:
 
+
 def p_iteration_statement_1(t):
     'iteration_statement : WHILE LPAREN expression RPAREN statement'
     pass
 
+
 def p_iteration_statement_2(t):
     'iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN statement '
     pass
 
+
 def p_iteration_statement_3(t):
     'iteration_statement : DO statement WHILE LPAREN expression RPAREN SEMI'
     pass
 
 # jump_statement:
 
+
 def p_jump_statement_1(t):
     'jump_statement : GOTO ID SEMI'
     pass
 
+
 def p_jump_statement_2(t):
     'jump_statement : CONTINUE SEMI'
     pass
 
+
 def p_jump_statement_3(t):
     'jump_statement : BREAK SEMI'
     pass
 
+
 def p_jump_statement_4(t):
     'jump_statement : RETURN expression_opt SEMI'
     pass
 
+
 def p_expression_opt_1(t):
     'expression_opt : empty'
     pass
 
+
 def p_expression_opt_2(t):
     'expression_opt : expression'
     pass
 
 # expression:
+
+
 def p_expression_1(t):
     'expression : assignment_expression'
     pass
 
+
 def p_expression_2(t):
     'expression : expression COMMA assignment_expression'
     pass
 
 # assigment_expression:
+
+
 def p_assignment_expression_1(t):
     'assignment_expression : conditional_expression'
     pass
 
+
 def p_assignment_expression_2(t):
     'assignment_expression : unary_expression assignment_operator assignment_expression'
     pass
 
 # assignment_operator:
+
+
 def p_assignment_operator(t):
     '''
     assignment_operator : EQUALS
@@ -598,66 +729,80 @@
     pass
 
 # conditional-expression
+
+
 def p_conditional_expression_1(t):
     'conditional_expression : logical_or_expression'
     pass
 
+
 def p_conditional_expression_2(t):
     'conditional_expression : logical_or_expression CONDOP expression COLON conditional_expression '
     pass
 
 # constant-expression
 
+
 def p_constant_expression(t):
     'constant_expression : conditional_expression'
     pass
 
 # logical-or-expression
 
+
 def p_logical_or_expression_1(t):
     'logical_or_expression : logical_and_expression'
     pass
 
+
 def p_logical_or_expression_2(t):
     'logical_or_expression : logical_or_expression LOR logical_and_expression'
     pass
 
 # logical-and-expression
 
+
 def p_logical_and_expression_1(t):
     'logical_and_expression : inclusive_or_expression'
     pass
 
+
 def p_logical_and_expression_2(t):
     'logical_and_expression : logical_and_expression LAND inclusive_or_expression'
     pass
 
 # inclusive-or-expression:
 
+
 def p_inclusive_or_expression_1(t):
     'inclusive_or_expression : exclusive_or_expression'
     pass
 
+
 def p_inclusive_or_expression_2(t):
     'inclusive_or_expression : inclusive_or_expression OR exclusive_or_expression'
     pass
 
 # exclusive-or-expression:
 
+
 def p_exclusive_or_expression_1(t):
     'exclusive_or_expression :  and_expression'
     pass
 
+
 def p_exclusive_or_expression_2(t):
     'exclusive_or_expression :  exclusive_or_expression XOR and_expression'
     pass
 
 # AND-expression
 
+
 def p_and_expression_1(t):
     'and_expression : equality_expression'
     pass
 
+
 def p_and_expression_2(t):
     'and_expression : and_expression AND equality_expression'
     pass
@@ -668,10 +813,12 @@
     'equality_expression : relational_expression'
     pass
 
+
 def p_equality_expression_2(t):
     'equality_expression : equality_expression EQ relational_expression'
     pass
 
+
 def p_equality_expression_3(t):
     'equality_expression : equality_expression NE relational_expression'
     pass
@@ -682,104 +829,129 @@
     'relational_expression : shift_expression'
     pass
 
+
 def p_relational_expression_2(t):
     'relational_expression : relational_expression LT shift_expression'
     pass
 
+
 def p_relational_expression_3(t):
     'relational_expression : relational_expression GT shift_expression'
     pass
 
+
 def p_relational_expression_4(t):
     'relational_expression : relational_expression LE shift_expression'
     pass
 
+
 def p_relational_expression_5(t):
     'relational_expression : relational_expression GE shift_expression'
     pass
 
 # shift-expression
 
+
 def p_shift_expression_1(t):
     'shift_expression : additive_expression'
     pass
 
+
 def p_shift_expression_2(t):
     'shift_expression : shift_expression LSHIFT additive_expression'
     pass
 
+
 def p_shift_expression_3(t):
     'shift_expression : shift_expression RSHIFT additive_expression'
     pass
 
 # additive-expression
 
+
 def p_additive_expression_1(t):
     'additive_expression : multiplicative_expression'
     pass
 
+
 def p_additive_expression_2(t):
     'additive_expression : additive_expression PLUS multiplicative_expression'
     pass
 
+
 def p_additive_expression_3(t):
     'additive_expression : additive_expression MINUS multiplicative_expression'
     pass
 
 # multiplicative-expression
 
+
 def p_multiplicative_expression_1(t):
     'multiplicative_expression : cast_expression'
     pass
 
+
 def p_multiplicative_expression_2(t):
     'multiplicative_expression : multiplicative_expression TIMES cast_expression'
     pass
 
+
 def p_multiplicative_expression_3(t):
     'multiplicative_expression : multiplicative_expression DIVIDE cast_expression'
     pass
 
+
 def p_multiplicative_expression_4(t):
     'multiplicative_expression : multiplicative_expression MOD cast_expression'
     pass
 
 # cast-expression:
 
+
 def p_cast_expression_1(t):
     'cast_expression : unary_expression'
     pass
 
+
 def p_cast_expression_2(t):
     'cast_expression : LPAREN type_name RPAREN cast_expression'
     pass
 
 # unary-expression:
+
+
 def p_unary_expression_1(t):
     'unary_expression : postfix_expression'
     pass
 
+
 def p_unary_expression_2(t):
     'unary_expression : PLUSPLUS unary_expression'
     pass
 
+
 def p_unary_expression_3(t):
     'unary_expression : MINUSMINUS unary_expression'
     pass
 
+
 def p_unary_expression_4(t):
     'unary_expression : unary_operator cast_expression'
     pass
 
+
 def p_unary_expression_5(t):
     'unary_expression : SIZEOF unary_expression'
     pass
 
+
 def p_unary_expression_6(t):
     'unary_expression : SIZEOF LPAREN type_name RPAREN'
     pass
-    
-#unary-operator
+
+# unary-operator
+
+
 def p_unary_operator(t):
     '''unary_operator : AND
                     | TIMES
@@ -790,39 +962,50 @@
     pass
 
 # postfix-expression:
+
+
 def p_postfix_expression_1(t):
     'postfix_expression : primary_expression'
     pass
 
+
 def p_postfix_expression_2(t):
     'postfix_expression : postfix_expression LBRACKET expression RBRACKET'
     pass
 
+
 def p_postfix_expression_3(t):
     'postfix_expression : postfix_expression LPAREN argument_expression_list RPAREN'
     pass
 
+
 def p_postfix_expression_4(t):
     'postfix_expression : postfix_expression LPAREN RPAREN'
     pass
 
+
 def p_postfix_expression_5(t):
     'postfix_expression : postfix_expression PERIOD ID'
     pass
 
+
 def p_postfix_expression_6(t):
     'postfix_expression : postfix_expression ARROW ID'
     pass
 
+
 def p_postfix_expression_7(t):
     'postfix_expression : postfix_expression PLUSPLUS'
     pass
 
+
 def p_postfix_expression_8(t):
     'postfix_expression : postfix_expression MINUSMINUS'
     pass
 
 # primary-expression:
+
+
 def p_primary_expression(t):
     '''primary_expression :  ID
                         |  constant
@@ -831,33 +1014,35 @@
     pass
 
 # argument-expression-list:
+
+
 def p_argument_expression_list(t):
     '''argument_expression_list :  assignment_expression
                               |  argument_expression_list COMMA assignment_expression'''
     pass
 
 # constant:
-def p_constant(t): 
-   '''constant : ICONST
-              | FCONST
-              | CCONST'''
-   pass
+
+
+def p_constant(t):
+    '''constant : ICONST
+               | FCONST
+               | CCONST'''
+    pass
 
 
 def p_empty(t):
     'empty : '
     pass
 
+
 def p_error(t):
     print("Whoa. We're hosed")
 
 import profile
 # Build the grammar
 
-yacc.yacc(method='LALR')
+yacc.yacc()
+#yacc.yacc(method='LALR',write_tables=False,debug=False)
 
 #profile.run("yacc.yacc(method='LALR')")
-
-
-
-
diff --git a/ext/ply/example/calc/calc.py b/ext/ply/example/calc/calc.py
index b923780..824c3d7 100644
--- a/ext/ply/example/calc/calc.py
+++ b/ext/ply/example/calc/calc.py
@@ -6,20 +6,21 @@
 # -----------------------------------------------------------------------------
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 if sys.version_info[0] >= 3:
     raw_input = input
 
 tokens = (
-    'NAME','NUMBER',
-    )
+    'NAME', 'NUMBER',
+)
 
-literals = ['=','+','-','*','/', '(',')']
+literals = ['=', '+', '-', '*', '/', '(', ')']
 
 # Tokens
 
-t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
 
 def t_NUMBER(t):
     r'\d+'
@@ -28,14 +29,16 @@
 
 t_ignore = " \t"
 
+
 def t_newline(t):
     r'\n+'
     t.lexer.lineno += t.value.count("\n")
-    
+
+
 def t_error(t):
     print("Illegal character '%s'" % t.value[0])
     t.lexer.skip(1)
-    
+
 # Build the lexer
 import ply.lex as lex
 lex.lex()
@@ -43,44 +46,55 @@
 # Parsing rules
 
 precedence = (
-    ('left','+','-'),
-    ('left','*','/'),
-    ('right','UMINUS'),
-    )
+    ('left', '+', '-'),
+    ('left', '*', '/'),
+    ('right', 'UMINUS'),
+)
 
 # dictionary of names
-names = { }
+names = {}
+
 
 def p_statement_assign(p):
     'statement : NAME "=" expression'
     names[p[1]] = p[3]
 
+
 def p_statement_expr(p):
     'statement : expression'
     print(p[1])
 
+
 def p_expression_binop(p):
     '''expression : expression '+' expression
                   | expression '-' expression
                   | expression '*' expression
                   | expression '/' expression'''
-    if p[2] == '+'  : p[0] = p[1] + p[3]
-    elif p[2] == '-': p[0] = p[1] - p[3]
-    elif p[2] == '*': p[0] = p[1] * p[3]
-    elif p[2] == '/': p[0] = p[1] / p[3]
+    if p[2] == '+':
+        p[0] = p[1] + p[3]
+    elif p[2] == '-':
+        p[0] = p[1] - p[3]
+    elif p[2] == '*':
+        p[0] = p[1] * p[3]
+    elif p[2] == '/':
+        p[0] = p[1] / p[3]
+
 
 def p_expression_uminus(p):
     "expression : '-' expression %prec UMINUS"
     p[0] = -p[2]
 
+
 def p_expression_group(p):
     "expression : '(' expression ')'"
     p[0] = p[2]
 
+
 def p_expression_number(p):
     "expression : NUMBER"
     p[0] = p[1]
 
+
 def p_expression_name(p):
     "expression : NAME"
     try:
@@ -89,6 +103,7 @@
         print("Undefined name '%s'" % p[1])
         p[0] = 0
 
+
 def p_error(p):
     if p:
         print("Syntax error at '%s'" % p.value)
@@ -103,5 +118,6 @@
         s = raw_input('calc > ')
     except EOFError:
         break
-    if not s: continue
+    if not s:
+        continue
     yacc.parse(s)
diff --git a/ext/ply/example/calcdebug/calc.py b/ext/ply/example/calcdebug/calc.py
index 6732f9f..06831e2 100644
--- a/ext/ply/example/calcdebug/calc.py
+++ b/ext/ply/example/calcdebug/calc.py
@@ -6,20 +6,21 @@
 # -----------------------------------------------------------------------------
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 if sys.version_info[0] >= 3:
     raw_input = input
 
 tokens = (
-    'NAME','NUMBER',
-    )
+    'NAME', 'NUMBER',
+)
 
-literals = ['=','+','-','*','/', '(',')']
+literals = ['=', '+', '-', '*', '/', '(', ')']
 
 # Tokens
 
-t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
 
 def t_NUMBER(t):
     r'\d+'
@@ -28,14 +29,16 @@
 
 t_ignore = " \t"
 
+
 def t_newline(t):
     r'\n+'
     t.lexer.lineno += t.value.count("\n")
-    
+
+
 def t_error(t):
     print("Illegal character '%s'" % t.value[0])
     t.lexer.skip(1)
-    
+
 # Build the lexer
 import ply.lex as lex
 lex.lex()
@@ -43,44 +46,55 @@
 # Parsing rules
 
 precedence = (
-    ('left','+','-'),
-    ('left','*','/'),
-    ('right','UMINUS'),
-    )
+    ('left', '+', '-'),
+    ('left', '*', '/'),
+    ('right', 'UMINUS'),
+)
 
 # dictionary of names
-names = { }
+names = {}
+
 
 def p_statement_assign(p):
     'statement : NAME "=" expression'
     names[p[1]] = p[3]
 
+
 def p_statement_expr(p):
     'statement : expression'
     print(p[1])
 
+
 def p_expression_binop(p):
     '''expression : expression '+' expression
                   | expression '-' expression
                   | expression '*' expression
                   | expression '/' expression'''
-    if p[2] == '+'  : p[0] = p[1] + p[3]
-    elif p[2] == '-': p[0] = p[1] - p[3]
-    elif p[2] == '*': p[0] = p[1] * p[3]
-    elif p[2] == '/': p[0] = p[1] / p[3]
+    if p[2] == '+':
+        p[0] = p[1] + p[3]
+    elif p[2] == '-':
+        p[0] = p[1] - p[3]
+    elif p[2] == '*':
+        p[0] = p[1] * p[3]
+    elif p[2] == '/':
+        p[0] = p[1] / p[3]
+
 
 def p_expression_uminus(p):
     "expression : '-' expression %prec UMINUS"
     p[0] = -p[2]
 
+
 def p_expression_group(p):
     "expression : '(' expression ')'"
     p[0] = p[2]
 
+
 def p_expression_number(p):
     "expression : NUMBER"
     p[0] = p[1]
 
+
 def p_expression_name(p):
     "expression : NAME"
     try:
@@ -89,6 +103,7 @@
         print("Undefined name '%s'" % p[1])
         p[0] = 0
 
+
 def p_error(p):
     if p:
         print("Syntax error at '%s'" % p.value)
@@ -109,5 +124,6 @@
         s = raw_input('calc > ')
     except EOFError:
         break
-    if not s: continue
-    yacc.parse(s,debug=logging.getLogger())
+    if not s:
+        continue
+    yacc.parse(s, debug=logging.getLogger())
diff --git a/ext/ply/example/calceof/calc.py b/ext/ply/example/calceof/calc.py
new file mode 100644
index 0000000..22b39a4
--- /dev/null
+++ b/ext/ply/example/calceof/calc.py
@@ -0,0 +1,132 @@
+# -----------------------------------------------------------------------------
+# calc.py
+#
+# A simple calculator with variables.  Asks the user for more input and
+# demonstrates the use of the t_eof() rule.
+# -----------------------------------------------------------------------------
+
+import sys
+sys.path.insert(0, "../..")
+
+if sys.version_info[0] >= 3:
+    raw_input = input
+
+tokens = (
+    'NAME', 'NUMBER',
+)
+
+literals = ['=', '+', '-', '*', '/', '(', ')']
+
+# Tokens
+
+t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
+
+def t_NUMBER(t):
+    r'\d+'
+    t.value = int(t.value)
+    return t
+
+t_ignore = " \t"
+
+
+def t_newline(t):
+    r'\n+'
+    t.lexer.lineno += t.value.count("\n")
+
+
+def t_eof(t):
+    more = raw_input('... ')
+    if more:
+        t.lexer.input(more + '\n')
+        return t.lexer.token()
+    else:
+        return None
+
+
+def t_error(t):
+    print("Illegal character '%s'" % t.value[0])
+    t.lexer.skip(1)
+
+# Build the lexer
+import ply.lex as lex
+lex.lex()
+
+# Parsing rules
+
+precedence = (
+    ('left', '+', '-'),
+    ('left', '*', '/'),
+    ('right', 'UMINUS'),
+)
+
+# dictionary of names
+names = {}
+
+
+def p_statement_assign(p):
+    'statement : NAME "=" expression'
+    names[p[1]] = p[3]
+
+
+def p_statement_expr(p):
+    'statement : expression'
+    print(p[1])
+
+
+def p_expression_binop(p):
+    '''expression : expression '+' expression
+                  | expression '-' expression
+                  | expression '*' expression
+                  | expression '/' expression'''
+    if p[2] == '+':
+        p[0] = p[1] + p[3]
+    elif p[2] == '-':
+        p[0] = p[1] - p[3]
+    elif p[2] == '*':
+        p[0] = p[1] * p[3]
+    elif p[2] == '/':
+        p[0] = p[1] / p[3]
+
+
+def p_expression_uminus(p):
+    "expression : '-' expression %prec UMINUS"
+    p[0] = -p[2]
+
+
+def p_expression_group(p):
+    "expression : '(' expression ')'"
+    p[0] = p[2]
+
+
+def p_expression_number(p):
+    "expression : NUMBER"
+    p[0] = p[1]
+
+
+def p_expression_name(p):
+    "expression : NAME"
+    try:
+        p[0] = names[p[1]]
+    except LookupError:
+        print("Undefined name '%s'" % p[1])
+        p[0] = 0
+
+
+def p_error(p):
+    if p:
+        print("Syntax error at '%s'" % p.value)
+    else:
+        print("Syntax error at EOF")
+
+import ply.yacc as yacc
+yacc.yacc()
+
+while 1:
+    try:
+        s = raw_input('calc > ')
+    except EOFError:
+        break
+    if not s:
+        continue
+    yacc.parse(s + '\n')
diff --git a/ext/ply/example/classcalc/calc.py b/ext/ply/example/classcalc/calc.py
index b0712fc..ada4afd 100755
--- a/ext/ply/example/classcalc/calc.py
+++ b/ext/ply/example/classcalc/calc.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
 
 # -----------------------------------------------------------------------------
 # calc.py
@@ -10,7 +10,7 @@
 # -----------------------------------------------------------------------------
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 if sys.version_info[0] >= 3:
     raw_input = input
@@ -19,6 +19,7 @@
 import ply.yacc as yacc
 import os
 
+
 class Parser:
     """
     Base class for a lexer/parser that has the rules defined as methods
@@ -28,14 +29,15 @@
 
     def __init__(self, **kw):
         self.debug = kw.get('debug', 0)
-        self.names = { }
+        self.names = {}
         try:
-            modname = os.path.split(os.path.splitext(__file__)[0])[1] + "_" + self.__class__.__name__
+            modname = os.path.split(os.path.splitext(__file__)[0])[
+                1] + "_" + self.__class__.__name__
         except:
-            modname = "parser"+"_"+self.__class__.__name__
+            modname = "parser" + "_" + self.__class__.__name__
         self.debugfile = modname + ".dbg"
         self.tabmodule = modname + "_" + "parsetab"
-        #print self.debugfile, self.tabmodule
+        # print self.debugfile, self.tabmodule
 
         # Build the lexer and parser
         lex.lex(module=self, debug=self.debug)
@@ -50,29 +52,30 @@
                 s = raw_input('calc > ')
             except EOFError:
                 break
-            if not s: continue
+            if not s:
+                continue
             yacc.parse(s)
 
-    
+
 class Calc(Parser):
 
     tokens = (
-        'NAME','NUMBER',
-        'PLUS','MINUS','EXP', 'TIMES','DIVIDE','EQUALS',
-        'LPAREN','RPAREN',
-        )
+        'NAME', 'NUMBER',
+        'PLUS', 'MINUS', 'EXP', 'TIMES', 'DIVIDE', 'EQUALS',
+        'LPAREN', 'RPAREN',
+    )
 
     # Tokens
 
-    t_PLUS    = r'\+'
-    t_MINUS   = r'-'
-    t_EXP     = r'\*\*'
-    t_TIMES   = r'\*'
-    t_DIVIDE  = r'/'
-    t_EQUALS  = r'='
-    t_LPAREN  = r'\('
-    t_RPAREN  = r'\)'
-    t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+    t_PLUS = r'\+'
+    t_MINUS = r'-'
+    t_EXP = r'\*\*'
+    t_TIMES = r'\*'
+    t_DIVIDE = r'/'
+    t_EQUALS = r'='
+    t_LPAREN = r'\('
+    t_RPAREN = r'\)'
+    t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
 
     def t_NUMBER(self, t):
         r'\d+'
@@ -81,7 +84,7 @@
         except ValueError:
             print("Integer value too large %s" % t.value)
             t.value = 0
-        #print "parsed number %s" % repr(t.value)
+        # print "parsed number %s" % repr(t.value)
         return t
 
     t_ignore = " \t"
@@ -89,7 +92,7 @@
     def t_newline(self, t):
         r'\n+'
         t.lexer.lineno += t.value.count("\n")
-    
+
     def t_error(self, t):
         print("Illegal character '%s'" % t.value[0])
         t.lexer.skip(1)
@@ -97,11 +100,11 @@
     # Parsing rules
 
     precedence = (
-        ('left','PLUS','MINUS'),
-        ('left','TIMES','DIVIDE'),
+        ('left', 'PLUS', 'MINUS'),
+        ('left', 'TIMES', 'DIVIDE'),
         ('left', 'EXP'),
-        ('right','UMINUS'),
-        )
+        ('right', 'UMINUS'),
+    )
 
     def p_statement_assign(self, p):
         'statement : NAME EQUALS expression'
@@ -119,12 +122,17 @@
                   | expression DIVIDE expression
                   | expression EXP expression
         """
-        #print [repr(p[i]) for i in range(0,4)]
-        if p[2] == '+'  : p[0] = p[1] + p[3]
-        elif p[2] == '-': p[0] = p[1] - p[3]
-        elif p[2] == '*': p[0] = p[1] * p[3]
-        elif p[2] == '/': p[0] = p[1] / p[3]
-        elif p[2] == '**': p[0] = p[1] ** p[3]
+        # print [repr(p[i]) for i in range(0,4)]
+        if p[2] == '+':
+            p[0] = p[1] + p[3]
+        elif p[2] == '-':
+            p[0] = p[1] - p[3]
+        elif p[2] == '*':
+            p[0] = p[1] * p[3]
+        elif p[2] == '/':
+            p[0] = p[1] / p[3]
+        elif p[2] == '**':
+            p[0] = p[1] ** p[3]
 
     def p_expression_uminus(self, p):
         'expression : MINUS expression %prec UMINUS'
diff --git a/ext/ply/example/closurecalc/calc.py b/ext/ply/example/closurecalc/calc.py
index 6598f58..6031b05 100644
--- a/ext/ply/example/closurecalc/calc.py
+++ b/ext/ply/example/closurecalc/calc.py
@@ -2,37 +2,38 @@
 # calc.py
 #
 # A calculator parser that makes use of closures. The function make_calculator()
-# returns a function that accepts an input string and returns a result.  All 
+# returns a function that accepts an input string and returns a result.  All
 # lexing rules, parsing rules, and internal state are held inside the function.
 # -----------------------------------------------------------------------------
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 if sys.version_info[0] >= 3:
     raw_input = input
 
 # Make a calculator function
 
+
 def make_calculator():
     import ply.lex as lex
     import ply.yacc as yacc
 
     # ------- Internal calculator state
 
-    variables = { }       # Dictionary of stored variables
+    variables = {}       # Dictionary of stored variables
 
     # ------- Calculator tokenizing rules
 
     tokens = (
-        'NAME','NUMBER',
+        'NAME', 'NUMBER',
     )
 
-    literals = ['=','+','-','*','/', '(',')']
+    literals = ['=', '+', '-', '*', '/', '(', ')']
 
     t_ignore = " \t"
 
-    t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+    t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
 
     def t_NUMBER(t):
         r'\d+'
@@ -42,20 +43,20 @@
     def t_newline(t):
         r'\n+'
         t.lexer.lineno += t.value.count("\n")
-    
+
     def t_error(t):
         print("Illegal character '%s'" % t.value[0])
         t.lexer.skip(1)
-    
+
     # Build the lexer
     lexer = lex.lex()
 
     # ------- Calculator parsing rules
 
     precedence = (
-        ('left','+','-'),
-        ('left','*','/'),
-        ('right','UMINUS'),
+        ('left', '+', '-'),
+        ('left', '*', '/'),
+        ('right', 'UMINUS'),
     )
 
     def p_statement_assign(p):
@@ -72,10 +73,14 @@
                       | expression '-' expression
                       | expression '*' expression
                       | expression '/' expression'''
-        if p[2] == '+'  : p[0] = p[1] + p[3]
-        elif p[2] == '-': p[0] = p[1] - p[3]
-        elif p[2] == '*': p[0] = p[1] * p[3]
-        elif p[2] == '/': p[0] = p[1] / p[3]
+        if p[2] == '+':
+            p[0] = p[1] + p[3]
+        elif p[2] == '-':
+            p[0] = p[1] - p[3]
+        elif p[2] == '*':
+            p[0] = p[1] * p[3]
+        elif p[2] == '/':
+            p[0] = p[1] / p[3]
 
     def p_expression_uminus(p):
         "expression : '-' expression %prec UMINUS"
@@ -103,14 +108,13 @@
         else:
             print("Syntax error at EOF")
 
-
     # Build the parser
     parser = yacc.yacc()
 
-    # ------- Input function 
-    
+    # ------- Input function
+
     def input(text):
-        result = parser.parse(text,lexer=lexer)
+        result = parser.parse(text, lexer=lexer)
         return result
 
     return input
@@ -126,5 +130,3 @@
     r = calc(s)
     if r:
         print(r)
-
-    
diff --git a/ext/ply/example/hedit/hedit.py b/ext/ply/example/hedit/hedit.py
index 2e80675..32da745 100644
--- a/ext/ply/example/hedit/hedit.py
+++ b/ext/ply/example/hedit/hedit.py
@@ -15,34 +15,34 @@
 # -----------------------------------------------------------------------------
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 
 tokens = (
     'H_EDIT_DESCRIPTOR',
-    )
+)
 
 # Tokens
 t_ignore = " \t\n"
 
+
 def t_H_EDIT_DESCRIPTOR(t):
     r"\d+H.*"                     # This grabs all of the remaining text
     i = t.value.index('H')
     n = eval(t.value[:i])
-    
+
     # Adjust the tokenizing position
-    t.lexer.lexpos -= len(t.value) - (i+1+n)
-    
-    t.value = t.value[i+1:i+1+n]
-    return t                                  
-    
+    t.lexer.lexpos -= len(t.value) - (i + 1 + n)
+
+    t.value = t.value[i + 1:i + 1 + n]
+    return t
+
+
 def t_error(t):
     print("Illegal character '%s'" % t.value[0])
     t.lexer.skip(1)
-    
+
 # Build the lexer
 import ply.lex as lex
 lex.lex()
 lex.runmain()
-
-
diff --git a/ext/ply/example/newclasscalc/calc.py b/ext/ply/example/newclasscalc/calc.py
index 5a8db84..43c9506 100755
--- a/ext/ply/example/newclasscalc/calc.py
+++ b/ext/ply/example/newclasscalc/calc.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
 
 # -----------------------------------------------------------------------------
 # calc.py
@@ -12,7 +12,7 @@
 # -----------------------------------------------------------------------------
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 if sys.version_info[0] >= 3:
     raw_input = input
@@ -21,6 +21,7 @@
 import ply.yacc as yacc
 import os
 
+
 class Parser(object):
     """
     Base class for a lexer/parser that has the rules defined as methods
@@ -28,17 +29,17 @@
     tokens = ()
     precedence = ()
 
-
     def __init__(self, **kw):
         self.debug = kw.get('debug', 0)
-        self.names = { }
+        self.names = {}
         try:
-            modname = os.path.split(os.path.splitext(__file__)[0])[1] + "_" + self.__class__.__name__
+            modname = os.path.split(os.path.splitext(__file__)[0])[
+                1] + "_" + self.__class__.__name__
         except:
-            modname = "parser"+"_"+self.__class__.__name__
+            modname = "parser" + "_" + self.__class__.__name__
         self.debugfile = modname + ".dbg"
         self.tabmodule = modname + "_" + "parsetab"
-        #print self.debugfile, self.tabmodule
+        # print self.debugfile, self.tabmodule
 
         # Build the lexer and parser
         lex.lex(module=self, debug=self.debug)
@@ -53,29 +54,30 @@
                 s = raw_input('calc > ')
             except EOFError:
                 break
-            if not s: continue     
+            if not s:
+                continue
             yacc.parse(s)
 
-    
+
 class Calc(Parser):
 
     tokens = (
-        'NAME','NUMBER',
-        'PLUS','MINUS','EXP', 'TIMES','DIVIDE','EQUALS',
-        'LPAREN','RPAREN',
-        )
+        'NAME', 'NUMBER',
+        'PLUS', 'MINUS', 'EXP', 'TIMES', 'DIVIDE', 'EQUALS',
+        'LPAREN', 'RPAREN',
+    )
 
     # Tokens
 
-    t_PLUS    = r'\+'
-    t_MINUS   = r'-'
-    t_EXP     = r'\*\*'
-    t_TIMES   = r'\*'
-    t_DIVIDE  = r'/'
-    t_EQUALS  = r'='
-    t_LPAREN  = r'\('
-    t_RPAREN  = r'\)'
-    t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+    t_PLUS = r'\+'
+    t_MINUS = r'-'
+    t_EXP = r'\*\*'
+    t_TIMES = r'\*'
+    t_DIVIDE = r'/'
+    t_EQUALS = r'='
+    t_LPAREN = r'\('
+    t_RPAREN = r'\)'
+    t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
 
     def t_NUMBER(self, t):
         r'\d+'
@@ -84,7 +86,7 @@
         except ValueError:
             print("Integer value too large %s" % t.value)
             t.value = 0
-        #print "parsed number %s" % repr(t.value)
+        # print "parsed number %s" % repr(t.value)
         return t
 
     t_ignore = " \t"
@@ -92,7 +94,7 @@
     def t_newline(self, t):
         r'\n+'
         t.lexer.lineno += t.value.count("\n")
-    
+
     def t_error(self, t):
         print("Illegal character '%s'" % t.value[0])
         t.lexer.skip(1)
@@ -100,11 +102,11 @@
     # Parsing rules
 
     precedence = (
-        ('left','PLUS','MINUS'),
-        ('left','TIMES','DIVIDE'),
+        ('left', 'PLUS', 'MINUS'),
+        ('left', 'TIMES', 'DIVIDE'),
         ('left', 'EXP'),
-        ('right','UMINUS'),
-        )
+        ('right', 'UMINUS'),
+    )
 
     def p_statement_assign(self, p):
         'statement : NAME EQUALS expression'
@@ -122,12 +124,17 @@
                   | expression DIVIDE expression
                   | expression EXP expression
         """
-        #print [repr(p[i]) for i in range(0,4)]
-        if p[2] == '+'  : p[0] = p[1] + p[3]
-        elif p[2] == '-': p[0] = p[1] - p[3]
-        elif p[2] == '*': p[0] = p[1] * p[3]
-        elif p[2] == '/': p[0] = p[1] / p[3]
-        elif p[2] == '**': p[0] = p[1] ** p[3]
+        # print [repr(p[i]) for i in range(0,4)]
+        if p[2] == '+':
+            p[0] = p[1] + p[3]
+        elif p[2] == '-':
+            p[0] = p[1] - p[3]
+        elif p[2] == '*':
+            p[0] = p[1] * p[3]
+        elif p[2] == '/':
+            p[0] = p[1] / p[3]
+        elif p[2] == '**':
+            p[0] = p[1] ** p[3]
 
     def p_expression_uminus(self, p):
         'expression : MINUS expression %prec UMINUS'
diff --git a/ext/ply/example/optcalc/calc.py b/ext/ply/example/optcalc/calc.py
index dd83351..0c223e5 100644
--- a/ext/ply/example/optcalc/calc.py
+++ b/ext/ply/example/optcalc/calc.py
@@ -6,27 +6,28 @@
 # -----------------------------------------------------------------------------
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 if sys.version_info[0] >= 3:
     raw_input = input
 
 tokens = (
-    'NAME','NUMBER',
-    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
-    'LPAREN','RPAREN',
-    )
+    'NAME', 'NUMBER',
+    'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'EQUALS',
+    'LPAREN', 'RPAREN',
+)
 
 # Tokens
 
-t_PLUS    = r'\+'
-t_MINUS   = r'-'
-t_TIMES   = r'\*'
-t_DIVIDE  = r'/'
-t_EQUALS  = r'='
-t_LPAREN  = r'\('
-t_RPAREN  = r'\)'
-t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_TIMES = r'\*'
+t_DIVIDE = r'/'
+t_EQUALS = r'='
+t_LPAREN = r'\('
+t_RPAREN = r'\)'
+t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
 
 def t_NUMBER(t):
     r'\d+'
@@ -39,14 +40,16 @@
 
 t_ignore = " \t"
 
+
 def t_newline(t):
     r'\n+'
     t.lexer.lineno += t.value.count("\n")
-    
+
+
 def t_error(t):
     print("Illegal character '%s'" % t.value[0])
     t.lexer.skip(1)
-    
+
 # Build the lexer
 import ply.lex as lex
 lex.lex(optimize=1)
@@ -54,45 +57,57 @@
 # Parsing rules
 
 precedence = (
-    ('left','PLUS','MINUS'),
-    ('left','TIMES','DIVIDE'),
-    ('right','UMINUS'),
-    )
+    ('left', 'PLUS', 'MINUS'),
+    ('left', 'TIMES', 'DIVIDE'),
+    ('right', 'UMINUS'),
+)
 
 # dictionary of names
-names = { }
+names = {}
+
 
 def p_statement_assign(t):
     'statement : NAME EQUALS expression'
     names[t[1]] = t[3]
 
+
 def p_statement_expr(t):
     'statement : expression'
     print(t[1])
 
+
 def p_expression_binop(t):
     '''expression : expression PLUS expression
                   | expression MINUS expression
                   | expression TIMES expression
                   | expression DIVIDE expression'''
-    if t[2] == '+'  : t[0] = t[1] + t[3]
-    elif t[2] == '-': t[0] = t[1] - t[3]
-    elif t[2] == '*': t[0] = t[1] * t[3]
-    elif t[2] == '/': t[0] = t[1] / t[3]
-    elif t[2] == '<': t[0] = t[1] < t[3]
+    if t[2] == '+':
+        t[0] = t[1] + t[3]
+    elif t[2] == '-':
+        t[0] = t[1] - t[3]
+    elif t[2] == '*':
+        t[0] = t[1] * t[3]
+    elif t[2] == '/':
+        t[0] = t[1] / t[3]
+    elif t[2] == '<':
+        t[0] = t[1] < t[3]
+
 
 def p_expression_uminus(t):
     'expression : MINUS expression %prec UMINUS'
     t[0] = -t[2]
 
+
 def p_expression_group(t):
     'expression : LPAREN expression RPAREN'
     t[0] = t[2]
 
+
 def p_expression_number(t):
     'expression : NUMBER'
     t[0] = t[1]
 
+
 def p_expression_name(t):
     'expression : NAME'
     try:
@@ -101,6 +116,7 @@
         print("Undefined name '%s'" % t[1])
         t[0] = 0
 
+
 def p_error(t):
     if t:
         print("Syntax error at '%s'" % t.value)
@@ -116,4 +132,3 @@
     except EOFError:
         break
     yacc.parse(s)
-
diff --git a/ext/ply/example/unicalc/calc.py b/ext/ply/example/unicalc/calc.py
index 55fb48d..901c4b9 100644
--- a/ext/ply/example/unicalc/calc.py
+++ b/ext/ply/example/unicalc/calc.py
@@ -8,24 +8,25 @@
 # -----------------------------------------------------------------------------
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 tokens = (
-    'NAME','NUMBER',
-    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
-    'LPAREN','RPAREN',
-    )
+    'NAME', 'NUMBER',
+    'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'EQUALS',
+    'LPAREN', 'RPAREN',
+)
 
 # Tokens
 
-t_PLUS    = ur'\+'
-t_MINUS   = ur'-'
-t_TIMES   = ur'\*'
-t_DIVIDE  = ur'/'
-t_EQUALS  = ur'='
-t_LPAREN  = ur'\('
-t_RPAREN  = ur'\)'
-t_NAME    = ur'[a-zA-Z_][a-zA-Z0-9_]*'
+t_PLUS = ur'\+'
+t_MINUS = ur'-'
+t_TIMES = ur'\*'
+t_DIVIDE = ur'/'
+t_EQUALS = ur'='
+t_LPAREN = ur'\('
+t_RPAREN = ur'\)'
+t_NAME = ur'[a-zA-Z_][a-zA-Z0-9_]*'
+
 
 def t_NUMBER(t):
     ur'\d+'
@@ -38,14 +39,16 @@
 
 t_ignore = u" \t"
 
+
 def t_newline(t):
     ur'\n+'
     t.lexer.lineno += t.value.count("\n")
-    
+
+
 def t_error(t):
     print "Illegal character '%s'" % t.value[0]
     t.lexer.skip(1)
-    
+
 # Build the lexer
 import ply.lex as lex
 lex.lex()
@@ -53,44 +56,55 @@
 # Parsing rules
 
 precedence = (
-    ('left','PLUS','MINUS'),
-    ('left','TIMES','DIVIDE'),
-    ('right','UMINUS'),
-    )
+    ('left', 'PLUS', 'MINUS'),
+    ('left', 'TIMES', 'DIVIDE'),
+    ('right', 'UMINUS'),
+)
 
 # dictionary of names
-names = { }
+names = {}
+
 
 def p_statement_assign(p):
     'statement : NAME EQUALS expression'
     names[p[1]] = p[3]
 
+
 def p_statement_expr(p):
     'statement : expression'
     print p[1]
 
+
 def p_expression_binop(p):
     '''expression : expression PLUS expression
                   | expression MINUS expression
                   | expression TIMES expression
                   | expression DIVIDE expression'''
-    if p[2] == u'+'  : p[0] = p[1] + p[3]
-    elif p[2] == u'-': p[0] = p[1] - p[3]
-    elif p[2] == u'*': p[0] = p[1] * p[3]
-    elif p[2] == u'/': p[0] = p[1] / p[3]
+    if p[2] == u'+':
+        p[0] = p[1] + p[3]
+    elif p[2] == u'-':
+        p[0] = p[1] - p[3]
+    elif p[2] == u'*':
+        p[0] = p[1] * p[3]
+    elif p[2] == u'/':
+        p[0] = p[1] / p[3]
+
 
 def p_expression_uminus(p):
     'expression : MINUS expression %prec UMINUS'
     p[0] = -p[2]
 
+
 def p_expression_group(p):
     'expression : LPAREN expression RPAREN'
     p[0] = p[2]
 
+
 def p_expression_number(p):
     'expression : NUMBER'
     p[0] = p[1]
 
+
 def p_expression_name(p):
     'expression : NAME'
     try:
@@ -99,6 +113,7 @@
         print "Undefined name '%s'" % p[1]
         p[0] = 0
 
+
 def p_error(p):
     if p:
         print "Syntax error at '%s'" % p.value
@@ -113,5 +128,6 @@
         s = raw_input('calc > ')
     except EOFError:
         break
-    if not s: continue
+    if not s:
+        continue
     yacc.parse(unicode(s))
diff --git a/ext/ply/example/yply/ylex.py b/ext/ply/example/yply/ylex.py
index 84f2f7a..16410e2 100644
--- a/ext/ply/example/yply/ylex.py
+++ b/ext/ply/example/yply/ylex.py
@@ -9,104 +9,111 @@
 from ply import *
 
 tokens = (
-    'LITERAL','SECTION','TOKEN','LEFT','RIGHT','PREC','START','TYPE','NONASSOC','UNION','CODE',
-    'ID','QLITERAL','NUMBER',
+    'LITERAL', 'SECTION', 'TOKEN', 'LEFT', 'RIGHT', 'PREC', 'START', 'TYPE', 'NONASSOC', 'UNION', 'CODE',
+    'ID', 'QLITERAL', 'NUMBER',
 )
 
-states = (('code','exclusive'),)
+states = (('code', 'exclusive'),)
 
-literals = [ ';', ',', '<', '>', '|',':' ]
+literals = [';', ',', '<', '>', '|', ':']
 t_ignore = ' \t'
 
-t_TOKEN     = r'%token'
-t_LEFT      = r'%left'
-t_RIGHT     = r'%right'
-t_NONASSOC  = r'%nonassoc'
-t_PREC      = r'%prec'
-t_START     = r'%start'
-t_TYPE      = r'%type'
-t_UNION     = r'%union'
-t_ID        = r'[a-zA-Z_][a-zA-Z_0-9]*'
+t_TOKEN = r'%token'
+t_LEFT = r'%left'
+t_RIGHT = r'%right'
+t_NONASSOC = r'%nonassoc'
+t_PREC = r'%prec'
+t_START = r'%start'
+t_TYPE = r'%type'
+t_UNION = r'%union'
+t_ID = r'[a-zA-Z_][a-zA-Z_0-9]*'
 t_QLITERAL  = r'''(?P<quote>['"]).*?(?P=quote)'''
-t_NUMBER    = r'\d+'
+t_NUMBER = r'\d+'
+
 
 def t_SECTION(t):
     r'%%'
-    if getattr(t.lexer,"lastsection",0):
-         t.value = t.lexer.lexdata[t.lexpos+2:]
-         t.lexer.lexpos = len(t.lexer.lexdata)
+    if getattr(t.lexer, "lastsection", 0):
+        t.value = t.lexer.lexdata[t.lexpos + 2:]
+        t.lexer.lexpos = len(t.lexer.lexdata)
     else:
-         t.lexer.lastsection = 0
+        t.lexer.lastsection = 0
     return t
 
 # Comments
+
+
 def t_ccomment(t):
     r'/\*(.|\n)*?\*/'
     t.lexer.lineno += t.value.count('\n')
 
 t_ignore_cppcomment = r'//.*'
 
+
 def t_LITERAL(t):
-   r'%\{(.|\n)*?%\}'
-   t.lexer.lineno += t.value.count("\n")
-   return t
+    r'%\{(.|\n)*?%\}'
+    t.lexer.lineno += t.value.count("\n")
+    return t
+
 
 def t_NEWLINE(t):
-   r'\n'
-   t.lexer.lineno += 1
+    r'\n'
+    t.lexer.lineno += 1
+
 
 def t_code(t):
-   r'\{'
-   t.lexer.codestart = t.lexpos
-   t.lexer.level = 1
-   t.lexer.begin('code')
+    r'\{'
+    t.lexer.codestart = t.lexpos
+    t.lexer.level = 1
+    t.lexer.begin('code')
+
 
 def t_code_ignore_string(t):
     r'\"([^\\\n]|(\\.))*?\"'
 
+
 def t_code_ignore_char(t):
     r'\'([^\\\n]|(\\.))*?\''
 
+
 def t_code_ignore_comment(t):
-   r'/\*(.|\n)*?\*/'
+    r'/\*(.|\n)*?\*/'
+
 
 def t_code_ignore_cppcom(t):
-   r'//.*'
+    r'//.*'
+
 
 def t_code_lbrace(t):
     r'\{'
     t.lexer.level += 1
 
+
 def t_code_rbrace(t):
     r'\}'
     t.lexer.level -= 1
     if t.lexer.level == 0:
-         t.type = 'CODE'
-         t.value = t.lexer.lexdata[t.lexer.codestart:t.lexpos+1]
-         t.lexer.begin('INITIAL')
-         t.lexer.lineno += t.value.count('\n')
-         return t
+        t.type = 'CODE'
+        t.value = t.lexer.lexdata[t.lexer.codestart:t.lexpos + 1]
+        t.lexer.begin('INITIAL')
+        t.lexer.lineno += t.value.count('\n')
+        return t
 
-t_code_ignore_nonspace   = r'[^\s\}\'\"\{]+'
+t_code_ignore_nonspace = r'[^\s\}\'\"\{]+'
 t_code_ignore_whitespace = r'\s+'
 t_code_ignore = ""
 
+
 def t_code_error(t):
     raise RuntimeError
 
+
 def t_error(t):
-    print "%d: Illegal character '%s'" % (t.lexer.lineno, t.value[0])
-    print t.value
+    print("%d: Illegal character '%s'" % (t.lexer.lineno, t.value[0]))
+    print(t.value)
     t.lexer.skip(1)
 
 lex.lex()
 
 if __name__ == '__main__':
     lex.runmain()
-
-            
-            
-           
-
-        
-
diff --git a/ext/ply/example/yply/yparse.py b/ext/ply/example/yply/yparse.py
index ab5b884..1f2e8d09 100644
--- a/ext/ply/example/yply/yparse.py
+++ b/ext/ply/example/yply/yparse.py
@@ -9,53 +9,61 @@
 from ply import *
 
 tokenlist = []
-preclist  = []
+preclist = []
 
 emit_code = 1
 
+
 def p_yacc(p):
     '''yacc : defsection rulesection'''
 
+
 def p_defsection(p):
     '''defsection : definitions SECTION
                   | SECTION'''
     p.lexer.lastsection = 1
-    print "tokens = ", repr(tokenlist)
-    print
-    print "precedence = ", repr(preclist)
-    print
-    print "# -------------- RULES ----------------"
-    print 
+    print("tokens = ", repr(tokenlist))
+    print()
+    print("precedence = ", repr(preclist))
+    print()
+    print("# -------------- RULES ----------------")
+    print()
+
 
 def p_rulesection(p):
     '''rulesection : rules SECTION'''
 
-    print "# -------------- RULES END ----------------"
-    print_code(p[2],0)
+    print("# -------------- RULES END ----------------")
+    print_code(p[2], 0)
+
 
 def p_definitions(p):
     '''definitions : definitions definition
                    | definition'''
 
+
 def p_definition_literal(p):
     '''definition : LITERAL'''
-    print_code(p[1],0)
+    print_code(p[1], 0)
+
 
 def p_definition_start(p):
     '''definition : START ID'''
-    print "start = '%s'" % p[2]
+    print("start = '%s'" % p[2])
+
 
 def p_definition_token(p):
     '''definition : toktype opttype idlist optsemi '''
     for i in p[3]:
-       if i[0] not in "'\"":
-           tokenlist.append(i)
+        if i[0] not in "'\"":
+            tokenlist.append(i)
     if p[1] == '%left':
         preclist.append(('left',) + tuple(p[3]))
     elif p[1] == '%right':
         preclist.append(('right',) + tuple(p[3]))
     elif p[1] == '%nonassoc':
-        preclist.append(('nonassoc',)+ tuple(p[3]))
+        preclist.append(('nonassoc',) + tuple(p[3]))
+
 
 def p_toktype(p):
     '''toktype : TOKEN
@@ -64,10 +72,12 @@
                | NONASSOC'''
     p[0] = p[1]
 
+
 def p_opttype(p):
     '''opttype : '<' ID '>'
                | empty'''
 
+
 def p_idlist(p):
     '''idlist  : idlist optcomma tokenid
                | tokenid'''
@@ -77,141 +87,158 @@
         p[0] = p[1]
         p[1].append(p[3])
 
+
 def p_tokenid(p):
     '''tokenid : ID 
                | ID NUMBER
                | QLITERAL
                | QLITERAL NUMBER'''
     p[0] = p[1]
-    
+
+
 def p_optsemi(p):
     '''optsemi : ';'
                | empty'''
 
+
 def p_optcomma(p):
     '''optcomma : ','
                 | empty'''
 
+
 def p_definition_type(p):
     '''definition : TYPE '<' ID '>' namelist optsemi'''
     # type declarations are ignored
 
+
 def p_namelist(p):
     '''namelist : namelist optcomma ID
                 | ID'''
 
+
 def p_definition_union(p):
     '''definition : UNION CODE optsemi'''
     # Union declarations are ignored
 
+
 def p_rules(p):
     '''rules   : rules rule
                | rule'''
     if len(p) == 2:
-       rule = p[1]
+        rule = p[1]
     else:
-       rule = p[2]
+        rule = p[2]
 
     # Print out a Python equivalent of this rule
 
-    embedded = [ ]      # Embedded actions (a mess)
+    embedded = []      # Embedded actions (a mess)
     embed_count = 0
 
     rulename = rule[0]
     rulecount = 1
     for r in rule[1]:
         # r contains one of the rule possibilities
-        print "def p_%s_%d(p):" % (rulename,rulecount)
+        print("def p_%s_%d(p):" % (rulename, rulecount))
         prod = []
         prodcode = ""
         for i in range(len(r)):
-             item = r[i]
-             if item[0] == '{':    # A code block
-                  if i == len(r) - 1:
-                      prodcode = item
-                      break
-                  else:
-                      # an embedded action
-                      embed_name = "_embed%d_%s" % (embed_count,rulename)
-                      prod.append(embed_name)
-                      embedded.append((embed_name,item))
-                      embed_count += 1
-             else:
-                  prod.append(item)
-        print "    '''%s : %s'''" % (rulename, " ".join(prod))
+            item = r[i]
+            if item[0] == '{':    # A code block
+                if i == len(r) - 1:
+                    prodcode = item
+                    break
+                else:
+                    # an embedded action
+                    embed_name = "_embed%d_%s" % (embed_count, rulename)
+                    prod.append(embed_name)
+                    embedded.append((embed_name, item))
+                    embed_count += 1
+            else:
+                prod.append(item)
+        print("    '''%s : %s'''" % (rulename, " ".join(prod)))
         # Emit code
-        print_code(prodcode,4)
-        print
+        print_code(prodcode, 4)
+        print()
         rulecount += 1
 
-    for e,code in embedded:
-        print "def p_%s(p):" % e
-        print "    '''%s : '''" % e
-        print_code(code,4)
-        print
+    for e, code in embedded:
+        print("def p_%s(p):" % e)
+        print("    '''%s : '''" % e)
+        print_code(code, 4)
+        print()
+
 
 def p_rule(p):
-   '''rule : ID ':' rulelist ';' '''
-   p[0] = (p[1],[p[3]])
+    '''rule : ID ':' rulelist ';' '''
+    p[0] = (p[1], [p[3]])
+
 
 def p_rule2(p):
-   '''rule : ID ':' rulelist morerules ';' '''
-   p[4].insert(0,p[3])
-   p[0] = (p[1],p[4])
+    '''rule : ID ':' rulelist morerules ';' '''
+    p[4].insert(0, p[3])
+    p[0] = (p[1], p[4])
+
 
 def p_rule_empty(p):
-   '''rule : ID ':' ';' '''
-   p[0] = (p[1],[[]])
+    '''rule : ID ':' ';' '''
+    p[0] = (p[1], [[]])
+
 
 def p_rule_empty2(p):
-   '''rule : ID ':' morerules ';' '''
-   
-   p[3].insert(0,[])
-   p[0] = (p[1],p[3])
+    '''rule : ID ':' morerules ';' '''
+
+    p[3].insert(0, [])
+    p[0] = (p[1], p[3])
+
 
 def p_morerules(p):
-   '''morerules : morerules '|' rulelist
-                | '|' rulelist
-                | '|'  '''
- 
-   if len(p) == 2:   
-       p[0] = [[]]
-   elif len(p) == 3: 
-       p[0] = [p[2]]
-   else:
-       p[0] = p[1]
-       p[0].append(p[3])
+    '''morerules : morerules '|' rulelist
+                 | '|' rulelist
+                 | '|'  '''
 
-#   print "morerules", len(p), p[0]
+    if len(p) == 2:
+        p[0] = [[]]
+    elif len(p) == 3:
+        p[0] = [p[2]]
+    else:
+        p[0] = p[1]
+        p[0].append(p[3])
+
+#   print("morerules", len(p), p[0])
+
 
 def p_rulelist(p):
-   '''rulelist : rulelist ruleitem
-               | ruleitem'''
+    '''rulelist : rulelist ruleitem
+                | ruleitem'''
 
-   if len(p) == 2:
+    if len(p) == 2:
         p[0] = [p[1]]
-   else:
+    else:
         p[0] = p[1]
         p[1].append(p[2])
 
+
 def p_ruleitem(p):
-   '''ruleitem : ID
-               | QLITERAL
-               | CODE
-               | PREC'''
-   p[0] = p[1]
+    '''ruleitem : ID
+                | QLITERAL
+                | CODE
+                | PREC'''
+    p[0] = p[1]
+
 
 def p_empty(p):
     '''empty : '''
 
+
 def p_error(p):
     pass
 
 yacc.yacc(debug=0)
 
-def print_code(code,indent):
-    if not emit_code: return
+
+def print_code(code, indent):
+    if not emit_code:
+        return
     codelines = code.splitlines()
     for c in codelines:
-         print "%s# %s" % (" "*indent,c)
-
+        print("%s# %s" % (" " * indent, c))
diff --git a/ext/ply/example/yply/yply.py b/ext/ply/example/yply/yply.py
index a439817..e24616c 100755
--- a/ext/ply/example/yply/yply.py
+++ b/ext/ply/example/yply/yply.py
@@ -21,7 +21,7 @@
 #
 
 import sys
-sys.path.insert(0,"../..")
+sys.path.insert(0, "../..")
 
 import ylex
 import yparse
@@ -29,25 +29,23 @@
 from ply import *
 
 if len(sys.argv) == 1:
-    print "usage : yply.py [-nocode] inputfile"
+    print("usage : yply.py [-nocode] inputfile")
     raise SystemExit
 
 if len(sys.argv) == 3:
     if sys.argv[1] == '-nocode':
-         yparse.emit_code = 0
+        yparse.emit_code = 0
     else:
-         print "Unknown option '%s'" % sys.argv[1]
-         raise SystemExit
+        print("Unknown option '%s'" % sys.argv[1])
+        raise SystemExit
     filename = sys.argv[2]
 else:
     filename = sys.argv[1]
 
 yacc.parse(open(filename).read())
 
-print """
+print("""
 if __name__ == '__main__':
     from ply import *
     yacc.yacc()
-"""
-
-
+""")
diff --git a/ext/ply/ply.egg-info/PKG-INFO b/ext/ply/ply.egg-info/PKG-INFO
new file mode 100644
index 0000000..f2d8c8a
--- /dev/null
+++ b/ext/ply/ply.egg-info/PKG-INFO
@@ -0,0 +1,23 @@
+Metadata-Version: 1.1
+Name: ply
+Version: 3.11
+Summary: Python Lex & Yacc
+Home-page: http://www.dabeaz.com/ply/
+Author: David Beazley
+Author-email: dave@dabeaz.com
+License: BSD
+Description-Content-Type: UNKNOWN
+Description: 
+        PLY is yet another implementation of lex and yacc for Python. Some notable
+        features include the fact that its implemented entirely in Python and it
+        uses LALR(1) parsing which is efficient and well suited for larger grammars.
+        
+        PLY provides most of the standard lex/yacc features including support for empty 
+        productions, precedence rules, error recovery, and support for ambiguous grammars. 
+        
+        PLY is extremely easy to use and provides very extensive error checking. 
+        It is compatible with both Python 2 and Python 3.
+        
+Platform: UNKNOWN
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 2
diff --git a/ext/ply/ply.egg-info/SOURCES.txt b/ext/ply/ply.egg-info/SOURCES.txt
new file mode 100644
index 0000000..f96bab6
--- /dev/null
+++ b/ext/ply/ply.egg-info/SOURCES.txt
@@ -0,0 +1,190 @@
+ANNOUNCE
+CHANGES
+MANIFEST.in
+README.md
+TODO
+setup.cfg
+setup.py
+doc/internal.html
+doc/makedoc.py
+doc/ply.html
+example/README
+example/cleanup.sh
+example/BASIC/README
+example/BASIC/basic.py
+example/BASIC/basiclex.py
+example/BASIC/basiclog.py
+example/BASIC/basinterp.py
+example/BASIC/basparse.py
+example/BASIC/dim.bas
+example/BASIC/func.bas
+example/BASIC/gcd.bas
+example/BASIC/gosub.bas
+example/BASIC/hello.bas
+example/BASIC/linear.bas
+example/BASIC/maxsin.bas
+example/BASIC/powers.bas
+example/BASIC/rand.bas
+example/BASIC/sales.bas
+example/BASIC/sears.bas
+example/BASIC/sqrt1.bas
+example/BASIC/sqrt2.bas
+example/GardenSnake/GardenSnake.py
+example/GardenSnake/README
+example/ansic/README
+example/ansic/clex.py
+example/ansic/cparse.py
+example/calc/calc.py
+example/calcdebug/calc.py
+example/calceof/calc.py
+example/classcalc/calc.py
+example/closurecalc/calc.py
+example/hedit/hedit.py
+example/newclasscalc/calc.py
+example/optcalc/README
+example/optcalc/calc.py
+example/unicalc/calc.py
+example/yply/README
+example/yply/ylex.py
+example/yply/yparse.py
+example/yply/yply.py
+ply/__init__.py
+ply/cpp.py
+ply/ctokens.py
+ply/lex.py
+ply/yacc.py
+ply/ygen.py
+ply.egg-info/PKG-INFO
+ply.egg-info/SOURCES.txt
+ply.egg-info/dependency_links.txt
+ply.egg-info/top_level.txt
+test/README
+test/calclex.py
+test/cleanup.sh
+test/lex_closure.py
+test/lex_doc1.py
+test/lex_dup1.py
+test/lex_dup2.py
+test/lex_dup3.py
+test/lex_empty.py
+test/lex_error1.py
+test/lex_error2.py
+test/lex_error3.py
+test/lex_error4.py
+test/lex_hedit.py
+test/lex_ignore.py
+test/lex_ignore2.py
+test/lex_literal1.py
+test/lex_literal2.py
+test/lex_literal3.py
+test/lex_many_tokens.py
+test/lex_module.py
+test/lex_module_import.py
+test/lex_object.py
+test/lex_opt_alias.py
+test/lex_optimize.py
+test/lex_optimize2.py
+test/lex_optimize3.py
+test/lex_optimize4.py
+test/lex_re1.py
+test/lex_re2.py
+test/lex_re3.py
+test/lex_rule1.py
+test/lex_rule2.py
+test/lex_rule3.py
+test/lex_state1.py
+test/lex_state2.py
+test/lex_state3.py
+test/lex_state4.py
+test/lex_state5.py
+test/lex_state_noerror.py
+test/lex_state_norule.py
+test/lex_state_try.py
+test/lex_token1.py
+test/lex_token2.py
+test/lex_token3.py
+test/lex_token4.py
+test/lex_token5.py
+test/lex_token_dup.py
+test/parser.out
+test/testcpp.py
+test/testlex.py
+test/testyacc.py
+test/yacc_badargs.py
+test/yacc_badid.py
+test/yacc_badprec.py
+test/yacc_badprec2.py
+test/yacc_badprec3.py
+test/yacc_badrule.py
+test/yacc_badtok.py
+test/yacc_dup.py
+test/yacc_error1.py
+test/yacc_error2.py
+test/yacc_error3.py
+test/yacc_error4.py
+test/yacc_error5.py
+test/yacc_error6.py
+test/yacc_error7.py
+test/yacc_inf.py
+test/yacc_literal.py
+test/yacc_misplaced.py
+test/yacc_missing1.py
+test/yacc_nested.py
+test/yacc_nodoc.py
+test/yacc_noerror.py
+test/yacc_nop.py
+test/yacc_notfunc.py
+test/yacc_notok.py
+test/yacc_prec1.py
+test/yacc_rr.py
+test/yacc_rr_unused.py
+test/yacc_simple.py
+test/yacc_sr.py
+test/yacc_term1.py
+test/yacc_unicode_literals.py
+test/yacc_unused.py
+test/yacc_unused_rule.py
+test/yacc_uprec.py
+test/yacc_uprec2.py
+test/pkg_test1/__init__.py
+test/pkg_test1/parsing/__init__.py
+test/pkg_test1/parsing/calclex.py
+test/pkg_test1/parsing/calcparse.py
+test/pkg_test1/parsing/lextab.py
+test/pkg_test1/parsing/parser.out
+test/pkg_test1/parsing/parsetab.py
+test/pkg_test2/__init__.py
+test/pkg_test2/parsing/__init__.py
+test/pkg_test2/parsing/calclex.py
+test/pkg_test2/parsing/calclextab.py
+test/pkg_test2/parsing/calcparse.py
+test/pkg_test2/parsing/calcparsetab.py
+test/pkg_test2/parsing/parser.out
+test/pkg_test3/__init__.py
+test/pkg_test3/generated/__init__.py
+test/pkg_test3/generated/lextab.py
+test/pkg_test3/generated/parser.out
+test/pkg_test3/generated/parsetab.py
+test/pkg_test3/parsing/__init__.py
+test/pkg_test3/parsing/calclex.py
+test/pkg_test3/parsing/calcparse.py
+test/pkg_test4/__init__.py
+test/pkg_test4/parsing/__init__.py
+test/pkg_test4/parsing/calclex.py
+test/pkg_test4/parsing/calcparse.py
+test/pkg_test5/__init__.py
+test/pkg_test5/parsing/__init__.py
+test/pkg_test5/parsing/calclex.py
+test/pkg_test5/parsing/calcparse.py
+test/pkg_test5/parsing/lextab.py
+test/pkg_test5/parsing/parser.out
+test/pkg_test5/parsing/parsetab.py
+test/pkg_test6/__init__.py
+test/pkg_test6/parsing/__init__.py
+test/pkg_test6/parsing/calclex.py
+test/pkg_test6/parsing/calcparse.py
+test/pkg_test6/parsing/expression.py
+test/pkg_test6/parsing/lextab.py
+test/pkg_test6/parsing/parser.out
+test/pkg_test6/parsing/parsetab.py
+test/pkg_test6/parsing/statement.py
\ No newline at end of file
diff --git a/ext/ply/ply.egg-info/dependency_links.txt b/ext/ply/ply.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/ext/ply/ply.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/ext/ply/ply.egg-info/top_level.txt b/ext/ply/ply.egg-info/top_level.txt
new file mode 100644
index 0000000..90412f0
--- /dev/null
+++ b/ext/ply/ply.egg-info/top_level.txt
@@ -0,0 +1 @@
+ply
diff --git a/ext/ply/ply/__init__.py b/ext/ply/ply/__init__.py
index 853a985..23707c6 100644
--- a/ext/ply/ply/__init__.py
+++ b/ext/ply/ply/__init__.py
@@ -1,4 +1,5 @@
 # PLY package
 # Author: David Beazley (dave@dabeaz.com)
 
+__version__ = '3.11'
 __all__ = ['lex','yacc']
diff --git a/ext/ply/ply/cpp.py b/ext/ply/ply/cpp.py
index 39f9d47..2422916 100644
--- a/ext/ply/ply/cpp.py
+++ b/ext/ply/ply/cpp.py
@@ -5,17 +5,26 @@
 # Copyright (C) 2007
 # All rights reserved
 #
-# This module implements an ANSI-C style lexical preprocessor for PLY. 
+# This module implements an ANSI-C style lexical preprocessor for PLY.
 # -----------------------------------------------------------------------------
 from __future__ import generators
 
+import sys
+
+# Some Python 3 compatibility shims
+if sys.version_info.major < 3:
+    STRING_TYPES = (str, unicode)
+else:
+    STRING_TYPES = str
+    xrange = range
+
 # -----------------------------------------------------------------------------
 # Default preprocessor lexer definitions.   These tokens are enough to get
 # a basic preprocessor working.   Other modules may import these if they want
 # -----------------------------------------------------------------------------
 
 tokens = (
-   'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT', 'CPP_POUND','CPP_DPOUND'
+   'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT1', 'CPP_COMMENT2', 'CPP_POUND','CPP_DPOUND'
 )
 
 literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\""
@@ -34,7 +43,7 @@
 
 # Integer literal
 def CPP_INTEGER(t):
-    r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU]|[lL]|[uU][lL]|[lL][uU])?)'
+    r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)'
     return t
 
 t_CPP_INTEGER = CPP_INTEGER
@@ -55,11 +64,21 @@
     return t
 
 # Comment
-def t_CPP_COMMENT(t):
-    r'(/\*(.|\n)*?\*/)|(//.*?\n)'
-    t.lexer.lineno += t.value.count("\n")
+def t_CPP_COMMENT1(t):
+    r'(/\*(.|\n)*?\*/)'
+    ncr = t.value.count("\n")
+    t.lexer.lineno += ncr
+    # replace with one space or a number of '\n'
+    t.type = 'CPP_WS'; t.value = '\n' * ncr if ncr else ' '
     return t
-    
+
+# Line comment
+def t_CPP_COMMENT2(t):
+    r'(//.*?(\n|$))'
+    # replace with '/n'
+    t.type = 'CPP_WS'; t.value = '\n'
+    return t
+
 def t_error(t):
     t.type = t.value[0]
     t.value = t.value[0]
@@ -73,8 +92,8 @@
 
 # -----------------------------------------------------------------------------
 # trigraph()
-# 
-# Given an input string, this function replaces all trigraph sequences. 
+#
+# Given an input string, this function replaces all trigraph sequences.
 # The following mapping is used:
 #
 #     ??=    #
@@ -176,7 +195,7 @@
     # ----------------------------------------------------------------------
 
     def error(self,file,line,msg):
-        print >>sys.stderr,"%s:%d %s" % (file,line,msg)
+        print("%s:%d %s" % (file,line,msg))
 
     # ----------------------------------------------------------------------
     # lexprobe()
@@ -193,7 +212,7 @@
         self.lexer.input("identifier")
         tok = self.lexer.token()
         if not tok or tok.value != "identifier":
-            print "Couldn't determine identifier type"
+            print("Couldn't determine identifier type")
         else:
             self.t_ID = tok.type
 
@@ -201,7 +220,7 @@
         self.lexer.input("12345")
         tok = self.lexer.token()
         if not tok or int(tok.value) != 12345:
-            print "Couldn't determine integer type"
+            print("Couldn't determine integer type")
         else:
             self.t_INTEGER = tok.type
             self.t_INTEGER_TYPE = type(tok.value)
@@ -210,7 +229,7 @@
         self.lexer.input("\"filename\"")
         tok = self.lexer.token()
         if not tok or tok.value != "\"filename\"":
-            print "Couldn't determine string type"
+            print("Couldn't determine string type")
         else:
             self.t_STRING = tok.type
 
@@ -227,7 +246,7 @@
         tok = self.lexer.token()
         if not tok or tok.value != "\n":
             self.t_NEWLINE = None
-            print "Couldn't determine token for newlines"
+            print("Couldn't determine token for newlines")
         else:
             self.t_NEWLINE = tok.type
 
@@ -239,12 +258,12 @@
             self.lexer.input(c)
             tok = self.lexer.token()
             if not tok or tok.value != c:
-                print "Unable to lex '%s' required for preprocessor" % c
+                print("Unable to lex '%s' required for preprocessor" % c)
 
     # ----------------------------------------------------------------------
     # add_path()
     #
-    # Adds a search path to the preprocessor.  
+    # Adds a search path to the preprocessor.
     # ----------------------------------------------------------------------
 
     def add_path(self,path):
@@ -288,7 +307,7 @@
 
     # ----------------------------------------------------------------------
     # tokenstrip()
-    # 
+    #
     # Remove leading/trailing whitespace tokens from a token list
     # ----------------------------------------------------------------------
 
@@ -314,7 +333,7 @@
     # argument.  Each argument is represented by a list of tokens.
     #
     # When collecting arguments, leading and trailing whitespace is removed
-    # from each argument.  
+    # from each argument.
     #
     # This function properly handles nested parenthesis and commas---these do not
     # define new arguments.
@@ -326,7 +345,7 @@
         current_arg = []
         nesting = 1
         tokenlen = len(tokenlist)
-    
+
         # Search for the opening '('.
         i = 0
         while (i < tokenlen) and (tokenlist[i].type in self.t_WS):
@@ -360,7 +379,7 @@
             else:
                 current_arg.append(t)
             i += 1
-    
+
         # Missing end argument
         self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments")
         return 0, [],[]
@@ -372,9 +391,9 @@
     # This is used to speed up macro expansion later on---we'll know
     # right away where to apply patches to the value to form the expansion
     # ----------------------------------------------------------------------
-    
+
     def macro_prescan(self,macro):
-        macro.patch     = []             # Standard macro arguments 
+        macro.patch     = []             # Standard macro arguments
         macro.str_patch = []             # String conversion expansion
         macro.var_comma_patch = []       # Variadic macro comma patch
         i = 0
@@ -392,10 +411,11 @@
                 elif (i > 0 and macro.value[i-1].value == '##'):
                     macro.patch.append(('c',argnum,i-1))
                     del macro.value[i-1]
+                    i -= 1
                     continue
                 elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'):
                     macro.patch.append(('c',argnum,i))
-                    i += 1
+                    del macro.value[i + 1]
                     continue
                 # Standard expansion
                 else:
@@ -421,7 +441,7 @@
         rep = [copy.copy(_x) for _x in macro.value]
 
         # Make string expansion patches.  These do not alter the length of the replacement sequence
-        
+
         str_expansion = {}
         for argnum, i in macro.str_patch:
             if argnum not in str_expansion:
@@ -439,7 +459,7 @@
         # Make all other patches.   The order of these matters.  It is assumed that the patch list
         # has been sorted in reverse order of patch location since replacements will cause the
         # size of the replacement sequence to expand from the patch point.
-        
+
         expanded = { }
         for ptype, argnum, i in macro.patch:
             # Concatenation.   Argument is left unexpanded
@@ -476,7 +496,7 @@
                 if t.value in self.macros and t.value not in expanded:
                     # Yes, we found a macro match
                     expanded[t.value] = True
-                    
+
                     m = self.macros[t.value]
                     if not m.arglist:
                         # A simple macro
@@ -490,7 +510,7 @@
                         j = i + 1
                         while j < len(tokens) and tokens[j].type in self.t_WS:
                             j += 1
-                        if tokens[j].value == '(':
+                        if j < len(tokens) and tokens[j].value == '(':
                             tokcount,args,positions = self.collect_args(tokens[j:])
                             if not m.variadic and len(args) !=  len(m.arglist):
                                 self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist)))
@@ -508,7 +528,7 @@
                                     else:
                                         args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1]
                                         del args[len(m.arglist):]
-                                        
+
                                 # Get macro replacement text
                                 rep = self.macro_expand_args(m,args)
                                 rep = self.expand_macros(rep,expanded)
@@ -516,18 +536,24 @@
                                     r.lineno = t.lineno
                                 tokens[i:j+tokcount] = rep
                                 i += len(rep)
+                        else:
+                            # This is not a macro. It is just a word which
+                            # equals to name of the macro. Hence, go to the
+                            # next token.
+                            i += 1
+
                     del expanded[t.value]
                     continue
                 elif t.value == '__LINE__':
                     t.type = self.t_INTEGER
                     t.value = self.t_INTEGER_TYPE(t.lineno)
-                
+
             i += 1
         return tokens
 
-    # ----------------------------------------------------------------------    
+    # ----------------------------------------------------------------------
     # evalexpr()
-    # 
+    #
     # Evaluate an expression token sequence for the purposes of evaluating
     # integral expressions.
     # ----------------------------------------------------------------------
@@ -574,14 +600,14 @@
                 tokens[i].value = str(tokens[i].value)
                 while tokens[i].value[-1] not in "0123456789abcdefABCDEF":
                     tokens[i].value = tokens[i].value[:-1]
-        
+
         expr = "".join([str(x.value) for x in tokens])
         expr = expr.replace("&&"," and ")
         expr = expr.replace("||"," or ")
         expr = expr.replace("!"," not ")
         try:
             result = eval(expr)
-        except StandardError:
+        except Exception:
             self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression")
             result = 0
         return result
@@ -599,7 +625,7 @@
 
         if not source:
             source = ""
-            
+
         self.define("__FILE__ \"%s\"" % source)
 
         self.source = source
@@ -614,10 +640,11 @@
             if tok.value == '#':
                 # Preprocessor directive
 
+                # insert necessary whitespace instead of eaten tokens
                 for tok in x:
-                    if tok in self.t_WS and '\n' in tok.value:
+                    if tok.type in self.t_WS and '\n' in tok.value:
                         chunk.append(tok)
-                
+
                 dirtokens = self.tokenstrip(x[i+1:])
                 if dirtokens:
                     name = dirtokens[0].value
@@ -625,7 +652,7 @@
                 else:
                     name = ""
                     args = []
-                
+
                 if name == 'define':
                     if enable:
                         for tok in self.expand_macros(chunk):
@@ -685,7 +712,7 @@
                                     iftrigger = True
                     else:
                         self.error(self.source,dirtokens[0].lineno,"Misplaced #elif")
-                        
+
                 elif name == 'else':
                     if ifstack:
                         if ifstack[-1][0]:
@@ -737,7 +764,7 @@
                         break
                     i += 1
                 else:
-                    print "Malformed #include <...>"
+                    print("Malformed #include <...>")
                     return
                 filename = "".join([x.value for x in tokens[1:i]])
                 path = self.path + [""] + self.temp_path
@@ -745,7 +772,7 @@
                 filename = tokens[0].value[1:-1]
                 path = self.temp_path + [""] + self.path
             else:
-                print "Malformed #include statement"
+                print("Malformed #include statement")
                 return
         for p in path:
             iname = os.path.join(p,filename)
@@ -759,10 +786,10 @@
                 if dname:
                     del self.temp_path[0]
                 break
-            except IOError,e:
+            except IOError:
                 pass
         else:
-            print "Couldn't find '%s'" % filename
+            print("Couldn't find '%s'" % filename)
 
     # ----------------------------------------------------------------------
     # define()
@@ -771,7 +798,7 @@
     # ----------------------------------------------------------------------
 
     def define(self,tokens):
-        if isinstance(tokens,(str,unicode)):
+        if isinstance(tokens,STRING_TYPES):
             tokens = self.tokenize(tokens)
 
         linetok = tokens
@@ -794,7 +821,7 @@
                 variadic = False
                 for a in args:
                     if variadic:
-                        print "No more arguments may follow a variadic argument"
+                        print("No more arguments may follow a variadic argument")
                         break
                     astr = "".join([str(_i.value) for _i in a])
                     if astr == "...":
@@ -813,7 +840,7 @@
                             a[0].value = a[0].value[:-3]
                         continue
                     if len(a) > 1 or a[0].type != self.t_ID:
-                        print "Invalid macro argument"
+                        print("Invalid macro argument")
                         break
                 else:
                     mvalue = self.tokenstrip(linetok[1+tokcount:])
@@ -830,9 +857,9 @@
                     self.macro_prescan(m)
                     self.macros[name.value] = m
             else:
-                print "Bad macro definition"
+                print("Bad macro definition")
         except LookupError:
-            print "Bad macro definition"
+            print("Bad macro definition")
 
     # ----------------------------------------------------------------------
     # undef()
@@ -855,7 +882,7 @@
     def parse(self,input,source=None,ignore={}):
         self.ignore = ignore
         self.parser = self.parsegen(input,source)
-        
+
     # ----------------------------------------------------------------------
     # token()
     #
@@ -864,7 +891,7 @@
     def token(self):
         try:
             while True:
-                tok = self.parser.next()
+                tok = next(self.parser)
                 if tok.type not in self.ignore: return tok
         except StopIteration:
             self.parser = None
@@ -884,15 +911,4 @@
     while True:
         tok = p.token()
         if not tok: break
-        print p.source, tok
-
-
-
-
-    
-
-
-
-
-
-
+        print(p.source, tok)
diff --git a/ext/ply/ply/ctokens.py b/ext/ply/ply/ctokens.py
index dd5f102..b265e59 100644
--- a/ext/ply/ply/ctokens.py
+++ b/ext/ply/ply/ctokens.py
@@ -9,27 +9,27 @@
 
 tokens = [
     # Literals (identifier, integer constant, float constant, string constant, char const)
-    'ID', 'TYPEID', 'ICONST', 'FCONST', 'SCONST', 'CCONST',
+    'ID', 'TYPEID', 'INTEGER', 'FLOAT', 'STRING', 'CHARACTER',
 
     # Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=)
-    'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MOD',
+    'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MODULO',
     'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT',
     'LOR', 'LAND', 'LNOT',
     'LT', 'LE', 'GT', 'GE', 'EQ', 'NE',
-    
+
     # Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=)
     'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL',
     'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL',
 
     # Increment/decrement (++,--)
-    'PLUSPLUS', 'MINUSMINUS',
+    'INCREMENT', 'DECREMENT',
 
     # Structure dereference (->)
     'ARROW',
 
     # Ternary operator (?)
     'TERNARY',
-    
+
     # Delimeters ( ) [ ] { } , . ; :
     'LPAREN', 'RPAREN',
     'LBRACKET', 'RBRACKET',
@@ -39,7 +39,7 @@
     # Ellipsis (...)
     'ELLIPSIS',
 ]
-    
+
 # Operators
 t_PLUS             = r'\+'
 t_MINUS            = r'-'
@@ -74,7 +74,7 @@
 t_RSHIFTEQUAL      = r'>>='
 t_ANDEQUAL         = r'&='
 t_OREQUAL          = r'\|='
-t_XOREQUAL         = r'^='
+t_XOREQUAL         = r'\^='
 
 # Increment/decrement
 t_INCREMENT        = r'\+\+'
@@ -125,9 +125,3 @@
     r'//.*\n'
     t.lexer.lineno += 1
     return t
-
-
-    
-
-
-
diff --git a/ext/ply/ply/lex.py b/ext/ply/ply/lex.py
index 4759d1b..f95bcdb 100644
--- a/ext/ply/ply/lex.py
+++ b/ext/ply/ply/lex.py
@@ -1,22 +1,22 @@
 # -----------------------------------------------------------------------------
 # ply: lex.py
 #
-# Copyright (C) 2001-2009,
+# Copyright (C) 2001-2018
 # David M. Beazley (Dabeaz LLC)
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met:
-# 
+#
 # * Redistributions of source code must retain the above copyright notice,
-#   this list of conditions and the following disclaimer.  
-# * Redistributions in binary form must reproduce the above copyright notice, 
+#   this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
 #   this list of conditions and the following disclaimer in the documentation
-#   and/or other materials provided with the distribution.  
+#   and/or other materials provided with the distribution.
 # * Neither the name of the David Beazley or Dabeaz LLC may be used to
 #   endorse or promote products derived from this software without
-#  specific prior written permission. 
+#  specific prior written permission.
 #
 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -31,10 +31,15 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 # -----------------------------------------------------------------------------
 
-__version__    = "3.2"
-__tabversion__ = "3.2"       # Version of table file used
+__version__    = '3.11'
+__tabversion__ = '3.10'
 
-import re, sys, types, copy, os
+import re
+import sys
+import types
+import copy
+import os
+import inspect
 
 # This tuple contains known string types
 try:
@@ -44,59 +49,55 @@
     # Python 3.0
     StringTypes = (str, bytes)
 
-# Extract the code attribute of a function. Different implementations
-# are for Python 2/3 compatibility.
-
-if sys.version_info[0] < 3:
-    def func_code(f):
-        return f.func_code
-else:
-    def func_code(f):
-        return f.__code__
-
 # This regular expression is used to match valid token names
 _is_identifier = re.compile(r'^[a-zA-Z0-9_]+$')
 
 # Exception thrown when invalid token encountered and no default error
 # handler is defined.
-
 class LexError(Exception):
-    def __init__(self,message,s):
-         self.args = (message,)
-         self.text = s
+    def __init__(self, message, s):
+        self.args = (message,)
+        self.text = s
+
 
 # Token class.  This class is used to represent the tokens produced.
 class LexToken(object):
     def __str__(self):
-        return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos)
+        return 'LexToken(%s,%r,%d,%d)' % (self.type, self.value, self.lineno, self.lexpos)
+
     def __repr__(self):
         return str(self)
 
-# This object is a stand-in for a logging object created by the 
-# logging module.  
+
+# This object is a stand-in for a logging object created by the
+# logging module.
 
 class PlyLogger(object):
-    def __init__(self,f):
+    def __init__(self, f):
         self.f = f
-    def critical(self,msg,*args,**kwargs):
-        self.f.write((msg % args) + "\n")
 
-    def warning(self,msg,*args,**kwargs):
-        self.f.write("WARNING: "+ (msg % args) + "\n")
+    def critical(self, msg, *args, **kwargs):
+        self.f.write((msg % args) + '\n')
 
-    def error(self,msg,*args,**kwargs):
-        self.f.write("ERROR: " + (msg % args) + "\n")
+    def warning(self, msg, *args, **kwargs):
+        self.f.write('WARNING: ' + (msg % args) + '\n')
+
+    def error(self, msg, *args, **kwargs):
+        self.f.write('ERROR: ' + (msg % args) + '\n')
 
     info = critical
     debug = critical
 
+
 # Null logger is used when no output is generated. Does nothing.
 class NullLogger(object):
-    def __getattribute__(self,name):
+    def __getattribute__(self, name):
         return self
-    def __call__(self,*args,**kwargs):
+
+    def __call__(self, *args, **kwargs):
         return self
 
+
 # -----------------------------------------------------------------------------
 #                        === Lexing Engine ===
 #
@@ -114,31 +115,33 @@
 class Lexer:
     def __init__(self):
         self.lexre = None             # Master regular expression. This is a list of
-                                      # tuples (re,findex) where re is a compiled
+                                      # tuples (re, findex) where re is a compiled
                                       # regular expression and findex is a list
                                       # mapping regex group numbers to rules
         self.lexretext = None         # Current regular expression strings
         self.lexstatere = {}          # Dictionary mapping lexer states to master regexs
         self.lexstateretext = {}      # Dictionary mapping lexer states to regex strings
         self.lexstaterenames = {}     # Dictionary mapping lexer states to symbol names
-        self.lexstate = "INITIAL"     # Current lexer state
+        self.lexstate = 'INITIAL'     # Current lexer state
         self.lexstatestack = []       # Stack of lexer states
         self.lexstateinfo = None      # State information
         self.lexstateignore = {}      # Dictionary of ignored characters for each state
         self.lexstateerrorf = {}      # Dictionary of error functions for each state
+        self.lexstateeoff = {}        # Dictionary of eof functions for each state
         self.lexreflags = 0           # Optional re compile flags
         self.lexdata = None           # Actual input data (as a string)
         self.lexpos = 0               # Current position in input text
         self.lexlen = 0               # Length of the input text
         self.lexerrorf = None         # Error rule (if any)
+        self.lexeoff = None           # EOF rule (if any)
         self.lextokens = None         # List of valid tokens
-        self.lexignore = ""           # Ignored characters
-        self.lexliterals = ""         # Literal characters that can be passed through
+        self.lexignore = ''           # Ignored characters
+        self.lexliterals = ''         # Literal characters that can be passed through
         self.lexmodule = None         # Module
         self.lineno = 1               # Current line number
-        self.lexoptimize = 0          # Optimized mode
+        self.lexoptimize = False      # Optimized mode
 
-    def clone(self,object=None):
+    def clone(self, object=None):
         c = copy.copy(self)
 
         # If the object parameter has been supplied, it means we are attaching the
@@ -146,113 +149,110 @@
         # the lexstatere and lexstateerrorf tables.
 
         if object:
-            newtab = { }
+            newtab = {}
             for key, ritem in self.lexstatere.items():
                 newre = []
                 for cre, findex in ritem:
-                     newfindex = []
-                     for f in findex:
-                         if not f or not f[0]:
-                             newfindex.append(f)
-                             continue
-                         newfindex.append((getattr(object,f[0].__name__),f[1]))
-                newre.append((cre,newfindex))
+                    newfindex = []
+                    for f in findex:
+                        if not f or not f[0]:
+                            newfindex.append(f)
+                            continue
+                        newfindex.append((getattr(object, f[0].__name__), f[1]))
+                newre.append((cre, newfindex))
                 newtab[key] = newre
             c.lexstatere = newtab
-            c.lexstateerrorf = { }
+            c.lexstateerrorf = {}
             for key, ef in self.lexstateerrorf.items():
-                c.lexstateerrorf[key] = getattr(object,ef.__name__)
+                c.lexstateerrorf[key] = getattr(object, ef.__name__)
             c.lexmodule = object
         return c
 
     # ------------------------------------------------------------
     # writetab() - Write lexer information to a table file
     # ------------------------------------------------------------
-    def writetab(self,tabfile,outputdir=""):
-        if isinstance(tabfile,types.ModuleType):
-            return
-        basetabfilename = tabfile.split(".")[-1]
-        filename = os.path.join(outputdir,basetabfilename)+".py"
-        tf = open(filename,"w")
-        tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__))
-        tf.write("_tabversion   = %s\n" % repr(__version__))
-        tf.write("_lextokens    = %s\n" % repr(self.lextokens))
-        tf.write("_lexreflags   = %s\n" % repr(self.lexreflags))
-        tf.write("_lexliterals  = %s\n" % repr(self.lexliterals))
-        tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo))
+    def writetab(self, lextab, outputdir=''):
+        if isinstance(lextab, types.ModuleType):
+            raise IOError("Won't overwrite existing lextab module")
+        basetabmodule = lextab.split('.')[-1]
+        filename = os.path.join(outputdir, basetabmodule) + '.py'
+        with open(filename, 'w') as tf:
+            tf.write('# %s.py. This file automatically created by PLY (version %s). Don\'t edit!\n' % (basetabmodule, __version__))
+            tf.write('_tabversion   = %s\n' % repr(__tabversion__))
+            tf.write('_lextokens    = set(%s)\n' % repr(tuple(sorted(self.lextokens))))
+            tf.write('_lexreflags   = %s\n' % repr(int(self.lexreflags)))
+            tf.write('_lexliterals  = %s\n' % repr(self.lexliterals))
+            tf.write('_lexstateinfo = %s\n' % repr(self.lexstateinfo))
 
-        tabre = { }
-        # Collect all functions in the initial state
-        initial = self.lexstatere["INITIAL"]
-        initialfuncs = []
-        for part in initial:
-            for f in part[1]:
-                if f and f[0]:
-                    initialfuncs.append(f)
+            # Rewrite the lexstatere table, replacing function objects with function names
+            tabre = {}
+            for statename, lre in self.lexstatere.items():
+                titem = []
+                for (pat, func), retext, renames in zip(lre, self.lexstateretext[statename], self.lexstaterenames[statename]):
+                    titem.append((retext, _funcs_to_names(func, renames)))
+                tabre[statename] = titem
 
-        for key, lre in self.lexstatere.items():
-             titem = []
-             for i in range(len(lre)):
-                  titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i])))
-             tabre[key] = titem
+            tf.write('_lexstatere   = %s\n' % repr(tabre))
+            tf.write('_lexstateignore = %s\n' % repr(self.lexstateignore))
 
-        tf.write("_lexstatere   = %s\n" % repr(tabre))
-        tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore))
+            taberr = {}
+            for statename, ef in self.lexstateerrorf.items():
+                taberr[statename] = ef.__name__ if ef else None
+            tf.write('_lexstateerrorf = %s\n' % repr(taberr))
 
-        taberr = { }
-        for key, ef in self.lexstateerrorf.items():
-             if ef:
-                  taberr[key] = ef.__name__
-             else:
-                  taberr[key] = None
-        tf.write("_lexstateerrorf = %s\n" % repr(taberr))
-        tf.close()
+            tabeof = {}
+            for statename, ef in self.lexstateeoff.items():
+                tabeof[statename] = ef.__name__ if ef else None
+            tf.write('_lexstateeoff = %s\n' % repr(tabeof))
 
     # ------------------------------------------------------------
     # readtab() - Read lexer information from a tab file
     # ------------------------------------------------------------
-    def readtab(self,tabfile,fdict):
-        if isinstance(tabfile,types.ModuleType):
+    def readtab(self, tabfile, fdict):
+        if isinstance(tabfile, types.ModuleType):
             lextab = tabfile
         else:
-            if sys.version_info[0] < 3:
-                exec("import %s as lextab" % tabfile)
-            else:
-                env = { }
-                exec("import %s as lextab" % tabfile, env,env)
-                lextab = env['lextab']
+            exec('import %s' % tabfile)
+            lextab = sys.modules[tabfile]
 
-        if getattr(lextab,"_tabversion","0.0") != __version__:
-            raise ImportError("Inconsistent PLY version")
+        if getattr(lextab, '_tabversion', '0.0') != __tabversion__:
+            raise ImportError('Inconsistent PLY version')
 
         self.lextokens      = lextab._lextokens
         self.lexreflags     = lextab._lexreflags
         self.lexliterals    = lextab._lexliterals
+        self.lextokens_all  = self.lextokens | set(self.lexliterals)
         self.lexstateinfo   = lextab._lexstateinfo
         self.lexstateignore = lextab._lexstateignore
-        self.lexstatere     = { }
-        self.lexstateretext = { }
-        for key,lre in lextab._lexstatere.items():
-             titem = []
-             txtitem = []
-             for i in range(len(lre)):
-                  titem.append((re.compile(lre[i][0],lextab._lexreflags),_names_to_funcs(lre[i][1],fdict)))
-                  txtitem.append(lre[i][0])
-             self.lexstatere[key] = titem
-             self.lexstateretext[key] = txtitem
-        self.lexstateerrorf = { }
-        for key,ef in lextab._lexstateerrorf.items():
-             self.lexstateerrorf[key] = fdict[ef]
+        self.lexstatere     = {}
+        self.lexstateretext = {}
+        for statename, lre in lextab._lexstatere.items():
+            titem = []
+            txtitem = []
+            for pat, func_name in lre:
+                titem.append((re.compile(pat, lextab._lexreflags), _names_to_funcs(func_name, fdict)))
+
+            self.lexstatere[statename] = titem
+            self.lexstateretext[statename] = txtitem
+
+        self.lexstateerrorf = {}
+        for statename, ef in lextab._lexstateerrorf.items():
+            self.lexstateerrorf[statename] = fdict[ef]
+
+        self.lexstateeoff = {}
+        for statename, ef in lextab._lexstateeoff.items():
+            self.lexstateeoff[statename] = fdict[ef]
+
         self.begin('INITIAL')
 
     # ------------------------------------------------------------
     # input() - Push a new string into the lexer
     # ------------------------------------------------------------
-    def input(self,s):
+    def input(self, s):
         # Pull off the first character to see if s looks like a string
         c = s[:1]
-        if not isinstance(c,StringTypes):
-            raise ValueError("Expected a string")
+        if not isinstance(c, StringTypes):
+            raise ValueError('Expected a string')
         self.lexdata = s
         self.lexpos = 0
         self.lexlen = len(s)
@@ -260,19 +260,20 @@
     # ------------------------------------------------------------
     # begin() - Changes the lexing state
     # ------------------------------------------------------------
-    def begin(self,state):
-        if not state in self.lexstatere:
-            raise ValueError("Undefined state")
+    def begin(self, state):
+        if state not in self.lexstatere:
+            raise ValueError('Undefined state')
         self.lexre = self.lexstatere[state]
         self.lexretext = self.lexstateretext[state]
-        self.lexignore = self.lexstateignore.get(state,"")
-        self.lexerrorf = self.lexstateerrorf.get(state,None)
+        self.lexignore = self.lexstateignore.get(state, '')
+        self.lexerrorf = self.lexstateerrorf.get(state, None)
+        self.lexeoff = self.lexstateeoff.get(state, None)
         self.lexstate = state
 
     # ------------------------------------------------------------
     # push_state() - Changes the lexing state and saves old on stack
     # ------------------------------------------------------------
-    def push_state(self,state):
+    def push_state(self, state):
         self.lexstatestack.append(self.lexstate)
         self.begin(state)
 
@@ -291,7 +292,7 @@
     # ------------------------------------------------------------
     # skip() - Skip ahead n characters
     # ------------------------------------------------------------
-    def skip(self,n):
+    def skip(self, n):
         self.lexpos += n
 
     # ------------------------------------------------------------
@@ -315,9 +316,10 @@
                 continue
 
             # Look for a regular expression match
-            for lexre,lexindexfunc in self.lexre:
-                m = lexre.match(lexdata,lexpos)
-                if not m: continue
+            for lexre, lexindexfunc in self.lexre:
+                m = lexre.match(lexdata, lexpos)
+                if not m:
+                    continue
 
                 # Create a token for return
                 tok = LexToken()
@@ -326,16 +328,16 @@
                 tok.lexpos = lexpos
 
                 i = m.lastindex
-                func,tok.type = lexindexfunc[i]
+                func, tok.type = lexindexfunc[i]
 
                 if not func:
-                   # If no token type was set, it's an ignored token
-                   if tok.type:
-                      self.lexpos = m.end()
-                      return tok
-                   else:
-                      lexpos = m.end()
-                      break
+                    # If no token type was set, it's an ignored token
+                    if tok.type:
+                        self.lexpos = m.end()
+                        return tok
+                    else:
+                        lexpos = m.end()
+                        break
 
                 lexpos = m.end()
 
@@ -355,10 +357,10 @@
 
                 # Verify type of the token.  If not in the token map, raise an error
                 if not self.lexoptimize:
-                    if not newtok.type in self.lextokens:
+                    if newtok.type not in self.lextokens_all:
                         raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % (
-                            func_code(func).co_filename, func_code(func).co_firstlineno,
-                            func.__name__, newtok.type),lexdata[lexpos:])
+                            func.__code__.co_filename, func.__code__.co_firstlineno,
+                            func.__name__, newtok.type), lexdata[lexpos:])
 
                 return newtok
             else:
@@ -377,7 +379,7 @@
                     tok = LexToken()
                     tok.value = self.lexdata[lexpos:]
                     tok.lineno = self.lineno
-                    tok.type = "error"
+                    tok.type = 'error'
                     tok.lexer = self
                     tok.lexpos = lexpos
                     self.lexpos = lexpos
@@ -386,15 +388,27 @@
                         # Error method didn't change text position at all. This is an error.
                         raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:])
                     lexpos = self.lexpos
-                    if not newtok: continue
+                    if not newtok:
+                        continue
                     return newtok
 
                 self.lexpos = lexpos
-                raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:])
+                raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:])
+
+        if self.lexeoff:
+            tok = LexToken()
+            tok.type = 'eof'
+            tok.value = ''
+            tok.lineno = self.lineno
+            tok.lexpos = lexpos
+            tok.lexer = self
+            self.lexpos = lexpos
+            newtok = self.lexeoff(tok)
+            return newtok
 
         self.lexpos = lexpos + 1
         if self.lexdata is None:
-             raise RuntimeError("No input string given with input()")
+            raise RuntimeError('No input string given with input()')
         return None
 
     # Iterator interface
@@ -417,27 +431,27 @@
 # -----------------------------------------------------------------------------
 
 # -----------------------------------------------------------------------------
+# _get_regex(func)
+#
+# Returns the regular expression assigned to a function either as a doc string
+# or as a .regex attribute attached by the @TOKEN decorator.
+# -----------------------------------------------------------------------------
+def _get_regex(func):
+    return getattr(func, 'regex', func.__doc__)
+
+# -----------------------------------------------------------------------------
 # get_caller_module_dict()
 #
 # This function returns a dictionary containing all of the symbols defined within
 # a caller further down the call stack.  This is used to get the environment
 # associated with the yacc() call if none was provided.
 # -----------------------------------------------------------------------------
-
 def get_caller_module_dict(levels):
-    try:
-        raise RuntimeError
-    except RuntimeError:
-        e,b,t = sys.exc_info()
-        f = t.tb_frame
-        while levels > 0:
-            f = f.f_back                   
-            levels -= 1
-        ldict = f.f_globals.copy()
-        if f.f_globals != f.f_locals:
-            ldict.update(f.f_locals)
-
-        return ldict
+    f = sys._getframe(levels)
+    ldict = f.f_globals.copy()
+    if f.f_globals != f.f_locals:
+        ldict.update(f.f_locals)
+    return ldict
 
 # -----------------------------------------------------------------------------
 # _funcs_to_names()
@@ -445,14 +459,13 @@
 # Given a list of regular expression functions, this converts it to a list
 # suitable for output to a table file
 # -----------------------------------------------------------------------------
-
-def _funcs_to_names(funclist,namelist):
+def _funcs_to_names(funclist, namelist):
     result = []
-    for f,name in zip(funclist,namelist):
-         if f and f[0]:
-             result.append((name, f[1]))
-         else:
-             result.append(f)
+    for f, name in zip(funclist, namelist):
+        if f and f[0]:
+            result.append((name, f[1]))
+        else:
+            result.append(f)
     return result
 
 # -----------------------------------------------------------------------------
@@ -461,15 +474,14 @@
 # Given a list of regular expression function names, this converts it back to
 # functions.
 # -----------------------------------------------------------------------------
-
-def _names_to_funcs(namelist,fdict):
-     result = []
-     for n in namelist:
-          if n and n[0]:
-              result.append((fdict[n[0]],n[1]))
-          else:
-              result.append(n)
-     return result
+def _names_to_funcs(namelist, fdict):
+    result = []
+    for n in namelist:
+        if n and n[0]:
+            result.append((fdict[n[0]], n[1]))
+        else:
+            result.append(n)
+    return result
 
 # -----------------------------------------------------------------------------
 # _form_master_re()
@@ -478,36 +490,37 @@
 # form the master regular expression.  Given limitations in the Python re
 # module, it may be necessary to break the master regex into separate expressions.
 # -----------------------------------------------------------------------------
-
-def _form_master_re(relist,reflags,ldict,toknames):
-    if not relist: return []
-    regex = "|".join(relist)
+def _form_master_re(relist, reflags, ldict, toknames):
+    if not relist:
+        return []
+    regex = '|'.join(relist)
     try:
-        lexre = re.compile(regex,re.VERBOSE | reflags)
+        lexre = re.compile(regex, reflags)
 
         # Build the index to function map for the matching engine
-        lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1)
+        lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1)
         lexindexnames = lexindexfunc[:]
 
-        for f,i in lexre.groupindex.items():
-            handle = ldict.get(f,None)
+        for f, i in lexre.groupindex.items():
+            handle = ldict.get(f, None)
             if type(handle) in (types.FunctionType, types.MethodType):
-                lexindexfunc[i] = (handle,toknames[f])
+                lexindexfunc[i] = (handle, toknames[f])
                 lexindexnames[i] = f
             elif handle is not None:
                 lexindexnames[i] = f
-                if f.find("ignore_") > 0:
-                    lexindexfunc[i] = (None,None)
+                if f.find('ignore_') > 0:
+                    lexindexfunc[i] = (None, None)
                 else:
                     lexindexfunc[i] = (None, toknames[f])
-        
-        return [(lexre,lexindexfunc)],[regex],[lexindexnames]
+
+        return [(lexre, lexindexfunc)], [regex], [lexindexnames]
     except Exception:
         m = int(len(relist)/2)
-        if m == 0: m = 1
-        llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames)
-        rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames)
-        return llist+rlist, lre+rre, lnames+rnames
+        if m == 0:
+            m = 1
+        llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames)
+        rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames)
+        return (llist+rlist), (lre+rre), (lnames+rnames)
 
 # -----------------------------------------------------------------------------
 # def _statetoken(s,names)
@@ -517,22 +530,22 @@
 # is a tuple of state names and tokenname is the name of the token.  For example,
 # calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM')
 # -----------------------------------------------------------------------------
+def _statetoken(s, names):
+    parts = s.split('_')
+    for i, part in enumerate(parts[1:], 1):
+        if part not in names and part != 'ANY':
+            break
 
-def _statetoken(s,names):
-    nonstate = 1
-    parts = s.split("_")
-    for i in range(1,len(parts)):
-         if not parts[i] in names and parts[i] != 'ANY': break
     if i > 1:
-       states = tuple(parts[1:i])
+        states = tuple(parts[1:i])
     else:
-       states = ('INITIAL',)
+        states = ('INITIAL',)
 
     if 'ANY' in states:
-       states = tuple(names)
+        states = tuple(names)
 
-    tokenname = "_".join(parts[i:])
-    return (states,tokenname)
+    tokenname = '_'.join(parts[i:])
+    return (states, tokenname)
 
 
 # -----------------------------------------------------------------------------
@@ -542,19 +555,15 @@
 # user's input file.
 # -----------------------------------------------------------------------------
 class LexerReflect(object):
-    def __init__(self,ldict,log=None,reflags=0):
+    def __init__(self, ldict, log=None, reflags=0):
         self.ldict      = ldict
         self.error_func = None
         self.tokens     = []
         self.reflags    = reflags
-        self.stateinfo  = { 'INITIAL' : 'inclusive'}
-        self.files      = {}
-        self.error      = 0
-
-        if log is None:
-            self.log = PlyLogger(sys.stderr)
-        else:
-            self.log = log
+        self.stateinfo  = {'INITIAL': 'inclusive'}
+        self.modules    = set()
+        self.error      = False
+        self.log        = PlyLogger(sys.stderr) if log is None else log
 
     # Get all of the basic information
     def get_all(self):
@@ -562,7 +571,7 @@
         self.get_literals()
         self.get_states()
         self.get_rules()
-        
+
     # Validate all of the information
     def validate_all(self):
         self.validate_tokens()
@@ -572,20 +581,20 @@
 
     # Get the tokens map
     def get_tokens(self):
-        tokens = self.ldict.get("tokens",None)
+        tokens = self.ldict.get('tokens', None)
         if not tokens:
-            self.log.error("No token list is defined")
-            self.error = 1
+            self.log.error('No token list is defined')
+            self.error = True
             return
 
-        if not isinstance(tokens,(list, tuple)):
-            self.log.error("tokens must be a list or tuple")
-            self.error = 1
+        if not isinstance(tokens, (list, tuple)):
+            self.log.error('tokens must be a list or tuple')
+            self.error = True
             return
-        
+
         if not tokens:
-            self.log.error("tokens is empty")
-            self.error = 1
+            self.log.error('tokens is empty')
+            self.error = True
             return
 
         self.tokens = tokens
@@ -595,280 +604,274 @@
         terminals = {}
         for n in self.tokens:
             if not _is_identifier.match(n):
-                self.log.error("Bad token name '%s'",n)
-                self.error = 1
+                self.log.error("Bad token name '%s'", n)
+                self.error = True
             if n in terminals:
                 self.log.warning("Token '%s' multiply defined", n)
             terminals[n] = 1
 
     # Get the literals specifier
     def get_literals(self):
-        self.literals = self.ldict.get("literals","")
+        self.literals = self.ldict.get('literals', '')
+        if not self.literals:
+            self.literals = ''
 
     # Validate literals
     def validate_literals(self):
         try:
             for c in self.literals:
-                if not isinstance(c,StringTypes) or len(c) > 1:
-                    self.log.error("Invalid literal %s. Must be a single character", repr(c))
-                    self.error = 1
-                    continue
+                if not isinstance(c, StringTypes) or len(c) > 1:
+                    self.log.error('Invalid literal %s. Must be a single character', repr(c))
+                    self.error = True
 
         except TypeError:
-            self.log.error("Invalid literals specification. literals must be a sequence of characters")
-            self.error = 1
+            self.log.error('Invalid literals specification. literals must be a sequence of characters')
+            self.error = True
 
     def get_states(self):
-        self.states = self.ldict.get("states",None)
+        self.states = self.ldict.get('states', None)
         # Build statemap
         if self.states:
-             if not isinstance(self.states,(tuple,list)):
-                  self.log.error("states must be defined as a tuple or list")
-                  self.error = 1
-             else:
-                  for s in self.states:
-                        if not isinstance(s,tuple) or len(s) != 2:
-                               self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s))
-                               self.error = 1
-                               continue
-                        name, statetype = s
-                        if not isinstance(name,StringTypes):
-                               self.log.error("State name %s must be a string", repr(name))
-                               self.error = 1
-                               continue
-                        if not (statetype == 'inclusive' or statetype == 'exclusive'):
-                               self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name)
-                               self.error = 1
-                               continue
-                        if name in self.stateinfo:
-                               self.log.error("State '%s' already defined",name)
-                               self.error = 1
-                               continue
-                        self.stateinfo[name] = statetype
+            if not isinstance(self.states, (tuple, list)):
+                self.log.error('states must be defined as a tuple or list')
+                self.error = True
+            else:
+                for s in self.states:
+                    if not isinstance(s, tuple) or len(s) != 2:
+                        self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')", repr(s))
+                        self.error = True
+                        continue
+                    name, statetype = s
+                    if not isinstance(name, StringTypes):
+                        self.log.error('State name %s must be a string', repr(name))
+                        self.error = True
+                        continue
+                    if not (statetype == 'inclusive' or statetype == 'exclusive'):
+                        self.log.error("State type for state %s must be 'inclusive' or 'exclusive'", name)
+                        self.error = True
+                        continue
+                    if name in self.stateinfo:
+                        self.log.error("State '%s' already defined", name)
+                        self.error = True
+                        continue
+                    self.stateinfo[name] = statetype
 
     # Get all of the symbols with a t_ prefix and sort them into various
     # categories (functions, strings, error functions, and ignore characters)
 
     def get_rules(self):
-        tsymbols = [f for f in self.ldict if f[:2] == 't_' ]
+        tsymbols = [f for f in self.ldict if f[:2] == 't_']
 
         # Now build up a list of functions and a list of strings
-
-        self.toknames = { }        # Mapping of symbols to token names
-        self.funcsym =  { }        # Symbols defined as functions
-        self.strsym =   { }        # Symbols defined as strings
-        self.ignore   = { }        # Ignore strings by state
-        self.errorf   = { }        # Error functions by state
+        self.toknames = {}        # Mapping of symbols to token names
+        self.funcsym  = {}        # Symbols defined as functions
+        self.strsym   = {}        # Symbols defined as strings
+        self.ignore   = {}        # Ignore strings by state
+        self.errorf   = {}        # Error functions by state
+        self.eoff     = {}        # EOF functions by state
 
         for s in self.stateinfo:
-             self.funcsym[s] = []
-             self.strsym[s] = []
+            self.funcsym[s] = []
+            self.strsym[s] = []
 
         if len(tsymbols) == 0:
-            self.log.error("No rules of the form t_rulename are defined")
-            self.error = 1
+            self.log.error('No rules of the form t_rulename are defined')
+            self.error = True
             return
 
         for f in tsymbols:
             t = self.ldict[f]
-            states, tokname = _statetoken(f,self.stateinfo)
+            states, tokname = _statetoken(f, self.stateinfo)
             self.toknames[f] = tokname
 
-            if hasattr(t,"__call__"):
+            if hasattr(t, '__call__'):
                 if tokname == 'error':
                     for s in states:
                         self.errorf[s] = t
+                elif tokname == 'eof':
+                    for s in states:
+                        self.eoff[s] = t
                 elif tokname == 'ignore':
-                    line = func_code(t).co_firstlineno
-                    file = func_code(t).co_filename
-                    self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__)
-                    self.error = 1
+                    line = t.__code__.co_firstlineno
+                    file = t.__code__.co_filename
+                    self.log.error("%s:%d: Rule '%s' must be defined as a string", file, line, t.__name__)
+                    self.error = True
                 else:
-                    for s in states: 
-                        self.funcsym[s].append((f,t))
+                    for s in states:
+                        self.funcsym[s].append((f, t))
             elif isinstance(t, StringTypes):
                 if tokname == 'ignore':
                     for s in states:
                         self.ignore[s] = t
-                    if "\\" in t:
-                        self.log.warning("%s contains a literal backslash '\\'",f)
+                    if '\\' in t:
+                        self.log.warning("%s contains a literal backslash '\\'", f)
 
                 elif tokname == 'error':
                     self.log.error("Rule '%s' must be defined as a function", f)
-                    self.error = 1
+                    self.error = True
                 else:
-                    for s in states: 
-                        self.strsym[s].append((f,t))
+                    for s in states:
+                        self.strsym[s].append((f, t))
             else:
-                self.log.error("%s not defined as a function or string", f)
-                self.error = 1
+                self.log.error('%s not defined as a function or string', f)
+                self.error = True
 
         # Sort the functions by line number
         for f in self.funcsym.values():
-            if sys.version_info[0] < 3:
-                f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno))
-            else:
-                # Python 3.0
-                f.sort(key=lambda x: func_code(x[1]).co_firstlineno)
+            f.sort(key=lambda x: x[1].__code__.co_firstlineno)
 
         # Sort the strings by regular expression length
         for s in self.strsym.values():
-            if sys.version_info[0] < 3:
-                s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1])))
-            else:
-                # Python 3.0
-                s.sort(key=lambda x: len(x[1]),reverse=True)
+            s.sort(key=lambda x: len(x[1]), reverse=True)
 
-    # Validate all of the t_rules collected 
+    # Validate all of the t_rules collected
     def validate_rules(self):
         for state in self.stateinfo:
             # Validate all rules defined by functions
 
-            
-
             for fname, f in self.funcsym[state]:
-                line = func_code(f).co_firstlineno
-                file = func_code(f).co_filename
-                self.files[file] = 1
+                line = f.__code__.co_firstlineno
+                file = f.__code__.co_filename
+                module = inspect.getmodule(f)
+                self.modules.add(module)
 
                 tokname = self.toknames[fname]
                 if isinstance(f, types.MethodType):
                     reqargs = 2
                 else:
                     reqargs = 1
-                nargs = func_code(f).co_argcount
+                nargs = f.__code__.co_argcount
                 if nargs > reqargs:
-                    self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
-                    self.error = 1
+                    self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__)
+                    self.error = True
                     continue
 
                 if nargs < reqargs:
-                    self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
-                    self.error = 1
+                    self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__)
+                    self.error = True
                     continue
 
-                if not f.__doc__:
-                    self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__)
-                    self.error = 1
+                if not _get_regex(f):
+                    self.log.error("%s:%d: No regular expression defined for rule '%s'", file, line, f.__name__)
+                    self.error = True
                     continue
 
                 try:
-                    c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | self.reflags)
-                    if c.match(""):
-                        self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__)
-                        self.error = 1
-                except re.error:
-                    _etype, e, _etrace = sys.exc_info()
-                    self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e)
-                    if '#' in f.__doc__:
-                        self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__)
-                    self.error = 1
+                    c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags)
+                    if c.match(''):
+                        self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file, line, f.__name__)
+                        self.error = True
+                except re.error as e:
+                    self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e)
+                    if '#' in _get_regex(f):
+                        self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'", file, line, f.__name__)
+                    self.error = True
 
             # Validate all rules defined by strings
-            for name,r in self.strsym[state]:
+            for name, r in self.strsym[state]:
                 tokname = self.toknames[name]
                 if tokname == 'error':
                     self.log.error("Rule '%s' must be defined as a function", name)
-                    self.error = 1
+                    self.error = True
                     continue
 
-                if not tokname in self.tokens and tokname.find("ignore_") < 0:
-                    self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname)
-                    self.error = 1
+                if tokname not in self.tokens and tokname.find('ignore_') < 0:
+                    self.log.error("Rule '%s' defined for an unspecified token %s", name, tokname)
+                    self.error = True
                     continue
 
                 try:
-                    c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags)
-                    if (c.match("")):
-                         self.log.error("Regular expression for rule '%s' matches empty string",name)
-                         self.error = 1
-                except re.error:
-                    _etype, e, _etrace = sys.exc_info()
-                    self.log.error("Invalid regular expression for rule '%s'. %s",name,e)
+                    c = re.compile('(?P<%s>%s)' % (name, r), self.reflags)
+                    if (c.match('')):
+                        self.log.error("Regular expression for rule '%s' matches empty string", name)
+                        self.error = True
+                except re.error as e:
+                    self.log.error("Invalid regular expression for rule '%s'. %s", name, e)
                     if '#' in r:
-                         self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name)
-                    self.error = 1
+                        self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'", name)
+                    self.error = True
 
             if not self.funcsym[state] and not self.strsym[state]:
-                self.log.error("No rules defined for state '%s'",state)
-                self.error = 1
+                self.log.error("No rules defined for state '%s'", state)
+                self.error = True
 
             # Validate the error function
-            efunc = self.errorf.get(state,None)
+            efunc = self.errorf.get(state, None)
             if efunc:
                 f = efunc
-                line = func_code(f).co_firstlineno
-                file = func_code(f).co_filename
-                self.files[file] = 1
+                line = f.__code__.co_firstlineno
+                file = f.__code__.co_filename
+                module = inspect.getmodule(f)
+                self.modules.add(module)
 
                 if isinstance(f, types.MethodType):
                     reqargs = 2
                 else:
                     reqargs = 1
-                nargs = func_code(f).co_argcount
+                nargs = f.__code__.co_argcount
                 if nargs > reqargs:
-                    self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
-                    self.error = 1
+                    self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__)
+                    self.error = True
 
                 if nargs < reqargs:
-                    self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
-                    self.error = 1
+                    self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__)
+                    self.error = True
 
-        for f in self.files:
-            self.validate_file(f)
-
+        for module in self.modules:
+            self.validate_module(module)
 
     # -----------------------------------------------------------------------------
-    # validate_file()
+    # validate_module()
     #
     # This checks to see if there are duplicated t_rulename() functions or strings
     # in the parser input file.  This is done using a simple regular expression
-    # match on each line in the given file.  
+    # match on each line in the source code of the given module.
     # -----------------------------------------------------------------------------
 
-    def validate_file(self,filename):
-        import os.path
-        base,ext = os.path.splitext(filename)
-        if ext != '.py': return         # No idea what the file is. Return OK
-
+    def validate_module(self, module):
         try:
-            f = open(filename)
-            lines = f.readlines()
-            f.close()
+            lines, linen = inspect.getsourcelines(module)
         except IOError:
-            return                      # Couldn't find the file.  Don't worry about it
+            return
 
         fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(')
         sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=')
 
-        counthash = { }
-        linen = 1
-        for l in lines:
-            m = fre.match(l)
+        counthash = {}
+        linen += 1
+        for line in lines:
+            m = fre.match(line)
             if not m:
-                m = sre.match(l)
+                m = sre.match(line)
             if m:
                 name = m.group(1)
                 prev = counthash.get(name)
                 if not prev:
                     counthash[name] = linen
                 else:
-                    self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev)
-                    self.error = 1
+                    filename = inspect.getsourcefile(module)
+                    self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev)
+                    self.error = True
             linen += 1
-            
+
 # -----------------------------------------------------------------------------
 # lex(module)
 #
 # Build all of the regular expression rules from definitions in the supplied module
 # -----------------------------------------------------------------------------
-def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None):
+def lex(module=None, object=None, debug=False, optimize=False, lextab='lextab',
+        reflags=int(re.VERBOSE), nowarn=False, outputdir=None, debuglog=None, errorlog=None):
+
+    if lextab is None:
+        lextab = 'lextab'
+
     global lexer
+
     ldict = None
-    stateinfo  = { 'INITIAL' : 'inclusive'}
+    stateinfo  = {'INITIAL': 'inclusive'}
     lexobj = Lexer()
     lexobj.lexoptimize = optimize
-    global token,input
+    global token, input
 
     if errorlog is None:
         errorlog = PlyLogger(sys.stderr)
@@ -878,16 +881,28 @@
             debuglog = PlyLogger(sys.stderr)
 
     # Get the module dictionary used for the lexer
-    if object: module = object
+    if object:
+        module = object
 
+    # Get the module dictionary used for the parser
     if module:
-        _items = [(k,getattr(module,k)) for k in dir(module)]
+        _items = [(k, getattr(module, k)) for k in dir(module)]
         ldict = dict(_items)
+        # If no __file__ attribute is available, try to obtain it from the __module__ instead
+        if '__file__' not in ldict:
+            ldict['__file__'] = sys.modules[ldict['__module__']].__file__
     else:
         ldict = get_caller_module_dict(2)
 
+    # Determine if the module is package of a package or not.
+    # If so, fix the tabmodule setting so that tables load correctly
+    pkg = ldict.get('__package__')
+    if pkg and isinstance(lextab, str):
+        if '.' not in lextab:
+            lextab = pkg + '.' + lextab
+
     # Collect parser information from the dictionary
-    linfo = LexerReflect(ldict,log=errorlog,reflags=reflags)
+    linfo = LexerReflect(ldict, log=errorlog, reflags=reflags)
     linfo.get_all()
     if not optimize:
         if linfo.validate_all():
@@ -895,7 +910,7 @@
 
     if optimize and lextab:
         try:
-            lexobj.readtab(lextab,ldict)
+            lexobj.readtab(lextab, ldict)
             token = lexobj.token
             input = lexobj.input
             lexer = lexobj
@@ -906,92 +921,97 @@
 
     # Dump some basic debugging information
     if debug:
-        debuglog.info("lex: tokens   = %r", linfo.tokens)
-        debuglog.info("lex: literals = %r", linfo.literals)
-        debuglog.info("lex: states   = %r", linfo.stateinfo)
+        debuglog.info('lex: tokens   = %r', linfo.tokens)
+        debuglog.info('lex: literals = %r', linfo.literals)
+        debuglog.info('lex: states   = %r', linfo.stateinfo)
 
     # Build a dictionary of valid token names
-    lexobj.lextokens = { }
+    lexobj.lextokens = set()
     for n in linfo.tokens:
-        lexobj.lextokens[n] = 1
+        lexobj.lextokens.add(n)
 
     # Get literals specification
-    if isinstance(linfo.literals,(list,tuple)):
+    if isinstance(linfo.literals, (list, tuple)):
         lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals)
     else:
         lexobj.lexliterals = linfo.literals
 
+    lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals)
+
     # Get the stateinfo dictionary
     stateinfo = linfo.stateinfo
 
-    regexs = { }
+    regexs = {}
     # Build the master regular expressions
     for state in stateinfo:
         regex_list = []
 
         # Add rules defined by functions first
         for fname, f in linfo.funcsym[state]:
-            line = func_code(f).co_firstlineno
-            file = func_code(f).co_filename
-            regex_list.append("(?P<%s>%s)" % (fname,f.__doc__))
+            regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f)))
             if debug:
-                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state)
+                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state)
 
         # Now add all of the simple rules
-        for name,r in linfo.strsym[state]:
-            regex_list.append("(?P<%s>%s)" % (name,r))
+        for name, r in linfo.strsym[state]:
+            regex_list.append('(?P<%s>%s)' % (name, r))
             if debug:
-                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state)
+                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state)
 
         regexs[state] = regex_list
 
     # Build the master regular expressions
 
     if debug:
-        debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====")
+        debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====')
 
     for state in regexs:
-        lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames)
+        lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames)
         lexobj.lexstatere[state] = lexre
         lexobj.lexstateretext[state] = re_text
         lexobj.lexstaterenames[state] = re_names
         if debug:
-            for i in range(len(re_text)):
-                debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i])
+            for i, text in enumerate(re_text):
+                debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text)
 
     # For inclusive states, we need to add the regular expressions from the INITIAL state
-    for state,stype in stateinfo.items():
-        if state != "INITIAL" and stype == 'inclusive':
-             lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
-             lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
-             lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])
+    for state, stype in stateinfo.items():
+        if state != 'INITIAL' and stype == 'inclusive':
+            lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
+            lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
+            lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])
 
     lexobj.lexstateinfo = stateinfo
-    lexobj.lexre = lexobj.lexstatere["INITIAL"]
-    lexobj.lexretext = lexobj.lexstateretext["INITIAL"]
+    lexobj.lexre = lexobj.lexstatere['INITIAL']
+    lexobj.lexretext = lexobj.lexstateretext['INITIAL']
+    lexobj.lexreflags = reflags
 
     # Set up ignore variables
     lexobj.lexstateignore = linfo.ignore
-    lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","")
+    lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '')
 
     # Set up error functions
     lexobj.lexstateerrorf = linfo.errorf
-    lexobj.lexerrorf = linfo.errorf.get("INITIAL",None)
+    lexobj.lexerrorf = linfo.errorf.get('INITIAL', None)
     if not lexobj.lexerrorf:
-        errorlog.warning("No t_error rule is defined")
+        errorlog.warning('No t_error rule is defined')
+
+    # Set up eof functions
+    lexobj.lexstateeoff = linfo.eoff
+    lexobj.lexeoff = linfo.eoff.get('INITIAL', None)
 
     # Check state information for ignore and error rules
-    for s,stype in stateinfo.items():
+    for s, stype in stateinfo.items():
         if stype == 'exclusive':
-              if not s in linfo.errorf:
-                   errorlog.warning("No error rule is defined for exclusive state '%s'", s)
-              if not s in linfo.ignore and lexobj.lexignore:
-                   errorlog.warning("No ignore rule is defined for exclusive state '%s'", s)
+            if s not in linfo.errorf:
+                errorlog.warning("No error rule is defined for exclusive state '%s'", s)
+            if s not in linfo.ignore and lexobj.lexignore:
+                errorlog.warning("No ignore rule is defined for exclusive state '%s'", s)
         elif stype == 'inclusive':
-              if not s in linfo.errorf:
-                   linfo.errorf[s] = linfo.errorf.get("INITIAL",None)
-              if not s in linfo.ignore:
-                   linfo.ignore[s] = linfo.ignore.get("INITIAL","")
+            if s not in linfo.errorf:
+                linfo.errorf[s] = linfo.errorf.get('INITIAL', None)
+            if s not in linfo.ignore:
+                linfo.ignore[s] = linfo.ignore.get('INITIAL', '')
 
     # Create global versions of the token() and input() functions
     token = lexobj.token
@@ -1000,7 +1020,28 @@
 
     # If in optimize mode, we write the lextab
     if lextab and optimize:
-        lexobj.writetab(lextab,outputdir)
+        if outputdir is None:
+            # If no output directory is set, the location of the output files
+            # is determined according to the following rules:
+            #     - If lextab specifies a package, files go into that package directory
+            #     - Otherwise, files go in the same directory as the specifying module
+            if isinstance(lextab, types.ModuleType):
+                srcfile = lextab.__file__
+            else:
+                if '.' not in lextab:
+                    srcfile = ldict['__file__']
+                else:
+                    parts = lextab.split('.')
+                    pkgname = '.'.join(parts[:-1])
+                    exec('import %s' % pkgname)
+                    srcfile = getattr(sys.modules[pkgname], '__file__', '')
+            outputdir = os.path.dirname(srcfile)
+        try:
+            lexobj.writetab(lextab, outputdir)
+            if lextab in sys.modules:
+                del sys.modules[lextab]
+        except IOError as e:
+            errorlog.warning("Couldn't write lextab module %r. %s" % (lextab, e))
 
     return lexobj
 
@@ -1010,7 +1051,7 @@
 # This runs the lexer as a main program
 # -----------------------------------------------------------------------------
 
-def runmain(lexer=None,data=None):
+def runmain(lexer=None, data=None):
     if not data:
         try:
             filename = sys.argv[1]
@@ -1018,7 +1059,7 @@
             data = f.read()
             f.close()
         except IndexError:
-            sys.stdout.write("Reading from standard input (type EOF to end):\n")
+            sys.stdout.write('Reading from standard input (type EOF to end):\n')
             data = sys.stdin.read()
 
     if lexer:
@@ -1031,10 +1072,11 @@
     else:
         _token = token
 
-    while 1:
+    while True:
         tok = _token()
-        if not tok: break
-        sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos))
+        if not tok:
+            break
+        sys.stdout.write('(%s,%r,%d,%d)\n' % (tok.type, tok.value, tok.lineno, tok.lexpos))
 
 # -----------------------------------------------------------------------------
 # @TOKEN(regex)
@@ -1044,14 +1086,13 @@
 # -----------------------------------------------------------------------------
 
 def TOKEN(r):
-    def set_doc(f):
-        if hasattr(r,"__call__"):
-            f.__doc__ = r.__doc__
+    def set_regex(f):
+        if hasattr(r, '__call__'):
+            f.regex = _get_regex(r)
         else:
-            f.__doc__ = r
+            f.regex = r
         return f
-    return set_doc
+    return set_regex
 
 # Alternative spelling of the TOKEN decorator
 Token = TOKEN
-
diff --git a/ext/ply/ply/yacc.py b/ext/ply/ply/yacc.py
index d4bb882..88188a1 100644
--- a/ext/ply/ply/yacc.py
+++ b/ext/ply/ply/yacc.py
@@ -1,22 +1,22 @@
 # -----------------------------------------------------------------------------
 # ply: yacc.py
 #
-# Copyright (C) 2001-2009,
+# Copyright (C) 2001-2018
 # David M. Beazley (Dabeaz LLC)
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met:
-# 
+#
 # * Redistributions of source code must retain the above copyright notice,
-#   this list of conditions and the following disclaimer.  
-# * Redistributions in binary form must reproduce the above copyright notice, 
+#   this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
 #   this list of conditions and the following disclaimer in the documentation
-#   and/or other materials provided with the distribution.  
+#   and/or other materials provided with the distribution.
 # * Neither the name of the David Beazley or Dabeaz LLC may be used to
 #   endorse or promote products derived from this software without
-#  specific prior written permission. 
+#  specific prior written permission.
 #
 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -32,7 +32,7 @@
 # -----------------------------------------------------------------------------
 #
 # This implements an LR parser that is constructed from grammar rules defined
-# as Python functions. The grammer is specified by supplying the BNF inside
+# as Python functions. The grammar is specified by supplying the BNF inside
 # Python documentation strings.  The inspiration for this technique was borrowed
 # from John Aycock's Spark parsing system.  PLY might be viewed as cross between
 # Spark and the GNU bison utility.
@@ -59,8 +59,15 @@
 # own risk!
 # ----------------------------------------------------------------------------
 
-__version__    = "3.2"
-__tabversion__ = "3.2"       # Table version
+import re
+import types
+import sys
+import os.path
+import inspect
+import warnings
+
+__version__    = '3.11'
+__tabversion__ = '3.10'
 
 #-----------------------------------------------------------------------------
 #                     === User configurable parameters ===
@@ -68,7 +75,7 @@
 # Change these to modify the default behavior of yacc (if you wish)
 #-----------------------------------------------------------------------------
 
-yaccdebug   = 0                # Debugging mode.  If set, yacc generates a
+yaccdebug   = True             # Debugging mode.  If set, yacc generates a
                                # a 'parser.out' file in the current directory
 
 debug_file  = 'parser.out'     # Default name of the debugging file
@@ -77,86 +84,117 @@
 
 error_count = 3                # Number of symbols that must be shifted to leave recovery mode
 
-yaccdevel   = 0                # Set to True if developing yacc.  This turns off optimized
+yaccdevel   = False            # Set to True if developing yacc.  This turns off optimized
                                # implementations of certain functions.
 
 resultlimit = 40               # Size limit of results when running in debug mode.
 
 pickle_protocol = 0            # Protocol to use when writing pickle files
 
-import re, types, sys, os.path
-
-# Compatibility function for python 2.6/3.0
+# String type-checking compatibility
 if sys.version_info[0] < 3:
-    def func_code(f):
-        return f.func_code
+    string_types = basestring
 else:
-    def func_code(f):
-        return f.__code__
+    string_types = str
 
-# Compatibility
-try:
-    MAXINT = sys.maxint
-except AttributeError:
-    MAXINT = sys.maxsize
+MAXINT = sys.maxsize
 
-# Python 2.x/3.0 compatibility.
-def load_ply_lex():
-    if sys.version_info[0] < 3:
-        import lex
-    else:
-        import ply.lex as lex
-    return lex
-
-# This object is a stand-in for a logging object created by the 
+# This object is a stand-in for a logging object created by the
 # logging module.   PLY will use this by default to create things
 # such as the parser.out file.  If a user wants more detailed
 # information, they can create their own logging object and pass
 # it into PLY.
 
 class PlyLogger(object):
-    def __init__(self,f):
+    def __init__(self, f):
         self.f = f
-    def debug(self,msg,*args,**kwargs):
-        self.f.write((msg % args) + "\n")
-    info     = debug
 
-    def warning(self,msg,*args,**kwargs):
-        self.f.write("WARNING: "+ (msg % args) + "\n")
+    def debug(self, msg, *args, **kwargs):
+        self.f.write((msg % args) + '\n')
 
-    def error(self,msg,*args,**kwargs):
-        self.f.write("ERROR: " + (msg % args) + "\n")
+    info = debug
+
+    def warning(self, msg, *args, **kwargs):
+        self.f.write('WARNING: ' + (msg % args) + '\n')
+
+    def error(self, msg, *args, **kwargs):
+        self.f.write('ERROR: ' + (msg % args) + '\n')
 
     critical = debug
 
 # Null logger is used when no output is generated. Does nothing.
 class NullLogger(object):
-    def __getattribute__(self,name):
+    def __getattribute__(self, name):
         return self
-    def __call__(self,*args,**kwargs):
+
+    def __call__(self, *args, **kwargs):
         return self
-        
+
 # Exception raised for yacc-related errors
-class YaccError(Exception):   pass
+class YaccError(Exception):
+    pass
 
 # Format the result message that the parser produces when running in debug mode.
 def format_result(r):
     repr_str = repr(r)
-    if '\n' in repr_str: repr_str = repr(repr_str)
+    if '\n' in repr_str:
+        repr_str = repr(repr_str)
     if len(repr_str) > resultlimit:
-        repr_str = repr_str[:resultlimit]+" ..."
-    result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str)
+        repr_str = repr_str[:resultlimit] + ' ...'
+    result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str)
     return result
 
-
 # Format stack entries when the parser is running in debug mode
 def format_stack_entry(r):
     repr_str = repr(r)
-    if '\n' in repr_str: repr_str = repr(repr_str)
+    if '\n' in repr_str:
+        repr_str = repr(repr_str)
     if len(repr_str) < 16:
         return repr_str
     else:
-        return "<%s @ 0x%x>" % (type(r).__name__,id(r))
+        return '<%s @ 0x%x>' % (type(r).__name__, id(r))
+
+# Panic mode error recovery support.   This feature is being reworked--much of the
+# code here is to offer a deprecation/backwards compatible transition
+
+_errok = None
+_token = None
+_restart = None
+_warnmsg = '''PLY: Don't use global functions errok(), token(), and restart() in p_error().
+Instead, invoke the methods on the associated parser instance:
+
+    def p_error(p):
+        ...
+        # Use parser.errok(), parser.token(), parser.restart()
+        ...
+
+    parser = yacc.yacc()
+'''
+
+def errok():
+    warnings.warn(_warnmsg)
+    return _errok()
+
+def restart():
+    warnings.warn(_warnmsg)
+    return _restart()
+
+def token():
+    warnings.warn(_warnmsg)
+    return _token()
+
+# Utility function to call the p_error() function with some deprecation hacks
+def call_errorfunc(errorfunc, token, parser):
+    global _errok, _token, _restart
+    _errok = parser.errok
+    _token = parser.token
+    _restart = parser.restart
+    r = errorfunc(token)
+    try:
+        del _errok, _token, _restart
+    except NameError:
+        pass
+    return r
 
 #-----------------------------------------------------------------------------
 #                        ===  LR Parsing Engine ===
@@ -176,8 +214,11 @@
 #        .endlexpos  = Ending lex position (optional, set automatically)
 
 class YaccSymbol:
-    def __str__(self):    return self.type
-    def __repr__(self):   return str(self)
+    def __str__(self):
+        return self.type
+
+    def __repr__(self):
+        return str(self)
 
 # This class is a wrapper around the objects actually passed to each
 # grammar rule.   Index lookup and assignment actually assign the
@@ -189,46 +230,53 @@
 # representing the range of positional information for a symbol.
 
 class YaccProduction:
-    def __init__(self,s,stack=None):
+    def __init__(self, s, stack=None):
         self.slice = s
         self.stack = stack
         self.lexer = None
-        self.parser= None
-    def __getitem__(self,n):
-        if n >= 0: return self.slice[n].value
-        else: return self.stack[n].value
+        self.parser = None
 
-    def __setitem__(self,n,v):
+    def __getitem__(self, n):
+        if isinstance(n, slice):
+            return [s.value for s in self.slice[n]]
+        elif n >= 0:
+            return self.slice[n].value
+        else:
+            return self.stack[n].value
+
+    def __setitem__(self, n, v):
         self.slice[n].value = v
 
-    def __getslice__(self,i,j):
+    def __getslice__(self, i, j):
         return [s.value for s in self.slice[i:j]]
 
     def __len__(self):
         return len(self.slice)
 
-    def lineno(self,n):
-        return getattr(self.slice[n],"lineno",0)
+    def lineno(self, n):
+        return getattr(self.slice[n], 'lineno', 0)
 
-    def set_lineno(self,n,lineno):
-        self.slice[n].lineno = n
+    def set_lineno(self, n, lineno):
+        self.slice[n].lineno = lineno
 
-    def linespan(self,n):
-        startline = getattr(self.slice[n],"lineno",0)
-        endline = getattr(self.slice[n],"endlineno",startline)
-        return startline,endline
+    def linespan(self, n):
+        startline = getattr(self.slice[n], 'lineno', 0)
+        endline = getattr(self.slice[n], 'endlineno', startline)
+        return startline, endline
 
-    def lexpos(self,n):
-        return getattr(self.slice[n],"lexpos",0)
+    def lexpos(self, n):
+        return getattr(self.slice[n], 'lexpos', 0)
 
-    def lexspan(self,n):
-        startpos = getattr(self.slice[n],"lexpos",0)
-        endpos = getattr(self.slice[n],"endlexpos",startpos)
-        return startpos,endpos
+    def set_lexpos(self, n, lexpos):
+        self.slice[n].lexpos = lexpos
+
+    def lexspan(self, n):
+        startpos = getattr(self.slice[n], 'lexpos', 0)
+        endpos = getattr(self.slice[n], 'endlexpos', startpos)
+        return startpos, endpos
 
     def error(self):
-       raise SyntaxError
-
+        raise SyntaxError
 
 # -----------------------------------------------------------------------------
 #                               == LRParser ==
@@ -237,14 +285,16 @@
 # -----------------------------------------------------------------------------
 
 class LRParser:
-    def __init__(self,lrtab,errorf):
+    def __init__(self, lrtab, errorf):
         self.productions = lrtab.lr_productions
-        self.action      = lrtab.lr_action
-        self.goto        = lrtab.lr_goto
-        self.errorfunc   = errorf
+        self.action = lrtab.lr_action
+        self.goto = lrtab.lr_goto
+        self.errorfunc = errorf
+        self.set_defaulted_states()
+        self.errorok = True
 
     def errok(self):
-        self.errorok     = 1
+        self.errorok = True
 
     def restart(self):
         del self.statestack[:]
@@ -254,24 +304,42 @@
         self.symstack.append(sym)
         self.statestack.append(0)
 
-    def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
+    # Defaulted state support.
+    # This method identifies parser states where there is only one possible reduction action.
+    # For such states, the parser can make a choose to make a rule reduction without consuming
+    # the next look-ahead token.  This delayed invocation of the tokenizer can be useful in
+    # certain kinds of advanced parsing situations where the lexer and parser interact with
+    # each other or change states (i.e., manipulation of scope, lexer states, etc.).
+    #
+    # See:  http://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions
+    def set_defaulted_states(self):
+        self.defaulted_states = {}
+        for state, actions in self.action.items():
+            rules = list(actions.values())
+            if len(rules) == 1 and rules[0] < 0:
+                self.defaulted_states[state] = rules[0]
+
+    def disable_defaulted_states(self):
+        self.defaulted_states = {}
+
+    def parse(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
         if debug or yaccdevel:
-            if isinstance(debug,int):
+            if isinstance(debug, int):
                 debug = PlyLogger(sys.stderr)
-            return self.parsedebug(input,lexer,debug,tracking,tokenfunc)
+            return self.parsedebug(input, lexer, debug, tracking, tokenfunc)
         elif tracking:
-            return self.parseopt(input,lexer,debug,tracking,tokenfunc)
+            return self.parseopt(input, lexer, debug, tracking, tokenfunc)
         else:
-            return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc)
-        
+            return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
+
 
     # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     # parsedebug().
     #
     # This is the debugging enabled version of parse().  All changes made to the
-    # parsing engine should be made here.   For the non-debugging version,
-    # copy this code to a method parseopt() and delete all of the sections
-    # enclosed in:
+    # parsing engine should be made here.   Optimized versions of this function
+    # are automatically created by the ply/ygen.py script.  This script cuts out
+    # sections enclosed in markers such as this:
     #
     #      #--! DEBUG
     #      statements
@@ -279,22 +347,24 @@
     #
     # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
-    def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None):
-        lookahead = None                 # Current lookahead symbol
-        lookaheadstack = [ ]             # Stack of lookahead symbols
-        actions = self.action            # Local reference to action table (to avoid lookup on self.)
-        goto    = self.goto              # Local reference to goto table (to avoid lookup on self.)
-        prod    = self.productions       # Local reference to production list (to avoid lookup on self.)
-        pslice  = YaccProduction(None)   # Production object passed to grammar rules
-        errorcount = 0                   # Used during error recovery 
+    def parsedebug(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
+        #--! parsedebug-start
+        lookahead = None                         # Current lookahead symbol
+        lookaheadstack = []                      # Stack of lookahead symbols
+        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
+        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
+        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
+        defaulted_states = self.defaulted_states # Local reference to defaulted states
+        pslice  = YaccProduction(None)           # Production object passed to grammar rules
+        errorcount = 0                           # Used during error recovery
 
-        # --! DEBUG
-        debug.info("PLY: PARSE DEBUG START")
-        # --! DEBUG
+        #--! DEBUG
+        debug.info('PLY: PARSE DEBUG START')
+        #--! DEBUG
 
         # If no lexer was given, we will try to use the lex module
         if not lexer:
-            lex = load_ply_lex()
+            from . import lex
             lexer = lex.lexer
 
         # Set up the lexer and parser objects on pslice
@@ -306,16 +376,19 @@
             lexer.input(input)
 
         if tokenfunc is None:
-           # Tokenize function
-           get_token = lexer.token
+            # Tokenize function
+            get_token = lexer.token
         else:
-           get_token = tokenfunc
+            get_token = tokenfunc
+
+        # Set the parser() token method (sometimes used in error recovery)
+        self.token = get_token
 
         # Set up the state and symbol stacks
 
-        statestack = [ ]                # Stack of parsing states
+        statestack = []                # Stack of parsing states
         self.statestack = statestack
-        symstack   = [ ]                # Stack of grammar symbols
+        symstack   = []                # Stack of grammar symbols
         self.symstack = symstack
 
         pslice.stack = symstack         # Put in the production
@@ -325,52 +398,59 @@
 
         statestack.append(0)
         sym = YaccSymbol()
-        sym.type = "$end"
+        sym.type = '$end'
         symstack.append(sym)
         state = 0
-        while 1:
+        while True:
             # Get the next symbol on the input.  If a lookahead symbol
             # is already set, we just use that. Otherwise, we'll pull
             # the next token off of the lookaheadstack or from the lexer
 
-            # --! DEBUG
+            #--! DEBUG
             debug.debug('')
             debug.debug('State  : %s', state)
-            # --! DEBUG
+            #--! DEBUG
 
-            if not lookahead:
-                if not lookaheadstack:
-                    lookahead = get_token()     # Get the next token
-                else:
-                    lookahead = lookaheadstack.pop()
+            if state not in defaulted_states:
                 if not lookahead:
-                    lookahead = YaccSymbol()
-                    lookahead.type = "$end"
+                    if not lookaheadstack:
+                        lookahead = get_token()     # Get the next token
+                    else:
+                        lookahead = lookaheadstack.pop()
+                    if not lookahead:
+                        lookahead = YaccSymbol()
+                        lookahead.type = '$end'
 
-            # --! DEBUG
+                # Check the action table
+                ltype = lookahead.type
+                t = actions[state].get(ltype)
+            else:
+                t = defaulted_states[state]
+                #--! DEBUG
+                debug.debug('Defaulted state %s: Reduce using %d', state, -t)
+                #--! DEBUG
+
+            #--! DEBUG
             debug.debug('Stack  : %s',
-                        ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
-            # --! DEBUG
-
-            # Check the action table
-            ltype = lookahead.type
-            t = actions[state].get(ltype)
+                        ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
+            #--! DEBUG
 
             if t is not None:
                 if t > 0:
                     # shift a symbol on the stack
                     statestack.append(t)
                     state = t
-                    
-                    # --! DEBUG
-                    debug.debug("Action : Shift and goto state %s", t)
-                    # --! DEBUG
+
+                    #--! DEBUG
+                    debug.debug('Action : Shift and goto state %s', t)
+                    #--! DEBUG
 
                     symstack.append(lookahead)
                     lookahead = None
 
                     # Decrease error count on successful shift
-                    if errorcount: errorcount -=1
+                    if errorcount:
+                        errorcount -= 1
                     continue
 
                 if t < 0:
@@ -384,72 +464,77 @@
                     sym.type = pname       # Production name
                     sym.value = None
 
-                    # --! DEBUG
+                    #--! DEBUG
                     if plen:
-                        debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, "["+",".join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+"]",-t)
+                        debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str,
+                                   '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']',
+                                   goto[statestack[-1-plen]][pname])
                     else:
-                        debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t)
-                        
-                    # --! DEBUG
+                        debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [],
+                                   goto[statestack[-1]][pname])
+
+                    #--! DEBUG
 
                     if plen:
                         targ = symstack[-plen-1:]
                         targ[0] = sym
 
-                        # --! TRACKING
+                        #--! TRACKING
                         if tracking:
-                           t1 = targ[1]
-                           sym.lineno = t1.lineno
-                           sym.lexpos = t1.lexpos
-                           t1 = targ[-1]
-                           sym.endlineno = getattr(t1,"endlineno",t1.lineno)
-                           sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos)
-
-                        # --! TRACKING
+                            t1 = targ[1]
+                            sym.lineno = t1.lineno
+                            sym.lexpos = t1.lexpos
+                            t1 = targ[-1]
+                            sym.endlineno = getattr(t1, 'endlineno', t1.lineno)
+                            sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos)
+                        #--! TRACKING
 
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
+                        # The code enclosed in this section is duplicated
                         # below as a performance optimization.  Make sure
                         # changes get made in both locations.
 
                         pslice.slice = targ
-                        
+
                         try:
                             # Call the grammar rule with our special slice object
                             del symstack[-plen:]
-                            del statestack[-plen:]
+                            self.state = state
                             p.callable(pslice)
-                            # --! DEBUG
-                            debug.info("Result : %s", format_result(pslice[0]))
-                            # --! DEBUG
+                            del statestack[-plen:]
+                            #--! DEBUG
+                            debug.info('Result : %s', format_result(pslice[0]))
+                            #--! DEBUG
                             symstack.append(sym)
                             state = goto[statestack[-1]][pname]
                             statestack.append(state)
                         except SyntaxError:
                             # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
+                            statestack.pop()                    # Pop back one state (before the reduce)
                             state = statestack[-1]
                             sym.type = 'error'
+                            sym.value = 'error'
                             lookahead = sym
                             errorcount = error_count
-                            self.errorok = 0
+                            self.errorok = False
+
                         continue
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-    
+
                     else:
 
-                        # --! TRACKING
+                        #--! TRACKING
                         if tracking:
-                           sym.lineno = lexer.lineno
-                           sym.lexpos = lexer.lexpos
-                        # --! TRACKING
+                            sym.lineno = lexer.lineno
+                            sym.lexpos = lexer.lexpos
+                        #--! TRACKING
 
-                        targ = [ sym ]
+                        targ = [sym]
 
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
+                        # The code enclosed in this section is duplicated
                         # above as a performance optimization.  Make sure
                         # changes get made in both locations.
 
@@ -457,41 +542,43 @@
 
                         try:
                             # Call the grammar rule with our special slice object
+                            self.state = state
                             p.callable(pslice)
-                            # --! DEBUG
-                            debug.info("Result : %s", format_result(pslice[0]))
-                            # --! DEBUG
+                            #--! DEBUG
+                            debug.info('Result : %s', format_result(pslice[0]))
+                            #--! DEBUG
                             symstack.append(sym)
                             state = goto[statestack[-1]][pname]
                             statestack.append(state)
                         except SyntaxError:
                             # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            statestack.pop()                    # Pop back one state (before the reduce)
                             state = statestack[-1]
                             sym.type = 'error'
+                            sym.value = 'error'
                             lookahead = sym
                             errorcount = error_count
-                            self.errorok = 0
+                            self.errorok = False
+
                         continue
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
                 if t == 0:
                     n = symstack[-1]
-                    result = getattr(n,"value",None)
-                    # --! DEBUG
-                    debug.info("Done   : Returning %s", format_result(result))
-                    debug.info("PLY: PARSE DEBUG END")
-                    # --! DEBUG
+                    result = getattr(n, 'value', None)
+                    #--! DEBUG
+                    debug.info('Done   : Returning %s', format_result(result))
+                    debug.info('PLY: PARSE DEBUG END')
+                    #--! DEBUG
                     return result
 
-            if t == None:
+            if t is None:
 
-                # --! DEBUG
+                #--! DEBUG
                 debug.error('Error  : %s',
-                            ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
-                # --! DEBUG
+                            ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
+                #--! DEBUG
 
                 # We have some kind of parsing error here.  To handle
                 # this, we are going to push the current token onto
@@ -505,20 +592,15 @@
                 # errorcount == 0.
                 if errorcount == 0 or self.errorok:
                     errorcount = error_count
-                    self.errorok = 0
+                    self.errorok = False
                     errtoken = lookahead
-                    if errtoken.type == "$end":
+                    if errtoken.type == '$end':
                         errtoken = None               # End of file!
                     if self.errorfunc:
-                        global errok,token,restart
-                        errok = self.errok        # Set some special functions available in error recovery
-                        token = get_token
-                        restart = self.restart
-                        if errtoken and not hasattr(errtoken,'lexer'):
+                        if errtoken and not hasattr(errtoken, 'lexer'):
                             errtoken.lexer = lexer
-                        tok = self.errorfunc(errtoken)
-                        del errok, token, restart   # Delete special functions
-
+                        self.state = state
+                        tok = call_errorfunc(self.errorfunc, errtoken, self)
                         if self.errorok:
                             # User must have done some kind of panic
                             # mode recovery on their own.  The
@@ -528,14 +610,16 @@
                             continue
                     else:
                         if errtoken:
-                            if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
-                            else: lineno = 0
-                            if lineno:
-                                sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
+                            if hasattr(errtoken, 'lineno'):
+                                lineno = lookahead.lineno
                             else:
-                                sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
+                                lineno = 0
+                            if lineno:
+                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
+                            else:
+                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
                         else:
-                            sys.stderr.write("yacc: Parse error in input. EOF\n")
+                            sys.stderr.write('yacc: Parse error in input. EOF\n')
                             return
 
                 else:
@@ -545,7 +629,7 @@
                 # entire parse has been rolled back and we're completely hosed.   The token is
                 # discarded and we just keep going.
 
-                if len(statestack) <= 1 and lookahead.type != "$end":
+                if len(statestack) <= 1 and lookahead.type != '$end':
                     lookahead = None
                     errtoken = None
                     state = 0
@@ -557,7 +641,7 @@
                 # at the end of the file. nuke the top entry and generate an error token
 
                 # Start nuking entries on the stack
-                if lookahead.type == "$end":
+                if lookahead.type == '$end':
                     # Whoa. We're really hosed here. Bail out
                     return
 
@@ -566,48 +650,67 @@
                     if sym.type == 'error':
                         # Hmmm. Error is on top of stack, we'll just nuke input
                         # symbol and continue
+                        #--! TRACKING
+                        if tracking:
+                            sym.endlineno = getattr(lookahead, 'lineno', sym.lineno)
+                            sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos)
+                        #--! TRACKING
                         lookahead = None
                         continue
+
+                    # Create the error symbol for the first time and make it the new lookahead symbol
                     t = YaccSymbol()
                     t.type = 'error'
-                    if hasattr(lookahead,"lineno"):
-                        t.lineno = lookahead.lineno
+
+                    if hasattr(lookahead, 'lineno'):
+                        t.lineno = t.endlineno = lookahead.lineno
+                    if hasattr(lookahead, 'lexpos'):
+                        t.lexpos = t.endlexpos = lookahead.lexpos
                     t.value = lookahead
                     lookaheadstack.append(lookahead)
                     lookahead = t
                 else:
-                    symstack.pop()
+                    sym = symstack.pop()
+                    #--! TRACKING
+                    if tracking:
+                        lookahead.lineno = sym.lineno
+                        lookahead.lexpos = sym.lexpos
+                    #--! TRACKING
                     statestack.pop()
-                    state = statestack[-1]       # Potential bug fix
+                    state = statestack[-1]
 
                 continue
 
             # Call an error function here
-            raise RuntimeError("yacc: internal parser error!!!\n")
+            raise RuntimeError('yacc: internal parser error!!!\n')
+
+        #--! parsedebug-end
 
     # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     # parseopt().
     #
-    # Optimized version of parse() method.  DO NOT EDIT THIS CODE DIRECTLY.
-    # Edit the debug version above, then copy any modifications to the method
-    # below while removing #--! DEBUG sections.
+    # Optimized version of parse() method.  DO NOT EDIT THIS CODE DIRECTLY!
+    # This code is automatically generated by the ply/ygen.py script. Make
+    # changes to the parsedebug() method instead.
     # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
+    def parseopt(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
+        #--! parseopt-start
+        lookahead = None                         # Current lookahead symbol
+        lookaheadstack = []                      # Stack of lookahead symbols
+        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
+        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
+        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
+        defaulted_states = self.defaulted_states # Local reference to defaulted states
+        pslice  = YaccProduction(None)           # Production object passed to grammar rules
+        errorcount = 0                           # Used during error recovery
 
-    def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
-        lookahead = None                 # Current lookahead symbol
-        lookaheadstack = [ ]             # Stack of lookahead symbols
-        actions = self.action            # Local reference to action table (to avoid lookup on self.)
-        goto    = self.goto              # Local reference to goto table (to avoid lookup on self.)
-        prod    = self.productions       # Local reference to production list (to avoid lookup on self.)
-        pslice  = YaccProduction(None)   # Production object passed to grammar rules
-        errorcount = 0                   # Used during error recovery 
 
         # If no lexer was given, we will try to use the lex module
         if not lexer:
-            lex = load_ply_lex()
+            from . import lex
             lexer = lex.lexer
-        
+
         # Set up the lexer and parser objects on pslice
         pslice.lexer = lexer
         pslice.parser = self
@@ -617,16 +720,19 @@
             lexer.input(input)
 
         if tokenfunc is None:
-           # Tokenize function
-           get_token = lexer.token
+            # Tokenize function
+            get_token = lexer.token
         else:
-           get_token = tokenfunc
+            get_token = tokenfunc
+
+        # Set the parser() token method (sometimes used in error recovery)
+        self.token = get_token
 
         # Set up the state and symbol stacks
 
-        statestack = [ ]                # Stack of parsing states
+        statestack = []                # Stack of parsing states
         self.statestack = statestack
-        symstack   = [ ]                # Stack of grammar symbols
+        symstack   = []                # Stack of grammar symbols
         self.symstack = symstack
 
         pslice.stack = symstack         # Put in the production
@@ -639,23 +745,28 @@
         sym.type = '$end'
         symstack.append(sym)
         state = 0
-        while 1:
+        while True:
             # Get the next symbol on the input.  If a lookahead symbol
             # is already set, we just use that. Otherwise, we'll pull
             # the next token off of the lookaheadstack or from the lexer
 
-            if not lookahead:
-                if not lookaheadstack:
-                    lookahead = get_token()     # Get the next token
-                else:
-                    lookahead = lookaheadstack.pop()
-                if not lookahead:
-                    lookahead = YaccSymbol()
-                    lookahead.type = '$end'
 
-            # Check the action table
-            ltype = lookahead.type
-            t = actions[state].get(ltype)
+            if state not in defaulted_states:
+                if not lookahead:
+                    if not lookaheadstack:
+                        lookahead = get_token()     # Get the next token
+                    else:
+                        lookahead = lookaheadstack.pop()
+                    if not lookahead:
+                        lookahead = YaccSymbol()
+                        lookahead.type = '$end'
+
+                # Check the action table
+                ltype = lookahead.type
+                t = actions[state].get(ltype)
+            else:
+                t = defaulted_states[state]
+
 
             if t is not None:
                 if t > 0:
@@ -663,11 +774,13 @@
                     statestack.append(t)
                     state = t
 
+
                     symstack.append(lookahead)
                     lookahead = None
 
                     # Decrease error count on successful shift
-                    if errorcount: errorcount -=1
+                    if errorcount:
+                        errorcount -= 1
                     continue
 
                 if t < 0:
@@ -681,61 +794,64 @@
                     sym.type = pname       # Production name
                     sym.value = None
 
+
                     if plen:
                         targ = symstack[-plen-1:]
                         targ[0] = sym
 
-                        # --! TRACKING
+                        #--! TRACKING
                         if tracking:
-                           t1 = targ[1]
-                           sym.lineno = t1.lineno
-                           sym.lexpos = t1.lexpos
-                           t1 = targ[-1]
-                           sym.endlineno = getattr(t1,"endlineno",t1.lineno)
-                           sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos)
-
-                        # --! TRACKING
+                            t1 = targ[1]
+                            sym.lineno = t1.lineno
+                            sym.lexpos = t1.lexpos
+                            t1 = targ[-1]
+                            sym.endlineno = getattr(t1, 'endlineno', t1.lineno)
+                            sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos)
+                        #--! TRACKING
 
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
+                        # The code enclosed in this section is duplicated
                         # below as a performance optimization.  Make sure
                         # changes get made in both locations.
 
                         pslice.slice = targ
-                        
+
                         try:
                             # Call the grammar rule with our special slice object
                             del symstack[-plen:]
-                            del statestack[-plen:]
+                            self.state = state
                             p.callable(pslice)
+                            del statestack[-plen:]
                             symstack.append(sym)
                             state = goto[statestack[-1]][pname]
                             statestack.append(state)
                         except SyntaxError:
                             # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
+                            statestack.pop()                    # Pop back one state (before the reduce)
                             state = statestack[-1]
                             sym.type = 'error'
+                            sym.value = 'error'
                             lookahead = sym
                             errorcount = error_count
-                            self.errorok = 0
+                            self.errorok = False
+
                         continue
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-    
+
                     else:
 
-                        # --! TRACKING
+                        #--! TRACKING
                         if tracking:
-                           sym.lineno = lexer.lineno
-                           sym.lexpos = lexer.lexpos
-                        # --! TRACKING
+                            sym.lineno = lexer.lineno
+                            sym.lexpos = lexer.lexpos
+                        #--! TRACKING
 
-                        targ = [ sym ]
+                        targ = [sym]
 
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
+                        # The code enclosed in this section is duplicated
                         # above as a performance optimization.  Make sure
                         # changes get made in both locations.
 
@@ -743,28 +859,32 @@
 
                         try:
                             # Call the grammar rule with our special slice object
+                            self.state = state
                             p.callable(pslice)
                             symstack.append(sym)
                             state = goto[statestack[-1]][pname]
                             statestack.append(state)
                         except SyntaxError:
                             # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            statestack.pop()                    # Pop back one state (before the reduce)
                             state = statestack[-1]
                             sym.type = 'error'
+                            sym.value = 'error'
                             lookahead = sym
                             errorcount = error_count
-                            self.errorok = 0
+                            self.errorok = False
+
                         continue
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
                 if t == 0:
                     n = symstack[-1]
-                    return getattr(n,"value",None)
+                    result = getattr(n, 'value', None)
+                    return result
 
-            if t == None:
+            if t is None:
+
 
                 # We have some kind of parsing error here.  To handle
                 # this, we are going to push the current token onto
@@ -778,20 +898,15 @@
                 # errorcount == 0.
                 if errorcount == 0 or self.errorok:
                     errorcount = error_count
-                    self.errorok = 0
+                    self.errorok = False
                     errtoken = lookahead
                     if errtoken.type == '$end':
                         errtoken = None               # End of file!
                     if self.errorfunc:
-                        global errok,token,restart
-                        errok = self.errok        # Set some special functions available in error recovery
-                        token = get_token
-                        restart = self.restart
-                        if errtoken and not hasattr(errtoken,'lexer'):
+                        if errtoken and not hasattr(errtoken, 'lexer'):
                             errtoken.lexer = lexer
-                        tok = self.errorfunc(errtoken)
-                        del errok, token, restart   # Delete special functions
-
+                        self.state = state
+                        tok = call_errorfunc(self.errorfunc, errtoken, self)
                         if self.errorok:
                             # User must have done some kind of panic
                             # mode recovery on their own.  The
@@ -801,14 +916,16 @@
                             continue
                     else:
                         if errtoken:
-                            if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
-                            else: lineno = 0
-                            if lineno:
-                                sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
+                            if hasattr(errtoken, 'lineno'):
+                                lineno = lookahead.lineno
                             else:
-                                sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
+                                lineno = 0
+                            if lineno:
+                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
+                            else:
+                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
                         else:
-                            sys.stderr.write("yacc: Parse error in input. EOF\n")
+                            sys.stderr.write('yacc: Parse error in input. EOF\n')
                             return
 
                 else:
@@ -839,47 +956,67 @@
                     if sym.type == 'error':
                         # Hmmm. Error is on top of stack, we'll just nuke input
                         # symbol and continue
+                        #--! TRACKING
+                        if tracking:
+                            sym.endlineno = getattr(lookahead, 'lineno', sym.lineno)
+                            sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos)
+                        #--! TRACKING
                         lookahead = None
                         continue
+
+                    # Create the error symbol for the first time and make it the new lookahead symbol
                     t = YaccSymbol()
                     t.type = 'error'
-                    if hasattr(lookahead,"lineno"):
-                        t.lineno = lookahead.lineno
+
+                    if hasattr(lookahead, 'lineno'):
+                        t.lineno = t.endlineno = lookahead.lineno
+                    if hasattr(lookahead, 'lexpos'):
+                        t.lexpos = t.endlexpos = lookahead.lexpos
                     t.value = lookahead
                     lookaheadstack.append(lookahead)
                     lookahead = t
                 else:
-                    symstack.pop()
+                    sym = symstack.pop()
+                    #--! TRACKING
+                    if tracking:
+                        lookahead.lineno = sym.lineno
+                        lookahead.lexpos = sym.lexpos
+                    #--! TRACKING
                     statestack.pop()
-                    state = statestack[-1]       # Potential bug fix
+                    state = statestack[-1]
 
                 continue
 
             # Call an error function here
-            raise RuntimeError("yacc: internal parser error!!!\n")
+            raise RuntimeError('yacc: internal parser error!!!\n')
+
+        #--! parseopt-end
 
     # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     # parseopt_notrack().
     #
-    # Optimized version of parseopt() with line number tracking removed. 
-    # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove
-    # code in the #--! TRACKING sections
+    # Optimized version of parseopt() with line number tracking removed.
+    # DO NOT EDIT THIS CODE DIRECTLY. This code is automatically generated
+    # by the ply/ygen.py script. Make changes to the parsedebug() method instead.
     # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
-    def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
-        lookahead = None                 # Current lookahead symbol
-        lookaheadstack = [ ]             # Stack of lookahead symbols
-        actions = self.action            # Local reference to action table (to avoid lookup on self.)
-        goto    = self.goto              # Local reference to goto table (to avoid lookup on self.)
-        prod    = self.productions       # Local reference to production list (to avoid lookup on self.)
-        pslice  = YaccProduction(None)   # Production object passed to grammar rules
-        errorcount = 0                   # Used during error recovery 
+    def parseopt_notrack(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
+        #--! parseopt-notrack-start
+        lookahead = None                         # Current lookahead symbol
+        lookaheadstack = []                      # Stack of lookahead symbols
+        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
+        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
+        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
+        defaulted_states = self.defaulted_states # Local reference to defaulted states
+        pslice  = YaccProduction(None)           # Production object passed to grammar rules
+        errorcount = 0                           # Used during error recovery
+
 
         # If no lexer was given, we will try to use the lex module
         if not lexer:
-            lex = load_ply_lex()
+            from . import lex
             lexer = lex.lexer
-        
+
         # Set up the lexer and parser objects on pslice
         pslice.lexer = lexer
         pslice.parser = self
@@ -889,16 +1026,19 @@
             lexer.input(input)
 
         if tokenfunc is None:
-           # Tokenize function
-           get_token = lexer.token
+            # Tokenize function
+            get_token = lexer.token
         else:
-           get_token = tokenfunc
+            get_token = tokenfunc
+
+        # Set the parser() token method (sometimes used in error recovery)
+        self.token = get_token
 
         # Set up the state and symbol stacks
 
-        statestack = [ ]                # Stack of parsing states
+        statestack = []                # Stack of parsing states
         self.statestack = statestack
-        symstack   = [ ]                # Stack of grammar symbols
+        symstack   = []                # Stack of grammar symbols
         self.symstack = symstack
 
         pslice.stack = symstack         # Put in the production
@@ -911,23 +1051,28 @@
         sym.type = '$end'
         symstack.append(sym)
         state = 0
-        while 1:
+        while True:
             # Get the next symbol on the input.  If a lookahead symbol
             # is already set, we just use that. Otherwise, we'll pull
             # the next token off of the lookaheadstack or from the lexer
 
-            if not lookahead:
-                if not lookaheadstack:
-                    lookahead = get_token()     # Get the next token
-                else:
-                    lookahead = lookaheadstack.pop()
-                if not lookahead:
-                    lookahead = YaccSymbol()
-                    lookahead.type = '$end'
 
-            # Check the action table
-            ltype = lookahead.type
-            t = actions[state].get(ltype)
+            if state not in defaulted_states:
+                if not lookahead:
+                    if not lookaheadstack:
+                        lookahead = get_token()     # Get the next token
+                    else:
+                        lookahead = lookaheadstack.pop()
+                    if not lookahead:
+                        lookahead = YaccSymbol()
+                        lookahead.type = '$end'
+
+                # Check the action table
+                ltype = lookahead.type
+                t = actions[state].get(ltype)
+            else:
+                t = defaulted_states[state]
+
 
             if t is not None:
                 if t > 0:
@@ -935,11 +1080,13 @@
                     statestack.append(t)
                     state = t
 
+
                     symstack.append(lookahead)
                     lookahead = None
 
                     # Decrease error count on successful shift
-                    if errorcount: errorcount -=1
+                    if errorcount:
+                        errorcount -= 1
                     continue
 
                 if t < 0:
@@ -953,44 +1100,50 @@
                     sym.type = pname       # Production name
                     sym.value = None
 
+
                     if plen:
                         targ = symstack[-plen-1:]
                         targ[0] = sym
 
+
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
+                        # The code enclosed in this section is duplicated
                         # below as a performance optimization.  Make sure
                         # changes get made in both locations.
 
                         pslice.slice = targ
-                        
+
                         try:
                             # Call the grammar rule with our special slice object
                             del symstack[-plen:]
-                            del statestack[-plen:]
+                            self.state = state
                             p.callable(pslice)
+                            del statestack[-plen:]
                             symstack.append(sym)
                             state = goto[statestack[-1]][pname]
                             statestack.append(state)
                         except SyntaxError:
                             # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
+                            statestack.pop()                    # Pop back one state (before the reduce)
                             state = statestack[-1]
                             sym.type = 'error'
+                            sym.value = 'error'
                             lookahead = sym
                             errorcount = error_count
-                            self.errorok = 0
+                            self.errorok = False
+
                         continue
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-    
+
                     else:
 
-                        targ = [ sym ]
+
+                        targ = [sym]
 
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
+                        # The code enclosed in this section is duplicated
                         # above as a performance optimization.  Make sure
                         # changes get made in both locations.
 
@@ -998,28 +1151,32 @@
 
                         try:
                             # Call the grammar rule with our special slice object
+                            self.state = state
                             p.callable(pslice)
                             symstack.append(sym)
                             state = goto[statestack[-1]][pname]
                             statestack.append(state)
                         except SyntaxError:
                             # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
+                            lookaheadstack.append(lookahead)    # Save the current lookahead token
+                            statestack.pop()                    # Pop back one state (before the reduce)
                             state = statestack[-1]
                             sym.type = 'error'
+                            sym.value = 'error'
                             lookahead = sym
                             errorcount = error_count
-                            self.errorok = 0
+                            self.errorok = False
+
                         continue
                         # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
                 if t == 0:
                     n = symstack[-1]
-                    return getattr(n,"value",None)
+                    result = getattr(n, 'value', None)
+                    return result
 
-            if t == None:
+            if t is None:
+
 
                 # We have some kind of parsing error here.  To handle
                 # this, we are going to push the current token onto
@@ -1033,20 +1190,15 @@
                 # errorcount == 0.
                 if errorcount == 0 or self.errorok:
                     errorcount = error_count
-                    self.errorok = 0
+                    self.errorok = False
                     errtoken = lookahead
                     if errtoken.type == '$end':
                         errtoken = None               # End of file!
                     if self.errorfunc:
-                        global errok,token,restart
-                        errok = self.errok        # Set some special functions available in error recovery
-                        token = get_token
-                        restart = self.restart
-                        if errtoken and not hasattr(errtoken,'lexer'):
+                        if errtoken and not hasattr(errtoken, 'lexer'):
                             errtoken.lexer = lexer
-                        tok = self.errorfunc(errtoken)
-                        del errok, token, restart   # Delete special functions
-
+                        self.state = state
+                        tok = call_errorfunc(self.errorfunc, errtoken, self)
                         if self.errorok:
                             # User must have done some kind of panic
                             # mode recovery on their own.  The
@@ -1056,14 +1208,16 @@
                             continue
                     else:
                         if errtoken:
-                            if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
-                            else: lineno = 0
-                            if lineno:
-                                sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
+                            if hasattr(errtoken, 'lineno'):
+                                lineno = lookahead.lineno
                             else:
-                                sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
+                                lineno = 0
+                            if lineno:
+                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
+                            else:
+                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
                         else:
-                            sys.stderr.write("yacc: Parse error in input. EOF\n")
+                            sys.stderr.write('yacc: Parse error in input. EOF\n')
                             return
 
                 else:
@@ -1096,32 +1250,37 @@
                         # symbol and continue
                         lookahead = None
                         continue
+
+                    # Create the error symbol for the first time and make it the new lookahead symbol
                     t = YaccSymbol()
                     t.type = 'error'
-                    if hasattr(lookahead,"lineno"):
-                        t.lineno = lookahead.lineno
+
+                    if hasattr(lookahead, 'lineno'):
+                        t.lineno = t.endlineno = lookahead.lineno
+                    if hasattr(lookahead, 'lexpos'):
+                        t.lexpos = t.endlexpos = lookahead.lexpos
                     t.value = lookahead
                     lookaheadstack.append(lookahead)
                     lookahead = t
                 else:
-                    symstack.pop()
+                    sym = symstack.pop()
                     statestack.pop()
-                    state = statestack[-1]       # Potential bug fix
+                    state = statestack[-1]
 
                 continue
 
             # Call an error function here
-            raise RuntimeError("yacc: internal parser error!!!\n")
+            raise RuntimeError('yacc: internal parser error!!!\n')
+
+        #--! parseopt-notrack-end
 
 # -----------------------------------------------------------------------------
 #                          === Grammar Representation ===
 #
 # The following functions, classes, and variables are used to represent and
-# manipulate the rules that make up a grammar. 
+# manipulate the rules that make up a grammar.
 # -----------------------------------------------------------------------------
 
-import re
-
 # regex matching identifiers
 _is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')
 
@@ -1131,7 +1290,7 @@
 # This class stores the raw information about a single production or grammar rule.
 # A grammar rule refers to a specification such as this:
 #
-#       expr : expr PLUS term 
+#       expr : expr PLUS term
 #
 # Here are the basic attributes defined on all productions
 #
@@ -1151,7 +1310,7 @@
 
 class Production(object):
     reduced = 0
-    def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0):
+    def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0):
         self.name     = name
         self.prod     = tuple(prod)
         self.number   = number
@@ -1162,11 +1321,11 @@
         self.prec     = precedence
 
         # Internal settings used during table construction
-        
+
         self.len  = len(self.prod)   # Length of the production
 
         # Create a list of unique production symbols used in the production
-        self.usyms = [ ]             
+        self.usyms = []
         for s in self.prod:
             if s not in self.usyms:
                 self.usyms.append(s)
@@ -1177,15 +1336,15 @@
 
         # Create a string representation
         if self.prod:
-            self.str = "%s -> %s" % (self.name," ".join(self.prod))
+            self.str = '%s -> %s' % (self.name, ' '.join(self.prod))
         else:
-            self.str = "%s -> <empty>" % self.name
+            self.str = '%s -> <empty>' % self.name
 
     def __str__(self):
         return self.str
 
     def __repr__(self):
-        return "Production("+str(self)+")"
+        return 'Production(' + str(self) + ')'
 
     def __len__(self):
         return len(self.prod)
@@ -1193,28 +1352,27 @@
     def __nonzero__(self):
         return 1
 
-    def __getitem__(self,index):
+    def __getitem__(self, index):
         return self.prod[index]
-            
-    # Return the nth lr_item from the production (or None if at the end)
-    def lr_item(self,n):
-        if n > len(self.prod): return None
-        p = LRItem(self,n)
 
-        # Precompute the list of productions immediately following.  Hack. Remove later
+    # Return the nth lr_item from the production (or None if at the end)
+    def lr_item(self, n):
+        if n > len(self.prod):
+            return None
+        p = LRItem(self, n)
+        # Precompute the list of productions immediately following.
         try:
-            p.lr_after = Prodnames[p.prod[n+1]]
-        except (IndexError,KeyError):
+            p.lr_after = self.Prodnames[p.prod[n+1]]
+        except (IndexError, KeyError):
             p.lr_after = []
         try:
             p.lr_before = p.prod[n-1]
         except IndexError:
             p.lr_before = None
-
         return p
-    
+
     # Bind the production function name to a callable
-    def bind(self,pdict):
+    def bind(self, pdict):
         if self.func:
             self.callable = pdict[self.func]
 
@@ -1223,7 +1381,7 @@
 # actually used by the LR parsing engine, plus some additional
 # debugging information.
 class MiniProduction(object):
-    def __init__(self,str,name,len,func,file,line):
+    def __init__(self, str, name, len, func, file, line):
         self.name     = name
         self.len      = len
         self.func     = func
@@ -1231,13 +1389,15 @@
         self.file     = file
         self.line     = line
         self.str      = str
+
     def __str__(self):
         return self.str
+
     def __repr__(self):
-        return "MiniProduction(%s)" % self.str
+        return 'MiniProduction(%s)' % self.str
 
     # Bind the production function name to a callable
-    def bind(self,pdict):
+    def bind(self, pdict):
         if self.func:
             self.callable = pdict[self.func]
 
@@ -1246,9 +1406,9 @@
 # class LRItem
 #
 # This class represents a specific stage of parsing a production rule.  For
-# example: 
+# example:
 #
-#       expr : expr . PLUS term 
+#       expr : expr . PLUS term
 #
 # In the above, the "." represents the current location of the parse.  Here
 # basic attributes:
@@ -1267,26 +1427,26 @@
 # -----------------------------------------------------------------------------
 
 class LRItem(object):
-    def __init__(self,p,n):
+    def __init__(self, p, n):
         self.name       = p.name
         self.prod       = list(p.prod)
         self.number     = p.number
         self.lr_index   = n
-        self.lookaheads = { }
-        self.prod.insert(n,".")
+        self.lookaheads = {}
+        self.prod.insert(n, '.')
         self.prod       = tuple(self.prod)
         self.len        = len(self.prod)
         self.usyms      = p.usyms
 
     def __str__(self):
         if self.prod:
-            s = "%s -> %s" % (self.name," ".join(self.prod))
+            s = '%s -> %s' % (self.name, ' '.join(self.prod))
         else:
-            s = "%s -> <empty>" % self.name
+            s = '%s -> <empty>' % self.name
         return s
 
     def __repr__(self):
-        return "LRItem("+str(self)+")"
+        return 'LRItem(' + str(self) + ')'
 
 # -----------------------------------------------------------------------------
 # rightmost_terminal()
@@ -1309,21 +1469,22 @@
 # This data is used for critical parts of the table generation process later.
 # -----------------------------------------------------------------------------
 
-class GrammarError(YaccError): pass
+class GrammarError(YaccError):
+    pass
 
 class Grammar(object):
-    def __init__(self,terminals):
+    def __init__(self, terminals):
         self.Productions  = [None]  # A list of all of the productions.  The first
                                     # entry is always reserved for the purpose of
                                     # building an augmented grammar
 
-        self.Prodnames    = { }     # A dictionary mapping the names of nonterminals to a list of all
+        self.Prodnames    = {}      # A dictionary mapping the names of nonterminals to a list of all
                                     # productions of that nonterminal.
 
-        self.Prodmap      = { }     # A dictionary that is only used to detect duplicate
+        self.Prodmap      = {}      # A dictionary that is only used to detect duplicate
                                     # productions.
 
-        self.Terminals    = { }     # A dictionary mapping the names of terminal symbols to a
+        self.Terminals    = {}      # A dictionary mapping the names of terminal symbols to a
                                     # list of the rules where they are used.
 
         for term in terminals:
@@ -1331,17 +1492,17 @@
 
         self.Terminals['error'] = []
 
-        self.Nonterminals = { }     # A dictionary mapping names of nonterminals to a list
+        self.Nonterminals = {}      # A dictionary mapping names of nonterminals to a list
                                     # of rule numbers where they are used.
 
-        self.First        = { }     # A dictionary of precomputed FIRST(x) symbols
+        self.First        = {}      # A dictionary of precomputed FIRST(x) symbols
 
-        self.Follow       = { }     # A dictionary of precomputed FOLLOW(x) symbols
+        self.Follow       = {}      # A dictionary of precomputed FOLLOW(x) symbols
 
-        self.Precedence   = { }     # Precedence rules for each terminal. Contains tuples of the
+        self.Precedence   = {}      # Precedence rules for each terminal. Contains tuples of the
                                     # form ('right',level) or ('nonassoc', level) or ('left',level)
 
-        self.UsedPrecedence = { }   # Precedence rules that were actually used by the grammer.
+        self.UsedPrecedence = set() # Precedence rules that were actually used by the grammer.
                                     # This is only used to provide error checking and to generate
                                     # a warning about unused precedence rules.
 
@@ -1351,7 +1512,7 @@
     def __len__(self):
         return len(self.Productions)
 
-    def __getitem__(self,index):
+    def __getitem__(self, index):
         return self.Productions[index]
 
     # -----------------------------------------------------------------------------
@@ -1362,14 +1523,14 @@
     #
     # -----------------------------------------------------------------------------
 
-    def set_precedence(self,term,assoc,level):
-        assert self.Productions == [None],"Must call set_precedence() before add_production()"
+    def set_precedence(self, term, assoc, level):
+        assert self.Productions == [None], 'Must call set_precedence() before add_production()'
         if term in self.Precedence:
-            raise GrammarError("Precedence already specified for terminal '%s'" % term)
-        if assoc not in ['left','right','nonassoc']:
+            raise GrammarError('Precedence already specified for terminal %r' % term)
+        if assoc not in ['left', 'right', 'nonassoc']:
             raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'")
-        self.Precedence[term] = (assoc,level)
- 
+        self.Precedence[term] = (assoc, level)
+
     # -----------------------------------------------------------------------------
     # add_production()
     #
@@ -1387,72 +1548,74 @@
     # are valid and that %prec is used correctly.
     # -----------------------------------------------------------------------------
 
-    def add_production(self,prodname,syms,func=None,file='',line=0):
+    def add_production(self, prodname, syms, func=None, file='', line=0):
 
         if prodname in self.Terminals:
-            raise GrammarError("%s:%d: Illegal rule name '%s'. Already defined as a token" % (file,line,prodname))
+            raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname))
         if prodname == 'error':
-            raise GrammarError("%s:%d: Illegal rule name '%s'. error is a reserved word" % (file,line,prodname))
+            raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname))
         if not _is_identifier.match(prodname):
-            raise GrammarError("%s:%d: Illegal rule name '%s'" % (file,line,prodname))
+            raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname))
 
-        # Look for literal tokens 
-        for n,s in enumerate(syms):
+        # Look for literal tokens
+        for n, s in enumerate(syms):
             if s[0] in "'\"":
-                 try:
-                     c = eval(s)
-                     if (len(c) > 1):
-                          raise GrammarError("%s:%d: Literal token %s in rule '%s' may only be a single character" % (file,line,s, prodname))
-                     if not c in self.Terminals:
-                          self.Terminals[c] = []
-                     syms[n] = c
-                     continue
-                 except SyntaxError:
-                     pass
+                try:
+                    c = eval(s)
+                    if (len(c) > 1):
+                        raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' %
+                                           (file, line, s, prodname))
+                    if c not in self.Terminals:
+                        self.Terminals[c] = []
+                    syms[n] = c
+                    continue
+                except SyntaxError:
+                    pass
             if not _is_identifier.match(s) and s != '%prec':
-                raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (file,line,s, prodname))
-        
+                raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname))
+
         # Determine the precedence level
         if '%prec' in syms:
             if syms[-1] == '%prec':
-                raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line))
+                raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line))
             if syms[-2] != '%prec':
-                raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line))
+                raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' %
+                                   (file, line))
             precname = syms[-1]
-            prodprec = self.Precedence.get(precname,None)
+            prodprec = self.Precedence.get(precname)
             if not prodprec:
-                raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname))
+                raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname))
             else:
-                self.UsedPrecedence[precname] = 1
+                self.UsedPrecedence.add(precname)
             del syms[-2:]     # Drop %prec from the rule
         else:
             # If no %prec, precedence is determined by the rightmost terminal symbol
-            precname = rightmost_terminal(syms,self.Terminals)
-            prodprec = self.Precedence.get(precname,('right',0)) 
-            
+            precname = rightmost_terminal(syms, self.Terminals)
+            prodprec = self.Precedence.get(precname, ('right', 0))
+
         # See if the rule is already in the rulemap
-        map = "%s -> %s" % (prodname,syms)
+        map = '%s -> %s' % (prodname, syms)
         if map in self.Prodmap:
             m = self.Prodmap[map]
-            raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) +
-                               "Previous definition at %s:%d" % (m.file, m.line))
+            raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) +
+                               'Previous definition at %s:%d' % (m.file, m.line))
 
         # From this point on, everything is valid.  Create a new Production instance
         pnumber  = len(self.Productions)
-        if not prodname in self.Nonterminals:
-            self.Nonterminals[prodname] = [ ]
+        if prodname not in self.Nonterminals:
+            self.Nonterminals[prodname] = []
 
         # Add the production number to Terminals and Nonterminals
         for t in syms:
             if t in self.Terminals:
                 self.Terminals[t].append(pnumber)
             else:
-                if not t in self.Nonterminals:
-                    self.Nonterminals[t] = [ ]
+                if t not in self.Nonterminals:
+                    self.Nonterminals[t] = []
                 self.Nonterminals[t].append(pnumber)
 
         # Create a production and add it to the list of productions
-        p = Production(pnumber,prodname,syms,prodprec,func,file,line)
+        p = Production(pnumber, prodname, syms, prodprec, func, file, line)
         self.Productions.append(p)
         self.Prodmap[map] = p
 
@@ -1460,22 +1623,21 @@
         try:
             self.Prodnames[prodname].append(p)
         except KeyError:
-            self.Prodnames[prodname] = [ p ]
-        return 0
+            self.Prodnames[prodname] = [p]
 
     # -----------------------------------------------------------------------------
     # set_start()
     #
-    # Sets the starting symbol and creates the augmented grammar.  Production 
+    # Sets the starting symbol and creates the augmented grammar.  Production
     # rule 0 is S' -> start where start is the start symbol.
     # -----------------------------------------------------------------------------
 
-    def set_start(self,start=None):
+    def set_start(self, start=None):
         if not start:
             start = self.Productions[1].name
         if start not in self.Nonterminals:
-            raise GrammarError("start symbol %s undefined" % start)
-        self.Productions[0] = Production(0,"S'",[start])
+            raise GrammarError('start symbol %s undefined' % start)
+        self.Productions[0] = Production(0, "S'", [start])
         self.Nonterminals[start].append(0)
         self.Start = start
 
@@ -1487,26 +1649,20 @@
     # -----------------------------------------------------------------------------
 
     def find_unreachable(self):
-        
+
         # Mark all symbols that are reachable from a symbol s
         def mark_reachable_from(s):
-            if reachable[s]:
-                # We've already reached symbol s.
+            if s in reachable:
                 return
-            reachable[s] = 1
-            for p in self.Prodnames.get(s,[]):
+            reachable.add(s)
+            for p in self.Prodnames.get(s, []):
                 for r in p.prod:
                     mark_reachable_from(r)
 
-        reachable   = { }
-        for s in list(self.Terminals) + list(self.Nonterminals):
-            reachable[s] = 0
+        reachable = set()
+        mark_reachable_from(self.Productions[0].prod[0])
+        return [s for s in self.Nonterminals if s not in reachable]
 
-        mark_reachable_from( self.Productions[0].prod[0] )
-
-        return [s for s in list(self.Nonterminals)
-                        if not reachable[s]]
-    
     # -----------------------------------------------------------------------------
     # infinite_cycles()
     #
@@ -1520,20 +1676,20 @@
 
         # Terminals:
         for t in self.Terminals:
-            terminates[t] = 1
+            terminates[t] = True
 
-        terminates['$end'] = 1
+        terminates['$end'] = True
 
         # Nonterminals:
 
         # Initialize to false:
         for n in self.Nonterminals:
-            terminates[n] = 0
+            terminates[n] = False
 
         # Then propagate termination until no change:
-        while 1:
-            some_change = 0
-            for (n,pl) in self.Prodnames.items():
+        while True:
+            some_change = False
+            for (n, pl) in self.Prodnames.items():
                 # Nonterminal n terminates iff any of its productions terminates.
                 for p in pl:
                     # Production p terminates iff all of its rhs symbols terminate.
@@ -1541,19 +1697,19 @@
                         if not terminates[s]:
                             # The symbol s does not terminate,
                             # so production p does not terminate.
-                            p_terminates = 0
+                            p_terminates = False
                             break
                     else:
                         # didn't break from the loop,
                         # so every symbol s terminates
                         # so production p terminates.
-                        p_terminates = 1
+                        p_terminates = True
 
                     if p_terminates:
                         # symbol n terminates!
                         if not terminates[n]:
-                            terminates[n] = 1
-                            some_change = 1
+                            terminates[n] = True
+                            some_change = True
                         # Don't need to consider any more productions for this n.
                         break
 
@@ -1561,9 +1717,9 @@
                 break
 
         infinite = []
-        for (s,term) in terminates.items():
+        for (s, term) in terminates.items():
             if not term:
-                if not s in self.Prodnames and not s in self.Terminals and s != 'error':
+                if s not in self.Prodnames and s not in self.Terminals and s != 'error':
                     # s is used-but-not-defined, and we've already warned of that,
                     # so it would be overkill to say that it's also non-terminating.
                     pass
@@ -1572,22 +1728,22 @@
 
         return infinite
 
-
     # -----------------------------------------------------------------------------
     # undefined_symbols()
     #
     # Find all symbols that were used the grammar, but not defined as tokens or
     # grammar rules.  Returns a list of tuples (sym, prod) where sym in the symbol
-    # and prod is the production where the symbol was used. 
+    # and prod is the production where the symbol was used.
     # -----------------------------------------------------------------------------
     def undefined_symbols(self):
         result = []
         for p in self.Productions:
-            if not p: continue
+            if not p:
+                continue
 
             for s in p.prod:
-                if not s in self.Prodnames and not s in self.Terminals and s != 'error':
-                    result.append((s,p))
+                if s not in self.Prodnames and s not in self.Terminals and s != 'error':
+                    result.append((s, p))
         return result
 
     # -----------------------------------------------------------------------------
@@ -1598,7 +1754,7 @@
     # -----------------------------------------------------------------------------
     def unused_terminals(self):
         unused_tok = []
-        for s,v in self.Terminals.items():
+        for s, v in self.Terminals.items():
             if s != 'error' and not v:
                 unused_tok.append(s)
 
@@ -1613,7 +1769,7 @@
 
     def unused_rules(self):
         unused_prod = []
-        for s,v in self.Nonterminals.items():
+        for s, v in self.Nonterminals.items():
             if not v:
                 p = self.Prodnames[s][0]
                 unused_prod.append(p)
@@ -1625,15 +1781,15 @@
     # Returns a list of tuples (term,precedence) corresponding to precedence
     # rules that were never used by the grammar.  term is the name of the terminal
     # on which precedence was applied and precedence is a string such as 'left' or
-    # 'right' corresponding to the type of precedence. 
+    # 'right' corresponding to the type of precedence.
     # -----------------------------------------------------------------------------
 
     def unused_precedence(self):
         unused = []
         for termname in self.Precedence:
             if not (termname in self.Terminals or termname in self.UsedPrecedence):
-                unused.append((termname,self.Precedence[termname][0]))
-                
+                unused.append((termname, self.Precedence[termname][0]))
+
         return unused
 
     # -------------------------------------------------------------------------
@@ -1644,19 +1800,20 @@
     # During execution of compute_first1, the result may be incomplete.
     # Afterward (e.g., when called from compute_follow()), it will be complete.
     # -------------------------------------------------------------------------
-    def _first(self,beta):
+    def _first(self, beta):
 
         # We are computing First(x1,x2,x3,...,xn)
-        result = [ ]
+        result = []
         for x in beta:
-            x_produces_empty = 0
+            x_produces_empty = False
 
             # Add all the non-<empty> symbols of First[x] to the result.
             for f in self.First[x]:
                 if f == '<empty>':
-                    x_produces_empty = 1
+                    x_produces_empty = True
                 else:
-                    if f not in result: result.append(f)
+                    if f not in result:
+                        result.append(f)
 
             if x_produces_empty:
                 # We have to consider the next x in beta,
@@ -1695,17 +1852,17 @@
             self.First[n] = []
 
         # Then propagate symbols until no change:
-        while 1:
-            some_change = 0
+        while True:
+            some_change = False
             for n in self.Nonterminals:
                 for p in self.Prodnames[n]:
                     for f in self._first(p.prod):
                         if f not in self.First[n]:
-                            self.First[n].append( f )
-                            some_change = 1
+                            self.First[n].append(f)
+                            some_change = True
             if not some_change:
                 break
-        
+
         return self.First
 
     # ---------------------------------------------------------------------
@@ -1715,7 +1872,7 @@
     # follow set is the set of all symbols that might follow a given
     # non-terminal.  See the Dragon book, 2nd Ed. p. 189.
     # ---------------------------------------------------------------------
-    def compute_follow(self,start=None):
+    def compute_follow(self, start=None):
         # If already computed, return the result
         if self.Follow:
             return self.Follow
@@ -1726,36 +1883,36 @@
 
         # Add '$end' to the follow list of the start symbol
         for k in self.Nonterminals:
-            self.Follow[k] = [ ]
+            self.Follow[k] = []
 
         if not start:
             start = self.Productions[1].name
 
-        self.Follow[start] = [ '$end' ]
+        self.Follow[start] = ['$end']
 
-        while 1:
-            didadd = 0
+        while True:
+            didadd = False
             for p in self.Productions[1:]:
                 # Here is the production set
-                for i in range(len(p.prod)):
-                    B = p.prod[i]
+                for i, B in enumerate(p.prod):
                     if B in self.Nonterminals:
                         # Okay. We got a non-terminal in a production
                         fst = self._first(p.prod[i+1:])
-                        hasempty = 0
+                        hasempty = False
                         for f in fst:
                             if f != '<empty>' and f not in self.Follow[B]:
                                 self.Follow[B].append(f)
-                                didadd = 1
+                                didadd = True
                             if f == '<empty>':
-                                hasempty = 1
+                                hasempty = True
                         if hasempty or i == (len(p.prod)-1):
                             # Add elements of follow(a) to follow(b)
                             for f in self.Follow[p.name]:
                                 if f not in self.Follow[B]:
                                     self.Follow[B].append(f)
-                                    didadd = 1
-            if not didadd: break
+                                    didadd = True
+            if not didadd:
+                break
         return self.Follow
 
 
@@ -1779,15 +1936,15 @@
             lastlri = p
             i = 0
             lr_items = []
-            while 1:
+            while True:
                 if i > len(p):
                     lri = None
                 else:
-                    lri = LRItem(p,i)
+                    lri = LRItem(p, i)
                     # Precompute the list of productions immediately following
                     try:
                         lri.lr_after = self.Prodnames[lri.prod[i+1]]
-                    except (IndexError,KeyError):
+                    except (IndexError, KeyError):
                         lri.lr_after = []
                     try:
                         lri.lr_before = lri.prod[i-1]
@@ -1795,7 +1952,8 @@
                         lri.lr_before = None
 
                 lastlri.lr_next = lri
-                if not lri: break
+                if not lri:
+                    break
                 lr_items.append(lri)
                 lastlri = lri
                 i += 1
@@ -1804,12 +1962,13 @@
 # -----------------------------------------------------------------------------
 #                            == Class LRTable ==
 #
-# This basic class represents a basic table of LR parsing information.  
+# This basic class represents a basic table of LR parsing information.
 # Methods for generating the tables are not defined here.  They are defined
 # in the derived class LRGeneratedTable.
 # -----------------------------------------------------------------------------
 
-class VersionError(YaccError): pass
+class VersionError(YaccError):
+    pass
 
 class LRTable(object):
     def __init__(self):
@@ -1818,19 +1977,15 @@
         self.lr_productions = None
         self.lr_method = None
 
-    def read_table(self,module):
-        if isinstance(module,types.ModuleType):
+    def read_table(self, module):
+        if isinstance(module, types.ModuleType):
             parsetab = module
         else:
-            if sys.version_info[0] < 3:
-                exec("import %s as parsetab" % module)
-            else:
-                env = { }
-                exec("import %s as parsetab" % module, env, env)
-                parsetab = env['parsetab']
+            exec('import %s' % module)
+            parsetab = sys.modules[module]
 
         if parsetab._tabversion != __tabversion__:
-            raise VersionError("yacc table file version is out of date")
+            raise VersionError('yacc table file version is out of date')
 
         self.lr_action = parsetab._lr_action
         self.lr_goto = parsetab._lr_goto
@@ -1842,17 +1997,20 @@
         self.lr_method = parsetab._lr_method
         return parsetab._lr_signature
 
-    def read_pickle(self,filename):
+    def read_pickle(self, filename):
         try:
             import cPickle as pickle
         except ImportError:
             import pickle
 
-        in_f = open(filename,"rb")
+        if not os.path.exists(filename):
+          raise ImportError
+
+        in_f = open(filename, 'rb')
 
         tabversion = pickle.load(in_f)
         if tabversion != __tabversion__:
-            raise VersionError("yacc table file version is out of date")
+            raise VersionError('yacc table file version is out of date')
         self.lr_method = pickle.load(in_f)
         signature      = pickle.load(in_f)
         self.lr_action = pickle.load(in_f)
@@ -1867,14 +2025,15 @@
         return signature
 
     # Bind all production function names to callable objects in pdict
-    def bind_callables(self,pdict):
+    def bind_callables(self, pdict):
         for p in self.lr_productions:
             p.bind(pdict)
-    
+
+
 # -----------------------------------------------------------------------------
 #                           === LR Generator ===
 #
-# The following classes and functions are used to generate LR parsing tables on 
+# The following classes and functions are used to generate LR parsing tables on
 # a grammar.
 # -----------------------------------------------------------------------------
 
@@ -1895,17 +2054,18 @@
 #          FP   - Set-valued function
 # ------------------------------------------------------------------------------
 
-def digraph(X,R,FP):
-    N = { }
+def digraph(X, R, FP):
+    N = {}
     for x in X:
-       N[x] = 0
+        N[x] = 0
     stack = []
-    F = { }
+    F = {}
     for x in X:
-        if N[x] == 0: traverse(x,N,stack,F,X,R,FP)
+        if N[x] == 0:
+            traverse(x, N, stack, F, X, R, FP)
     return F
 
-def traverse(x,N,stack,F,X,R,FP):
+def traverse(x, N, stack, F, X, R, FP):
     stack.append(x)
     d = len(stack)
     N[x] = d
@@ -1914,20 +2074,22 @@
     rel = R(x)               # Get y's related to x
     for y in rel:
         if N[y] == 0:
-             traverse(y,N,stack,F,X,R,FP)
-        N[x] = min(N[x],N[y])
-        for a in F.get(y,[]):
-            if a not in F[x]: F[x].append(a)
+            traverse(y, N, stack, F, X, R, FP)
+        N[x] = min(N[x], N[y])
+        for a in F.get(y, []):
+            if a not in F[x]:
+                F[x].append(a)
     if N[x] == d:
-       N[stack[-1]] = MAXINT
-       F[stack[-1]] = F[x]
-       element = stack.pop()
-       while element != x:
-           N[stack[-1]] = MAXINT
-           F[stack[-1]] = F[x]
-           element = stack.pop()
+        N[stack[-1]] = MAXINT
+        F[stack[-1]] = F[x]
+        element = stack.pop()
+        while element != x:
+            N[stack[-1]] = MAXINT
+            F[stack[-1]] = F[x]
+            element = stack.pop()
 
-class LALRError(YaccError): pass
+class LALRError(YaccError):
+    pass
 
 # -----------------------------------------------------------------------------
 #                             == LRGeneratedTable ==
@@ -1937,9 +2099,9 @@
 # -----------------------------------------------------------------------------
 
 class LRGeneratedTable(LRTable):
-    def __init__(self,grammar,method='LALR',log=None):
-        if method not in ['SLR','LALR']:
-            raise LALRError("Unsupported method %s" % method)
+    def __init__(self, grammar, method='LALR', log=None):
+        if method not in ['SLR', 'LALR']:
+            raise LALRError('Unsupported method %s' % method)
 
         self.grammar = grammar
         self.lr_method = method
@@ -1974,21 +2136,22 @@
 
     # Compute the LR(0) closure operation on I, where I is a set of LR(0) items.
 
-    def lr0_closure(self,I):
+    def lr0_closure(self, I):
         self._add_count += 1
 
         # Add everything in I to J
         J = I[:]
-        didadd = 1
+        didadd = True
         while didadd:
-            didadd = 0
+            didadd = False
             for j in J:
                 for x in j.lr_after:
-                    if getattr(x,"lr0_added",0) == self._add_count: continue
+                    if getattr(x, 'lr0_added', 0) == self._add_count:
+                        continue
                     # Add B --> .G to J
                     J.append(x.lr_next)
                     x.lr0_added = self._add_count
-                    didadd = 1
+                    didadd = True
 
         return J
 
@@ -1999,43 +2162,43 @@
     # objects).  With uniqueness, we can later do fast set comparisons using
     # id(obj) instead of element-wise comparison.
 
-    def lr0_goto(self,I,x):
+    def lr0_goto(self, I, x):
         # First we look for a previously cached entry
-        g = self.lr_goto_cache.get((id(I),x),None)
-        if g: return g
+        g = self.lr_goto_cache.get((id(I), x))
+        if g:
+            return g
 
         # Now we generate the goto set in a way that guarantees uniqueness
         # of the result
 
-        s = self.lr_goto_cache.get(x,None)
+        s = self.lr_goto_cache.get(x)
         if not s:
-            s = { }
+            s = {}
             self.lr_goto_cache[x] = s
 
-        gs = [ ]
+        gs = []
         for p in I:
             n = p.lr_next
             if n and n.lr_before == x:
-                s1 = s.get(id(n),None)
+                s1 = s.get(id(n))
                 if not s1:
-                    s1 = { }
+                    s1 = {}
                     s[id(n)] = s1
                 gs.append(n)
                 s = s1
-        g = s.get('$end',None)
+        g = s.get('$end')
         if not g:
             if gs:
                 g = self.lr0_closure(gs)
                 s['$end'] = g
             else:
                 s['$end'] = gs
-        self.lr_goto_cache[(id(I),x)] = g
+        self.lr_goto_cache[(id(I), x)] = g
         return g
 
     # Compute the LR(0) sets of item function
     def lr0_items(self):
-
-        C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ]
+        C = [self.lr0_closure([self.grammar.Productions[0].lr_next])]
         i = 0
         for I in C:
             self.lr0_cidhash[id(I)] = i
@@ -2048,15 +2211,15 @@
             i += 1
 
             # Collect all of the symbols that could possibly be in the goto(I,X) sets
-            asyms = { }
+            asyms = {}
             for ii in I:
                 for s in ii.usyms:
                     asyms[s] = None
 
             for x in asyms:
-                g = self.lr0_goto(I,x)
-                if not g:  continue
-                if id(g) in self.lr0_cidhash: continue
+                g = self.lr0_goto(I, x)
+                if not g or id(g) in self.lr0_cidhash:
+                    continue
                 self.lr0_cidhash[id(g)] = len(C)
                 C.append(g)
 
@@ -2091,19 +2254,21 @@
     # -----------------------------------------------------------------------------
 
     def compute_nullable_nonterminals(self):
-        nullable = {}
+        nullable = set()
         num_nullable = 0
-        while 1:
-           for p in self.grammar.Productions[1:]:
-               if p.len == 0:
-                    nullable[p.name] = 1
+        while True:
+            for p in self.grammar.Productions[1:]:
+                if p.len == 0:
+                    nullable.add(p.name)
                     continue
-               for t in p.prod:
-                    if not t in nullable: break
-               else:
-                    nullable[p.name] = 1
-           if len(nullable) == num_nullable: break
-           num_nullable = len(nullable)
+                for t in p.prod:
+                    if t not in nullable:
+                        break
+                else:
+                    nullable.add(p.name)
+            if len(nullable) == num_nullable:
+                break
+            num_nullable = len(nullable)
         return nullable
 
     # -----------------------------------------------------------------------------
@@ -2117,16 +2282,16 @@
     # The input C is the set of LR(0) items.
     # -----------------------------------------------------------------------------
 
-    def find_nonterminal_transitions(self,C):
-         trans = []
-         for state in range(len(C)):
-             for p in C[state]:
-                 if p.lr_index < p.len - 1:
-                      t = (state,p.prod[p.lr_index+1])
-                      if t[1] in self.grammar.Nonterminals:
-                            if t not in trans: trans.append(t)
-             state = state + 1
-         return trans
+    def find_nonterminal_transitions(self, C):
+        trans = []
+        for stateno, state in enumerate(C):
+            for p in state:
+                if p.lr_index < p.len - 1:
+                    t = (stateno, p.prod[p.lr_index+1])
+                    if t[1] in self.grammar.Nonterminals:
+                        if t not in trans:
+                            trans.append(t)
+        return trans
 
     # -----------------------------------------------------------------------------
     # dr_relation()
@@ -2137,21 +2302,21 @@
     # Returns a list of terminals.
     # -----------------------------------------------------------------------------
 
-    def dr_relation(self,C,trans,nullable):
-        dr_set = { }
-        state,N = trans
+    def dr_relation(self, C, trans, nullable):
+        state, N = trans
         terms = []
 
-        g = self.lr0_goto(C[state],N)
+        g = self.lr0_goto(C[state], N)
         for p in g:
-           if p.lr_index < p.len - 1:
-               a = p.prod[p.lr_index+1]
-               if a in self.grammar.Terminals:
-                   if a not in terms: terms.append(a)
+            if p.lr_index < p.len - 1:
+                a = p.prod[p.lr_index+1]
+                if a in self.grammar.Terminals:
+                    if a not in terms:
+                        terms.append(a)
 
         # This extra bit is to handle the start state
         if state == 0 and N == self.grammar.Productions[0].prod[0]:
-           terms.append('$end')
+            terms.append('$end')
 
         return terms
 
@@ -2161,18 +2326,18 @@
     # Computes the READS() relation (p,A) READS (t,C).
     # -----------------------------------------------------------------------------
 
-    def reads_relation(self,C, trans, empty):
+    def reads_relation(self, C, trans, empty):
         # Look for empty transitions
         rel = []
         state, N = trans
 
-        g = self.lr0_goto(C[state],N)
-        j = self.lr0_cidhash.get(id(g),-1)
+        g = self.lr0_goto(C[state], N)
+        j = self.lr0_cidhash.get(id(g), -1)
         for p in g:
             if p.lr_index < p.len - 1:
-                 a = p.prod[p.lr_index + 1]
-                 if a in empty:
-                      rel.append((j,a))
+                a = p.prod[p.lr_index + 1]
+                if a in empty:
+                    rel.append((j, a))
 
         return rel
 
@@ -2204,8 +2369,7 @@
     #
     # -----------------------------------------------------------------------------
 
-    def compute_lookback_includes(self,C,trans,nullable):
-
+    def compute_lookback_includes(self, C, trans, nullable):
         lookdict = {}          # Dictionary of lookback relations
         includedict = {}       # Dictionary of include relations
 
@@ -2215,11 +2379,12 @@
             dtrans[t] = 1
 
         # Loop over all transitions and compute lookbacks and includes
-        for state,N in trans:
+        for state, N in trans:
             lookb = []
             includes = []
             for p in C[state]:
-                if p.name != N: continue
+                if p.name != N:
+                    continue
 
                 # Okay, we have a name match.  We now follow the production all the way
                 # through the state machine until we get the . on the right hand side
@@ -2227,44 +2392,50 @@
                 lr_index = p.lr_index
                 j = state
                 while lr_index < p.len - 1:
-                     lr_index = lr_index + 1
-                     t = p.prod[lr_index]
+                    lr_index = lr_index + 1
+                    t = p.prod[lr_index]
 
-                     # Check to see if this symbol and state are a non-terminal transition
-                     if (j,t) in dtrans:
-                           # Yes.  Okay, there is some chance that this is an includes relation
-                           # the only way to know for certain is whether the rest of the
-                           # production derives empty
+                    # Check to see if this symbol and state are a non-terminal transition
+                    if (j, t) in dtrans:
+                        # Yes.  Okay, there is some chance that this is an includes relation
+                        # the only way to know for certain is whether the rest of the
+                        # production derives empty
 
-                           li = lr_index + 1
-                           while li < p.len:
-                                if p.prod[li] in self.grammar.Terminals: break      # No forget it
-                                if not p.prod[li] in nullable: break
-                                li = li + 1
-                           else:
-                                # Appears to be a relation between (j,t) and (state,N)
-                                includes.append((j,t))
+                        li = lr_index + 1
+                        while li < p.len:
+                            if p.prod[li] in self.grammar.Terminals:
+                                break      # No forget it
+                            if p.prod[li] not in nullable:
+                                break
+                            li = li + 1
+                        else:
+                            # Appears to be a relation between (j,t) and (state,N)
+                            includes.append((j, t))
 
-                     g = self.lr0_goto(C[j],t)               # Go to next set
-                     j = self.lr0_cidhash.get(id(g),-1)     # Go to next state
+                    g = self.lr0_goto(C[j], t)               # Go to next set
+                    j = self.lr0_cidhash.get(id(g), -1)      # Go to next state
 
                 # When we get here, j is the final state, now we have to locate the production
                 for r in C[j]:
-                     if r.name != p.name: continue
-                     if r.len != p.len:   continue
-                     i = 0
-                     # This look is comparing a production ". A B C" with "A B C ."
-                     while i < r.lr_index:
-                          if r.prod[i] != p.prod[i+1]: break
-                          i = i + 1
-                     else:
-                          lookb.append((j,r))
+                    if r.name != p.name:
+                        continue
+                    if r.len != p.len:
+                        continue
+                    i = 0
+                    # This look is comparing a production ". A B C" with "A B C ."
+                    while i < r.lr_index:
+                        if r.prod[i] != p.prod[i+1]:
+                            break
+                        i = i + 1
+                    else:
+                        lookb.append((j, r))
             for i in includes:
-                 if not i in includedict: includedict[i] = []
-                 includedict[i].append((state,N))
-            lookdict[(state,N)] = lookb
+                if i not in includedict:
+                    includedict[i] = []
+                includedict[i].append((state, N))
+            lookdict[(state, N)] = lookb
 
-        return lookdict,includedict
+        return lookdict, includedict
 
     # -----------------------------------------------------------------------------
     # compute_read_sets()
@@ -2278,10 +2449,10 @@
     # Returns a set containing the read sets
     # -----------------------------------------------------------------------------
 
-    def compute_read_sets(self,C, ntrans, nullable):
-        FP = lambda x: self.dr_relation(C,x,nullable)
-        R =  lambda x: self.reads_relation(C,x,nullable)
-        F = digraph(ntrans,R,FP)
+    def compute_read_sets(self, C, ntrans, nullable):
+        FP = lambda x: self.dr_relation(C, x, nullable)
+        R =  lambda x: self.reads_relation(C, x, nullable)
+        F = digraph(ntrans, R, FP)
         return F
 
     # -----------------------------------------------------------------------------
@@ -2300,11 +2471,11 @@
     # Returns a set containing the follow sets
     # -----------------------------------------------------------------------------
 
-    def compute_follow_sets(self,ntrans,readsets,inclsets):
-         FP = lambda x: readsets[x]
-         R  = lambda x: inclsets.get(x,[])
-         F = digraph(ntrans,R,FP)
-         return F
+    def compute_follow_sets(self, ntrans, readsets, inclsets):
+        FP = lambda x: readsets[x]
+        R  = lambda x: inclsets.get(x, [])
+        F = digraph(ntrans, R, FP)
+        return F
 
     # -----------------------------------------------------------------------------
     # add_lookaheads()
@@ -2318,15 +2489,16 @@
     # in the lookbacks set
     # -----------------------------------------------------------------------------
 
-    def add_lookaheads(self,lookbacks,followset):
-        for trans,lb in lookbacks.items():
+    def add_lookaheads(self, lookbacks, followset):
+        for trans, lb in lookbacks.items():
             # Loop over productions in lookback
-            for state,p in lb:
-                 if not state in p.lookaheads:
-                      p.lookaheads[state] = []
-                 f = followset.get(trans,[])
-                 for a in f:
-                      if a not in p.lookaheads[state]: p.lookaheads[state].append(a)
+            for state, p in lb:
+                if state not in p.lookaheads:
+                    p.lookaheads[state] = []
+                f = followset.get(trans, [])
+                for a in f:
+                    if a not in p.lookaheads[state]:
+                        p.lookaheads[state].append(a)
 
     # -----------------------------------------------------------------------------
     # add_lalr_lookaheads()
@@ -2335,7 +2507,7 @@
     # with LALR parsing
     # -----------------------------------------------------------------------------
 
-    def add_lalr_lookaheads(self,C):
+    def add_lalr_lookaheads(self, C):
         # Determine all of the nullable nonterminals
         nullable = self.compute_nullable_nonterminals()
 
@@ -2343,16 +2515,16 @@
         trans = self.find_nonterminal_transitions(C)
 
         # Compute read sets
-        readsets = self.compute_read_sets(C,trans,nullable)
+        readsets = self.compute_read_sets(C, trans, nullable)
 
         # Compute lookback/includes relations
-        lookd, included = self.compute_lookback_includes(C,trans,nullable)
+        lookd, included = self.compute_lookback_includes(C, trans, nullable)
 
         # Compute LALR FOLLOW sets
-        followsets = self.compute_follow_sets(trans,readsets,included)
+        followsets = self.compute_follow_sets(trans, readsets, included)
 
         # Add all of the lookaheads
-        self.add_lookaheads(lookd,followsets)
+        self.add_lookaheads(lookd, followsets)
 
     # -----------------------------------------------------------------------------
     # lr_parse_table()
@@ -2366,9 +2538,9 @@
         action = self.lr_action       # Action array
         log    = self.log             # Logger for output
 
-        actionp = { }                 # Action production array (temporary)
-        
-        log.info("Parsing method: %s", self.lr_method)
+        actionp = {}                  # Action production array (temporary)
+
+        log.info('Parsing method: %s', self.lr_method)
 
         # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items
         # This determines the number of states
@@ -2382,23 +2554,23 @@
         st = 0
         for I in C:
             # Loop over each production in I
-            actlist = [ ]              # List of actions
-            st_action  = { }
-            st_actionp = { }
-            st_goto    = { }
-            log.info("")
-            log.info("state %d", st)
-            log.info("")
+            actlist = []              # List of actions
+            st_action  = {}
+            st_actionp = {}
+            st_goto    = {}
+            log.info('')
+            log.info('state %d', st)
+            log.info('')
             for p in I:
-                log.info("    (%d) %s", p.number, str(p))
-            log.info("")
+                log.info('    (%d) %s', p.number, p)
+            log.info('')
 
             for p in I:
                     if p.len == p.lr_index + 1:
                         if p.name == "S'":
                             # Start symbol. Accept!
-                            st_action["$end"] = 0
-                            st_actionp["$end"] = p
+                            st_action['$end'] = 0
+                            st_actionp['$end'] = p
                         else:
                             # We are at the end of a production.  Reduce!
                             if self.lr_method == 'LALR':
@@ -2406,31 +2578,36 @@
                             else:
                                 laheads = self.grammar.Follow[p.name]
                             for a in laheads:
-                                actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p)))
-                                r = st_action.get(a,None)
+                                actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p)))
+                                r = st_action.get(a)
                                 if r is not None:
                                     # Whoa. Have a shift/reduce or reduce/reduce conflict
                                     if r > 0:
                                         # Need to decide on shift or reduce here
                                         # By default we favor shifting. Need to add
                                         # some precedence rules here.
-                                        sprec,slevel = Productions[st_actionp[a].number].prec
-                                        rprec,rlevel = Precedence.get(a,('right',0))
+
+                                        # Shift precedence comes from the token
+                                        sprec, slevel = Precedence.get(a, ('right', 0))
+
+                                        # Reduce precedence comes from rule being reduced (p)
+                                        rprec, rlevel = Productions[p.number].prec
+
                                         if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')):
                                             # We really need to reduce here.
                                             st_action[a] = -p.number
                                             st_actionp[a] = p
                                             if not slevel and not rlevel:
-                                                log.info("  ! shift/reduce conflict for %s resolved as reduce",a)
-                                                self.sr_conflicts.append((st,a,'reduce'))
+                                                log.info('  ! shift/reduce conflict for %s resolved as reduce', a)
+                                                self.sr_conflicts.append((st, a, 'reduce'))
                                             Productions[p.number].reduced += 1
                                         elif (slevel == rlevel) and (rprec == 'nonassoc'):
                                             st_action[a] = None
                                         else:
                                             # Hmmm. Guess we'll keep the shift
                                             if not rlevel:
-                                                log.info("  ! shift/reduce conflict for %s resolved as shift",a)
-                                                self.sr_conflicts.append((st,a,'shift'))
+                                                log.info('  ! shift/reduce conflict for %s resolved as shift', a)
+                                                self.sr_conflicts.append((st, a, 'shift'))
                                     elif r < 0:
                                         # Reduce/reduce conflict.   In this case, we favor the rule
                                         # that was defined first in the grammar file
@@ -2439,15 +2616,16 @@
                                         if oldp.line > pp.line:
                                             st_action[a] = -p.number
                                             st_actionp[a] = p
-                                            chosenp,rejectp = pp,oldp
+                                            chosenp, rejectp = pp, oldp
                                             Productions[p.number].reduced += 1
                                             Productions[oldp.number].reduced -= 1
                                         else:
-                                            chosenp,rejectp = oldp,pp
-                                        self.rr_conflicts.append((st,chosenp,rejectp))
-                                        log.info("  ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a])
+                                            chosenp, rejectp = oldp, pp
+                                        self.rr_conflicts.append((st, chosenp, rejectp))
+                                        log.info('  ! reduce/reduce conflict for %s resolved using rule %d (%s)',
+                                                 a, st_actionp[a].number, st_actionp[a])
                                     else:
-                                        raise LALRError("Unknown conflict in state %d" % st)
+                                        raise LALRError('Unknown conflict in state %d' % st)
                                 else:
                                     st_action[a] = -p.number
                                     st_actionp[a] = p
@@ -2456,205 +2634,211 @@
                         i = p.lr_index
                         a = p.prod[i+1]       # Get symbol right after the "."
                         if a in self.grammar.Terminals:
-                            g = self.lr0_goto(I,a)
-                            j = self.lr0_cidhash.get(id(g),-1)
+                            g = self.lr0_goto(I, a)
+                            j = self.lr0_cidhash.get(id(g), -1)
                             if j >= 0:
                                 # We are in a shift state
-                                actlist.append((a,p,"shift and go to state %d" % j))
-                                r = st_action.get(a,None)
+                                actlist.append((a, p, 'shift and go to state %d' % j))
+                                r = st_action.get(a)
                                 if r is not None:
                                     # Whoa have a shift/reduce or shift/shift conflict
                                     if r > 0:
                                         if r != j:
-                                            raise LALRError("Shift/shift conflict in state %d" % st)
+                                            raise LALRError('Shift/shift conflict in state %d' % st)
                                     elif r < 0:
                                         # Do a precedence check.
                                         #   -  if precedence of reduce rule is higher, we reduce.
                                         #   -  if precedence of reduce is same and left assoc, we reduce.
                                         #   -  otherwise we shift
-                                        rprec,rlevel = Productions[st_actionp[a].number].prec
-                                        sprec,slevel = Precedence.get(a,('right',0))
+
+                                        # Shift precedence comes from the token
+                                        sprec, slevel = Precedence.get(a, ('right', 0))
+
+                                        # Reduce precedence comes from the rule that could have been reduced
+                                        rprec, rlevel = Productions[st_actionp[a].number].prec
+
                                         if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')):
                                             # We decide to shift here... highest precedence to shift
                                             Productions[st_actionp[a].number].reduced -= 1
                                             st_action[a] = j
                                             st_actionp[a] = p
                                             if not rlevel:
-                                                log.info("  ! shift/reduce conflict for %s resolved as shift",a)
-                                                self.sr_conflicts.append((st,a,'shift'))
+                                                log.info('  ! shift/reduce conflict for %s resolved as shift', a)
+                                                self.sr_conflicts.append((st, a, 'shift'))
                                         elif (slevel == rlevel) and (rprec == 'nonassoc'):
                                             st_action[a] = None
                                         else:
                                             # Hmmm. Guess we'll keep the reduce
                                             if not slevel and not rlevel:
-                                                log.info("  ! shift/reduce conflict for %s resolved as reduce",a)
-                                                self.sr_conflicts.append((st,a,'reduce'))
+                                                log.info('  ! shift/reduce conflict for %s resolved as reduce', a)
+                                                self.sr_conflicts.append((st, a, 'reduce'))
 
                                     else:
-                                        raise LALRError("Unknown conflict in state %d" % st)
+                                        raise LALRError('Unknown conflict in state %d' % st)
                                 else:
                                     st_action[a] = j
                                     st_actionp[a] = p
 
             # Print the actions associated with each terminal
-            _actprint = { }
-            for a,p,m in actlist:
+            _actprint = {}
+            for a, p, m in actlist:
                 if a in st_action:
                     if p is st_actionp[a]:
-                        log.info("    %-15s %s",a,m)
-                        _actprint[(a,m)] = 1
-            log.info("")
+                        log.info('    %-15s %s', a, m)
+                        _actprint[(a, m)] = 1
+            log.info('')
             # Print the actions that were not used. (debugging)
             not_used = 0
-            for a,p,m in actlist:
+            for a, p, m in actlist:
                 if a in st_action:
                     if p is not st_actionp[a]:
-                        if not (a,m) in _actprint:
-                            log.debug("  ! %-15s [ %s ]",a,m)
+                        if not (a, m) in _actprint:
+                            log.debug('  ! %-15s [ %s ]', a, m)
                             not_used = 1
-                            _actprint[(a,m)] = 1
+                            _actprint[(a, m)] = 1
             if not_used:
-                log.debug("")
+                log.debug('')
 
             # Construct the goto table for this state
 
-            nkeys = { }
+            nkeys = {}
             for ii in I:
                 for s in ii.usyms:
                     if s in self.grammar.Nonterminals:
                         nkeys[s] = None
             for n in nkeys:
-                g = self.lr0_goto(I,n)
-                j = self.lr0_cidhash.get(id(g),-1)
+                g = self.lr0_goto(I, n)
+                j = self.lr0_cidhash.get(id(g), -1)
                 if j >= 0:
                     st_goto[n] = j
-                    log.info("    %-30s shift and go to state %d",n,j)
+                    log.info('    %-30s shift and go to state %d', n, j)
 
             action[st] = st_action
             actionp[st] = st_actionp
             goto[st] = st_goto
             st += 1
 
-
     # -----------------------------------------------------------------------------
     # write()
     #
     # This function writes the LR parsing tables to a file
     # -----------------------------------------------------------------------------
 
-    def write_table(self,modulename,outputdir='',signature=""):
-        basemodulename = modulename.split(".")[-1]
-        filename = os.path.join(outputdir,basemodulename) + ".py"
-        try:
-            f = open(filename,"w")
+    def write_table(self, tabmodule, outputdir='', signature=''):
+        if isinstance(tabmodule, types.ModuleType):
+            raise IOError("Won't overwrite existing tabmodule")
 
-            f.write("""
+        basemodulename = tabmodule.split('.')[-1]
+        filename = os.path.join(outputdir, basemodulename) + '.py'
+        try:
+            f = open(filename, 'w')
+
+            f.write('''
 # %s
 # This file is automatically generated. Do not edit.
+# pylint: disable=W,C,R
 _tabversion = %r
 
 _lr_method = %r
 
 _lr_signature = %r
-    """ % (filename, __tabversion__, self.lr_method, signature))
+    ''' % (os.path.basename(filename), __tabversion__, self.lr_method, signature))
 
             # Change smaller to 0 to go back to original tables
             smaller = 1
 
             # Factor out names to try and make smaller
             if smaller:
-                items = { }
+                items = {}
 
-                for s,nd in self.lr_action.items():
-                   for name,v in nd.items():
-                      i = items.get(name)
-                      if not i:
-                         i = ([],[])
-                         items[name] = i
-                      i[0].append(s)
-                      i[1].append(v)
+                for s, nd in self.lr_action.items():
+                    for name, v in nd.items():
+                        i = items.get(name)
+                        if not i:
+                            i = ([], [])
+                            items[name] = i
+                        i[0].append(s)
+                        i[1].append(v)
 
-                f.write("\n_lr_action_items = {")
-                for k,v in items.items():
-                    f.write("%r:([" % k)
+                f.write('\n_lr_action_items = {')
+                for k, v in items.items():
+                    f.write('%r:([' % k)
                     for i in v[0]:
-                        f.write("%r," % i)
-                    f.write("],[")
+                        f.write('%r,' % i)
+                    f.write('],[')
                     for i in v[1]:
-                        f.write("%r," % i)
+                        f.write('%r,' % i)
 
-                    f.write("]),")
-                f.write("}\n")
+                    f.write(']),')
+                f.write('}\n')
 
-                f.write("""
-_lr_action = { }
+                f.write('''
+_lr_action = {}
 for _k, _v in _lr_action_items.items():
    for _x,_y in zip(_v[0],_v[1]):
-      if not _x in _lr_action:  _lr_action[_x] = { }
+      if not _x in _lr_action:  _lr_action[_x] = {}
       _lr_action[_x][_k] = _y
 del _lr_action_items
-""")
+''')
 
             else:
-                f.write("\n_lr_action = { ");
-                for k,v in self.lr_action.items():
-                    f.write("(%r,%r):%r," % (k[0],k[1],v))
-                f.write("}\n");
+                f.write('\n_lr_action = { ')
+                for k, v in self.lr_action.items():
+                    f.write('(%r,%r):%r,' % (k[0], k[1], v))
+                f.write('}\n')
 
             if smaller:
                 # Factor out names to try and make smaller
-                items = { }
+                items = {}
 
-                for s,nd in self.lr_goto.items():
-                   for name,v in nd.items():
-                      i = items.get(name)
-                      if not i:
-                         i = ([],[])
-                         items[name] = i
-                      i[0].append(s)
-                      i[1].append(v)
+                for s, nd in self.lr_goto.items():
+                    for name, v in nd.items():
+                        i = items.get(name)
+                        if not i:
+                            i = ([], [])
+                            items[name] = i
+                        i[0].append(s)
+                        i[1].append(v)
 
-                f.write("\n_lr_goto_items = {")
-                for k,v in items.items():
-                    f.write("%r:([" % k)
+                f.write('\n_lr_goto_items = {')
+                for k, v in items.items():
+                    f.write('%r:([' % k)
                     for i in v[0]:
-                        f.write("%r," % i)
-                    f.write("],[")
+                        f.write('%r,' % i)
+                    f.write('],[')
                     for i in v[1]:
-                        f.write("%r," % i)
+                        f.write('%r,' % i)
 
-                    f.write("]),")
-                f.write("}\n")
+                    f.write(']),')
+                f.write('}\n')
 
-                f.write("""
-_lr_goto = { }
+                f.write('''
+_lr_goto = {}
 for _k, _v in _lr_goto_items.items():
-   for _x,_y in zip(_v[0],_v[1]):
-       if not _x in _lr_goto: _lr_goto[_x] = { }
+   for _x, _y in zip(_v[0], _v[1]):
+       if not _x in _lr_goto: _lr_goto[_x] = {}
        _lr_goto[_x][_k] = _y
 del _lr_goto_items
-""")
+''')
             else:
-                f.write("\n_lr_goto = { ");
-                for k,v in self.lr_goto.items():
-                    f.write("(%r,%r):%r," % (k[0],k[1],v))
-                f.write("}\n");
+                f.write('\n_lr_goto = { ')
+                for k, v in self.lr_goto.items():
+                    f.write('(%r,%r):%r,' % (k[0], k[1], v))
+                f.write('}\n')
 
             # Write production table
-            f.write("_lr_productions = [\n")
+            f.write('_lr_productions = [\n')
             for p in self.lr_productions:
                 if p.func:
-                    f.write("  (%r,%r,%d,%r,%r,%d),\n" % (p.str,p.name, p.len, p.func,p.file,p.line))
+                    f.write('  (%r,%r,%d,%r,%r,%d),\n' % (p.str, p.name, p.len,
+                                                          p.func, os.path.basename(p.file), p.line))
                 else:
-                    f.write("  (%r,%r,%d,None,None,None),\n" % (str(p),p.name, p.len))
-            f.write("]\n")
+                    f.write('  (%r,%r,%d,None,None,None),\n' % (str(p), p.name, p.len))
+            f.write(']\n')
             f.close()
 
-        except IOError:
-            e = sys.exc_info()[1]
-            sys.stderr.write("Unable to create '%s'\n" % filename)
-            sys.stderr.write(str(e)+"\n")
-            return
+        except IOError as e:
+            raise
 
 
     # -----------------------------------------------------------------------------
@@ -2663,26 +2847,25 @@
     # This function pickles the LR parsing tables to a supplied file object
     # -----------------------------------------------------------------------------
 
-    def pickle_table(self,filename,signature=""):
+    def pickle_table(self, filename, signature=''):
         try:
             import cPickle as pickle
         except ImportError:
             import pickle
-        outf = open(filename,"wb")
-        pickle.dump(__tabversion__,outf,pickle_protocol)
-        pickle.dump(self.lr_method,outf,pickle_protocol)
-        pickle.dump(signature,outf,pickle_protocol)
-        pickle.dump(self.lr_action,outf,pickle_protocol)
-        pickle.dump(self.lr_goto,outf,pickle_protocol)
+        with open(filename, 'wb') as outf:
+            pickle.dump(__tabversion__, outf, pickle_protocol)
+            pickle.dump(self.lr_method, outf, pickle_protocol)
+            pickle.dump(signature, outf, pickle_protocol)
+            pickle.dump(self.lr_action, outf, pickle_protocol)
+            pickle.dump(self.lr_goto, outf, pickle_protocol)
 
-        outp = []
-        for p in self.lr_productions:
-            if p.func:
-                outp.append((p.str,p.name, p.len, p.func,p.file,p.line))
-            else:
-                outp.append((str(p),p.name,p.len,None,None,None))
-        pickle.dump(outp,outf,pickle_protocol)
-        outf.close()
+            outp = []
+            for p in self.lr_productions:
+                if p.func:
+                    outp.append((p.str, p.name, p.len, p.func, os.path.basename(p.file), p.line))
+                else:
+                    outp.append((str(p), p.name, p.len, None, None, None))
+            pickle.dump(outp, outf, pickle_protocol)
 
 # -----------------------------------------------------------------------------
 #                            === INTROSPECTION ===
@@ -2700,26 +2883,18 @@
 # -----------------------------------------------------------------------------
 
 def get_caller_module_dict(levels):
-    try:
-        raise RuntimeError
-    except RuntimeError:
-        e,b,t = sys.exc_info()
-        f = t.tb_frame
-        while levels > 0:
-            f = f.f_back                   
-            levels -= 1
-        ldict = f.f_globals.copy()
-        if f.f_globals != f.f_locals:
-            ldict.update(f.f_locals)
-
-        return ldict
+    f = sys._getframe(levels)
+    ldict = f.f_globals.copy()
+    if f.f_globals != f.f_locals:
+        ldict.update(f.f_locals)
+    return ldict
 
 # -----------------------------------------------------------------------------
 # parse_grammar()
 #
 # This takes a raw grammar rule string and parses it into production data
 # -----------------------------------------------------------------------------
-def parse_grammar(doc,file,line):
+def parse_grammar(doc, file, line):
     grammar = []
     # Split the doc string into lines
     pstrings = doc.splitlines()
@@ -2728,12 +2903,13 @@
     for ps in pstrings:
         dline += 1
         p = ps.split()
-        if not p: continue
+        if not p:
+            continue
         try:
             if p[0] == '|':
                 # This is a continuation of a previous rule
                 if not lastp:
-                    raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline))
+                    raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline))
                 prodname = lastp
                 syms = p[1:]
             else:
@@ -2742,13 +2918,13 @@
                 syms   = p[2:]
                 assign = p[1]
                 if assign != ':' and assign != '::=':
-                    raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline))
+                    raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline))
 
-            grammar.append((file,dline,prodname,syms))
+            grammar.append((file, dline, prodname, syms))
         except SyntaxError:
             raise
         except Exception:
-            raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip()))
+            raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip()))
 
     return grammar
 
@@ -2760,14 +2936,14 @@
 # etc.
 # -----------------------------------------------------------------------------
 class ParserReflect(object):
-    def __init__(self,pdict,log=None):
+    def __init__(self, pdict, log=None):
         self.pdict      = pdict
         self.start      = None
         self.error_func = None
         self.tokens     = None
-        self.files      = {}
+        self.modules    = set()
         self.grammar    = []
-        self.error      = 0
+        self.error      = False
 
         if log is None:
             self.log = PlyLogger(sys.stderr)
@@ -2781,7 +2957,7 @@
         self.get_tokens()
         self.get_precedence()
         self.get_pfunctions()
-        
+
     # Validate all of the information
     def validate_all(self):
         self.validate_start()
@@ -2789,32 +2965,28 @@
         self.validate_tokens()
         self.validate_precedence()
         self.validate_pfunctions()
-        self.validate_files()
+        self.validate_modules()
         return self.error
 
     # Compute a signature over the grammar
     def signature(self):
+        parts = []
         try:
-            from hashlib import md5
-        except ImportError:
-            from md5 import md5
-        try:
-            sig = md5()
             if self.start:
-                sig.update(self.start.encode('latin-1'))
+                parts.append(self.start)
             if self.prec:
-                sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1'))
+                parts.append(''.join([''.join(p) for p in self.prec]))
             if self.tokens:
-                sig.update(" ".join(self.tokens).encode('latin-1'))
+                parts.append(' '.join(self.tokens))
             for f in self.pfuncs:
                 if f[3]:
-                    sig.update(f[3].encode('latin-1'))
-        except (TypeError,ValueError):
+                    parts.append(f[3])
+        except (TypeError, ValueError):
             pass
-        return sig.digest()
+        return ''.join(parts)
 
     # -----------------------------------------------------------------------------
-    # validate_file()
+    # validate_modules()
     #
     # This method checks to see if there are duplicated p_rulename() functions
     # in the parser module file.  Without this function, it is really easy for
@@ -2824,32 +2996,29 @@
     # to try and detect duplicates.
     # -----------------------------------------------------------------------------
 
-    def validate_files(self):
+    def validate_modules(self):
         # Match def p_funcname(
         fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(')
 
-        for filename in self.files.keys():
-            base,ext = os.path.splitext(filename)
-            if ext != '.py': return 1          # No idea. Assume it's okay.
-
+        for module in self.modules:
             try:
-                f = open(filename)
-                lines = f.readlines()
-                f.close()
+                lines, linen = inspect.getsourcelines(module)
             except IOError:
                 continue
 
-            counthash = { }
-            for linen,l in enumerate(lines):
+            counthash = {}
+            for linen, line in enumerate(lines):
                 linen += 1
-                m = fre.match(l)
+                m = fre.match(line)
                 if m:
                     name = m.group(1)
                     prev = counthash.get(name)
                     if not prev:
                         counthash[name] = linen
                     else:
-                        self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev)
+                        filename = inspect.getsourcefile(module)
+                        self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d',
+                                         filename, linen, name, prev)
 
     # Get the start symbol
     def get_start(self):
@@ -2858,7 +3027,7 @@
     # Validate the start symbol
     def validate_start(self):
         if self.start is not None:
-            if not isinstance(self.start,str):
+            if not isinstance(self.start, string_types):
                 self.log.error("'start' must be a string")
 
     # Look for error handler
@@ -2868,162 +3037,173 @@
     # Validate the error function
     def validate_error_func(self):
         if self.error_func:
-            if isinstance(self.error_func,types.FunctionType):
+            if isinstance(self.error_func, types.FunctionType):
                 ismethod = 0
             elif isinstance(self.error_func, types.MethodType):
                 ismethod = 1
             else:
                 self.log.error("'p_error' defined, but is not a function or method")
-                self.error = 1
+                self.error = True
                 return
 
-            eline = func_code(self.error_func).co_firstlineno
-            efile = func_code(self.error_func).co_filename
-            self.files[efile] = 1
+            eline = self.error_func.__code__.co_firstlineno
+            efile = self.error_func.__code__.co_filename
+            module = inspect.getmodule(self.error_func)
+            self.modules.add(module)
 
-            if (func_code(self.error_func).co_argcount != 1+ismethod):
-                self.log.error("%s:%d: p_error() requires 1 argument",efile,eline)
-                self.error = 1
+            argcount = self.error_func.__code__.co_argcount - ismethod
+            if argcount != 1:
+                self.log.error('%s:%d: p_error() requires 1 argument', efile, eline)
+                self.error = True
 
     # Get the tokens map
     def get_tokens(self):
-        tokens = self.pdict.get("tokens",None)
+        tokens = self.pdict.get('tokens')
         if not tokens:
-            self.log.error("No token list is defined")
-            self.error = 1
+            self.log.error('No token list is defined')
+            self.error = True
             return
 
-        if not isinstance(tokens,(list, tuple)):
-            self.log.error("tokens must be a list or tuple")
-            self.error = 1
-            return
-        
-        if not tokens:
-            self.log.error("tokens is empty")
-            self.error = 1
+        if not isinstance(tokens, (list, tuple)):
+            self.log.error('tokens must be a list or tuple')
+            self.error = True
             return
 
-        self.tokens = tokens
+        if not tokens:
+            self.log.error('tokens is empty')
+            self.error = True
+            return
+
+        self.tokens = sorted(tokens)
 
     # Validate the tokens
     def validate_tokens(self):
         # Validate the tokens.
         if 'error' in self.tokens:
             self.log.error("Illegal token name 'error'. Is a reserved word")
-            self.error = 1
+            self.error = True
             return
 
-        terminals = {}
+        terminals = set()
         for n in self.tokens:
             if n in terminals:
-                self.log.warning("Token '%s' multiply defined", n)
-            terminals[n] = 1
+                self.log.warning('Token %r multiply defined', n)
+            terminals.add(n)
 
     # Get the precedence map (if any)
     def get_precedence(self):
-        self.prec = self.pdict.get("precedence",None)
+        self.prec = self.pdict.get('precedence')
 
     # Validate and parse the precedence map
     def validate_precedence(self):
         preclist = []
         if self.prec:
-            if not isinstance(self.prec,(list,tuple)):
-                self.log.error("precedence must be a list or tuple")
-                self.error = 1
+            if not isinstance(self.prec, (list, tuple)):
+                self.log.error('precedence must be a list or tuple')
+                self.error = True
                 return
-            for level,p in enumerate(self.prec):
-                if not isinstance(p,(list,tuple)):
-                    self.log.error("Bad precedence table")
-                    self.error = 1
+            for level, p in enumerate(self.prec):
+                if not isinstance(p, (list, tuple)):
+                    self.log.error('Bad precedence table')
+                    self.error = True
                     return
 
                 if len(p) < 2:
-                    self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p)
-                    self.error = 1
+                    self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p)
+                    self.error = True
                     return
                 assoc = p[0]
-                if not isinstance(assoc,str):
-                    self.log.error("precedence associativity must be a string")
-                    self.error = 1
+                if not isinstance(assoc, string_types):
+                    self.log.error('precedence associativity must be a string')
+                    self.error = True
                     return
                 for term in p[1:]:
-                    if not isinstance(term,str):
-                        self.log.error("precedence items must be strings")
-                        self.error = 1
+                    if not isinstance(term, string_types):
+                        self.log.error('precedence items must be strings')
+                        self.error = True
                         return
-                    preclist.append((term,assoc,level+1))
+                    preclist.append((term, assoc, level+1))
         self.preclist = preclist
 
     # Get all p_functions from the grammar
     def get_pfunctions(self):
         p_functions = []
         for name, item in self.pdict.items():
-            if name[:2] != 'p_': continue
-            if name == 'p_error': continue
-            if isinstance(item,(types.FunctionType,types.MethodType)):
-                line = func_code(item).co_firstlineno
-                file = func_code(item).co_filename
-                p_functions.append((line,file,name,item.__doc__))
+            if not name.startswith('p_') or name == 'p_error':
+                continue
+            if isinstance(item, (types.FunctionType, types.MethodType)):
+                line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno)
+                module = inspect.getmodule(item)
+                p_functions.append((line, module, name, item.__doc__))
 
-        # Sort all of the actions by line number
-        p_functions.sort()
+        # Sort all of the actions by line number; make sure to stringify
+        # modules to make them sortable, since `line` may not uniquely sort all
+        # p functions
+        p_functions.sort(key=lambda p_function: (
+            p_function[0],
+            str(p_function[1]),
+            p_function[2],
+            p_function[3]))
         self.pfuncs = p_functions
 
-
     # Validate all of the p_functions
     def validate_pfunctions(self):
         grammar = []
         # Check for non-empty symbols
         if len(self.pfuncs) == 0:
-            self.log.error("no rules of the form p_rulename are defined")
-            self.error = 1
-            return 
-        
-        for line, file, name, doc in self.pfuncs:
+            self.log.error('no rules of the form p_rulename are defined')
+            self.error = True
+            return
+
+        for line, module, name, doc in self.pfuncs:
+            file = inspect.getsourcefile(module)
             func = self.pdict[name]
             if isinstance(func, types.MethodType):
                 reqargs = 2
             else:
                 reqargs = 1
-            if func_code(func).co_argcount > reqargs:
-                self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,func.__name__)
-                self.error = 1
-            elif func_code(func).co_argcount < reqargs:
-                self.log.error("%s:%d: Rule '%s' requires an argument",file,line,func.__name__)
-                self.error = 1
+            if func.__code__.co_argcount > reqargs:
+                self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__)
+                self.error = True
+            elif func.__code__.co_argcount < reqargs:
+                self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__)
+                self.error = True
             elif not func.__doc__:
-                self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__)
+                self.log.warning('%s:%d: No documentation string specified in function %r (ignored)',
+                                 file, line, func.__name__)
             else:
                 try:
-                    parsed_g = parse_grammar(doc,file,line)
+                    parsed_g = parse_grammar(doc, file, line)
                     for g in parsed_g:
                         grammar.append((name, g))
-                except SyntaxError:
-                    e = sys.exc_info()[1]
+                except SyntaxError as e:
                     self.log.error(str(e))
-                    self.error = 1
+                    self.error = True
 
                 # Looks like a valid grammar rule
                 # Mark the file in which defined.
-                self.files[file] = 1
+                self.modules.add(module)
 
         # Secondary validation step that looks for p_ definitions that are not functions
         # or functions that look like they might be grammar rules.
 
-        for n,v in self.pdict.items():
-            if n[0:2] == 'p_' and isinstance(v, (types.FunctionType, types.MethodType)): continue
-            if n[0:2] == 't_': continue
-            if n[0:2] == 'p_' and n != 'p_error':
-                self.log.warning("'%s' not defined as a function", n)
-            if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or
-                (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)):
-                try:
-                    doc = v.__doc__.split(" ")
-                    if doc[1] == ':':
-                        self.log.warning("%s:%d: Possible grammar rule '%s' defined without p_ prefix",
-                                         func_code(v).co_filename, func_code(v).co_firstlineno,n)
-                except Exception:
-                    pass
+        for n, v in self.pdict.items():
+            if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)):
+                continue
+            if n.startswith('t_'):
+                continue
+            if n.startswith('p_') and n != 'p_error':
+                self.log.warning('%r not defined as a function', n)
+            if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or
+                   (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)):
+                if v.__doc__:
+                    try:
+                        doc = v.__doc__.split(' ')
+                        if doc[1] == ':':
+                            self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix',
+                                             v.__code__.co_filename, v.__code__.co_firstlineno, n)
+                    except IndexError:
+                        pass
 
         self.grammar = grammar
 
@@ -3033,14 +3213,17 @@
 # Build a parser
 # -----------------------------------------------------------------------------
 
-def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, 
-         check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='',
-         debuglog=None, errorlog = None, picklefile=None):
+def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None,
+         check_recursion=True, optimize=False, write_tables=True, debugfile=debug_file,
+         outputdir=None, debuglog=None, errorlog=None, picklefile=None):
 
-    global parse                 # Reference to the parsing method of the last built parser
+    if tabmodule is None:
+        tabmodule = tab_module
+
+    # Reference to the parsing method of the last built parser
+    global parse
 
     # If pickling is enabled, table files are not created
-
     if picklefile:
         write_tables = 0
 
@@ -3049,17 +3232,54 @@
 
     # Get the module dictionary used for the parser
     if module:
-        _items = [(k,getattr(module,k)) for k in dir(module)]
+        _items = [(k, getattr(module, k)) for k in dir(module)]
         pdict = dict(_items)
+        # If no __file__ or __package__ attributes are available, try to obtain them
+        # from the __module__ instead
+        if '__file__' not in pdict:
+            pdict['__file__'] = sys.modules[pdict['__module__']].__file__
+        if '__package__' not in pdict and '__module__' in pdict:
+            if hasattr(sys.modules[pdict['__module__']], '__package__'):
+                pdict['__package__'] = sys.modules[pdict['__module__']].__package__
     else:
         pdict = get_caller_module_dict(2)
 
+    if outputdir is None:
+        # If no output directory is set, the location of the output files
+        # is determined according to the following rules:
+        #     - If tabmodule specifies a package, files go into that package directory
+        #     - Otherwise, files go in the same directory as the specifying module
+        if isinstance(tabmodule, types.ModuleType):
+            srcfile = tabmodule.__file__
+        else:
+            if '.' not in tabmodule:
+                srcfile = pdict['__file__']
+            else:
+                parts = tabmodule.split('.')
+                pkgname = '.'.join(parts[:-1])
+                exec('import %s' % pkgname)
+                srcfile = getattr(sys.modules[pkgname], '__file__', '')
+        outputdir = os.path.dirname(srcfile)
+
+    # Determine if the module is package of a package or not.
+    # If so, fix the tabmodule setting so that tables load correctly
+    pkg = pdict.get('__package__')
+    if pkg and isinstance(tabmodule, str):
+        if '.' not in tabmodule:
+            tabmodule = pkg + '.' + tabmodule
+
+
+
+    # Set start symbol if it's specified directly using an argument
+    if start is not None:
+        pdict['start'] = start
+
     # Collect parser information from the dictionary
-    pinfo = ParserReflect(pdict,log=errorlog)
+    pinfo = ParserReflect(pdict, log=errorlog)
     pinfo.get_all()
 
     if pinfo.error:
-        raise YaccError("Unable to build parser")
+        raise YaccError('Unable to build parser')
 
     # Check signature against table files (if any)
     signature = pinfo.signature()
@@ -3074,35 +3294,36 @@
         if optimize or (read_signature == signature):
             try:
                 lr.bind_callables(pinfo.pdict)
-                parser = LRParser(lr,pinfo.error_func)
+                parser = LRParser(lr, pinfo.error_func)
                 parse = parser.parse
                 return parser
-            except Exception:
-                e = sys.exc_info()[1]
-                errorlog.warning("There was a problem loading the table file: %s", repr(e))
-    except VersionError:
-        e = sys.exc_info()
+            except Exception as e:
+                errorlog.warning('There was a problem loading the table file: %r', e)
+    except VersionError as e:
         errorlog.warning(str(e))
-    except Exception:
+    except ImportError:
         pass
 
     if debuglog is None:
         if debug:
-            debuglog = PlyLogger(open(debugfile,"w"))
+            try:
+                debuglog = PlyLogger(open(os.path.join(outputdir, debugfile), 'w'))
+            except IOError as e:
+                errorlog.warning("Couldn't open %r. %s" % (debugfile, e))
+                debuglog = NullLogger()
         else:
             debuglog = NullLogger()
 
-    debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__)
+    debuglog.info('Created by PLY version %s (http://www.dabeaz.com/ply)', __version__)
 
-
-    errors = 0
+    errors = False
 
     # Validate the parser information
     if pinfo.validate_all():
-        raise YaccError("Unable to build parser")
-    
+        raise YaccError('Unable to build parser')
+
     if not pinfo.error_func:
-        errorlog.warning("no p_error() function is defined")
+        errorlog.warning('no p_error() function is defined')
 
     # Create a grammar object
     grammar = Grammar(pinfo.tokens)
@@ -3110,20 +3331,18 @@
     # Set precedence level for terminals
     for term, assoc, level in pinfo.preclist:
         try:
-            grammar.set_precedence(term,assoc,level)
-        except GrammarError:
-            e = sys.exc_info()[1]
-            errorlog.warning("%s",str(e))
+            grammar.set_precedence(term, assoc, level)
+        except GrammarError as e:
+            errorlog.warning('%s', e)
 
     # Add productions to the grammar
     for funcname, gram in pinfo.grammar:
         file, line, prodname, syms = gram
         try:
-            grammar.add_production(prodname,syms,funcname,file,line)
-        except GrammarError:
-            e = sys.exc_info()[1]
-            errorlog.error("%s",str(e))
-            errors = 1
+            grammar.add_production(prodname, syms, funcname, file, line)
+        except GrammarError as e:
+            errorlog.error('%s', e)
+            errors = True
 
     # Set the grammar start symbols
     try:
@@ -3131,146 +3350,153 @@
             grammar.set_start(pinfo.start)
         else:
             grammar.set_start(start)
-    except GrammarError:
-        e = sys.exc_info()[1]
+    except GrammarError as e:
         errorlog.error(str(e))
-        errors = 1
+        errors = True
 
     if errors:
-        raise YaccError("Unable to build parser")
+        raise YaccError('Unable to build parser')
 
     # Verify the grammar structure
     undefined_symbols = grammar.undefined_symbols()
     for sym, prod in undefined_symbols:
-        errorlog.error("%s:%d: Symbol '%s' used, but not defined as a token or a rule",prod.file,prod.line,sym)
-        errors = 1
+        errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym)
+        errors = True
 
     unused_terminals = grammar.unused_terminals()
     if unused_terminals:
-        debuglog.info("")
-        debuglog.info("Unused terminals:")
-        debuglog.info("")
+        debuglog.info('')
+        debuglog.info('Unused terminals:')
+        debuglog.info('')
         for term in unused_terminals:
-            errorlog.warning("Token '%s' defined, but not used", term)
-            debuglog.info("    %s", term)
+            errorlog.warning('Token %r defined, but not used', term)
+            debuglog.info('    %s', term)
 
     # Print out all productions to the debug log
     if debug:
-        debuglog.info("")
-        debuglog.info("Grammar")
-        debuglog.info("")
-        for n,p in enumerate(grammar.Productions):
-            debuglog.info("Rule %-5d %s", n, p)
+        debuglog.info('')
+        debuglog.info('Grammar')
+        debuglog.info('')
+        for n, p in enumerate(grammar.Productions):
+            debuglog.info('Rule %-5d %s', n, p)
 
     # Find unused non-terminals
     unused_rules = grammar.unused_rules()
     for prod in unused_rules:
-        errorlog.warning("%s:%d: Rule '%s' defined, but not used", prod.file, prod.line, prod.name)
+        errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name)
 
     if len(unused_terminals) == 1:
-        errorlog.warning("There is 1 unused token")
+        errorlog.warning('There is 1 unused token')
     if len(unused_terminals) > 1:
-        errorlog.warning("There are %d unused tokens", len(unused_terminals))
+        errorlog.warning('There are %d unused tokens', len(unused_terminals))
 
     if len(unused_rules) == 1:
-        errorlog.warning("There is 1 unused rule")
+        errorlog.warning('There is 1 unused rule')
     if len(unused_rules) > 1:
-        errorlog.warning("There are %d unused rules", len(unused_rules))
+        errorlog.warning('There are %d unused rules', len(unused_rules))
 
     if debug:
-        debuglog.info("")
-        debuglog.info("Terminals, with rules where they appear")
-        debuglog.info("")
+        debuglog.info('')
+        debuglog.info('Terminals, with rules where they appear')
+        debuglog.info('')
         terms = list(grammar.Terminals)
         terms.sort()
         for term in terms:
-            debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]]))
-        
-        debuglog.info("")
-        debuglog.info("Nonterminals, with rules where they appear")
-        debuglog.info("")
+            debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]]))
+
+        debuglog.info('')
+        debuglog.info('Nonterminals, with rules where they appear')
+        debuglog.info('')
         nonterms = list(grammar.Nonterminals)
         nonterms.sort()
         for nonterm in nonterms:
-            debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]]))
-        debuglog.info("")
+            debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]]))
+        debuglog.info('')
 
     if check_recursion:
         unreachable = grammar.find_unreachable()
         for u in unreachable:
-            errorlog.warning("Symbol '%s' is unreachable",u)
+            errorlog.warning('Symbol %r is unreachable', u)
 
         infinite = grammar.infinite_cycles()
         for inf in infinite:
-            errorlog.error("Infinite recursion detected for symbol '%s'", inf)
-            errors = 1
-        
+            errorlog.error('Infinite recursion detected for symbol %r', inf)
+            errors = True
+
     unused_prec = grammar.unused_precedence()
     for term, assoc in unused_prec:
-        errorlog.error("Precedence rule '%s' defined for unknown symbol '%s'", assoc, term)
-        errors = 1
+        errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term)
+        errors = True
 
     if errors:
-        raise YaccError("Unable to build parser")
-    
+        raise YaccError('Unable to build parser')
+
     # Run the LRGeneratedTable on the grammar
     if debug:
-        errorlog.debug("Generating %s tables", method)
-            
-    lr = LRGeneratedTable(grammar,method,debuglog)
+        errorlog.debug('Generating %s tables', method)
+
+    lr = LRGeneratedTable(grammar, method, debuglog)
 
     if debug:
         num_sr = len(lr.sr_conflicts)
 
         # Report shift/reduce and reduce/reduce conflicts
         if num_sr == 1:
-            errorlog.warning("1 shift/reduce conflict")
+            errorlog.warning('1 shift/reduce conflict')
         elif num_sr > 1:
-            errorlog.warning("%d shift/reduce conflicts", num_sr)
+            errorlog.warning('%d shift/reduce conflicts', num_sr)
 
         num_rr = len(lr.rr_conflicts)
         if num_rr == 1:
-            errorlog.warning("1 reduce/reduce conflict")
+            errorlog.warning('1 reduce/reduce conflict')
         elif num_rr > 1:
-            errorlog.warning("%d reduce/reduce conflicts", num_rr)
+            errorlog.warning('%d reduce/reduce conflicts', num_rr)
 
     # Write out conflicts to the output file
     if debug and (lr.sr_conflicts or lr.rr_conflicts):
-        debuglog.warning("")
-        debuglog.warning("Conflicts:")
-        debuglog.warning("")
+        debuglog.warning('')
+        debuglog.warning('Conflicts:')
+        debuglog.warning('')
 
         for state, tok, resolution in lr.sr_conflicts:
-            debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s",  tok, state, resolution)
-        
-        already_reported = {}
+            debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s',  tok, state, resolution)
+
+        already_reported = set()
         for state, rule, rejected in lr.rr_conflicts:
-            if (state,id(rule),id(rejected)) in already_reported:
+            if (state, id(rule), id(rejected)) in already_reported:
                 continue
-            debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule)
-            debuglog.warning("rejected rule (%s) in state %d", rejected,state)
-            errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule)
-            errorlog.warning("rejected rule (%s) in state %d", rejected, state)
-            already_reported[state,id(rule),id(rejected)] = 1
-        
+            debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
+            debuglog.warning('rejected rule (%s) in state %d', rejected, state)
+            errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
+            errorlog.warning('rejected rule (%s) in state %d', rejected, state)
+            already_reported.add((state, id(rule), id(rejected)))
+
         warned_never = []
         for state, rule, rejected in lr.rr_conflicts:
             if not rejected.reduced and (rejected not in warned_never):
-                debuglog.warning("Rule (%s) is never reduced", rejected)
-                errorlog.warning("Rule (%s) is never reduced", rejected)
+                debuglog.warning('Rule (%s) is never reduced', rejected)
+                errorlog.warning('Rule (%s) is never reduced', rejected)
                 warned_never.append(rejected)
 
     # Write the table file if requested
     if write_tables:
-        lr.write_table(tabmodule,outputdir,signature)
+        try:
+            lr.write_table(tabmodule, outputdir, signature)
+            if tabmodule in sys.modules:
+                del sys.modules[tabmodule]
+        except IOError as e:
+            errorlog.warning("Couldn't create %r. %s" % (tabmodule, e))
 
     # Write a pickled version of the tables
     if picklefile:
-        lr.pickle_table(picklefile,signature)
+        try:
+            lr.pickle_table(picklefile, signature)
+        except IOError as e:
+            errorlog.warning("Couldn't create %r. %s" % (picklefile, e))
 
     # Build the parser
     lr.bind_callables(pinfo.pdict)
-    parser = LRParser(lr,pinfo.error_func)
+    parser = LRParser(lr, pinfo.error_func)
 
     parse = parser.parse
     return parser
diff --git a/ext/ply/ply/ygen.py b/ext/ply/ply/ygen.py
new file mode 100644
index 0000000..03b9318
--- /dev/null
+++ b/ext/ply/ply/ygen.py
@@ -0,0 +1,69 @@
+# ply: ygen.py
+#
+# This is a support program that auto-generates different versions of the YACC parsing
+# function with different features removed for the purposes of performance.
+#
+# Users should edit the method LRParser.parsedebug() in yacc.py.   The source code
+# for that method is then used to create the other methods.   See the comments in
+# yacc.py for further details.
+
+import os.path
+import shutil
+
+def get_source_range(lines, tag):
+    srclines = enumerate(lines)
+    start_tag = '#--! %s-start' % tag
+    end_tag = '#--! %s-end' % tag
+
+    for start_index, line in srclines:
+        if line.strip().startswith(start_tag):
+            break
+
+    for end_index, line in srclines:
+        if line.strip().endswith(end_tag):
+            break
+
+    return (start_index + 1, end_index)
+
+def filter_section(lines, tag):
+    filtered_lines = []
+    include = True
+    tag_text = '#--! %s' % tag
+    for line in lines:
+        if line.strip().startswith(tag_text):
+            include = not include
+        elif include:
+            filtered_lines.append(line)
+    return filtered_lines
+
+def main():
+    dirname = os.path.dirname(__file__)
+    shutil.copy2(os.path.join(dirname, 'yacc.py'), os.path.join(dirname, 'yacc.py.bak'))
+    with open(os.path.join(dirname, 'yacc.py'), 'r') as f:
+        lines = f.readlines()
+
+    parse_start, parse_end = get_source_range(lines, 'parsedebug')
+    parseopt_start, parseopt_end = get_source_range(lines, 'parseopt')
+    parseopt_notrack_start, parseopt_notrack_end = get_source_range(lines, 'parseopt-notrack')
+
+    # Get the original source
+    orig_lines = lines[parse_start:parse_end]
+
+    # Filter the DEBUG sections out
+    parseopt_lines = filter_section(orig_lines, 'DEBUG')
+
+    # Filter the TRACKING sections out
+    parseopt_notrack_lines = filter_section(parseopt_lines, 'TRACKING')
+
+    # Replace the parser source sections with updated versions
+    lines[parseopt_notrack_start:parseopt_notrack_end] = parseopt_notrack_lines
+    lines[parseopt_start:parseopt_end] = parseopt_lines
+
+    lines = [line.rstrip()+'\n' for line in lines]
+    with open(os.path.join(dirname, 'yacc.py'), 'w') as f:
+        f.writelines(lines)
+
+    print('Updated yacc.py')
+
+if __name__ == '__main__':
+    main()
diff --git a/ext/ply/setup.cfg b/ext/ply/setup.cfg
new file mode 100644
index 0000000..819449e
--- /dev/null
+++ b/ext/ply/setup.cfg
@@ -0,0 +1,10 @@
+[bdist_wheel]
+universal = 1
+
+[metadata]
+description-file = README.md
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff --git a/ext/ply/setup.py b/ext/ply/setup.py
index 606b29c..46bc6b3 100644
--- a/ext/ply/setup.py
+++ b/ext/ply/setup.py
@@ -14,13 +14,18 @@
 productions, precedence rules, error recovery, and support for ambiguous grammars. 
 
 PLY is extremely easy to use and provides very extensive error checking. 
+It is compatible with both Python 2 and Python 3.
 """,
             license="""BSD""",
-            version = "3.2",
+            version = "3.11",
             author = "David Beazley",
             author_email = "dave@dabeaz.com",
             maintainer = "David Beazley",
             maintainer_email = "dave@dabeaz.com",
             url = "http://www.dabeaz.com/ply/",
             packages = ['ply'],
+            classifiers = [
+              'Programming Language :: Python :: 3',
+              'Programming Language :: Python :: 2',
+              ]
             )
diff --git a/ext/ply/test/README b/ext/ply/test/README
index aac12b0..03b167c 100644
--- a/ext/ply/test/README
+++ b/ext/ply/test/README
@@ -1,11 +1,8 @@
 This directory mostly contains tests for various types of error
 conditions.  To run:
 
-  $ python testlex.py .
-  $ python testyacc.py .
-
-The tests can also be run using the Python unittest module.
-
-   $ python rununit.py
+  $ python testlex.py 
+  $ python testyacc.py 
+  $ python testcpp.py
 
 The script 'cleanup.sh' cleans up this directory to its original state.
diff --git a/ext/ply/test/calclex.py b/ext/ply/test/calclex.py
index 67d245f..030a986 100644
--- a/ext/ply/test/calclex.py
+++ b/ext/ply/test/calclex.py
@@ -36,14 +36,14 @@
 
 def t_newline(t):
     r'\n+'
-    t.lineno += t.value.count("\n")
+    t.lexer.lineno += t.value.count("\n")
     
 def t_error(t):
     print("Illegal character '%s'" % t.value[0])
     t.lexer.skip(1)
     
 # Build the lexer
-lex.lex()
+lexer = lex.lex()
 
 
 
diff --git a/ext/ply/test/cleanup.sh b/ext/ply/test/cleanup.sh
index 9db9368..9374f2c 100755
--- a/ext/ply/test/cleanup.sh
+++ b/ext/ply/test/cleanup.sh
@@ -1,4 +1,4 @@
 #!/bin/sh
 
-rm -f *~ *.pyc *.pyo *.dif *.out 
+rm -rf *~ *.pyc *.pyo *.dif *.out __pycache__
 
diff --git a/ext/ply/test/lex_literal3.py b/ext/ply/test/lex_literal3.py
new file mode 100644
index 0000000..91ab980
--- /dev/null
+++ b/ext/ply/test/lex_literal3.py
@@ -0,0 +1,26 @@
+# lex_literal3.py
+#
+# An empty literal specification given as a list
+# Issue 8 : Literals empty list causes IndexError
+
+import sys
+if ".." not in sys.path: sys.path.insert(0,"..")
+
+import ply.lex as lex
+
+tokens = [
+    "NUMBER",
+    ]
+
+literals = []
+
+def t_NUMBER(t):
+    r'\d+'
+    return t
+
+def t_error(t):
+    pass
+
+lex.lex()
+
+
diff --git a/ext/ply/test/lex_optimize3.py b/ext/ply/test/lex_optimize3.py
index c6c8cce..b8df5aa 100644
--- a/ext/ply/test/lex_optimize3.py
+++ b/ext/ply/test/lex_optimize3.py
@@ -45,7 +45,7 @@
     t.lexer.skip(1)
     
 # Build the lexer
-lex.lex(optimize=1,lextab="lexdir.sub.calctab",outputdir="lexdir/sub")
+lex.lex(optimize=1,lextab="lexdir.sub.calctab" ,outputdir="lexdir/sub")
 lex.runmain(data="3+4")
 
 
diff --git a/ext/ply/test/lex_optimize4.py b/ext/ply/test/lex_optimize4.py
new file mode 100644
index 0000000..cc6e2a9
--- /dev/null
+++ b/ext/ply/test/lex_optimize4.py
@@ -0,0 +1,26 @@
+# -----------------------------------------------------------------------------
+# lex_optimize4.py
+# -----------------------------------------------------------------------------
+import re
+import sys
+
+if ".." not in sys.path: sys.path.insert(0,"..")
+import ply.lex as lex
+
+tokens = [
+    "PLUS",
+    "MINUS",
+    "NUMBER",
+    ]
+
+t_PLUS = r'\+?'
+t_MINUS = r'-'
+t_NUMBER = r'(\d+)'
+
+def t_error(t):
+    pass
+
+
+# Build the lexer
+lex.lex(optimize=True, lextab="opt4tab", reflags=re.UNICODE)
+lex.runmain(data="3+4")
diff --git a/ext/ply/test/pkg_test1/__init__.py b/ext/ply/test/pkg_test1/__init__.py
new file mode 100644
index 0000000..0e19558
--- /dev/null
+++ b/ext/ply/test/pkg_test1/__init__.py
@@ -0,0 +1,9 @@
+# Tests proper handling of lextab and parsetab files in package structures
+
+# Here for testing purposes
+import sys
+if '..' not in sys.path:  
+    sys.path.insert(0, '..')
+
+from .parsing.calcparse import parser
+
diff --git a/ext/ply/test/pkg_test1/parsing/__init__.py b/ext/ply/test/pkg_test1/parsing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/ply/test/pkg_test1/parsing/__init__.py
diff --git a/ext/ply/test/pkg_test1/parsing/calclex.py b/ext/ply/test/pkg_test1/parsing/calclex.py
new file mode 100644
index 0000000..b3c1a4d
--- /dev/null
+++ b/ext/ply/test/pkg_test1/parsing/calclex.py
@@ -0,0 +1,47 @@
+# -----------------------------------------------------------------------------
+# calclex.py
+# -----------------------------------------------------------------------------
+
+import ply.lex as lex
+
+tokens = (
+    'NAME','NUMBER',
+    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
+    'LPAREN','RPAREN',
+    )
+
+# Tokens
+
+t_PLUS    = r'\+'
+t_MINUS   = r'-'
+t_TIMES   = r'\*'
+t_DIVIDE  = r'/'
+t_EQUALS  = r'='
+t_LPAREN  = r'\('
+t_RPAREN  = r'\)'
+t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
+def t_NUMBER(t):
+    r'\d+'
+    try:
+        t.value = int(t.value)
+    except ValueError:
+        print("Integer value too large %s" % t.value)
+        t.value = 0
+    return t
+
+t_ignore = " \t"
+
+def t_newline(t):
+    r'\n+'
+    t.lexer.lineno += t.value.count("\n")
+    
+def t_error(t):
+    print("Illegal character '%s'" % t.value[0])
+    t.lexer.skip(1)
+    
+# Build the lexer
+lexer = lex.lex(optimize=True)
+
+
+
diff --git a/ext/ply/test/pkg_test1/parsing/calcparse.py b/ext/ply/test/pkg_test1/parsing/calcparse.py
new file mode 100644
index 0000000..c058e9f
--- /dev/null
+++ b/ext/ply/test/pkg_test1/parsing/calcparse.py
@@ -0,0 +1,66 @@
+# -----------------------------------------------------------------------------
+# yacc_simple.py
+#
+# A simple, properly specifier grammar
+# -----------------------------------------------------------------------------
+
+from .calclex import tokens
+from ply import yacc
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+    'statement : NAME EQUALS expression'
+    names[t[1]] = t[3]
+
+def p_statement_expr(t):
+    'statement : expression'
+    t[0] = t[1]
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_group(t):
+    'expression : LPAREN expression RPAREN'
+    t[0] = t[2]
+
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_expression_name(t):
+    'expression : NAME'
+    try:
+        t[0] = names[t[1]]
+    except LookupError:
+        print("Undefined name '%s'" % t[1])
+        t[0] = 0
+
+def p_error(t):
+    print("Syntax error at '%s'" % t.value)
+
+parser = yacc.yacc()
+
+
+
+
+
diff --git a/ext/ply/test/pkg_test1/parsing/lextab.py b/ext/ply/test/pkg_test1/parsing/lextab.py
new file mode 100644
index 0000000..52376b2
--- /dev/null
+++ b/ext/ply/test/pkg_test1/parsing/lextab.py
@@ -0,0 +1,10 @@
+# lextab.py. This file automatically created by PLY (version 3.11). Don't edit!
+_tabversion   = '3.10'
+_lextokens    = set(('DIVIDE', 'EQUALS', 'LPAREN', 'MINUS', 'NAME', 'NUMBER', 'PLUS', 'RPAREN', 'TIMES'))
+_lexreflags   = 64
+_lexliterals  = ''
+_lexstateinfo = {'INITIAL': 'inclusive'}
+_lexstatere   = {'INITIAL': [('(?P<t_NUMBER>\\d+)|(?P<t_newline>\\n+)|(?P<t_NAME>[a-zA-Z_][a-zA-Z0-9_]*)|(?P<t_PLUS>\\+)|(?P<t_LPAREN>\\()|(?P<t_TIMES>\\*)|(?P<t_RPAREN>\\))|(?P<t_EQUALS>=)|(?P<t_DIVIDE>/)|(?P<t_MINUS>-)', [None, ('t_NUMBER', 'NUMBER'), ('t_newline', 'newline'), (None, 'NAME'), (None, 'PLUS'), (None, 'LPAREN'), (None, 'TIMES'), (None, 'RPAREN'), (None, 'EQUALS'), (None, 'DIVIDE'), (None, 'MINUS')])]}
+_lexstateignore = {'INITIAL': ' \t'}
+_lexstateerrorf = {'INITIAL': 't_error'}
+_lexstateeoff = {}
diff --git a/ext/ply/test/pkg_test2/__init__.py b/ext/ply/test/pkg_test2/__init__.py
new file mode 100644
index 0000000..0e19558
--- /dev/null
+++ b/ext/ply/test/pkg_test2/__init__.py
@@ -0,0 +1,9 @@
+# Tests proper handling of lextab and parsetab files in package structures
+
+# Here for testing purposes
+import sys
+if '..' not in sys.path:  
+    sys.path.insert(0, '..')
+
+from .parsing.calcparse import parser
+
diff --git a/ext/ply/test/pkg_test2/parsing/__init__.py b/ext/ply/test/pkg_test2/parsing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/ply/test/pkg_test2/parsing/__init__.py
diff --git a/ext/ply/test/pkg_test2/parsing/calclex.py b/ext/ply/test/pkg_test2/parsing/calclex.py
new file mode 100644
index 0000000..789e13f
--- /dev/null
+++ b/ext/ply/test/pkg_test2/parsing/calclex.py
@@ -0,0 +1,47 @@
+# -----------------------------------------------------------------------------
+# calclex.py
+# -----------------------------------------------------------------------------
+
+import ply.lex as lex
+
+tokens = (
+    'NAME','NUMBER',
+    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
+    'LPAREN','RPAREN',
+    )
+
+# Tokens
+
+t_PLUS    = r'\+'
+t_MINUS   = r'-'
+t_TIMES   = r'\*'
+t_DIVIDE  = r'/'
+t_EQUALS  = r'='
+t_LPAREN  = r'\('
+t_RPAREN  = r'\)'
+t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
+def t_NUMBER(t):
+    r'\d+'
+    try:
+        t.value = int(t.value)
+    except ValueError:
+        print("Integer value too large %s" % t.value)
+        t.value = 0
+    return t
+
+t_ignore = " \t"
+
+def t_newline(t):
+    r'\n+'
+    t.lexer.lineno += t.value.count("\n")
+    
+def t_error(t):
+    print("Illegal character '%s'" % t.value[0])
+    t.lexer.skip(1)
+    
+# Build the lexer
+lexer = lex.lex(optimize=True, lextab='calclextab')
+
+
+
diff --git a/ext/ply/test/pkg_test2/parsing/calclextab.py b/ext/ply/test/pkg_test2/parsing/calclextab.py
new file mode 100644
index 0000000..a616c39
--- /dev/null
+++ b/ext/ply/test/pkg_test2/parsing/calclextab.py
@@ -0,0 +1,10 @@
+# calclextab.py. This file automatically created by PLY (version 3.11). Don't edit!
+_tabversion   = '3.10'
+_lextokens    = set(('DIVIDE', 'EQUALS', 'LPAREN', 'MINUS', 'NAME', 'NUMBER', 'PLUS', 'RPAREN', 'TIMES'))
+_lexreflags   = 64
+_lexliterals  = ''
+_lexstateinfo = {'INITIAL': 'inclusive'}
+_lexstatere   = {'INITIAL': [('(?P<t_NUMBER>\\d+)|(?P<t_newline>\\n+)|(?P<t_NAME>[a-zA-Z_][a-zA-Z0-9_]*)|(?P<t_PLUS>\\+)|(?P<t_LPAREN>\\()|(?P<t_TIMES>\\*)|(?P<t_RPAREN>\\))|(?P<t_EQUALS>=)|(?P<t_DIVIDE>/)|(?P<t_MINUS>-)', [None, ('t_NUMBER', 'NUMBER'), ('t_newline', 'newline'), (None, 'NAME'), (None, 'PLUS'), (None, 'LPAREN'), (None, 'TIMES'), (None, 'RPAREN'), (None, 'EQUALS'), (None, 'DIVIDE'), (None, 'MINUS')])]}
+_lexstateignore = {'INITIAL': ' \t'}
+_lexstateerrorf = {'INITIAL': 't_error'}
+_lexstateeoff = {}
diff --git a/ext/ply/test/pkg_test2/parsing/calcparse.py b/ext/ply/test/pkg_test2/parsing/calcparse.py
new file mode 100644
index 0000000..f519338
--- /dev/null
+++ b/ext/ply/test/pkg_test2/parsing/calcparse.py
@@ -0,0 +1,66 @@
+# -----------------------------------------------------------------------------
+# yacc_simple.py
+#
+# A simple, properly specifier grammar
+# -----------------------------------------------------------------------------
+
+from .calclex import tokens
+from ply import yacc
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+    'statement : NAME EQUALS expression'
+    names[t[1]] = t[3]
+
+def p_statement_expr(t):
+    'statement : expression'
+    t[0] = t[1]
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_group(t):
+    'expression : LPAREN expression RPAREN'
+    t[0] = t[2]
+
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_expression_name(t):
+    'expression : NAME'
+    try:
+        t[0] = names[t[1]]
+    except LookupError:
+        print("Undefined name '%s'" % t[1])
+        t[0] = 0
+
+def p_error(t):
+    print("Syntax error at '%s'" % t.value)
+
+parser = yacc.yacc(tabmodule='calcparsetab')
+
+
+
+
+
diff --git a/ext/ply/test/pkg_test2/parsing/calcparsetab.py b/ext/ply/test/pkg_test2/parsing/calcparsetab.py
new file mode 100644
index 0000000..23e3208
--- /dev/null
+++ b/ext/ply/test/pkg_test2/parsing/calcparsetab.py
@@ -0,0 +1,40 @@
+
+# calcparsetab.py
+# This file is automatically generated. Do not edit.
+# pylint: disable=W,C,R
+_tabversion = '3.10'
+
+_lr_method = 'LALR'
+
+_lr_signature = 'leftPLUSMINUSleftTIMESDIVIDErightUMINUSDIVIDE EQUALS LPAREN MINUS NAME NUMBER PLUS RPAREN TIMESstatement : NAME EQUALS expressionstatement : expressionexpression : expression PLUS expression\n                  | expression MINUS expression\n                  | expression TIMES expression\n                  | expression DIVIDE expressionexpression : MINUS expression %prec UMINUSexpression : LPAREN expression RPARENexpression : NUMBERexpression : NAME'
+    
+_lr_action_items = {'PLUS':([2,4,6,7,8,9,15,16,17,18,19,20,],[-9,-10,11,-10,-7,11,-8,11,-3,-4,-6,-5,]),'MINUS':([0,1,2,3,4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,],[1,1,-9,1,-10,12,-10,-7,12,1,1,1,1,1,-8,12,-3,-4,-6,-5,]),'EQUALS':([4,],[10,]),'NUMBER':([0,1,3,10,11,12,13,14,],[2,2,2,2,2,2,2,2,]),'LPAREN':([0,1,3,10,11,12,13,14,],[3,3,3,3,3,3,3,3,]),'NAME':([0,1,3,10,11,12,13,14,],[4,7,7,7,7,7,7,7,]),'TIMES':([2,4,6,7,8,9,15,16,17,18,19,20,],[-9,-10,14,-10,-7,14,-8,14,14,14,-6,-5,]),'$end':([2,4,5,6,7,8,15,16,17,18,19,20,],[-9,-10,0,-2,-10,-7,-8,-1,-3,-4,-6,-5,]),'RPAREN':([2,7,8,9,15,17,18,19,20,],[-9,-10,-7,15,-8,-3,-4,-6,-5,]),'DIVIDE':([2,4,6,7,8,9,15,16,17,18,19,20,],[-9,-10,13,-10,-7,13,-8,13,13,13,-6,-5,]),}
+
+_lr_action = {}
+for _k, _v in _lr_action_items.items():
+   for _x,_y in zip(_v[0],_v[1]):
+      if not _x in _lr_action:  _lr_action[_x] = {}
+      _lr_action[_x][_k] = _y
+del _lr_action_items
+
+_lr_goto_items = {'statement':([0,],[5,]),'expression':([0,1,3,10,11,12,13,14,],[6,8,9,16,17,18,19,20,]),}
+
+_lr_goto = {}
+for _k, _v in _lr_goto_items.items():
+   for _x, _y in zip(_v[0], _v[1]):
+       if not _x in _lr_goto: _lr_goto[_x] = {}
+       _lr_goto[_x][_k] = _y
+del _lr_goto_items
+_lr_productions = [
+  ("S' -> statement","S'",1,None,None,None),
+  ('statement -> NAME EQUALS expression','statement',3,'p_statement_assign','calcparse.py',21),
+  ('statement -> expression','statement',1,'p_statement_expr','calcparse.py',25),
+  ('expression -> expression PLUS expression','expression',3,'p_expression_binop','calcparse.py',29),
+  ('expression -> expression MINUS expression','expression',3,'p_expression_binop','calcparse.py',30),
+  ('expression -> expression TIMES expression','expression',3,'p_expression_binop','calcparse.py',31),
+  ('expression -> expression DIVIDE expression','expression',3,'p_expression_binop','calcparse.py',32),
+  ('expression -> MINUS expression','expression',2,'p_expression_uminus','calcparse.py',39),
+  ('expression -> LPAREN expression RPAREN','expression',3,'p_expression_group','calcparse.py',43),
+  ('expression -> NUMBER','expression',1,'p_expression_number','calcparse.py',47),
+  ('expression -> NAME','expression',1,'p_expression_name','calcparse.py',51),
+]
diff --git a/ext/ply/test/pkg_test3/__init__.py b/ext/ply/test/pkg_test3/__init__.py
new file mode 100644
index 0000000..0e19558
--- /dev/null
+++ b/ext/ply/test/pkg_test3/__init__.py
@@ -0,0 +1,9 @@
+# Tests proper handling of lextab and parsetab files in package structures
+
+# Here for testing purposes
+import sys
+if '..' not in sys.path:  
+    sys.path.insert(0, '..')
+
+from .parsing.calcparse import parser
+
diff --git a/ext/ply/test/pkg_test3/generated/__init__.py b/ext/ply/test/pkg_test3/generated/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/ply/test/pkg_test3/generated/__init__.py
diff --git a/ext/ply/test/pkg_test3/generated/lextab.py b/ext/ply/test/pkg_test3/generated/lextab.py
new file mode 100644
index 0000000..52376b2
--- /dev/null
+++ b/ext/ply/test/pkg_test3/generated/lextab.py
@@ -0,0 +1,10 @@
+# lextab.py. This file automatically created by PLY (version 3.11). Don't edit!
+_tabversion   = '3.10'
+_lextokens    = set(('DIVIDE', 'EQUALS', 'LPAREN', 'MINUS', 'NAME', 'NUMBER', 'PLUS', 'RPAREN', 'TIMES'))
+_lexreflags   = 64
+_lexliterals  = ''
+_lexstateinfo = {'INITIAL': 'inclusive'}
+_lexstatere   = {'INITIAL': [('(?P<t_NUMBER>\\d+)|(?P<t_newline>\\n+)|(?P<t_NAME>[a-zA-Z_][a-zA-Z0-9_]*)|(?P<t_PLUS>\\+)|(?P<t_LPAREN>\\()|(?P<t_TIMES>\\*)|(?P<t_RPAREN>\\))|(?P<t_EQUALS>=)|(?P<t_DIVIDE>/)|(?P<t_MINUS>-)', [None, ('t_NUMBER', 'NUMBER'), ('t_newline', 'newline'), (None, 'NAME'), (None, 'PLUS'), (None, 'LPAREN'), (None, 'TIMES'), (None, 'RPAREN'), (None, 'EQUALS'), (None, 'DIVIDE'), (None, 'MINUS')])]}
+_lexstateignore = {'INITIAL': ' \t'}
+_lexstateerrorf = {'INITIAL': 't_error'}
+_lexstateeoff = {}
diff --git a/ext/ply/test/pkg_test3/parsing/__init__.py b/ext/ply/test/pkg_test3/parsing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/ply/test/pkg_test3/parsing/__init__.py
diff --git a/ext/ply/test/pkg_test3/parsing/calclex.py b/ext/ply/test/pkg_test3/parsing/calclex.py
new file mode 100644
index 0000000..6ca2c4f
--- /dev/null
+++ b/ext/ply/test/pkg_test3/parsing/calclex.py
@@ -0,0 +1,47 @@
+# -----------------------------------------------------------------------------
+# calclex.py
+# -----------------------------------------------------------------------------
+
+import ply.lex as lex
+
+tokens = (
+    'NAME','NUMBER',
+    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
+    'LPAREN','RPAREN',
+    )
+
+# Tokens
+
+t_PLUS    = r'\+'
+t_MINUS   = r'-'
+t_TIMES   = r'\*'
+t_DIVIDE  = r'/'
+t_EQUALS  = r'='
+t_LPAREN  = r'\('
+t_RPAREN  = r'\)'
+t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
+def t_NUMBER(t):
+    r'\d+'
+    try:
+        t.value = int(t.value)
+    except ValueError:
+        print("Integer value too large %s" % t.value)
+        t.value = 0
+    return t
+
+t_ignore = " \t"
+
+def t_newline(t):
+    r'\n+'
+    t.lexer.lineno += t.value.count("\n")
+    
+def t_error(t):
+    print("Illegal character '%s'" % t.value[0])
+    t.lexer.skip(1)
+    
+# Build the lexer
+lexer = lex.lex(optimize=True, lextab='pkg_test3.generated.lextab')
+
+
+
diff --git a/ext/ply/test/pkg_test3/parsing/calcparse.py b/ext/ply/test/pkg_test3/parsing/calcparse.py
new file mode 100644
index 0000000..2dcb52b
--- /dev/null
+++ b/ext/ply/test/pkg_test3/parsing/calcparse.py
@@ -0,0 +1,66 @@
+# -----------------------------------------------------------------------------
+# yacc_simple.py
+#
+# A simple, properly specifier grammar
+# -----------------------------------------------------------------------------
+
+from .calclex import tokens
+from ply import yacc
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+    'statement : NAME EQUALS expression'
+    names[t[1]] = t[3]
+
+def p_statement_expr(t):
+    'statement : expression'
+    t[0] = t[1]
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_group(t):
+    'expression : LPAREN expression RPAREN'
+    t[0] = t[2]
+
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_expression_name(t):
+    'expression : NAME'
+    try:
+        t[0] = names[t[1]]
+    except LookupError:
+        print("Undefined name '%s'" % t[1])
+        t[0] = 0
+
+def p_error(t):
+    print("Syntax error at '%s'" % t.value)
+
+parser = yacc.yacc(tabmodule='pkg_test3.generated.parsetab')
+
+
+
+
+
diff --git a/ext/ply/test/pkg_test4/__init__.py b/ext/ply/test/pkg_test4/__init__.py
new file mode 100644
index 0000000..ba9ddac
--- /dev/null
+++ b/ext/ply/test/pkg_test4/__init__.py
@@ -0,0 +1,25 @@
+# Tests proper handling of lextab and parsetab files in package structures
+# Check of warning messages when files aren't writable
+
+# Here for testing purposes
+import sys
+if '..' not in sys.path:  
+    sys.path.insert(0, '..')
+
+import ply.lex
+import ply.yacc
+
+def patched_open(filename, mode):
+    if 'w' in mode:
+        raise IOError("Permission denied %r" % filename)
+    return open(filename, mode)
+
+ply.lex.open = patched_open
+ply.yacc.open = patched_open
+try:
+    from .parsing.calcparse import parser
+finally:
+    del ply.lex.open
+    del ply.yacc.open
+
+
diff --git a/ext/ply/test/pkg_test4/parsing/__init__.py b/ext/ply/test/pkg_test4/parsing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/ply/test/pkg_test4/parsing/__init__.py
diff --git a/ext/ply/test/pkg_test4/parsing/calclex.py b/ext/ply/test/pkg_test4/parsing/calclex.py
new file mode 100644
index 0000000..b3c1a4d
--- /dev/null
+++ b/ext/ply/test/pkg_test4/parsing/calclex.py
@@ -0,0 +1,47 @@
+# -----------------------------------------------------------------------------
+# calclex.py
+# -----------------------------------------------------------------------------
+
+import ply.lex as lex
+
+tokens = (
+    'NAME','NUMBER',
+    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
+    'LPAREN','RPAREN',
+    )
+
+# Tokens
+
+t_PLUS    = r'\+'
+t_MINUS   = r'-'
+t_TIMES   = r'\*'
+t_DIVIDE  = r'/'
+t_EQUALS  = r'='
+t_LPAREN  = r'\('
+t_RPAREN  = r'\)'
+t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
+def t_NUMBER(t):
+    r'\d+'
+    try:
+        t.value = int(t.value)
+    except ValueError:
+        print("Integer value too large %s" % t.value)
+        t.value = 0
+    return t
+
+t_ignore = " \t"
+
+def t_newline(t):
+    r'\n+'
+    t.lexer.lineno += t.value.count("\n")
+    
+def t_error(t):
+    print("Illegal character '%s'" % t.value[0])
+    t.lexer.skip(1)
+    
+# Build the lexer
+lexer = lex.lex(optimize=True)
+
+
+
diff --git a/ext/ply/test/pkg_test4/parsing/calcparse.py b/ext/ply/test/pkg_test4/parsing/calcparse.py
new file mode 100644
index 0000000..c058e9f
--- /dev/null
+++ b/ext/ply/test/pkg_test4/parsing/calcparse.py
@@ -0,0 +1,66 @@
+# -----------------------------------------------------------------------------
+# yacc_simple.py
+#
+# A simple, properly specifier grammar
+# -----------------------------------------------------------------------------
+
+from .calclex import tokens
+from ply import yacc
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+    'statement : NAME EQUALS expression'
+    names[t[1]] = t[3]
+
+def p_statement_expr(t):
+    'statement : expression'
+    t[0] = t[1]
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_group(t):
+    'expression : LPAREN expression RPAREN'
+    t[0] = t[2]
+
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_expression_name(t):
+    'expression : NAME'
+    try:
+        t[0] = names[t[1]]
+    except LookupError:
+        print("Undefined name '%s'" % t[1])
+        t[0] = 0
+
+def p_error(t):
+    print("Syntax error at '%s'" % t.value)
+
+parser = yacc.yacc()
+
+
+
+
+
diff --git a/ext/ply/test/pkg_test5/__init__.py b/ext/ply/test/pkg_test5/__init__.py
new file mode 100644
index 0000000..0e19558
--- /dev/null
+++ b/ext/ply/test/pkg_test5/__init__.py
@@ -0,0 +1,9 @@
+# Tests proper handling of lextab and parsetab files in package structures
+
+# Here for testing purposes
+import sys
+if '..' not in sys.path:  
+    sys.path.insert(0, '..')
+
+from .parsing.calcparse import parser
+
diff --git a/ext/ply/test/pkg_test5/parsing/__init__.py b/ext/ply/test/pkg_test5/parsing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/ply/test/pkg_test5/parsing/__init__.py
diff --git a/ext/ply/test/pkg_test5/parsing/calclex.py b/ext/ply/test/pkg_test5/parsing/calclex.py
new file mode 100644
index 0000000..e8759b6
--- /dev/null
+++ b/ext/ply/test/pkg_test5/parsing/calclex.py
@@ -0,0 +1,48 @@
+# -----------------------------------------------------------------------------
+# calclex.py
+# -----------------------------------------------------------------------------
+
+import ply.lex as lex
+
+tokens = (
+    'NAME','NUMBER',
+    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
+    'LPAREN','RPAREN',
+    )
+
+# Tokens
+
+t_PLUS    = r'\+'
+t_MINUS   = r'-'
+t_TIMES   = r'\*'
+t_DIVIDE  = r'/'
+t_EQUALS  = r'='
+t_LPAREN  = r'\('
+t_RPAREN  = r'\)'
+t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
+def t_NUMBER(t):
+    r'\d+'
+    try:
+        t.value = int(t.value)
+    except ValueError:
+        print("Integer value too large %s" % t.value)
+        t.value = 0
+    return t
+
+t_ignore = " \t"
+
+def t_newline(t):
+    r'\n+'
+    t.lexer.lineno += t.value.count("\n")
+    
+def t_error(t):
+    print("Illegal character '%s'" % t.value[0])
+    t.lexer.skip(1)
+    
+# Build the lexer
+import os.path
+lexer = lex.lex(optimize=True, outputdir=os.path.dirname(__file__))
+
+
+
diff --git a/ext/ply/test/pkg_test5/parsing/calcparse.py b/ext/ply/test/pkg_test5/parsing/calcparse.py
new file mode 100644
index 0000000..2a1ddfe
--- /dev/null
+++ b/ext/ply/test/pkg_test5/parsing/calcparse.py
@@ -0,0 +1,67 @@
+# -----------------------------------------------------------------------------
+# yacc_simple.py
+#
+# A simple, properly specifier grammar
+# -----------------------------------------------------------------------------
+
+from .calclex import tokens
+from ply import yacc
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+    'statement : NAME EQUALS expression'
+    names[t[1]] = t[3]
+
+def p_statement_expr(t):
+    'statement : expression'
+    t[0] = t[1]
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_group(t):
+    'expression : LPAREN expression RPAREN'
+    t[0] = t[2]
+
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_expression_name(t):
+    'expression : NAME'
+    try:
+        t[0] = names[t[1]]
+    except LookupError:
+        print("Undefined name '%s'" % t[1])
+        t[0] = 0
+
+def p_error(t):
+    print("Syntax error at '%s'" % t.value)
+
+import os.path
+parser = yacc.yacc(outputdir=os.path.dirname(__file__))
+
+
+
+
+
diff --git a/ext/ply/test/pkg_test5/parsing/lextab.py b/ext/ply/test/pkg_test5/parsing/lextab.py
new file mode 100644
index 0000000..8cab298
--- /dev/null
+++ b/ext/ply/test/pkg_test5/parsing/lextab.py
@@ -0,0 +1,10 @@
+# lextab.py. This file automatically created by PLY (version 3.11). Don't edit!
+_tabversion   = '3.10'
+_lextokens    = set(('DIVIDE', 'EQUALS', 'LPAREN', 'MINUS', 'NAME', 'NUMBER', 'PLUS', 'RPAREN', 'TIMES'))
+_lexreflags   = 64
+_lexliterals  = ''
+_lexstateinfo = {'INITIAL': 'inclusive'}
+_lexstatere   = {'INITIAL': [('(?P<t_NUMBER>\\d+)|(?P<t_newline>\\n+)|(?P<t_NAME>[a-zA-Z_][a-zA-Z0-9_]*)|(?P<t_LPAREN>\\()|(?P<t_PLUS>\\+)|(?P<t_TIMES>\\*)|(?P<t_RPAREN>\\))|(?P<t_EQUALS>=)|(?P<t_DIVIDE>/)|(?P<t_MINUS>-)', [None, ('t_NUMBER', 'NUMBER'), ('t_newline', 'newline'), (None, 'NAME'), (None, 'LPAREN'), (None, 'PLUS'), (None, 'TIMES'), (None, 'RPAREN'), (None, 'EQUALS'), (None, 'DIVIDE'), (None, 'MINUS')])]}
+_lexstateignore = {'INITIAL': ' \t'}
+_lexstateerrorf = {'INITIAL': 't_error'}
+_lexstateeoff = {}
diff --git a/ext/ply/test/pkg_test6/__init__.py b/ext/ply/test/pkg_test6/__init__.py
new file mode 100644
index 0000000..5dbe0cb
--- /dev/null
+++ b/ext/ply/test/pkg_test6/__init__.py
@@ -0,0 +1,9 @@
+# Tests proper sorting of modules in yacc.ParserReflect.get_pfunctions
+
+# Here for testing purposes
+import sys
+if '..' not in sys.path:
+    sys.path.insert(0, '..')
+
+from .parsing.calcparse import parser
+
diff --git a/ext/ply/test/pkg_test6/parsing/__init__.py b/ext/ply/test/pkg_test6/parsing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ext/ply/test/pkg_test6/parsing/__init__.py
diff --git a/ext/ply/test/pkg_test6/parsing/calclex.py b/ext/ply/test/pkg_test6/parsing/calclex.py
new file mode 100644
index 0000000..e8759b6
--- /dev/null
+++ b/ext/ply/test/pkg_test6/parsing/calclex.py
@@ -0,0 +1,48 @@
+# -----------------------------------------------------------------------------
+# calclex.py
+# -----------------------------------------------------------------------------
+
+import ply.lex as lex
+
+tokens = (
+    'NAME','NUMBER',
+    'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
+    'LPAREN','RPAREN',
+    )
+
+# Tokens
+
+t_PLUS    = r'\+'
+t_MINUS   = r'-'
+t_TIMES   = r'\*'
+t_DIVIDE  = r'/'
+t_EQUALS  = r'='
+t_LPAREN  = r'\('
+t_RPAREN  = r'\)'
+t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
+def t_NUMBER(t):
+    r'\d+'
+    try:
+        t.value = int(t.value)
+    except ValueError:
+        print("Integer value too large %s" % t.value)
+        t.value = 0
+    return t
+
+t_ignore = " \t"
+
+def t_newline(t):
+    r'\n+'
+    t.lexer.lineno += t.value.count("\n")
+    
+def t_error(t):
+    print("Illegal character '%s'" % t.value[0])
+    t.lexer.skip(1)
+    
+# Build the lexer
+import os.path
+lexer = lex.lex(optimize=True, outputdir=os.path.dirname(__file__))
+
+
+
diff --git a/ext/ply/test/pkg_test6/parsing/calcparse.py b/ext/ply/test/pkg_test6/parsing/calcparse.py
new file mode 100644
index 0000000..6defaf9
--- /dev/null
+++ b/ext/ply/test/pkg_test6/parsing/calcparse.py
@@ -0,0 +1,33 @@
+# -----------------------------------------------------------------------------
+# yacc_simple.py
+#
+# A simple, properly specifier grammar
+# -----------------------------------------------------------------------------
+
+from .calclex import tokens
+from ply import yacc
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+# dictionary of names
+names = { }
+
+from .statement import *
+
+from .expression import *
+
+def p_error(t):
+    print("Syntax error at '%s'" % t.value)
+
+import os.path
+parser = yacc.yacc(outputdir=os.path.dirname(__file__))
+
+
+
+
+
diff --git a/ext/ply/test/pkg_test6/parsing/expression.py b/ext/ply/test/pkg_test6/parsing/expression.py
new file mode 100644
index 0000000..028f662
--- /dev/null
+++ b/ext/ply/test/pkg_test6/parsing/expression.py
@@ -0,0 +1,31 @@
+# This file contains definitions of expression grammar
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_group(t):
+    'expression : LPAREN expression RPAREN'
+    t[0] = t[2]
+
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_expression_name(t):
+    'expression : NAME'
+    try:
+        t[0] = names[t[1]]
+    except LookupError:
+        print("Undefined name '%s'" % t[1])
+        t[0] = 0
diff --git a/ext/ply/test/pkg_test6/parsing/lextab.py b/ext/ply/test/pkg_test6/parsing/lextab.py
new file mode 100644
index 0000000..8cab298
--- /dev/null
+++ b/ext/ply/test/pkg_test6/parsing/lextab.py
@@ -0,0 +1,10 @@
+# lextab.py. This file automatically created by PLY (version 3.11). Don't edit!
+_tabversion   = '3.10'
+_lextokens    = set(('DIVIDE', 'EQUALS', 'LPAREN', 'MINUS', 'NAME', 'NUMBER', 'PLUS', 'RPAREN', 'TIMES'))
+_lexreflags   = 64
+_lexliterals  = ''
+_lexstateinfo = {'INITIAL': 'inclusive'}
+_lexstatere   = {'INITIAL': [('(?P<t_NUMBER>\\d+)|(?P<t_newline>\\n+)|(?P<t_NAME>[a-zA-Z_][a-zA-Z0-9_]*)|(?P<t_LPAREN>\\()|(?P<t_PLUS>\\+)|(?P<t_TIMES>\\*)|(?P<t_RPAREN>\\))|(?P<t_EQUALS>=)|(?P<t_DIVIDE>/)|(?P<t_MINUS>-)', [None, ('t_NUMBER', 'NUMBER'), ('t_newline', 'newline'), (None, 'NAME'), (None, 'LPAREN'), (None, 'PLUS'), (None, 'TIMES'), (None, 'RPAREN'), (None, 'EQUALS'), (None, 'DIVIDE'), (None, 'MINUS')])]}
+_lexstateignore = {'INITIAL': ' \t'}
+_lexstateerrorf = {'INITIAL': 't_error'}
+_lexstateeoff = {}
diff --git a/ext/ply/test/pkg_test6/parsing/statement.py b/ext/ply/test/pkg_test6/parsing/statement.py
new file mode 100644
index 0000000..ef7dc55
--- /dev/null
+++ b/ext/ply/test/pkg_test6/parsing/statement.py
@@ -0,0 +1,9 @@
+# This file contains definitions of statement grammar
+
+def p_statement_assign(t):
+    'statement : NAME EQUALS expression'
+    names[t[1]] = t[3]
+
+def p_statement_expr(t):
+    'statement : expression'
+    t[0] = t[1]
diff --git a/ext/ply/test/testcpp.py b/ext/ply/test/testcpp.py
new file mode 100644
index 0000000..2e98edd
--- /dev/null
+++ b/ext/ply/test/testcpp.py
@@ -0,0 +1,101 @@
+from unittest import TestCase, main
+
+from multiprocessing import Process, Queue
+from six.moves.queue import Empty
+
+import sys
+
+if ".." not in sys.path:
+    sys.path.insert(0, "..")
+
+from ply.lex import lex
+from ply.cpp import *
+
+
+def preprocessing(in_, out_queue):
+    out = None
+
+    try:
+        p = Preprocessor(lex())
+        p.parse(in_)
+        tokens = [t.value for t in p.parser]
+        out = "".join(tokens)
+    finally:
+        out_queue.put(out)
+
+class CPPTests(TestCase):
+    "Tests related to ANSI-C style lexical preprocessor."
+
+    def __test_preprocessing(self, in_, expected, time_limit = 1.0):
+        out_queue = Queue()
+
+        preprocessor = Process(
+            name = "PLY`s C preprocessor",
+            target = preprocessing,
+            args = (in_, out_queue)
+        )
+
+        preprocessor.start()
+
+        try:
+            out = out_queue.get(timeout = time_limit)
+        except Empty:
+            preprocessor.terminate()
+            raise RuntimeError("Time limit exceeded!")
+        else:
+            self.assertMultiLineEqual(out, expected)
+
+    def test_concatenation(self):
+        self.__test_preprocessing("""\
+#define a(x) x##_
+#define b(x) _##x
+#define c(x) _##x##_
+#define d(x,y) _##x##y##_
+
+a(i)
+b(j)
+c(k)
+d(q,s)"""
+            , """\
+
+
+
+
+
+i_
+_j
+_k_
+_qs_"""
+        )
+
+    def test_deadloop_macro(self):
+        # If there is a word which equals to name of a parametrized macro, then
+        # attempt to expand such word as a macro manages the parser to fall
+        # into an infinite loop.
+
+        self.__test_preprocessing("""\
+#define a(x) x
+
+a;"""
+            , """\
+
+
+a;"""
+        )
+
+    def test_index_error(self):
+        # If there are no tokens after a word ("a") which equals to name of
+        # a parameterized macro, then attempt to expand this word leads to
+        # IndexError.
+
+        self.__test_preprocessing("""\
+#define a(x) x
+
+a"""
+            , """\
+
+
+a"""
+        )
+
+main()
diff --git a/ext/ply/test/testlex.py b/ext/ply/test/testlex.py
index 606387d..83070a7 100755
--- a/ext/ply/test/testlex.py
+++ b/ext/ply/test/testlex.py
@@ -7,12 +7,57 @@
     import io as StringIO
 
 import sys
+import os
+import warnings
+import platform
+
 sys.path.insert(0,"..")
 sys.tracebacklimit = 0
 
 import ply.lex
 
-def check_expected(result,expected):
+try:
+    from importlib.util import cache_from_source
+except ImportError:
+    # Python 2.7, but we don't care.
+    cache_from_source = None
+
+
+def make_pymodule_path(filename, optimization=None):
+    path = os.path.dirname(filename)
+    file = os.path.basename(filename)
+    mod, ext = os.path.splitext(file)
+
+    if sys.hexversion >= 0x3050000:
+        fullpath = cache_from_source(filename, optimization=optimization)
+    elif sys.hexversion >= 0x3040000:
+        fullpath = cache_from_source(filename, ext=='.pyc')
+    elif sys.hexversion >= 0x3020000:
+        import imp
+        modname = mod+"."+imp.get_tag()+ext
+        fullpath = os.path.join(path,'__pycache__',modname)
+    else:
+        fullpath = filename
+    return fullpath
+
+def pymodule_out_exists(filename, optimization=None):
+    return os.path.exists(make_pymodule_path(filename,
+                                             optimization=optimization))
+
+def pymodule_out_remove(filename, optimization=None):
+    os.remove(make_pymodule_path(filename, optimization=optimization))
+
+def implementation():
+    if platform.system().startswith("Java"):
+        return "Jython"
+    elif hasattr(sys, "pypy_version_info"):
+        return "PyPy"
+    else:
+        return "CPython"
+
+test_pyo = (implementation() == 'CPython')
+
+def check_expected(result, expected, contains=False):
     if sys.version_info[0] >= 3:
         if isinstance(result,str):
             result = result.encode('ascii')
@@ -21,13 +66,16 @@
     resultlines = result.splitlines()
     expectedlines = expected.splitlines()
 
-
     if len(resultlines) != len(expectedlines):
         return False
 
     for rline,eline in zip(resultlines,expectedlines):
-        if not rline.endswith(eline):
-            return False
+        if contains:
+            if eline not in rline:
+                return False
+        else:
+            if not rline.endswith(eline):
+                return False
     return True
 
 def run_import(module):
@@ -40,6 +88,9 @@
     def setUp(self):
         sys.stderr = StringIO.StringIO()
         sys.stdout = StringIO.StringIO()
+        if sys.hexversion >= 0x3020000:
+            warnings.filterwarnings('ignore',category=ResourceWarning)
+
     def tearDown(self):
         sys.stderr = sys.__stderr__
         sys.stdout = sys.__stdout__
@@ -114,8 +165,13 @@
     def test_lex_re1(self):
         self.assertRaises(SyntaxError,run_import,"lex_re1")
         result = sys.stderr.getvalue()
+        if sys.hexversion < 0x3050000:
+            msg = "Invalid regular expression for rule 't_NUMBER'. unbalanced parenthesis\n"
+        else:
+            msg = "Invalid regular expression for rule 't_NUMBER'. missing ), unterminated subpattern at position 0"
         self.assert_(check_expected(result,
-                                    "Invalid regular expression for rule 't_NUMBER'. unbalanced parenthesis\n"))
+                                    msg,
+                                    contains=True))
 
     def test_lex_re2(self):
         self.assertRaises(SyntaxError,run_import,"lex_re2")
@@ -126,9 +182,19 @@
     def test_lex_re3(self):
         self.assertRaises(SyntaxError,run_import,"lex_re3")
         result = sys.stderr.getvalue()
+#        self.assert_(check_expected(result,
+#                                    "Invalid regular expression for rule 't_POUND'. unbalanced parenthesis\n"
+#                                    "Make sure '#' in rule 't_POUND' is escaped with '\\#'\n"))
+
+        if sys.hexversion < 0x3050000:
+            msg = ("Invalid regular expression for rule 't_POUND'. unbalanced parenthesis\n"
+                   "Make sure '#' in rule 't_POUND' is escaped with '\\#'\n")
+        else:
+            msg = ("Invalid regular expression for rule 't_POUND'. missing ), unterminated subpattern at position 0\n"
+                   "ERROR: Make sure '#' in rule 't_POUND' is escaped with '\#'")
         self.assert_(check_expected(result,
-                                    "Invalid regular expression for rule 't_POUND'. unbalanced parenthesis\n"
-                                    "Make sure '#' in rule 't_POUND' is escaped with '\\#'\n"))
+                                    msg,
+                                    contains=True), result)
 
     def test_lex_rule1(self):
         self.assertRaises(SyntaxError,run_import,"lex_rule1")
@@ -294,6 +360,7 @@
                                     "(NUMBER,3,1,0)\n"
                                     "(PLUS,'+',1,1)\n"
                                     "(NUMBER,4,1,2)\n"))
+
     def test_lex_optimize(self):
         try:
             os.remove("lextab.py")
@@ -316,7 +383,6 @@
                                     "(NUMBER,4,1,2)\n"))
         self.assert_(os.path.exists("lextab.py"))
 
-
         p = subprocess.Popen([sys.executable,'-O','lex_optimize.py'],
                              stdout=subprocess.PIPE)
         result = p.stdout.read()
@@ -325,9 +391,10 @@
                                     "(NUMBER,3,1,0)\n"
                                     "(PLUS,'+',1,1)\n"
                                     "(NUMBER,4,1,2)\n"))
-        self.assert_(os.path.exists("lextab.pyo"))
+        if test_pyo:
+            self.assert_(pymodule_out_exists("lextab.pyo", 1))
+            pymodule_out_remove("lextab.pyo", 1)
 
-        os.remove("lextab.pyo")
         p = subprocess.Popen([sys.executable,'-OO','lex_optimize.py'],
                              stdout=subprocess.PIPE)
         result = p.stdout.read()
@@ -335,17 +402,19 @@
                                     "(NUMBER,3,1,0)\n"
                                     "(PLUS,'+',1,1)\n"
                                     "(NUMBER,4,1,2)\n"))
-        self.assert_(os.path.exists("lextab.pyo"))
+
+        if test_pyo:
+            self.assert_(pymodule_out_exists("lextab.pyo", 2))
         try:
             os.remove("lextab.py")
         except OSError:
             pass
         try:
-            os.remove("lextab.pyc")
+            pymodule_out_remove("lextab.pyc")
         except OSError:
             pass
         try:
-            os.remove("lextab.pyo")
+            pymodule_out_remove("lextab.pyo", 2)
         except OSError:
             pass
 
@@ -377,8 +446,9 @@
                                     "(NUMBER,3,1,0)\n"
                                     "(PLUS,'+',1,1)\n"
                                     "(NUMBER,4,1,2)\n"))
-        self.assert_(os.path.exists("opt2tab.pyo"))
-        os.remove("opt2tab.pyo")
+        if test_pyo:
+            self.assert_(pymodule_out_exists("opt2tab.pyo", 1))
+            pymodule_out_remove("opt2tab.pyo", 1)
         p = subprocess.Popen([sys.executable,'-OO','lex_optimize2.py'],
                              stdout=subprocess.PIPE)
         result = p.stdout.read()
@@ -386,17 +456,18 @@
                                     "(NUMBER,3,1,0)\n"
                                     "(PLUS,'+',1,1)\n"
                                     "(NUMBER,4,1,2)\n"))
-        self.assert_(os.path.exists("opt2tab.pyo"))
+        if test_pyo:
+            self.assert_(pymodule_out_exists("opt2tab.pyo", 2))
         try:
             os.remove("opt2tab.py")
         except OSError:
             pass
         try:
-            os.remove("opt2tab.pyc")
+            pymodule_out_remove("opt2tab.pyc")
         except OSError:
             pass
         try:
-            os.remove("opt2tab.pyo")
+            pymodule_out_remove("opt2tab.pyo", 2)
         except OSError:
             pass
 
@@ -425,8 +496,10 @@
                                     "(NUMBER,3,1,0)\n"
                                     "(PLUS,'+',1,1)\n"
                                     "(NUMBER,4,1,2)\n"))
-        self.assert_(os.path.exists("lexdir/sub/calctab.pyo"))
-        os.remove("lexdir/sub/calctab.pyo")
+        if test_pyo:
+            self.assert_(pymodule_out_exists("lexdir/sub/calctab.pyo", 1))
+            pymodule_out_remove("lexdir/sub/calctab.pyo", 1)
+
         p = subprocess.Popen([sys.executable,'-OO','lex_optimize3.py'],
                              stdout=subprocess.PIPE)
         result = p.stdout.read()
@@ -434,12 +507,33 @@
                                     "(NUMBER,3,1,0)\n"
                                     "(PLUS,'+',1,1)\n"
                                     "(NUMBER,4,1,2)\n"))
-        self.assert_(os.path.exists("lexdir/sub/calctab.pyo"))
+        if test_pyo:
+            self.assert_(pymodule_out_exists("lexdir/sub/calctab.pyo", 2))
         try:
             shutil.rmtree("lexdir")
         except OSError:
             pass
 
+    def test_lex_optimize4(self):
+
+        # Regression test to make sure that reflags works correctly
+        # on Python 3.
+
+        for extension in ['py', 'pyc']:
+            try:
+                os.remove("opt4tab.{0}".format(extension))
+            except OSError:
+                pass
+
+        run_import("lex_optimize4")
+        run_import("lex_optimize4")
+
+        for extension in ['py', 'pyc']:
+            try:
+                os.remove("opt4tab.{0}".format(extension))
+            except OSError:
+                pass
+
     def test_lex_opt_alias(self):
         try:
             os.remove("aliastab.py")
@@ -468,8 +562,10 @@
                                     "(NUMBER,3,1,0)\n"
                                     "(+,'+',1,1)\n"
                                     "(NUMBER,4,1,2)\n"))
-        self.assert_(os.path.exists("aliastab.pyo"))
-        os.remove("aliastab.pyo")
+        if test_pyo:
+            self.assert_(pymodule_out_exists("aliastab.pyo", 1))
+            pymodule_out_remove("aliastab.pyo", 1)
+
         p = subprocess.Popen([sys.executable,'-OO','lex_opt_alias.py'],
                              stdout=subprocess.PIPE)
         result = p.stdout.read()
@@ -477,17 +573,19 @@
                                     "(NUMBER,3,1,0)\n"
                                     "(+,'+',1,1)\n"
                                     "(NUMBER,4,1,2)\n"))
-        self.assert_(os.path.exists("aliastab.pyo"))
+
+        if test_pyo:
+            self.assert_(pymodule_out_exists("aliastab.pyo", 2))
         try:
             os.remove("aliastab.py")
         except OSError:
             pass
         try:
-            os.remove("aliastab.pyc")
+            pymodule_out_remove("aliastab.pyc")
         except OSError:
             pass
         try:
-            os.remove("aliastab.pyo")
+            pymodule_out_remove("aliastab.pyo", 2)
         except OSError:
             pass
 
@@ -518,21 +616,22 @@
 
         self.assert_(os.path.exists("manytab.py"))
 
-        p = subprocess.Popen([sys.executable,'-O','lex_many_tokens.py'],
-                             stdout=subprocess.PIPE)
-        result = p.stdout.read()
-        self.assert_(check_expected(result,
-                                    "(TOK34,'TOK34:',1,0)\n"
-                                    "(TOK143,'TOK143:',1,7)\n"
-                                    "(TOK269,'TOK269:',1,15)\n"
-                                    "(TOK372,'TOK372:',1,23)\n"
-                                    "(TOK452,'TOK452:',1,31)\n"
-                                    "(TOK561,'TOK561:',1,39)\n"
-                                    "(TOK999,'TOK999:',1,47)\n"
-                                    ))
+        if implementation() == 'CPython':
+            p = subprocess.Popen([sys.executable,'-O','lex_many_tokens.py'],
+                                 stdout=subprocess.PIPE)
+            result = p.stdout.read()
+            self.assert_(check_expected(result,
+                                        "(TOK34,'TOK34:',1,0)\n"
+                                        "(TOK143,'TOK143:',1,7)\n"
+                                        "(TOK269,'TOK269:',1,15)\n"
+                                        "(TOK372,'TOK372:',1,23)\n"
+                                        "(TOK452,'TOK452:',1,31)\n"
+                                        "(TOK561,'TOK561:',1,39)\n"
+                                        "(TOK999,'TOK999:',1,47)\n"
+                                        ))
 
-        self.assert_(os.path.exists("manytab.pyo"))
-        os.remove("manytab.pyo")
+            self.assert_(pymodule_out_exists("manytab.pyo", 1))
+            pymodule_out_remove("manytab.pyo", 1)
         try:
             os.remove("manytab.py")
         except OSError:
diff --git a/ext/ply/test/testyacc.py b/ext/ply/test/testyacc.py
index cc53b6d..7e69f09 100644
--- a/ext/ply/test/testyacc.py
+++ b/ext/ply/test/testyacc.py
@@ -8,28 +8,68 @@
 
 import sys
 import os
+import warnings
+import re
+import platform
 
 sys.path.insert(0,"..")
 sys.tracebacklimit = 0
 
 import ply.yacc
 
-def check_expected(result,expected):
-    resultlines = []
+def make_pymodule_path(filename):
+    path = os.path.dirname(filename)
+    file = os.path.basename(filename)
+    mod, ext = os.path.splitext(file)
+
+    if sys.hexversion >= 0x3040000:
+        import importlib.util
+        fullpath = importlib.util.cache_from_source(filename, ext=='.pyc')
+    elif sys.hexversion >= 0x3020000:
+        import imp
+        modname = mod+"."+imp.get_tag()+ext
+        fullpath = os.path.join(path,'__pycache__',modname)
+    else:
+        fullpath = filename
+    return fullpath
+
+def pymodule_out_exists(filename):
+    return os.path.exists(make_pymodule_path(filename))
+
+def pymodule_out_remove(filename):
+    os.remove(make_pymodule_path(filename))
+
+def implementation():
+    if platform.system().startswith("Java"):
+        return "Jython"
+    elif hasattr(sys, "pypy_version_info"):
+        return "PyPy"
+    else:
+        return "CPython"
+
+# Check the output to see if it contains all of a set of expected output lines.
+# This alternate implementation looks weird, but is needed to properly handle
+# some variations in error message order that occurs due to dict hash table
+# randomization that was introduced in Python 3.3
+def check_expected(result, expected):
+    # Normalize 'state n' text to account for randomization effects in Python 3.3
+    expected = re.sub(r' state \d+', 'state <n>', expected)
+    result = re.sub(r' state \d+', 'state <n>', result)
+
+    resultlines = set()
     for line in result.splitlines():
         if line.startswith("WARNING: "):
             line = line[9:]
         elif line.startswith("ERROR: "):
             line = line[7:]
-        resultlines.append(line)
+        resultlines.add(line)
 
-    expectedlines = expected.splitlines()
-    if len(resultlines) != len(expectedlines):
-        return False
-    for rline,eline in zip(resultlines,expectedlines):
-        if not rline.endswith(eline):
-            return False
-    return True
+    # Selectively remove expected lines from the output
+    for eline in expected.splitlines():
+        resultlines = set(line for line in resultlines if not line.endswith(eline))
+
+    # Return True if no result lines remain
+    return not bool(resultlines)
 
 def run_import(module):
     code = "import "+module
@@ -43,10 +83,14 @@
         sys.stdout = StringIO.StringIO()
         try:
             os.remove("parsetab.py")
-            os.remove("parsetab.pyc")
+            pymodule_out_remove("parsetab.pyc")
         except OSError:
             pass
         
+        if sys.hexversion >= 0x3020000:
+            warnings.filterwarnings('ignore', category=ResourceWarning)
+        warnings.filterwarnings('ignore', category=DeprecationWarning)
+
     def tearDown(self):
         sys.stderr = sys.__stderr__
         sys.stdout = sys.__stdout__
@@ -148,7 +192,38 @@
         self.assert_(check_expected(result,
                                     "yacc_error4.py:62: Illegal rule name 'error'. Already defined as a token\n"
                                     ))
-        
+
+
+    def test_yacc_error5(self):
+        run_import("yacc_error5")
+        result = sys.stdout.getvalue()
+        self.assert_(check_expected(result,
+                                    "Group at 3:10 to 3:12\n"
+                                    "Undefined name 'a'\n"
+                                    "Syntax error at 'b'\n"
+                                    "Syntax error at 4:18 to 4:22\n"
+                                    "Assignment Error at 2:5 to 5:27\n"
+                                    "13\n"
+            ))
+
+    def test_yacc_error6(self):
+        run_import("yacc_error6")
+        result = sys.stdout.getvalue()
+        self.assert_(check_expected(result,
+                                    "a=7\n"
+                                    "Line 3: Syntax error at '*'\n"
+                                    "c=21\n"
+            ))
+
+    def test_yacc_error7(self):
+        run_import("yacc_error7")
+        result = sys.stdout.getvalue()
+        self.assert_(check_expected(result,
+                                    "a=7\n"
+                                    "Line 3: Syntax error at '*'\n"
+                                    "c=21\n"
+            ))
+
     def test_yacc_inf(self):
         self.assertRaises(ply.yacc.YaccError,run_import,"yacc_inf")
         result = sys.stderr.getvalue()
@@ -261,6 +336,7 @@
         self.assert_(check_expected(result,
                                     "Generating LALR tables\n"
                                     ))
+
     def test_yacc_sr(self):
         run_import("yacc_sr")
         result = sys.stderr.getvalue()
@@ -276,6 +352,13 @@
                                     "yacc_term1.py:24: Illegal rule name 'NUMBER'. Already defined as a token\n"
                                     ))
 
+    def test_yacc_unicode_literals(self):
+        run_import("yacc_unicode_literals")
+        result = sys.stderr.getvalue()
+        self.assert_(check_expected(result,
+                                    "Generating LALR tables\n"
+                                    ))
+
     def test_yacc_unused(self):
         self.assertRaises(ply.yacc.YaccError,run_import,"yacc_unused")
         result = sys.stderr.getvalue()
@@ -297,7 +380,6 @@
     def test_yacc_uprec(self):
         self.assertRaises(ply.yacc.YaccError,run_import,"yacc_uprec")
         result = sys.stderr.getvalue()
-        print repr(result)
         self.assert_(check_expected(result,
                                     "yacc_uprec.py:37: Nothing known about the precedence of 'UMINUS'\n"
                                     ))
@@ -319,6 +401,52 @@
                                     "Precedence rule 'left' defined for unknown symbol '/'\n"
                                     ))
 
+    def test_pkg_test1(self):
+        from pkg_test1 import parser
+        self.assertTrue(os.path.exists('pkg_test1/parsing/parsetab.py'))
+        self.assertTrue(os.path.exists('pkg_test1/parsing/lextab.py'))
+        self.assertTrue(os.path.exists('pkg_test1/parsing/parser.out'))
+        r = parser.parse('3+4+5')
+        self.assertEqual(r, 12)
 
-            
+    def test_pkg_test2(self):
+        from pkg_test2 import parser
+        self.assertTrue(os.path.exists('pkg_test2/parsing/calcparsetab.py'))
+        self.assertTrue(os.path.exists('pkg_test2/parsing/calclextab.py'))
+        self.assertTrue(os.path.exists('pkg_test2/parsing/parser.out'))
+        r = parser.parse('3+4+5')
+        self.assertEqual(r, 12)
+
+    def test_pkg_test3(self):
+        from pkg_test3 import parser
+        self.assertTrue(os.path.exists('pkg_test3/generated/parsetab.py'))
+        self.assertTrue(os.path.exists('pkg_test3/generated/lextab.py'))
+        self.assertTrue(os.path.exists('pkg_test3/generated/parser.out'))
+        r = parser.parse('3+4+5')
+        self.assertEqual(r, 12)
+
+    def test_pkg_test4(self):
+        from pkg_test4 import parser
+        self.assertFalse(os.path.exists('pkg_test4/parsing/parsetab.py'))
+        self.assertFalse(os.path.exists('pkg_test4/parsing/lextab.py'))
+        self.assertFalse(os.path.exists('pkg_test4/parsing/parser.out'))
+        r = parser.parse('3+4+5')
+        self.assertEqual(r, 12)
+
+    def test_pkg_test5(self):
+        from pkg_test5 import parser
+        self.assertTrue(os.path.exists('pkg_test5/parsing/parsetab.py'))
+        self.assertTrue(os.path.exists('pkg_test5/parsing/lextab.py'))
+        self.assertTrue(os.path.exists('pkg_test5/parsing/parser.out'))
+        r = parser.parse('3+4+5')
+        self.assertEqual(r, 12)
+
+    def test_pkg_test6(self):
+        from pkg_test6 import parser
+        self.assertTrue(os.path.exists('pkg_test6/parsing/parsetab.py'))
+        self.assertTrue(os.path.exists('pkg_test6/parsing/lextab.py'))
+        self.assertTrue(os.path.exists('pkg_test6/parsing/parser.out'))
+        r = parser.parse('3+4+5')
+        self.assertEqual(r, 12)
+
 unittest.main()
diff --git a/ext/ply/test/yacc_error5.py b/ext/ply/test/yacc_error5.py
new file mode 100644
index 0000000..9eb0f85
--- /dev/null
+++ b/ext/ply/test/yacc_error5.py
@@ -0,0 +1,94 @@
+# -----------------------------------------------------------------------------
+# yacc_error5.py
+#
+# Lineno and position tracking with error tokens
+# -----------------------------------------------------------------------------
+import sys
+
+if ".." not in sys.path: sys.path.insert(0,"..")
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+    'statement : NAME EQUALS expression'
+    names[t[1]] = t[3]
+
+def p_statement_assign_error(t):
+    'statement : NAME EQUALS error'
+    line_start, line_end = t.linespan(3)
+    pos_start, pos_end = t.lexspan(3)
+    print("Assignment Error at %d:%d to %d:%d" % (line_start,pos_start,line_end,pos_end))
+
+def p_statement_expr(t):
+    'statement : expression'
+    print(t[1])
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_group(t):
+    'expression : LPAREN expression RPAREN'
+    line_start, line_end = t.linespan(2)
+    pos_start, pos_end = t.lexspan(2)
+    print("Group at %d:%d to %d:%d" % (line_start,pos_start, line_end, pos_end))
+    t[0] = t[2]
+
+def p_expression_group_error(t):
+    'expression : LPAREN error RPAREN'
+    line_start, line_end = t.linespan(2)
+    pos_start, pos_end = t.lexspan(2)
+    print("Syntax error at %d:%d to %d:%d" % (line_start,pos_start, line_end, pos_end))
+    t[0] = 0
+    
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_expression_name(t):
+    'expression : NAME'
+    try:
+        t[0] = names[t[1]]
+    except LookupError:
+        print("Undefined name '%s'" % t[1])
+        t[0] = 0
+
+def p_error(t):
+    print("Syntax error at '%s'" % t.value)
+
+parser = yacc.yacc()
+import calclex
+calclex.lexer.lineno=1
+parser.parse("""
+a = 3 +
+(4*5) +
+(a b c) +
++ 6 + 7
+""", tracking=True)
+
+
+
+
+
+
diff --git a/ext/ply/test/yacc_error6.py b/ext/ply/test/yacc_error6.py
new file mode 100644
index 0000000..8d0ec85
--- /dev/null
+++ b/ext/ply/test/yacc_error6.py
@@ -0,0 +1,80 @@
+# -----------------------------------------------------------------------------
+# yacc_error6.py
+#
+# Panic mode recovery test
+# -----------------------------------------------------------------------------
+import sys
+
+if ".." not in sys.path: sys.path.insert(0,"..")
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+def p_statements(t):
+    'statements : statements statement'
+    pass
+
+def p_statements_1(t):
+    'statements : statement'
+    pass
+
+def p_statement_assign(p):
+    'statement : LPAREN NAME EQUALS expression RPAREN'
+    print("%s=%s" % (p[2],p[4]))
+
+def p_statement_expr(t):
+    'statement : LPAREN expression RPAREN'
+    print(t[1])
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_error(p):
+    if p:
+        print("Line %d: Syntax error at '%s'" % (p.lineno, p.value))
+    # Scan ahead looking for a name token
+    while True:
+        tok = parser.token()
+        if not tok or tok.type == 'RPAREN':
+            break
+    if tok:
+        parser.restart()
+    return None
+
+parser = yacc.yacc()
+import calclex
+calclex.lexer.lineno=1
+
+parser.parse("""
+(a = 3 + 4)
+(b = 4 + * 5 - 6 + *)
+(c = 10 + 11)
+""")
+
+
+
+
+
+
diff --git a/ext/ply/test/yacc_error7.py b/ext/ply/test/yacc_error7.py
new file mode 100644
index 0000000..fb131be
--- /dev/null
+++ b/ext/ply/test/yacc_error7.py
@@ -0,0 +1,80 @@
+# -----------------------------------------------------------------------------
+# yacc_error7.py
+#
+# Panic mode recovery test using deprecated functionality
+# -----------------------------------------------------------------------------
+import sys
+
+if ".." not in sys.path: sys.path.insert(0,"..")
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+def p_statements(t):
+    'statements : statements statement'
+    pass
+
+def p_statements_1(t):
+    'statements : statement'
+    pass
+
+def p_statement_assign(p):
+    'statement : LPAREN NAME EQUALS expression RPAREN'
+    print("%s=%s" % (p[2],p[4]))
+
+def p_statement_expr(t):
+    'statement : LPAREN expression RPAREN'
+    print(t[1])
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_error(p):
+    if p:
+        print("Line %d: Syntax error at '%s'" % (p.lineno, p.value))
+    # Scan ahead looking for a name token
+    while True:
+        tok = yacc.token()
+        if not tok or tok.type == 'RPAREN':
+            break
+    if tok:
+        yacc.restart()
+    return None
+
+parser = yacc.yacc()
+import calclex
+calclex.lexer.lineno=1
+
+parser.parse("""
+(a = 3 + 4)
+(b = 4 + * 5 - 6 + *)
+(c = 10 + 11)
+""")
+
+
+
+
+
+
diff --git a/ext/ply/test/yacc_prec1.py b/ext/ply/test/yacc_prec1.py
index 2ca6afc..99fcd90 100644
--- a/ext/ply/test/yacc_prec1.py
+++ b/ext/ply/test/yacc_prec1.py
@@ -12,8 +12,8 @@
 
 # Parsing rules
 precedence = (
-    ('left','+','-'),
-    ('left','*','/'),
+    ('left', '+', '-'),
+    ('left', '*', '/'),
     ('right','UMINUS'),
     )
 
diff --git a/ext/ply/test/yacc_unicode_literals.py b/ext/ply/test/yacc_unicode_literals.py
new file mode 100644
index 0000000..5ae4f5b
--- /dev/null
+++ b/ext/ply/test/yacc_unicode_literals.py
@@ -0,0 +1,70 @@
+# -----------------------------------------------------------------------------
+# yacc_unicode_literals
+#
+# Test for unicode literals on Python 2.x
+# -----------------------------------------------------------------------------
+from __future__ import unicode_literals
+
+import sys
+
+if ".." not in sys.path: sys.path.insert(0,"..")
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+    ('left','PLUS','MINUS'),
+    ('left','TIMES','DIVIDE'),
+    ('right','UMINUS'),
+    )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+    'statement : NAME EQUALS expression'
+    names[t[1]] = t[3]
+
+def p_statement_expr(t):
+    'statement : expression'
+    print(t[1])
+
+def p_expression_binop(t):
+    '''expression : expression PLUS expression
+                  | expression MINUS expression
+                  | expression TIMES expression
+                  | expression DIVIDE expression'''
+    if t[2] == '+'  : t[0] = t[1] + t[3]
+    elif t[2] == '-': t[0] = t[1] - t[3]
+    elif t[2] == '*': t[0] = t[1] * t[3]
+    elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+    'expression : MINUS expression %prec UMINUS'
+    t[0] = -t[2]
+
+def p_expression_group(t):
+    'expression : LPAREN expression RPAREN'
+    t[0] = t[2]
+
+def p_expression_number(t):
+    'expression : NUMBER'
+    t[0] = t[1]
+
+def p_expression_name(t):
+    'expression : NAME'
+    try:
+        t[0] = names[t[1]]
+    except LookupError:
+        print("Undefined name '%s'" % t[1])
+        t[0] = 0
+
+def p_error(t):
+    print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/ext/softfloat/SConscript b/ext/softfloat/SConscript
index f08a022..689cbcf 100644
--- a/ext/softfloat/SConscript
+++ b/ext/softfloat/SConscript
@@ -31,19 +31,19 @@
 
 import os
 
-Import('main')
+Import('env')
 
-env = main.Clone()
-if env['GCC']:
-    env.Append(CCFLAGS=['-Wno-unused-variable',
-                        '-Wno-unused-label',
-                        '-Wno-implicit-fallthrough',
-                        '-g'])
+sf_env = env.Clone()
+if sf_env['GCC']:
+    sf_env.Append(CCFLAGS=['-Wno-unused-variable',
+                           '-Wno-unused-label',
+                           '-Wno-implicit-fallthrough',
+                           '-g'])
 
-elif env['CLANG']:
-    env.Append(CCFLAGS=['-Wno-unused-variable',
-                        '-Wno-unused-label',
-                        '-g'])
+elif sf_env['CLANG']:
+    sf_env.Append(CCFLAGS=['-Wno-unused-variable',
+                           '-Wno-unused-label',
+                           '-g'])
 
 # Add the appropriate files for the library
 softfloat_files = []
@@ -80,6 +80,7 @@
 SoftfloatFile('f128_to_ui64.c')
 SoftfloatFile('f128_to_ui64_r_minMag.c')
 SoftfloatFile('f16_add.c')
+SoftfloatFile('f16_classify.c')
 SoftfloatFile('f16_div.c')
 SoftfloatFile('f16_eq.c')
 SoftfloatFile('f16_eq_signaling.c')
@@ -271,8 +272,8 @@
 SoftfloatFile('ui64_to_f32.c')
 SoftfloatFile('ui64_to_f64.c')
 
-env.Library('softfloat', [env.SharedObject(f) for f in softfloat_files])
+sf_env.Library('softfloat', [sf_env.SharedObject(f) for f in softfloat_files])
 
-main.Prepend(CPPPATH=Dir('./'))
-main.Append(LIBS=['softfloat'])
-main.Prepend(LIBPATH=[Dir('.')])
+env.Prepend(CPPPATH=Dir('./'))
+env.Append(LIBS=['softfloat'])
+env.Prepend(LIBPATH=[Dir('.')])
diff --git a/ext/softfloat/f16_classify.c b/ext/softfloat/f16_classify.c
new file mode 100644
index 0000000..9402ff1
--- /dev/null
+++ b/ext/softfloat/f16_classify.c
@@ -0,0 +1,36 @@
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "platform.h"
+#include "internals.h"
+#include "specialize.h"
+#include "softfloat.h"
+
+uint_fast16_t f16_classify( float16_t a )
+{
+    union ui16_f16 uA;
+    uint_fast16_t uiA;
+
+    uA.f = a;
+    uiA = uA.ui;
+
+    uint_fast16_t infOrNaN = expF16UI( uiA ) == 0x1F;
+    uint_fast16_t subnormalOrZero = expF16UI( uiA ) == 0;
+    bool sign = signF16UI( uiA );
+    bool fracZero = fracF16UI( uiA ) == 0;
+    bool isNaN = isNaNF16UI( uiA );
+    bool isSNaN = softfloat_isSigNaNF16UI( uiA );
+
+    return
+        (  sign && infOrNaN && fracZero )          << 0 |
+        (  sign && !infOrNaN && !subnormalOrZero ) << 1 |
+        (  sign && subnormalOrZero && !fracZero )  << 2 |
+        (  sign && subnormalOrZero && fracZero )   << 3 |
+        ( !sign && infOrNaN && fracZero )          << 7 |
+        ( !sign && !infOrNaN && !subnormalOrZero ) << 6 |
+        ( !sign && subnormalOrZero && !fracZero )  << 5 |
+        ( !sign && subnormalOrZero && fracZero )   << 4 |
+        ( isNaN &&  isSNaN )                       << 8 |
+        ( isNaN && !isSNaN )                       << 9;
+}
+
diff --git a/ext/softfloat/softfloat.h b/ext/softfloat/softfloat.h
index 71592ae..41cacbc 100644
--- a/ext/softfloat/softfloat.h
+++ b/ext/softfloat/softfloat.h
@@ -173,6 +173,7 @@
 bool f16_le_quiet( float16_t, float16_t );
 bool f16_lt_quiet( float16_t, float16_t );
 bool f16_isSignalingNaN( float16_t );
+uint_fast16_t f16_classify( float16_t );
 
 /*----------------------------------------------------------------------------
 | 32-bit (single-precision) floating-point operations.
diff --git a/ext/softfloat/softfloat.mk.in b/ext/softfloat/softfloat.mk.in
index 77c1357..7cfe960 100644
--- a/ext/softfloat/softfloat.mk.in
+++ b/ext/softfloat/softfloat.mk.in
@@ -37,6 +37,7 @@
 	f128_to_ui64.c \
 	f128_to_ui64_r_minMag.c \
 	f16_add.c \
+	f16_classify.c \
 	f16_div.c \
 	f16_eq.c \
 	f16_eq_signaling.c \
diff --git a/ext/systemc/SConscript b/ext/systemc/SConscript
index d0cb6f8..89ef920 100644
--- a/ext/systemc/SConscript
+++ b/ext/systemc/SConscript
@@ -26,8 +26,8 @@
 import os
 from m5.util.terminal import get_termcap
 
-Import('main')
-systemc = main.Clone()
+Import('env')
+systemc = env.Clone()
 
 build_root = Dir('.').abspath
 src_root = Dir('.').srcdir.abspath
diff --git a/ext/testlib/configuration.py b/ext/testlib/configuration.py
index d0fca74..cc40b0d 100644
--- a/ext/testlib/configuration.py
+++ b/ext/testlib/configuration.py
@@ -213,7 +213,7 @@
                                                       os.pardir,
                                                       os.pardir))
     defaults.result_path = os.path.join(os.getcwd(), 'testing-results')
-    defaults.resource_url = 'http://dist.gem5.org/dist/v21-2'
+    defaults.resource_url = 'http://dist.gem5.org/dist/v22-0'
     defaults.resource_path = os.path.abspath(os.path.join(defaults.base_dir,
                                             'tests',
                                             'gem5',
@@ -233,12 +233,14 @@
     constants.isa_tag_type = 'isa'
     constants.x86_tag = 'X86'
     constants.gcn3_x86_tag = 'GCN3_X86'
+    constants.vega_x86_tag = 'VEGA_X86'
     constants.sparc_tag = 'SPARC'
     constants.riscv_tag = 'RISCV'
     constants.arm_tag = 'ARM'
     constants.mips_tag = 'MIPS'
     constants.power_tag = 'POWER'
     constants.null_tag = 'NULL'
+    constants.all_compiled_tag = 'ALL'
 
     constants.variant_tag_type = 'variant'
     constants.opt_tag = 'opt'
@@ -254,16 +256,20 @@
     constants.host_x86_64_tag = 'x86_64'
     constants.host_arm_tag = 'aarch64'
 
+    constants.kvm_tag = 'kvm'
+
     constants.supported_tags = {
         constants.isa_tag_type : (
             constants.x86_tag,
             constants.gcn3_x86_tag,
+            constants.vega_x86_tag,
             constants.sparc_tag,
             constants.riscv_tag,
             constants.arm_tag,
             constants.mips_tag,
             constants.power_tag,
             constants.null_tag,
+            constants.all_compiled_tag,
             ),
         constants.variant_tag_type: (
             constants.opt_tag,
@@ -287,11 +293,13 @@
         constants.arm_tag   : (constants.host_arm_tag,),
         constants.x86_tag   : (constants.host_x86_64_tag,),
         constants.gcn3_x86_tag : (constants.host_x86_64_tag,),
+        constants.vega_x86_tag : (constants.host_x86_64_tag,),
         constants.sparc_tag : (constants.host_x86_64_tag,),
         constants.riscv_tag : (constants.host_x86_64_tag,),
         constants.mips_tag  : (constants.host_x86_64_tag,),
         constants.power_tag : (constants.host_x86_64_tag,),
-        constants.null_tag  : (None,)
+        constants.null_tag  : (None,),
+        constants.all_compiled_tag: (None,),
     }
 
     constants.supported_isas = constants.supported_tags['isa']
diff --git a/ext/testlib/helper.py b/ext/testlib/helper.py
index 1cb13f0..ed6e325 100644
--- a/ext/testlib/helper.py
+++ b/ext/testlib/helper.py
@@ -41,7 +41,8 @@
 '''
 Helper classes for writing tests with this test library.
 '''
-from collections import MutableSet, namedtuple
+from collections import namedtuple
+from collections.abc import MutableSet
 
 import difflib
 import errno
diff --git a/ext/testlib/main.py b/ext/testlib/main.py
index 6087a8e..b9d8e93 100644
--- a/ext/testlib/main.py
+++ b/ext/testlib/main.py
@@ -125,7 +125,7 @@
     if tags is None:
         tags = tuple()
 
-    filters = list(itertools.chain(tags, final_tags))
+    filters = list(itertools.chain(final_tags, tags))
     string = 'Filtering suites with tags as follows:\n'
     filter_string = '\t\n'.join((str(f) for f in filters))
     log.test_log.trace(string + filter_string)
diff --git a/site_scons/gem5_python_paths.py b/site_scons/gem5_python_paths.py
index 2833a4c..2e51e2f 100644
--- a/site_scons/gem5_python_paths.py
+++ b/site_scons/gem5_python_paths.py
@@ -45,6 +45,7 @@
 extra_python_nodes = [
     root.Dir('src').Dir('python').srcnode(), # gem5 includes
     root.Dir('ext').Dir('ply').srcnode(), # ply is used by several files
+    root.Dir('ext').Dir('Kconfiglib').Dir('import').srcnode(), # kconfiglib
 ]
 
 extra_python_paths = [ node.abspath for node in extra_python_nodes ]
diff --git a/site_scons/gem5_scons/builders/config_file.py b/site_scons/gem5_scons/builders/config_file.py
index 458c731..85820b9 100755
--- a/site_scons/gem5_scons/builders/config_file.py
+++ b/site_scons/gem5_scons/builders/config_file.py
@@ -67,10 +67,10 @@
         variable = str(target[0])
         # True target is config header file
         target = env.Dir('config').File(variable.lower() + '.hh')
-        val = env[variable]
+        val = env['CONF'][variable]
         if isinstance(val, bool):
             # Force value to 0/1
-            val = int(val)
+            val = str(int(val))
         elif isinstance(val, str):
             val = '"' + val + '"'
 
diff --git a/site_scons/gem5_scons/configure.py b/site_scons/gem5_scons/configure.py
index 24a4a3d..53ee14a 100644
--- a/site_scons/gem5_scons/configure.py
+++ b/site_scons/gem5_scons/configure.py
@@ -144,9 +144,9 @@
 @contextlib.contextmanager
 def Configure(env, *args, **kwargs):
     kwargs.setdefault('conf_dir',
-            os.path.join(env['BUILDROOT'], '.scons_config'))
+            os.path.join(env['GEM5BUILD'], 'scons_config'))
     kwargs.setdefault('log_file',
-            os.path.join(env['BUILDROOT'], 'scons_config.log'))
+            os.path.join(env['GEM5BUILD'], 'scons_config.log'))
     kwargs.setdefault('custom_tests', {})
     kwargs['custom_tests'].update({
             'CheckCxxFlag' : CheckCxxFlag,
diff --git a/site_scons/gem5_scons/defaults.py b/site_scons/gem5_scons/defaults.py
index 1f8d5d5..7a24589 100644
--- a/site_scons/gem5_scons/defaults.py
+++ b/site_scons/gem5_scons/defaults.py
@@ -46,7 +46,9 @@
     # export TERM so that clang reports errors in color
     use_vars = set([ 'AS', 'AR', 'CC', 'CXX', 'HOME', 'LD_LIBRARY_PATH',
                      'LIBRARY_PATH', 'PATH', 'PKG_CONFIG_PATH', 'PROTOC',
-                     'PYTHONPATH', 'RANLIB', 'TERM' ])
+                     'PYTHONPATH', 'RANLIB', 'TERM', 'PYTHON_CONFIG',
+                     'CCFLAGS_EXTRA', 'GEM5PY_CCFLAGS_EXTRA',
+                     'GEM5PY_LINKFLAGS_EXTRA', 'LINKFLAGS_EXTRA', 'LANG'])
 
     use_prefixes = [
         "ASAN_",           # address sanitizer symbolizer path and settings
@@ -63,6 +65,21 @@
                 any([key.startswith(prefix) for prefix in use_prefixes]):
             env['ENV'][key] = val
 
+    # These variables from the environment override/become SCons variables,
+    # with a default if they weren't in the host environment.
+    var_overrides = {
+        'CC': env['CC'],
+        'CXX': env['CXX'],
+        'PROTOC': 'protoc',
+        'PYTHON_CONFIG': [ 'python3-config', 'python-config' ],
+        'CCFLAGS_EXTRA': '',
+        'GEM5PY_CCFLAGS_EXTRA': '',
+        'GEM5PY_LINKFLAGS_EXTRA': '',
+        'LINKFLAGS_EXTRA': '',
+    }
+    for key,default in var_overrides.items():
+        env[key] = env['ENV'].get(key, default)
+
     # Tell scons to avoid implicit command dependencies to avoid issues
     # with the param wrappes being compiled twice (see
     # https://github.com/SCons/scons/issues/2811
diff --git a/site_scons/gem5_scons/sources.py b/site_scons/gem5_scons/sources.py
index 7a2a641..85b0b4e 100644
--- a/site_scons/gem5_scons/sources.py
+++ b/site_scons/gem5_scons/sources.py
@@ -188,13 +188,13 @@
         super(SourceMeta, cls).__init__(name, bases, dict)
         cls.all = SourceList()
 
-class SourceFile(object, metaclass=SourceMeta):
-    '''Base object that encapsulates the notion of a source file.
-    This includes, the source node, target node, various manipulations
-    of those.  A source file also specifies a set of tags which
-    describing arbitrary properties of the source file.'''
-
+class SourceItem(object, metaclass=SourceMeta):
+    '''Base object that encapsulates the notion of a source component for
+    gem5. This specifies a set of tags which help group components into groups
+    based on arbitrary properties.'''
     def __init__(self, source, tags=None, add_tags=None, append=None):
+        self.source = source
+
         if tags is None:
             tags='gem5 lib'
         if isinstance(tags, str):
@@ -212,16 +212,24 @@
 
         self.append = append
 
+        for base in type(self).__mro__:
+            if issubclass(base, SourceItem):
+                base.all.append(self)
+
+class SourceFile(SourceItem):
+    '''Base object that encapsulates the notion of a source file.
+    This includes, the source node, target node, various manipulations
+    of those.'''
+
+    def __init__(self, source, tags=None, add_tags=None, append=None):
+        super().__init__(source, tags=tags, add_tags=add_tags, append=append)
+
         tnode = SCons.Script.File(source)
 
         self.tnode = tnode
         self.filename = str(self.tnode)
         self.snode = tnode.srcnode()
 
-        for base in type(self).__mro__:
-            if issubclass(base, SourceFile):
-                base.all.append(self)
-
     def static(self, env):
         if self.append:
             env = env.Clone()
@@ -234,6 +242,7 @@
             env.Append(**self.append)
         return env.SharedObject(self.tnode)
 
+
 __all__ = ['TagImpliesTool', 'SourceFilter', 'SourceList', 'SourceFile',
-           'with_any_tags', 'with_all_tags', 'with_tag', 'without_tags',
-           'without_tag']
+           'SourceItem', 'with_any_tags', 'with_all_tags', 'with_tag',
+           'without_tags', 'without_tag']
diff --git a/site_scons/site_init.py b/site_scons/site_init.py
index 6a03d84..5eeb290 100644
--- a/site_scons/site_init.py
+++ b/site_scons/site_init.py
@@ -63,7 +63,7 @@
 *Step 1*: ensure Python 3 is installed. On Ubuntu like systems, you can try \
 this command:
 
-    sudo apt-get install python3 python3-six python-is-python3 python3-pydot
+    sudo apt-get install python3 python-is-python3 python3-pydot
 
 To run Python 3 from a container, you can try the Docker files in \
 util/dockerfiles folder.
diff --git a/src/Doxyfile b/src/Doxyfile
index 6a3889a..9edf43f 100644
--- a/src/Doxyfile
+++ b/src/Doxyfile
@@ -31,7 +31,7 @@
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = v21.2.1.1
+PROJECT_NUMBER         = v22.0.0.0
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
 # base path where the generated documentation will be put. 
diff --git a/src/SConscript b/src/SConscript
index e40262a..2ddf4bf 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -37,13 +37,10 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-import bisect
 import collections
+import copy
 import distutils.spawn
-import importlib
-import importlib.abc
-import importlib.machinery
-import importlib.util
+import itertools
 import os
 import os.path
 import re
@@ -61,10 +58,13 @@
 
 Import('*')
 
+if env['CONF']['USE_EFENCE']:
+    env.Append(LIBS=['efence'])
+
 # Children need to see the environment
 Export('env')
 
-build_env = [(opt, env[opt]) for opt in export_vars]
+build_env = list(env['CONF'].items())
 
 from code_formatter import code_formatter
 
@@ -75,6 +75,11 @@
 class Source(SourceFile):
     pass
 
+class SourceLib(SourceItem):
+    def __init__(self, *args, **kwargs):
+        self.priority = kwargs.pop('priority', 0)
+        super().__init__(*args, **kwargs)
+
 build_tools = Dir('#build_tools')
 
 # Build a small helper that runs Python code using the same version of Python
@@ -93,8 +98,6 @@
 
 class PySource(SourceFile):
     '''Add a python source file to the named package'''
-    modules = {}
-
     def __init__(self, package, source, tags=None, add_tags=None):
         '''specify the python package, the source file, and any tags'''
         super().__init__(source, tags, add_tags)
@@ -116,11 +119,7 @@
         if not os.path.exists(abspath):
             abspath = self.tnode.abspath
 
-        self.modname = modname
         self.modpath = modpath
-        self.abspath = abspath
-
-        PySource.modules[modpath] = self
 
         cpp = File(self.filename + '.cc')
 
@@ -143,12 +142,6 @@
     '''Add a SimObject python file as a python source object and add
     it to a list of sim object modules'''
 
-    fixed = False
-
-    sim_objects = dict()
-    enums = dict()
-    tags = dict()
-
     def __init__(self, source, *, sim_objects=None, enums=None,
             tags=None, add_tags=None):
         '''Specify the source file and any tags (automatically in
@@ -162,34 +155,104 @@
             enums = []
 
         super().__init__('m5.objects', source, tags, add_tags)
-        if self.fixed:
-            error("Too late to call SimObject now.")
 
-        SimObject.sim_objects[self.modpath] = sim_objects
-        SimObject.enums[self.modpath] = enums
-        SimObject.tags[self.modpath] = self.tags
+        build_dir = Dir(env['BUILDDIR'])
+        module = self.modpath
+
+        # Generate all of the SimObject param C++ files.
+        for simobj in sim_objects:
+            # Some helper functions
+            srcs = [ Value(module), Value(simobj),
+                    "${GEM5PY_M5}", "${PYSCRIPT}" ]
+            def cmdline(*args):
+                all_args = [ 'GEM5PY_M5', 'PYSCRIPT', 'MODULE' ] + list(args)
+                return ' '.join(list('"${%s}"' % arg for arg in all_args))
+
+            # Params header.
+            gem5py_env.Command([ "${PARAMS_HH}" ], srcs,
+                    MakeAction(cmdline('PARAMS_HH'), Transform("SO Param", 2)),
+                    MODULE=module,
+                    SIMOBJ=simobj,
+                    PYSCRIPT=build_tools.File('sim_object_param_struct_hh.py'),
+                    PARAMS_HH=build_dir.File(f'params/{simobj}.hh'))
+
+            # Params cc.
+            cc_file = build_dir.File(f'python/_m5/param_{simobj}.cc')
+            gem5py_env.Command([ "${PARAMS_CC}" ], srcs,
+                    MakeAction(cmdline('PARAMS_CC', 'USE_PYTHON'),
+                        Transform("SO Param", 2)),
+                    PYSCRIPT=build_tools.File('sim_object_param_struct_cc.py'),
+                    MODULE=module,
+                    SIMOBJ=simobj,
+                    PARAMS_CC=cc_file,
+                    USE_PYTHON=env['USE_PYTHON'])
+            Source(cc_file, tags=self.tags,
+                   add_tags=('python' if env['USE_PYTHON'] else None))
+
+            # CXX config header.
+            gem5py_env.Command([ "${CXXCONFIG_HH}" ], srcs,
+                    MakeAction(cmdline('CXXCONFIG_HH'),
+                        Transform("CXXCPRHH", 2)),
+                    PYSCRIPT=build_tools.File('cxx_config_hh.py'),
+                    MODULE=module,
+                    CXXCONFIG_HH=build_dir.File(f'cxx_config/{simobj}.hh'))
+
+            # CXX config cc.
+            cc_file=build_dir.File(f'cxx_config/{simobj}.cc')
+            gem5py_env.Command([ "${CXXCONFIG_CC}" ], srcs,
+                    MakeAction(cmdline('CXXCONFIG_CC'),
+                        Transform("CXXCPRCC", 2)),
+                    PYSCRIPT=build_tools.File('cxx_config_cc.py'),
+                    MODULE=module,
+                    CXXCONFIG_CC=cc_file)
+            if GetOption('with_cxx_config'):
+                Source(cc_file, tags=self.tags)
+
+        # C++ versions of enum params.
+        for enum in enums:
+            gem5py_env.Command([ "${ENUM_HH}" ],
+                    [ Value(module), Value(enum),
+                        "${GEM5PY_M5}", "${ENUMHH_PY}" ],
+                    MakeAction('"${GEM5PY_M5}" "${ENUMHH_PY}" "${MODULE}" ' \
+                            '"${ENUM_HH}"',
+                        Transform("ENUMDECL", 2)),
+                    MODULE=module,
+                    ENUM=enum,
+                    ENUM_HH=build_dir.File(f'enums/{enum}.hh'),
+                    ENUMHH_PY=build_tools.File('enum_hh.py'))
+            cc_file = build_dir.File(f'enums/{enum}.cc')
+            gem5py_env.Command([ "${ENUM_CC}" ],
+                    [ Value(module), Value(enum),
+                        "${GEM5PY_M5}", "${ENUMCC_PY}" ],
+                    MakeAction('"${GEM5PY_M5}" "${ENUMCC_PY}" "${MODULE}" ' \
+                            '"${ENUM_CC}" "${USE_PYTHON}"',
+                        Transform("ENUM STR", 2)),
+                    MODULE=module,
+                    ENUM=enum,
+                    ENUM_CC=cc_file,
+                    ENUMCC_PY=build_tools.File('enum_cc.py'),
+                    USE_PYTHON=env['USE_PYTHON'])
+            Source(cc_file, tags=self.tags,
+                   add_tags=('python' if env['USE_PYTHON'] else None))
 
 # This regular expression is simplistic and assumes that the import takes up
 # the entire line, doesn't have the keyword "public", uses double quotes, has
 # no whitespace at the end before or after the ;, and is all on one line. This
 # should still cover most cases, and a completely accurate scanner would be
 # MUCH more complex.
-protoc_import_re = re.compile(r'^import\s+\"(.*\.proto)\"\;$', re.M)
-
-def protoc_scanner(node, env, path):
-    deps = []
-    for imp in protoc_import_re.findall(node.get_text_contents()):
-        deps.append(Dir(env['BUILDDIR']).File(imp))
-    return deps
-
-env.Append(SCANNERS=Scanner(function=protoc_scanner, skeys=['.proto']))
+protoc_scanner = SCons.Scanner.Classic(name='ProtobufScanner',
+                                       suffixes=['.proto'],
+                                       path_variable='BUILDDIR',
+                                       regex=r'^import\s+\"(.*\.proto)\"\;$')
+env.Append(SCANNERS=protoc_scanner)
 
 def protoc_emitter(target, source, env):
     root, ext = os.path.splitext(source[0].get_abspath())
     return [root + '.pb.cc', root + '.pb.h'], source
 
 protoc_action = MakeAction('${PROTOC} --cpp_out ${BUILDDIR} '
-        '--proto_path ${BUILDDIR} ${SOURCE.get_abspath()}',
+        '--proto_path ${BUILDDIR} --proto_path ${SOURCE.dir} '
+        '${SOURCE.get_abspath()}',
         Transform("PROTOC"))
 protobuf_builder = Builder(action=protoc_action, emitter=protoc_emitter,
         src_suffix='.proto')
@@ -201,11 +264,6 @@
 
 def ProtoBuf(source, tags=None, add_tags=None):
     '''Add a Protocol Buffer to build'''
-
-    if not env['HAVE_PROTOC'] or not env['HAVE_PROTOBUF']:
-        error('Got protobuf to build, but lacks support!')
-
-    '''Specify the source file, and any tags'''
     Source(source, tags, add_tags, append={'CXXFLAGS': '-Wno-array-bounds'})
 
 env['PROTOC_GRPC'] = distutils.spawn.find_executable('grpc_cpp_plugin')
@@ -259,11 +317,15 @@
         self.target = target
 
         isFilter = lambda arg: isinstance(arg, SourceFilter)
-        self.filters = filter(isFilter, srcs_and_filts)
-        sources = filter(lambda a: not isFilter(a), srcs_and_filts)
+        isLib = lambda arg: isinstance(arg, SourceLib)
+        # If something isn't a library or filter, assume it's a source file.
+        isSourceFile = lambda arg: not isFilter(arg) and not isLib(arg)
+        self.filters = list(filter(isFilter, srcs_and_filts))
+        self.sourceLibs = list(filter(isLib, srcs_and_filts))
+        source_files = list(filter(isSourceFile, srcs_and_filts))
 
         srcs = SourceList()
-        for src in sources:
+        for src in source_files:
             if not isinstance(src, SourceFile):
                 src = Source(src, tags=[])
             srcs.append(src)
@@ -272,11 +334,17 @@
         self.dir = Dir('.')
 
     def sources(self, env):
-        srcs = self.srcs
+        srcs = copy.copy(self.srcs)
         for f in self.filters:
             srcs += Source.all.apply_filter(env, f)
         return srcs
 
+    def libs(self, env):
+        libs = copy.copy(self.sourceLibs)
+        for f in self.filters:
+            libs += SourceLib.all.apply_filter(env, f)
+        return libs
+
     def srcs_to_objs(self, env, sources):
         return list([ s.static(env) for s in sources ])
 
@@ -323,6 +391,12 @@
         env['BIN_RPATH_PREFIX'] = os.path.relpath(
                 env['BUILDDIR'], self.path(env).dir.abspath)
 
+        libs = self.libs(env)
+        # Higher priority libraries should be earlier in the list.
+        libs.sort(key=lambda l: l.priority, reverse=True)
+        if libs:
+            env.Append(LIBS=list(lib.source for lib in libs))
+
         executable = env.Program(self.path(env).abspath, objs)[0]
 
         if sys.platform == 'sunos5':
@@ -380,6 +454,7 @@
 # Children should have access
 Export('GdbXml')
 Export('Source')
+Export('SourceLib')
 Export('PySource')
 Export('SimObject')
 Export('ProtoBuf')
@@ -481,12 +556,12 @@
             build_dir = os.path.join(env['BUILDDIR'], root[prefix_len:])
             SConscript(os.path.join(root, 'SConscript'), variant_dir=build_dir)
 
-for opt in export_vars:
+for opt in env['CONF'].keys():
     env.ConfigFile(opt)
 
 def makeTheISA(source, target, env):
     isas = sorted(set(env.Split('${ALL_ISAS}')))
-    target_isa = env['TARGET_ISA']
+    target_isa = env['CONF']['TARGET_ISA']
     is_null_isa = '1' if (target_isa.lower() == 'null') else '0'
 
     def namespace(isa):
@@ -509,9 +584,12 @@
             MakeAction(makeTheISA, Transform("CFG ISA", 0)))
 
 def makeTheGPUISA(source, target, env):
-    gpu_isa = env['TARGET_GPU_ISA']
+    gpu_isa = env['CONF']['TARGET_GPU_ISA']
 
-    namespace = gpu_isa[0].upper() + gpu_isa[1:].lower() + 'ISA'
+    if gpu_isa:
+        namespace = gpu_isa[0].upper() + gpu_isa[1:].lower() + 'ISA'
+    else:
+        namespace = 'None'
 
     code = code_formatter()
     code('''\
@@ -524,112 +602,6 @@
 env.Command('config/the_gpu_isa.hh', [],
             MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
 
-########################################################################
-#
-# Prevent any SimObjects from being added after this point, they
-# should all have been added in the SConscripts above
-#
-SimObject.fixed = True
-
-class SimpleModuleLoader(importlib.abc.Loader):
-    '''A simple wrapper which delegates setting up a module to a function.'''
-    def __init__(self, executor):
-        super().__init__()
-        self.executor = executor
-    def create_module(self, spec):
-        return None
-
-    def exec_module(self, module):
-        self.executor(module)
-
-class M5MetaPathFinder(importlib.abc.MetaPathFinder):
-    def __init__(self, modules):
-        super().__init__()
-        self.modules = modules
-        self.installed = set()
-
-    def unload(self):
-        import sys
-        for module in self.installed:
-            del sys.modules[module]
-        self.installed = set()
-
-    def find_spec(self, fullname, path, target=None):
-        spec = None
-
-        # If this isn't even in the m5 package, ignore it.
-        if fullname.startswith('m5.'):
-            if fullname.startswith('m5.objects'):
-                # When imported in this context, return a spec for a dummy
-                # package which just serves to house the modules within it.
-                # This is subtley different from "import * from m5.objects"
-                # which relies on the __init__.py in m5.objects. That in turn
-                # indirectly relies on the c++ based _m5 package which doesn't
-                # exist yet.
-                if fullname == 'm5.objects':
-                    dummy_loader = SimpleModuleLoader(lambda x: None)
-                    spec = importlib.machinery.ModuleSpec(
-                            name=fullname, loader=dummy_loader,
-                            is_package=True)
-                    spec.loader_state = self.modules.keys()
-
-                # If this is a module within the m5.objects package, return a
-                # spec that maps to its source file.
-                elif fullname in self.modules:
-                    source = self.modules[fullname]
-                    spec = importlib.util.spec_from_file_location(
-                            name=fullname, location=source.abspath)
-
-            # The artificial m5.defines subpackage.
-            elif fullname == 'm5.defines':
-                def build_m5_defines(module):
-                    module.__dict__['buildEnv'] = dict(build_env)
-
-                spec = importlib.util.spec_from_loader(name=fullname,
-                        loader=SimpleModuleLoader(build_m5_defines))
-
-        # If we're handling this module, write it down so we can unload it
-        # later.
-        if spec is not None:
-            self.installed.add(fullname)
-
-        return spec
-
-import m5.SimObject
-import m5.params
-
-m5.SimObject.clear()
-m5.params.clear()
-
-# install the python importer so we can grab stuff from the source
-# tree itself.  We can't have SimObjects added after this point or
-# else we won't know about them for the rest of the stuff.
-importer = M5MetaPathFinder(PySource.modules)
-sys.meta_path[0:0] = [ importer ]
-
-import_globals = globals().copy()
-# import all sim objects so we can populate the all_objects list
-# make sure that we're working with a list, then let's sort it
-gem5_lib_simobjects = SimObject.all.with_tag(env, 'gem5 lib')
-gem5_lib_modnames = sorted(map(lambda so: so.modname, gem5_lib_simobjects))
-for modname in gem5_lib_modnames:
-    exec('from m5.objects import %s' % modname, import_globals)
-
-# we need to unload all of the currently imported modules so that they
-# will be re-imported the next time the sconscript is run
-importer.unload()
-sys.meta_path.remove(importer)
-
-sim_objects = m5.SimObject.allClasses
-all_enums = m5.params.allEnums
-
-########################################################################
-#
-# calculate extra dependencies
-#
-module_depends = ["m5", "m5.SimObject", "m5.params"]
-depends = [ PySource.modules[dep].snode for dep in module_depends ]
-depends.sort(key = lambda x: x.name)
 
 ########################################################################
 #
@@ -667,144 +639,6 @@
 m5_module_static = list(map(lambda s: s.static(gem5py_env), m5_module_source))
 gem5py_env.Program(gem5py_m5, [ 'python/gem5py.cc' ] + m5_module_static)
 
-########################################################################
-#
-# Create all of the SimObject param headers and enum headers
-#
-
-# Generate all of the SimObject param C++ struct header files
-
-for module, simobjs in sorted(SimObject.sim_objects.items()):
-    tags = SimObject.tags[module]
-    for simobj in simobjs:
-        gem5py_env.Command([ "${PARAMS_HH}" ],
-                [ Value(module), Value(simobj),
-                    "${GEM5PY_M5}", "${PARAMSTRUCT_PY}" ],
-                MakeAction('"${GEM5PY_M5}" "${PARAMSTRUCT_PY}" "${MODULE}" ' \
-                        '"${PARAMS_HH}"',
-                    Transform("SO Param", 2)),
-                MODULE=module,
-                SIMOBJ=simobj,
-                PARAMSTRUCT_PY=build_tools.File(
-                    'sim_object_param_struct_hh.py'),
-                PARAMS_HH=File(f'params/{simobj}.hh'))
-        cc_file = File(f'python/_m5/param_{simobj}.cc')
-        gem5py_env.Command([ "${PARAMS_CC}" ],
-                [ Value(module), Value(simobj),
-                    "${GEM5PY_M5}", "${PARAMSTRUCT_PY}" ],
-                MakeAction('"${GEM5PY_M5}" "${PARAMSTRUCT_PY}" "${MODULE}" ' \
-                        '"${PARAMS_CC}" "${USE_PYTHON}"',
-                    Transform("SO Param", 2)),
-                PARAMSTRUCT_PY=build_tools.File(
-                    'sim_object_param_struct_cc.py'),
-                MODULE=module,
-                SIMOBJ=simobj,
-                PARAMS_CC=cc_file,
-                USE_PYTHON=env['USE_PYTHON'])
-        Source(cc_file, tags=tags, add_tags='python')
-
-# C++ parameter description files
-if GetOption('with_cxx_config'):
-    def createSimObjectCxxConfig(is_header):
-        def body(target, source, env):
-            assert len(target) == 1 and len(source) == 1
-
-            name = source[0].get_contents().decode('utf-8')
-            obj = sim_objects[name]
-
-            code = code_formatter()
-            obj.cxx_config_param_file(code, is_header)
-            code.write(target[0].abspath)
-        return body
-
-    for name,simobj in sorted(sim_objects.items()):
-        py_source = PySource.modules[simobj.__module__]
-        extra_deps = [ py_source.tnode ]
-
-        cxx_config_hh_file = File('cxx_config/%s.hh' % name)
-        cxx_config_cc_file = File('cxx_config/%s.cc' % name)
-        env.Command(cxx_config_hh_file, Value(name),
-                    MakeAction(createSimObjectCxxConfig(True),
-                    Transform("CXXCPRHH")))
-        env.Command(cxx_config_cc_file, Value(name),
-                    MakeAction(createSimObjectCxxConfig(False),
-                    Transform("CXXCPRCC")))
-        env.Depends(cxx_config_hh_file, depends + extra_deps)
-        env.Depends(cxx_config_cc_file, depends + extra_deps)
-        Source(cxx_config_cc_file)
-
-    cxx_config_init_cc_file = File('cxx_config/init.cc')
-
-    def createCxxConfigInitCC(target, source, env):
-        assert len(target) == 1
-
-        code = code_formatter()
-
-        for name,simobj in sorted(sim_objects.items()):
-            if not hasattr(simobj, 'abstract') or not simobj.abstract:
-                code('#include "cxx_config/${name}.hh"')
-        code()
-        code('namespace gem5')
-        code('{')
-        code()
-        code('void cxxConfigInit()')
-        code('{')
-        code.indent()
-        for name,simobj in sorted(sim_objects.items()):
-            not_abstract = not hasattr(simobj, 'abstract') or \
-                not simobj.abstract
-            if not_abstract and 'type' in simobj.__dict__:
-                code('cxx_config_directory["${name}"] = '
-                     '${name}CxxConfigParams::makeDirectoryEntry();')
-        code.dedent()
-        code('}')
-        code('')
-        code('} // namespace gem5')
-        code.write(target[0].abspath)
-
-    env.Command(cxx_config_init_cc_file, [],
-        MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
-    Source(cxx_config_init_cc_file)
-
-# Generate all enum header files
-def createEnumStrings(target, source, env):
-    assert len(target) == 1 and len(source) == 2
-
-    name = source[0].get_text_contents()
-    use_python = source[1].read()
-    obj = all_enums[name]
-
-    code = code_formatter()
-    obj.cxx_def(code)
-    if use_python:
-        obj.pybind_def(code)
-    code.write(target[0].abspath)
-
-def createEnumDecls(target, source, env):
-    assert len(target) == 1 and len(source) == 1
-
-    name = source[0].get_text_contents()
-    obj = all_enums[name]
-
-    code = code_formatter()
-    obj.cxx_decl(code)
-    code.write(target[0].abspath)
-
-for name,enum in sorted(all_enums.items()):
-    py_source = PySource.modules[enum.__module__]
-    extra_deps = [ py_source.tnode ]
-
-    cc_file = File('enums/%s.cc' % name)
-    env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
-                MakeAction(createEnumStrings, Transform("ENUM STR")))
-    env.Depends(cc_file, depends + extra_deps)
-    Source(cc_file)
-
-    hh_file = File('enums/%s.hh' % name)
-    env.Command(hh_file, Value(name),
-                MakeAction(createEnumDecls, Transform("ENUMDECL")))
-    env.Depends(hh_file, depends + extra_deps)
-
 
 # version tags
 tags = \
diff --git a/src/arch/SConscript b/src/arch/SConscript
index 3034dac..1fe9815 100644
--- a/src/arch/SConscript
+++ b/src/arch/SConscript
@@ -40,6 +40,7 @@
 
 import sys
 import os
+import os.path
 import re
 
 from gem5_scons import Transform
@@ -55,18 +56,18 @@
 #
 #################################################################
 
-env.TagImplies(env.subst('${TARGET_ISA} isa'), 'gem5 lib')
+env.TagImplies(env.subst('${CONF["TARGET_ISA"]} isa'), 'gem5 lib')
 
 env.SwitchingHeaders(
     Split('''
         isa.hh
         vecregs.hh
         '''),
-    env.subst('${TARGET_ISA}'))
+    env.subst('${CONF["TARGET_ISA"]}'))
 
 amdgpu_isa = ['gcn3', 'vega']
 
-if env['BUILD_GPU']:
+if env['CONF']['BUILD_GPU']:
     env.SwitchingHeaders(
         Split('''
             gpu_decoder.hh
@@ -74,8 +75,9 @@
             gpu_registers.hh
             gpu_types.hh
             '''),
-        '{}'.format('amdgpu/' if env['TARGET_GPU_ISA'] in amdgpu_isa else '')+
-        env.subst('${TARGET_GPU_ISA}'))
+        '{}'.format('amdgpu/' if
+            env['CONF']['TARGET_GPU_ISA'] in amdgpu_isa else '') +
+        env.subst('${CONF["TARGET_GPU_ISA"]}'))
 
 #################################################################
 #
@@ -103,8 +105,12 @@
 # Now create a Builder object that uses isa_parser.py to generate C++
 # output from the ISA description (*.isa) files.
 #
-
-parser_files = Glob('isa_parser/*.py')
+parser_files = []
+for root, dirnames, filenames in os.walk(Dir("isa_parser").srcnode().abspath):
+    for name in filenames:
+        _, ext = os.path.splitext(name)
+        if ext == '.py':
+            parser_files.append(os.path.join(root, name))
 micro_asm_py = File('micro_asm.py')
 
 # import ply here because SCons screws with sys.path when performing actions.
@@ -114,7 +120,7 @@
 
 def run_parser(target, source, env):
     # Add the current directory to the system path so we can import files.
-    sys.path[0:0] = [ arch_dir.abspath ]
+    sys.path[0:0] = [ arch_dir.srcnode().abspath ]
     import isa_parser
 
     parser = isa_parser.ISAParser(target[0].dir.abspath)
diff --git a/src/arch/SConsopts b/src/arch/SConsopts
index 38b02f5..048814e 100644
--- a/src/arch/SConsopts
+++ b/src/arch/SConsopts
@@ -33,5 +33,3 @@
             sorted(set(main.Split('${ALL_GPU_ISAS}')))),
         )
 AfterSConsopts(add_isa_lists)
-
-export_vars.extend(['TARGET_ISA', 'TARGET_GPU_ISA'])
diff --git a/src/arch/amdgpu/common/SConscript b/src/arch/amdgpu/common/SConscript
index 2e49e7a..ffa5fcb 100644
--- a/src/arch/amdgpu/common/SConscript
+++ b/src/arch/amdgpu/common/SConscript
@@ -31,10 +31,10 @@
 
 Import('*')
 
-if not env['BUILD_GPU']:
+if not env['CONF']['BUILD_GPU']:
     Return()
 
-if env['TARGET_GPU_ISA'] == 'gcn3' or env['TARGET_GPU_ISA'] == 'vega':
+if env['CONF']['TARGET_GPU_ISA'] in ('gcn3', 'vega'):
     SimObject('X86GPUTLB.py', sim_objects=['X86GPUTLB', 'TLBCoalescer'])
 
     Source('tlb.cc')
diff --git a/src/arch/amdgpu/common/gpu_translation_state.hh b/src/arch/amdgpu/common/gpu_translation_state.hh
new file mode 100644
index 0000000..0fa528c
--- /dev/null
+++ b/src/arch/amdgpu/common/gpu_translation_state.hh
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2022 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARCH_AMDGPU_COMMON_GPU_TRANSLATION_STATE_HH__
+#define __ARCH_AMDGPU_COMMON_GPU_TRANSLATION_STATE_HH__
+
+#include "arch/generic/mmu.hh"
+
+namespace gem5
+{
+
+class ResponsePort;
+
+/**
+ * GPU TranslationState: this currently is a somewhat bastardization of
+ * the usage of SenderState, whereby the receiver of a packet is not
+ * usually supposed to need to look at the contents of the senderState,
+ * you're really only supposed to look at what you pushed on, pop it
+ * off, and send it back.
+ *
+ * However, since there is state that we want to pass to the TLBs using
+ * the send/recv Timing/Functional/etc. APIs, which don't allow for new
+ * arguments, we need a common TLB senderState to pass between TLBs,
+ * both "forwards" and "backwards."
+ *
+ * So, basically, the rule is that any packet received by a TLB port
+ * (cpuside OR memside) must be safely castable to a GpuTranslationState.
+ */
+
+struct GpuTranslationState : public Packet::SenderState
+{
+    // TLB mode, read or write
+    BaseMMU::Mode tlbMode;
+    // SE mode thread context associated with this req
+    ThreadContext *tc;
+    // FS mode related fields
+    int deviceId;
+    int pasId; // Process Address Space ID
+
+    /*
+    * TLB entry to be populated and passed back and filled in
+    * previous TLBs.  Equivalent to the data cache concept of
+    * "data return."
+    */
+    Serializable *tlbEntry;
+    // Is this a TLB prefetch request?
+    bool isPrefetch;
+    // When was the req for this translation issued
+    uint64_t issueTime;
+    // Remember where this came from
+    std::vector<ResponsePort*>ports;
+
+    // keep track of #uncoalesced reqs per packet per TLB level;
+    // reqCnt per level >= reqCnt higher level
+    std::vector<int> reqCnt;
+    // TLB level this packet hit in; 0 if it hit in the page table
+    int hitLevel;
+    Packet::SenderState *saved;
+
+    GpuTranslationState(BaseMMU::Mode tlb_mode, ThreadContext *_tc,
+                        bool _prefetch=false,
+                        Packet::SenderState *_saved=nullptr)
+        : tlbMode(tlb_mode), tc(_tc), deviceId(0), pasId(0), tlbEntry(nullptr),
+          isPrefetch(_prefetch), issueTime(0), hitLevel(0), saved(_saved)
+    { }
+
+    GpuTranslationState(BaseMMU::Mode tlb_mode,
+                       bool _prefetch=false,
+                       Packet::SenderState *_saved=nullptr)
+        : tlbMode(tlb_mode), tc(nullptr), deviceId(0), pasId(0),
+          tlbEntry(nullptr), isPrefetch(_prefetch), issueTime(0), hitLevel(0),
+          saved(_saved)
+    { }
+};
+
+} // namespace gem5
+
+#endif // __ARCH_AMDGPU_COMMON_GPU_TRANSLATION_STATE_HH__
diff --git a/src/arch/amdgpu/common/tlb.cc b/src/arch/amdgpu/common/tlb.cc
index 7bee4c3..f1e2e5f 100644
--- a/src/arch/amdgpu/common/tlb.cc
+++ b/src/arch/amdgpu/common/tlb.cc
@@ -35,6 +35,7 @@
 #include <cmath>
 #include <cstring>
 
+#include "arch/amdgpu/common/gpu_translation_state.hh"
 #include "arch/x86/faults.hh"
 #include "arch/x86/insts/microldstop.hh"
 #include "arch/x86/page_size.hh"
@@ -42,6 +43,7 @@
 #include "arch/x86/pagetable_walker.hh"
 #include "arch/x86/regs/misc.hh"
 #include "arch/x86/regs/msr.hh"
+#include "arch/x86/regs/segment.hh"
 #include "arch/x86/x86_traits.hh"
 #include "base/bitfield.hh"
 #include "base/logging.hh"
@@ -280,7 +282,7 @@
     {
 
     Cycles
-    localMiscRegAccess(bool read, MiscRegIndex regNum,
+    localMiscRegAccess(bool read, RegIndex regNum,
                        ThreadContext *tc, PacketPtr pkt)
     {
         if (read) {
@@ -308,12 +310,12 @@
         } else if (prefix == IntAddrPrefixMSR) {
             vaddr = (vaddr >> 3) & ~IntAddrPrefixMask;
 
-            MiscRegIndex regNum;
+            RegIndex regNum;
             if (!msrAddrToIndex(regNum, vaddr))
                 return std::make_shared<GeneralProtection>(0);
 
             req->setLocalAccessor(
-                [read,regNum](ThreadContext *tc, PacketPtr pkt)
+                [read, regNum](ThreadContext *tc, PacketPtr pkt)
                 {
                     return localMiscRegAccess(read, regNum, tc, pkt);
                 }
@@ -333,13 +335,13 @@
                     [read](ThreadContext *tc, PacketPtr pkt)
                     {
                         return localMiscRegAccess(
-                                read, MISCREG_PCI_CONFIG_ADDRESS, tc, pkt);
+                                read, misc_reg::PciConfigAddress, tc, pkt);
                     }
                 );
             } else if ((IOPort & ~mask(2)) == 0xCFC) {
                 req->setFlags(Request::UNCACHEABLE | Request::STRICT_ORDER);
                 Addr configAddress =
-                    tc->readMiscRegNoEffect(MISCREG_PCI_CONFIG_ADDRESS);
+                    tc->readMiscRegNoEffect(misc_reg::PciConfigAddress);
                 if (bits(configAddress, 31, 31)) {
                     req->setPaddr(PhysAddrPrefixPciConfig |
                             mbits(configAddress, 30, 2) |
@@ -375,10 +377,10 @@
         int seg = flags & SegmentFlagMask;
     #endif
 
-        assert(seg != SEGMENT_REG_MS);
+        assert(seg != segment_idx::Ms);
         Addr vaddr = req->getVaddr();
         DPRINTF(GPUTLB, "TLB Lookup for vaddr %#x.\n", vaddr);
-        HandyM5Reg m5Reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
+        HandyM5Reg m5Reg = tc->readMiscRegNoEffect(misc_reg::M5Reg);
 
         if (m5Reg.prot) {
             DPRINTF(GPUTLB, "In protected mode.\n");
@@ -425,7 +427,7 @@
 
         // If this is true, we're dealing with a request
         // to a non-memory address space.
-        if (seg == SEGMENT_REG_MS) {
+        if (seg == segment_idx::Ms) {
             return translateInt(mode == Mode::Read, req, tc);
         }
 
@@ -433,7 +435,7 @@
         Addr vaddr = req->getVaddr();
         DPRINTF(GPUTLB, "Translating vaddr %#x.\n", vaddr);
 
-        HandyM5Reg m5Reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
+        HandyM5Reg m5Reg = tc->readMiscRegNoEffect(misc_reg::M5Reg);
 
         // If protected mode has been enabled...
         if (m5Reg.prot) {
@@ -444,16 +446,16 @@
                         "protection.\n");
 
                 // Check for a null segment selector.
-                if (!(seg == SEGMENT_REG_TSG || seg == SYS_SEGMENT_REG_IDTR ||
-                    seg == SEGMENT_REG_HS || seg == SEGMENT_REG_LS)
-                    && !tc->readMiscRegNoEffect(MISCREG_SEG_SEL(seg))) {
+                if (!(seg == segment_idx::Tsg || seg == segment_idx::Idtr ||
+                    seg == segment_idx::Hs || seg == segment_idx::Ls)
+                    && !tc->readMiscRegNoEffect(misc_reg::segSel(seg))) {
                     return std::make_shared<GeneralProtection>(0);
                 }
 
                 bool expandDown = false;
-                SegAttr attr = tc->readMiscRegNoEffect(MISCREG_SEG_ATTR(seg));
+                SegAttr attr = tc->readMiscRegNoEffect(misc_reg::segAttr(seg));
 
-                if (seg >= SEGMENT_REG_ES && seg <= SEGMENT_REG_HS) {
+                if (seg >= segment_idx::Es && seg <= segment_idx::Hs) {
                     if (!attr.writable && (mode == BaseMMU::Write ||
                         storeCheck))
                         return std::make_shared<GeneralProtection>(0);
@@ -465,20 +467,12 @@
 
                 }
 
-                Addr base = tc->readMiscRegNoEffect(MISCREG_SEG_BASE(seg));
-                Addr limit = tc->readMiscRegNoEffect(MISCREG_SEG_LIMIT(seg));
-                // This assumes we're not in 64 bit mode. If we were, the
-                // default address size is 64 bits, overridable to 32.
-                int size = 32;
-                bool sizeOverride = (flags & (AddrSizeFlagBit << FlagShift));
-                SegAttr csAttr = tc->readMiscRegNoEffect(MISCREG_CS_ATTR);
+                Addr base = tc->readMiscRegNoEffect(misc_reg::segBase(seg));
+                Addr limit = tc->readMiscRegNoEffect(misc_reg::segLimit(seg));
+                Addr logSize = (flags >> AddrSizeFlagShift) & AddrSizeFlagMask;
+                int size = 8 << logSize;
 
-                if ((csAttr.defaultSize && sizeOverride) ||
-                    (!csAttr.defaultSize && !sizeOverride)) {
-                    size = 16;
-                }
-
-                Addr offset = bits(vaddr - base, size - 1, 0);
+                Addr offset = (vaddr - base) & mask(size);
                 Addr endOffset = offset + req->getSize() - 1;
 
                 if (expandDown) {
@@ -552,10 +546,9 @@
                 }
 
                 // Do paging protection checks.
-                bool inUser = (m5Reg.cpl == 3 &&
-                               !(flags & (CPL0FlagBit << FlagShift)));
+                bool inUser = m5Reg.cpl == 3 && !(flags & CPL0FlagBit);
 
-                CR0 cr0 = tc->readMiscRegNoEffect(MISCREG_CR0);
+                CR0 cr0 = tc->readMiscRegNoEffect(misc_reg::Cr0);
                 bool badWrite = (!entry->writable && (inUser || cr0.wp));
 
                 if ((inUser && !entry->user) || (mode == BaseMMU::Write &&
@@ -602,7 +595,7 @@
         // Check for an access to the local APIC
         if (FullSystem) {
             LocalApicBase localApicBase =
-                tc->readMiscRegNoEffect(MISCREG_APIC_BASE);
+                tc->readMiscRegNoEffect(misc_reg::ApicBase);
 
             Addr baseAddr = localApicBase.base * PageBytes;
             Addr paddr = req->getPaddr();
@@ -673,8 +666,8 @@
         Addr virt_page_addr = roundDown(pkt->req->getVaddr(),
                                         X86ISA::PageBytes);
 
-        TranslationState *sender_state =
-                safe_cast<TranslationState*>(pkt->senderState);
+        GpuTranslationState *sender_state =
+                safe_cast<GpuTranslationState*>(pkt->senderState);
 
         bool update_stats = !sender_state->isPrefetch;
         ThreadContext * tmp_tc = sender_state->tc;
@@ -760,14 +753,13 @@
     GpuTLB::pagingProtectionChecks(ThreadContext *tc, PacketPtr pkt,
             TlbEntry * tlb_entry, Mode mode)
     {
-        HandyM5Reg m5Reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
+        HandyM5Reg m5Reg = tc->readMiscRegNoEffect(misc_reg::M5Reg);
         uint32_t flags = pkt->req->getFlags();
         bool storeCheck = flags & Request::READ_MODIFY_WRITE;
 
         // Do paging protection checks.
-        bool inUser
-            = (m5Reg.cpl == 3 && !(flags & (CPL0FlagBit << FlagShift)));
-        CR0 cr0 = tc->readMiscRegNoEffect(MISCREG_CR0);
+        bool inUser = m5Reg.cpl == 3 && !(flags & CPL0FlagBit);
+        CR0 cr0 = tc->readMiscRegNoEffect(misc_reg::Cr0);
 
         bool badWrite = (!tlb_entry->writable && (inUser || cr0.wp));
 
@@ -798,8 +790,8 @@
         assert(pkt);
         Addr vaddr = pkt->req->getVaddr();
 
-        TranslationState *sender_state =
-            safe_cast<TranslationState*>(pkt->senderState);
+        GpuTranslationState *sender_state =
+            safe_cast<GpuTranslationState*>(pkt->senderState);
 
         ThreadContext *tc = sender_state->tc;
         Mode mode = sender_state->tlbMode;
@@ -809,7 +801,7 @@
         if (tlb_outcome == TLB_HIT) {
             DPRINTF(GPUTLB, "Translation Done - TLB Hit for addr %#x\n",
                 vaddr);
-            local_entry = sender_state->tlbEntry;
+            local_entry = safe_cast<TlbEntry *>(sender_state->tlbEntry);
         } else {
             DPRINTF(GPUTLB, "Translation Done - TLB Miss for addr %#x\n",
                     vaddr);
@@ -819,7 +811,7 @@
              * lower TLB level. The senderState should be "carrying" a pointer
              * to the correct TLBEntry.
              */
-            new_entry = sender_state->tlbEntry;
+            new_entry = safe_cast<TlbEntry *>(sender_state->tlbEntry);
             assert(new_entry);
             local_entry = new_entry;
 
@@ -887,8 +879,8 @@
         assert(translationReturnEvent[virtPageAddr]);
         assert(pkt);
 
-        TranslationState *tmp_sender_state =
-            safe_cast<TranslationState*>(pkt->senderState);
+        GpuTranslationState *tmp_sender_state =
+            safe_cast<GpuTranslationState*>(pkt->senderState);
 
         int req_cnt = tmp_sender_state->reqCnt.back();
         bool update_stats = !tmp_sender_state->isPrefetch;
@@ -955,8 +947,8 @@
             DPRINTF(GPUTLB, "Doing a page walk for address %#x\n",
                     virtPageAddr);
 
-            TranslationState *sender_state =
-                safe_cast<TranslationState*>(pkt->senderState);
+            GpuTranslationState *sender_state =
+                safe_cast<GpuTranslationState*>(pkt->senderState);
 
             Process *p = sender_state->tc->getProcessPtr();
             Addr vaddr = pkt->req->getVaddr();
@@ -1048,8 +1040,8 @@
     void
     GpuTLB::handleFuncTranslationReturn(PacketPtr pkt, tlbOutcome tlb_outcome)
     {
-        TranslationState *sender_state =
-            safe_cast<TranslationState*>(pkt->senderState);
+        GpuTranslationState *sender_state =
+            safe_cast<GpuTranslationState*>(pkt->senderState);
 
         ThreadContext *tc = sender_state->tc;
         Mode mode = sender_state->tlbMode;
@@ -1061,7 +1053,7 @@
             DPRINTF(GPUTLB, "Functional Translation Done - TLB hit for addr "
                     "%#x\n", vaddr);
 
-            local_entry = sender_state->tlbEntry;
+            local_entry = safe_cast<TlbEntry *>(sender_state->tlbEntry);
         } else {
             DPRINTF(GPUTLB, "Functional Translation Done - TLB miss for addr "
                     "%#x\n", vaddr);
@@ -1071,7 +1063,7 @@
              * lower TLB level. The senderState should be "carrying" a pointer
              * to the correct TLBEntry.
              */
-            new_entry = sender_state->tlbEntry;
+            new_entry = safe_cast<TlbEntry *>(sender_state->tlbEntry);
             assert(new_entry);
             local_entry = new_entry;
 
@@ -1120,8 +1112,8 @@
     void
     GpuTLB::CpuSidePort::recvFunctional(PacketPtr pkt)
     {
-        TranslationState *sender_state =
-            safe_cast<TranslationState*>(pkt->senderState);
+        GpuTranslationState *sender_state =
+            safe_cast<GpuTranslationState*>(pkt->senderState);
 
         ThreadContext *tc = sender_state->tc;
         bool update_stats = !sender_state->isPrefetch;
diff --git a/src/arch/amdgpu/common/tlb.hh b/src/arch/amdgpu/common/tlb.hh
index dec0d61..6e9014e 100644
--- a/src/arch/amdgpu/common/tlb.hh
+++ b/src/arch/amdgpu/common/tlb.hh
@@ -264,56 +264,6 @@
         Port &getPort(const std::string &if_name,
                       PortID idx=InvalidPortID) override;
 
-        /**
-         * TLB TranslationState: this currently is a somewhat bastardization of
-         * the usage of SenderState, whereby the receiver of a packet is not
-         * usually supposed to need to look at the contents of the senderState,
-         * you're really only supposed to look at what you pushed on, pop it
-         * off, and send it back.
-         *
-         * However, since there is state that we want to pass to the TLBs using
-         * the send/recv Timing/Functional/etc. APIs, which don't allow for new
-         * arguments, we need a common TLB senderState to pass between TLBs,
-         * both "forwards" and "backwards."
-         *
-         * So, basically, the rule is that any packet received by a TLB port
-         * (cpuside OR memside) must be safely castable to a TranslationState.
-         */
-
-        struct TranslationState : public Packet::SenderState
-        {
-            // TLB mode, read or write
-            Mode tlbMode;
-            // Thread context associated with this req
-            ThreadContext *tc;
-
-            /*
-            * TLB entry to be populated and passed back and filled in
-            * previous TLBs.  Equivalent to the data cache concept of
-            * "data return."
-            */
-            TlbEntry *tlbEntry;
-            // Is this a TLB prefetch request?
-            bool isPrefetch;
-            // When was the req for this translation issued
-            uint64_t issueTime;
-            // Remember where this came from
-            std::vector<ResponsePort*>ports;
-
-            // keep track of #uncoalesced reqs per packet per TLB level;
-            // reqCnt per level >= reqCnt higher level
-            std::vector<int> reqCnt;
-            // TLB level this packet hit in; 0 if it hit in the page table
-            int hitLevel;
-            Packet::SenderState *saved;
-
-            TranslationState(Mode tlb_mode, ThreadContext *_tc,
-                             bool is_prefetch=false,
-                             Packet::SenderState *_saved=nullptr)
-                : tlbMode(tlb_mode), tc(_tc), tlbEntry(nullptr),
-                  isPrefetch(is_prefetch), issueTime(0),
-                  hitLevel(0),saved(_saved) { }
-        };
 
         // maximum number of permitted coalesced requests per cycle
         int maxCoalescedReqs;
@@ -436,8 +386,6 @@
     };
 }
 
-using GpuTranslationState = X86ISA::GpuTLB::TranslationState;
-
 } // namespace gem5
 
 #endif // __GPU_TLB_HH__
diff --git a/src/arch/amdgpu/common/tlb_coalescer.cc b/src/arch/amdgpu/common/tlb_coalescer.cc
index 367ce5c..1279ee3 100644
--- a/src/arch/amdgpu/common/tlb_coalescer.cc
+++ b/src/arch/amdgpu/common/tlb_coalescer.cc
@@ -33,6 +33,7 @@
 
 #include <cstring>
 
+#include "arch/amdgpu/common/gpu_translation_state.hh"
 #include "arch/x86/page_size.hh"
 #include "base/logging.hh"
 #include "debug/GPUTLB.hh"
@@ -149,7 +150,8 @@
     GpuTranslationState *sender_state =
         safe_cast<GpuTranslationState*>(pkt->senderState);
 
-    TheISA::TlbEntry *tlb_entry = sender_state->tlbEntry;
+    X86ISA::TlbEntry *tlb_entry =
+        safe_cast<X86ISA::TlbEntry *>(sender_state->tlbEntry);
     assert(tlb_entry);
     Addr first_entry_vaddr = tlb_entry->vaddr;
     Addr first_entry_paddr = tlb_entry->paddr;
diff --git a/src/arch/amdgpu/gcn3/SConscript b/src/arch/amdgpu/gcn3/SConscript
index 6b20648..732d526 100644
--- a/src/arch/amdgpu/gcn3/SConscript
+++ b/src/arch/amdgpu/gcn3/SConscript
@@ -33,10 +33,10 @@
 
 Import('*')
 
-if not env['BUILD_GPU']:
+if not env['CONF']['BUILD_GPU']:
     Return()
 
-if env['TARGET_GPU_ISA'] == 'gcn3':
+if env['CONF']['TARGET_GPU_ISA'] == 'gcn3':
     Source('decoder.cc')
     Source('insts/gpu_static_inst.cc')
     Source('insts/instructions.cc')
diff --git a/src/arch/amdgpu/vega/SConscript b/src/arch/amdgpu/vega/SConscript
index a8bab57..9c6a01b 100644
--- a/src/arch/amdgpu/vega/SConscript
+++ b/src/arch/amdgpu/vega/SConscript
@@ -33,11 +33,27 @@
 
 Import('*')
 
-if env['TARGET_GPU_ISA'] == 'vega':
+if not env['CONF']['BUILD_GPU']:
+    Return()
+
+SimObject('VegaGPUTLB.py', sim_objects=['VegaPagetableWalker',
+                                        'VegaGPUTLB',
+                                        'VegaTLBCoalescer'])
+
+Source('faults.cc')
+Source('pagetable.cc')
+Source('pagetable_walker.cc')
+Source('tlb.cc')
+Source('tlb_coalescer.cc')
+
+DebugFlag('GPUPTWalker', 'Debug flag for GPU page table walker')
+
+if env['CONF']['TARGET_GPU_ISA'] == 'vega':
     Source('decoder.cc')
     Source('insts/gpu_static_inst.cc')
     Source('insts/instructions.cc')
     Source('insts/op_encodings.cc')
     Source('isa.cc')
     Source('registers.cc')
+
     DebugFlag('VEGA', 'Debug flag for VEGA GPU ISA')
diff --git a/src/arch/amdgpu/vega/VegaGPUTLB.py b/src/arch/amdgpu/vega/VegaGPUTLB.py
new file mode 100644
index 0000000..4d77f51
--- /dev/null
+++ b/src/arch/amdgpu/vega/VegaGPUTLB.py
@@ -0,0 +1,74 @@
+# Copyright (c) 2021 Advanced Micro Devices, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from m5.defines import buildEnv
+from m5.params import *
+from m5.proxy import *
+
+from m5.objects.ClockedObject import ClockedObject
+from m5.objects.AMDGPU import AMDGPUDevice
+from m5.SimObject import SimObject
+
+class VegaPagetableWalker(ClockedObject):
+    type = 'VegaPagetableWalker'
+    cxx_class = 'gem5::VegaISA::Walker'
+    cxx_header = 'arch/amdgpu/vega/pagetable_walker.hh'
+    port = RequestPort("Port for the hardware table walker")
+    system = Param.System(Parent.any, "system object")
+
+class VegaGPUTLB(ClockedObject):
+    type = 'VegaGPUTLB'
+    cxx_class = 'gem5::VegaISA::GpuTLB'
+    cxx_header = 'arch/amdgpu/vega/tlb.hh'
+    size = Param.Int(64, "TLB size (number of entries)")
+    assoc = Param.Int(64, "TLB associativity")
+
+    walker = Param.VegaPagetableWalker(VegaPagetableWalker(),
+                                   "page table walker")
+    gpu_device = Param.AMDGPUDevice(NULL, 'GPU Device')
+
+    hitLatency = Param.Int(2, "Latency of a TLB hit")
+    missLatency1 = Param.Int(5, "Latency #1 of a TLB miss")
+    missLatency2 = Param.Int(100, "Latency #2 of a TLB miss")
+    maxOutstandingReqs = Param.Int(64, "# of maximum outstanding requests")
+    cpu_side_ports = VectorResponsePort("Port on side closer to CPU/CU")
+    mem_side_ports = VectorRequestPort("Port on side closer to memory")
+    allocationPolicy = Param.Bool(True, "Allocate on an access")
+
+class VegaTLBCoalescer(ClockedObject):
+    type = 'VegaTLBCoalescer'
+    cxx_class = 'gem5::VegaTLBCoalescer'
+    cxx_header = 'arch/amdgpu/vega/tlb_coalescer.hh'
+    tlb_level = Param.Int(64, "tlb level")
+    maxDownstream = Param.Int(64, "max downstream @ this level")
+    probesPerCycle = Param.Int(2, "Number of TLB probes per cycle")
+    coalescingWindow = Param.Int(1, "Permit coalescing across that many ticks")
+    cpu_side_ports = VectorResponsePort("Port on side closer to CPU/CU")
+    mem_side_ports = VectorRequestPort("Port on side closer to memory")
+    disableCoalescing = Param.Bool(False,"Dispable Coalescing")
diff --git a/src/arch/amdgpu/vega/decoder.cc b/src/arch/amdgpu/vega/decoder.cc
index 3344365..f716636 100644
--- a/src/arch/amdgpu/vega/decoder.cc
+++ b/src/arch/amdgpu/vega/decoder.cc
@@ -4438,14 +4438,13 @@
     GPUStaticInst*
     Decoder::decode_OP_SOP2__S_MUL_HI_U32(MachInst iFmt)
     {
-        fatal("Trying to decode instruction without a class\n");
-        return nullptr;
+        return new Inst_SOP2__S_MUL_HI_U32(&iFmt->iFmt_SOP2);
     }
 
     GPUStaticInst*
     Decoder::decode_OP_SOP2__S_MUL_HI_I32(MachInst iFmt)
     {
-        return new Inst_SOP2__S_MUL_I32(&iFmt->iFmt_SOP2);
+        return new Inst_SOP2__S_MUL_HI_I32(&iFmt->iFmt_SOP2);
     }
 
     GPUStaticInst*
diff --git a/src/arch/amdgpu/vega/faults.cc b/src/arch/amdgpu/vega/faults.cc
new file mode 100644
index 0000000..e443118
--- /dev/null
+++ b/src/arch/amdgpu/vega/faults.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "arch/amdgpu/vega/faults.hh"
+
+#include "cpu/thread_context.hh"
+
+namespace gem5
+{
+namespace VegaISA
+{
+
+void
+VegaFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
+{
+    // Currently only Vega10 is supported, which is not using XNACK on
+    // translation error. On hardware the process would be killed. Emulate
+    // that behavior in gem5 by exiting simulation.
+    panic("Vega translation resulted in page fault!");
+}
+
+} // namespace VegaISA
+} // namespace gem5
diff --git a/src/arch/amdgpu/vega/faults.hh b/src/arch/amdgpu/vega/faults.hh
new file mode 100644
index 0000000..67c5bfd
--- /dev/null
+++ b/src/arch/amdgpu/vega/faults.hh
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARCH_AMDGPU_VEGA_FAULTS_HH__
+#define __ARCH_AMDGPU_VEGA_FAULTS_HH__
+
+#include <string>
+
+#include "arch/generic/mmu.hh"
+#include "sim/faults.hh"
+
+namespace gem5
+{
+namespace VegaISA
+{
+
+enum ExceptionCode : uint64_t
+{
+    INST_PAGE = 0,
+    LOAD_PAGE = 1,
+    STORE_PAGE = 2
+};
+
+class VegaFault : public FaultBase
+{
+  protected:
+    const FaultName _name;
+    const bool _interrupt;
+    ExceptionCode _code;
+
+    VegaFault(FaultName n, bool i, ExceptionCode c)
+        : _name(n), _interrupt(i), _code(c)
+    {}
+
+    FaultName name() const override { return _name; }
+    bool isInterrupt() const { return _interrupt; }
+    ExceptionCode exception() const { return _code; }
+    virtual RegVal trap_value() const { return 0; }
+
+    void invoke(ThreadContext *tc, const StaticInstPtr &inst) override;
+};
+
+class PageFault : public VegaFault
+{
+  protected:
+    Addr addr;
+
+  public:
+    PageFault(Addr _addr, ExceptionCode code, bool present,
+              BaseMMU::Mode mode, bool user)
+        : VegaFault("PageFault", false, code), addr(_addr)
+    {
+    }
+
+    RegVal trap_value() const override { return addr; }
+};
+
+} // namespace VegaISA
+} // namespace gem5
+
+#endif // __ARCH_VEGA_FAULTS_HH__
diff --git a/src/arch/amdgpu/vega/gpu_isa.hh b/src/arch/amdgpu/vega/gpu_isa.hh
index 82dd8dd..b449d5b 100644
--- a/src/arch/amdgpu/vega/gpu_isa.hh
+++ b/src/arch/amdgpu/vega/gpu_isa.hh
@@ -36,6 +36,7 @@
 #include <type_traits>
 
 #include "arch/amdgpu/vega/gpu_registers.hh"
+#include "arch/amdgpu/vega/tlb.hh"
 #include "gpu-compute/dispatcher.hh"
 #include "gpu-compute/hsa_queue_entry.hh"
 #include "gpu-compute/misc.hh"
diff --git a/src/arch/amdgpu/vega/insts/instructions.cc b/src/arch/amdgpu/vega/insts/instructions.cc
index 56ccc3d..edf908d 100644
--- a/src/arch/amdgpu/vega/insts/instructions.cc
+++ b/src/arch/amdgpu/vega/insts/instructions.cc
@@ -1473,6 +1473,68 @@
     {
         panicUnimplemented();
     } // execute
+    // --- Inst_SOP2__S_MUL_HI_U32 class methods ---
+
+    Inst_SOP2__S_MUL_HI_U32::Inst_SOP2__S_MUL_HI_U32(InFmt_SOP2 *iFmt)
+        : Inst_SOP2(iFmt, "s_mul_hi_u32")
+    {
+        setFlag(ALU);
+    } // Inst_SOP2__S_MUL_HI_U32
+
+    Inst_SOP2__S_MUL_HI_U32::~Inst_SOP2__S_MUL_HI_U32()
+    {
+    } // ~Inst_SOP2__S_MUL_HI_U32
+
+    // --- description from .arch file ---
+    // D.u = (S0.u * S1.u) >> 32;
+    void
+    Inst_SOP2__S_MUL_HI_U32::execute(GPUDynInstPtr gpuDynInst)
+    {
+        ConstScalarOperandU32 src0(gpuDynInst, instData.SSRC0);
+        ConstScalarOperandU32 src1(gpuDynInst, instData.SSRC1);
+        ScalarOperandU32 sdst(gpuDynInst, instData.SDST);
+
+        src0.read();
+        src1.read();
+
+        VecElemU64 tmp_dst =
+            ((VecElemU64)src0.rawData() * (VecElemU64)src1.rawData());
+        sdst = (tmp_dst >> 32);
+
+        sdst.write();
+    } // execute
+    // --- Inst_SOP2__S_MUL_HI_I32 class methods ---
+
+    Inst_SOP2__S_MUL_HI_I32::Inst_SOP2__S_MUL_HI_I32(InFmt_SOP2 *iFmt)
+        : Inst_SOP2(iFmt, "s_mul_hi_i32")
+    {
+        setFlag(ALU);
+    } // Inst_SOP2__S_MUL_HI_I32
+
+    Inst_SOP2__S_MUL_HI_I32::~Inst_SOP2__S_MUL_HI_I32()
+    {
+    } // ~Inst_SOP2__S_MUL_HI_I32
+
+    // --- description from .arch file ---
+    // D.u = (S0.u * S1.u) >> 32;
+    void
+    Inst_SOP2__S_MUL_HI_I32::execute(GPUDynInstPtr gpuDynInst)
+    {
+        ConstScalarOperandI32 src0(gpuDynInst, instData.SSRC0);
+        ConstScalarOperandI32 src1(gpuDynInst, instData.SSRC1);
+        ScalarOperandI32 sdst(gpuDynInst, instData.SDST);
+
+        src0.read();
+        src1.read();
+
+        VecElemI64 tmp_src0 =
+            sext<std::numeric_limits<VecElemI64>::digits>(src0.rawData());
+        VecElemI64 tmp_src1 =
+            sext<std::numeric_limits<VecElemI64>::digits>(src1.rawData());
+        sdst = (VecElemI32)((tmp_src0 * tmp_src1) >> 32);
+
+        sdst.write();
+    } // execute
     // --- Inst_SOPK__S_MOVK_I32 class methods ---
 
     Inst_SOPK__S_MOVK_I32::Inst_SOPK__S_MOVK_I32(InFmt_SOPK *iFmt)
@@ -35467,6 +35529,15 @@
         }
 
         vdst.write();
+
+        /**
+         * This is needed because we treat this instruction as a load
+         * but it's not an actual memory request.
+         * Without this, the destination register never gets marked as
+         * free, leading to a  possible deadlock
+         */
+        wf->computeUnit->vrf[wf->simdId]->
+            scheduleWriteOperandsFromLoad(wf, gpuDynInst);
     } // execute
     // --- Inst_DS__DS_PERMUTE_B32 class methods ---
 
@@ -35541,6 +35612,15 @@
         }
 
         vdst.write();
+
+        /**
+         * This is needed because we treat this instruction as a load
+         * but it's not an actual memory request.
+         * Without this, the destination register never gets marked as
+         * free, leading to a  possible deadlock
+         */
+        wf->computeUnit->vrf[wf->simdId]->
+            scheduleWriteOperandsFromLoad(wf, gpuDynInst);
     } // execute
     // --- Inst_DS__DS_BPERMUTE_B32 class methods ---
 
@@ -35615,6 +35695,15 @@
         }
 
         vdst.write();
+
+        /**
+         * This is needed because we treat this instruction as a load
+         * but it's not an actual memory request.
+         * Without this, the destination register never gets marked as
+         * free, leading to a  possible deadlock
+         */
+        wf->computeUnit->vrf[wf->simdId]->
+            scheduleWriteOperandsFromLoad(wf, gpuDynInst);
     } // execute
 
     // --- Inst_DS__DS_ADD_U64 class methods ---
@@ -37766,8 +37855,51 @@
     void
     Inst_DS__DS_WRITE_B96::execute(GPUDynInstPtr gpuDynInst)
     {
-        panicUnimplemented();
+        Wavefront *wf = gpuDynInst->wavefront();
+        gpuDynInst->execUnitId = wf->execUnitId;
+        gpuDynInst->latency.init(gpuDynInst->computeUnit());
+        gpuDynInst->latency.set(
+                gpuDynInst->computeUnit()->cyclesToTicks(Cycles(24)));
+        ConstVecOperandU32 addr(gpuDynInst, extData.ADDR);
+        ConstVecOperandU32 data0(gpuDynInst, extData.DATA0);
+        ConstVecOperandU32 data1(gpuDynInst, extData.DATA0 + 1);
+        ConstVecOperandU32 data2(gpuDynInst, extData.DATA0 + 2);
+
+        addr.read();
+        data0.read();
+        data1.read();
+        data2.read();
+
+        calcAddr(gpuDynInst, addr);
+
+        for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+            if (gpuDynInst->exec_mask[lane]) {
+                (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4] = data0[lane];
+                (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 1] = data1[lane];
+                (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 2] = data2[lane];
+            }
+        }
+
+        gpuDynInst->computeUnit()->localMemoryPipe.issueRequest(gpuDynInst);
     } // execute
+
+    void
+    Inst_DS__DS_WRITE_B96::initiateAcc(GPUDynInstPtr gpuDynInst)
+    {
+        Addr offset0 = instData.OFFSET0;
+        Addr offset1 = instData.OFFSET1;
+        Addr offset = (offset1 << 8) | offset0;
+
+        initMemWrite<3>(gpuDynInst, offset);
+    } // initiateAcc
+
+    void
+    Inst_DS__DS_WRITE_B96::completeAcc(GPUDynInstPtr gpuDynInst)
+    {
+    } // completeAcc
     // --- Inst_DS__DS_WRITE_B128 class methods ---
 
     Inst_DS__DS_WRITE_B128::Inst_DS__DS_WRITE_B128(InFmt_DS *iFmt)
@@ -37787,8 +37919,55 @@
     void
     Inst_DS__DS_WRITE_B128::execute(GPUDynInstPtr gpuDynInst)
     {
-        panicUnimplemented();
+        Wavefront *wf = gpuDynInst->wavefront();
+        gpuDynInst->execUnitId = wf->execUnitId;
+        gpuDynInst->latency.init(gpuDynInst->computeUnit());
+        gpuDynInst->latency.set(
+                gpuDynInst->computeUnit()->cyclesToTicks(Cycles(24)));
+        ConstVecOperandU32 addr(gpuDynInst, extData.ADDR);
+        ConstVecOperandU32 data0(gpuDynInst, extData.DATA0);
+        ConstVecOperandU32 data1(gpuDynInst, extData.DATA0 + 1);
+        ConstVecOperandU32 data2(gpuDynInst, extData.DATA0 + 2);
+        ConstVecOperandU32 data3(gpuDynInst, extData.DATA0 + 3);
+
+        addr.read();
+        data0.read();
+        data1.read();
+        data2.read();
+        data3.read();
+
+        calcAddr(gpuDynInst, addr);
+
+        for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+            if (gpuDynInst->exec_mask[lane]) {
+                (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4] = data0[lane];
+                (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 1] = data1[lane];
+                (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 2] = data2[lane];
+                (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 3] = data3[lane];
+            }
+        }
+
+        gpuDynInst->computeUnit()->localMemoryPipe.issueRequest(gpuDynInst);
     } // execute
+
+    void
+    Inst_DS__DS_WRITE_B128::initiateAcc(GPUDynInstPtr gpuDynInst)
+    {
+        Addr offset0 = instData.OFFSET0;
+        Addr offset1 = instData.OFFSET1;
+        Addr offset = (offset1 << 8) | offset0;
+
+        initMemWrite<4>(gpuDynInst, offset);
+    } // initiateAcc
+
+    void
+    Inst_DS__DS_WRITE_B128::completeAcc(GPUDynInstPtr gpuDynInst)
+    {
+    } // completeAcc
     // --- Inst_DS__DS_READ_B96 class methods ---
 
     Inst_DS__DS_READ_B96::Inst_DS__DS_READ_B96(InFmt_DS *iFmt)
@@ -37807,8 +37986,52 @@
     void
     Inst_DS__DS_READ_B96::execute(GPUDynInstPtr gpuDynInst)
     {
-        panicUnimplemented();
+        Wavefront *wf = gpuDynInst->wavefront();
+        gpuDynInst->execUnitId = wf->execUnitId;
+        gpuDynInst->latency.init(gpuDynInst->computeUnit());
+        gpuDynInst->latency.set(
+                gpuDynInst->computeUnit()->cyclesToTicks(Cycles(24)));
+        ConstVecOperandU32 addr(gpuDynInst, extData.ADDR);
+
+        addr.read();
+
+        calcAddr(gpuDynInst, addr);
+
+        gpuDynInst->computeUnit()->localMemoryPipe.issueRequest(gpuDynInst);
     } // execute
+
+    void
+    Inst_DS__DS_READ_B96::initiateAcc(GPUDynInstPtr gpuDynInst)
+    {
+        Addr offset0 = instData.OFFSET0;
+        Addr offset1 = instData.OFFSET1;
+        Addr offset = (offset1 << 8) | offset0;
+
+        initMemRead<3>(gpuDynInst, offset);
+    }
+
+    void
+    Inst_DS__DS_READ_B96::completeAcc(GPUDynInstPtr gpuDynInst)
+    {
+        VecOperandU32 vdst0(gpuDynInst, extData.VDST);
+        VecOperandU32 vdst1(gpuDynInst, extData.VDST + 1);
+        VecOperandU32 vdst2(gpuDynInst, extData.VDST + 2);
+
+        for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+            if (gpuDynInst->exec_mask[lane]) {
+                vdst0[lane] = (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4];
+                vdst1[lane] = (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 1];
+                vdst2[lane] = (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 2];
+            }
+        }
+
+        vdst0.write();
+        vdst1.write();
+        vdst2.write();
+    }
     // --- Inst_DS__DS_READ_B128 class methods ---
 
     Inst_DS__DS_READ_B128::Inst_DS__DS_READ_B128(InFmt_DS *iFmt)
@@ -37827,8 +38050,56 @@
     void
     Inst_DS__DS_READ_B128::execute(GPUDynInstPtr gpuDynInst)
     {
-        panicUnimplemented();
+        Wavefront *wf = gpuDynInst->wavefront();
+        gpuDynInst->execUnitId = wf->execUnitId;
+        gpuDynInst->latency.init(gpuDynInst->computeUnit());
+        gpuDynInst->latency.set(
+                gpuDynInst->computeUnit()->cyclesToTicks(Cycles(24)));
+        ConstVecOperandU32 addr(gpuDynInst, extData.ADDR);
+
+        addr.read();
+
+        calcAddr(gpuDynInst, addr);
+
+        gpuDynInst->computeUnit()->localMemoryPipe.issueRequest(gpuDynInst);
     } // execute
+
+    void
+    Inst_DS__DS_READ_B128::initiateAcc(GPUDynInstPtr gpuDynInst)
+    {
+        Addr offset0 = instData.OFFSET0;
+        Addr offset1 = instData.OFFSET1;
+        Addr offset = (offset1 << 8) | offset0;
+
+        initMemRead<4>(gpuDynInst, offset);
+    } // initiateAcc
+
+    void
+    Inst_DS__DS_READ_B128::completeAcc(GPUDynInstPtr gpuDynInst)
+    {
+        VecOperandU32 vdst0(gpuDynInst, extData.VDST);
+        VecOperandU32 vdst1(gpuDynInst, extData.VDST + 1);
+        VecOperandU32 vdst2(gpuDynInst, extData.VDST + 2);
+        VecOperandU32 vdst3(gpuDynInst, extData.VDST + 3);
+
+        for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+            if (gpuDynInst->exec_mask[lane]) {
+                vdst0[lane] = (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4];
+                vdst1[lane] = (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 1];
+                vdst2[lane] = (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 2];
+                vdst3[lane] = (reinterpret_cast<VecElemU32*>(
+                    gpuDynInst->d_data))[lane * 4 + 3];
+            }
+        }
+
+        vdst0.write();
+        vdst1.write();
+        vdst2.write();
+        vdst3.write();
+    } // completeAcc
     // --- Inst_MUBUF__BUFFER_LOAD_FORMAT_X class methods ---
 
     Inst_MUBUF__BUFFER_LOAD_FORMAT_X
@@ -39639,7 +39910,13 @@
         gpuDynInst->execUnitId = wf->execUnitId;
         gpuDynInst->latency.init(gpuDynInst->computeUnit());
         gpuDynInst->latency.set(gpuDynInst->computeUnit()->clockPeriod());
-        gpuDynInst->computeUnit()->globalMemoryPipe.issueRequest(gpuDynInst);
+
+        if (gpuDynInst->executedAs() == enums::SC_GLOBAL) {
+            gpuDynInst->computeUnit()->globalMemoryPipe.
+                issueRequest(gpuDynInst);
+        } else {
+            fatal("Unsupported scope for flat instruction.\n");
+        }
     } // execute
 
     void
@@ -39692,7 +39969,13 @@
         gpuDynInst->execUnitId = wf->execUnitId;
         gpuDynInst->latency.init(gpuDynInst->computeUnit());
         gpuDynInst->latency.set(gpuDynInst->computeUnit()->clockPeriod());
-        gpuDynInst->computeUnit()->globalMemoryPipe.issueRequest(gpuDynInst);
+
+        if (gpuDynInst->executedAs() == enums::SC_GLOBAL) {
+            gpuDynInst->computeUnit()->globalMemoryPipe.
+                issueRequest(gpuDynInst);
+        } else {
+            fatal("Unsupported scope for flat instruction.\n");
+        }
     } // execute
     void
     Inst_MUBUF__BUFFER_WBINVL1_VOL::initiateAcc(GPUDynInstPtr gpuDynInst)
diff --git a/src/arch/amdgpu/vega/insts/instructions.hh b/src/arch/amdgpu/vega/insts/instructions.hh
index 95c66ba..e9361c3 100644
--- a/src/arch/amdgpu/vega/insts/instructions.hh
+++ b/src/arch/amdgpu/vega/insts/instructions.hh
@@ -1538,6 +1538,74 @@
         void execute(GPUDynInstPtr) override;
     }; // Inst_SOP2__S_RFE_RESTORE_B64
 
+    class Inst_SOP2__S_MUL_HI_U32 : public Inst_SOP2
+    {
+      public:
+        Inst_SOP2__S_MUL_HI_U32(InFmt_SOP2*);
+        ~Inst_SOP2__S_MUL_HI_U32();
+
+        int
+        getNumOperands() override
+        {
+            return numDstRegOperands() + numSrcRegOperands();
+        } // getNumOperands
+
+        int numDstRegOperands() override { return 1; }
+        int numSrcRegOperands() override { return 2; }
+
+        int
+        getOperandSize(int opIdx) override
+        {
+            switch (opIdx) {
+              case 0: //ssrc_0
+                return 4;
+              case 1: //ssrc_1
+                return 4;
+              case 2: //sdst
+                return 4;
+              default:
+                fatal("op idx %i out of bounds\n", opIdx);
+                return -1;
+            }
+        } // getOperandSize
+
+        void execute(GPUDynInstPtr) override;
+    }; // Inst_SOP2__S_MUL_HI_U32
+
+    class Inst_SOP2__S_MUL_HI_I32 : public Inst_SOP2
+    {
+      public:
+        Inst_SOP2__S_MUL_HI_I32(InFmt_SOP2*);
+        ~Inst_SOP2__S_MUL_HI_I32();
+
+        int
+        getNumOperands() override
+        {
+            return numDstRegOperands() + numSrcRegOperands();
+        } // getNumOperands
+
+        int numDstRegOperands() override { return 1; }
+        int numSrcRegOperands() override { return 2; }
+
+        int
+        getOperandSize(int opIdx) override
+        {
+            switch (opIdx) {
+              case 0: //ssrc_0
+                return 4;
+              case 1: //ssrc_1
+                return 4;
+              case 2: //sdst
+                return 4;
+              default:
+                fatal("op idx %i out of bounds\n", opIdx);
+                return -1;
+            }
+        } // getOperandSize
+
+        void execute(GPUDynInstPtr) override;
+    }; // Inst_SOP2__S_MUL_HI_I32
+
     class Inst_SOPK__S_MOVK_I32 : public Inst_SOPK
     {
       public:
@@ -35542,6 +35610,8 @@
         } // getOperandSize
 
         void execute(GPUDynInstPtr) override;
+        void initiateAcc(GPUDynInstPtr) override;
+        void completeAcc(GPUDynInstPtr) override;
     }; // Inst_DS__DS_WRITE_B96
 
     class Inst_DS__DS_WRITE_B128 : public Inst_DS
@@ -35574,6 +35644,8 @@
         } // getOperandSize
 
         void execute(GPUDynInstPtr) override;
+        void initiateAcc(GPUDynInstPtr) override;
+        void completeAcc(GPUDynInstPtr) override;
     }; // Inst_DS__DS_WRITE_B128
 
     class Inst_DS__DS_READ_B96 : public Inst_DS
@@ -35606,6 +35678,8 @@
         } // getOperandSize
 
         void execute(GPUDynInstPtr) override;
+        void initiateAcc(GPUDynInstPtr) override;
+        void completeAcc(GPUDynInstPtr) override;
     }; // Inst_DS__DS_READ_B96
 
     class Inst_DS__DS_READ_B128 : public Inst_DS
@@ -35638,6 +35712,8 @@
         } // getOperandSize
 
         void execute(GPUDynInstPtr) override;
+        void initiateAcc(GPUDynInstPtr) override;
+        void completeAcc(GPUDynInstPtr) override;
     }; // Inst_DS__DS_READ_B128
 
     class Inst_MUBUF__BUFFER_LOAD_FORMAT_X : public Inst_MUBUF
diff --git a/src/arch/amdgpu/vega/insts/op_encodings.cc b/src/arch/amdgpu/vega/insts/op_encodings.cc
index 40910e4..6f78b69 100644
--- a/src/arch/amdgpu/vega/insts/op_encodings.cc
+++ b/src/arch/amdgpu/vega/insts/op_encodings.cc
@@ -1274,12 +1274,12 @@
 
             reg = extData.SRSRC;
             srcOps.emplace_back(reg, getOperandSize(opNum), true,
-                                  true, false, false);
+                                  isScalarReg(reg), false, false);
             opNum++;
 
             reg = extData.SOFFSET;
             srcOps.emplace_back(reg, getOperandSize(opNum), true,
-                                  true, false, false);
+                                  isScalarReg(reg), false, false);
             opNum++;
         }
 
@@ -1365,12 +1365,12 @@
 
         reg = extData.SRSRC;
         srcOps.emplace_back(reg, getOperandSize(opNum), true,
-                              true, false, false);
+                              isScalarReg(reg), false, false);
         opNum++;
 
         reg = extData.SOFFSET;
         srcOps.emplace_back(reg, getOperandSize(opNum), true,
-                              true, false, false);
+                              isScalarReg(reg), false, false);
         opNum++;
 
         // extData.VDATA moves in the reg list depending on the instruction
@@ -1438,13 +1438,13 @@
 
         reg = extData.SRSRC;
         srcOps.emplace_back(reg, getOperandSize(opNum), true,
-                              true, false, false);
+                              isScalarReg(reg), false, false);
         opNum++;
 
         if (getNumOperands() == 4) {
             reg = extData.SSAMP;
             srcOps.emplace_back(reg, getOperandSize(opNum), true,
-                                  true, false, false);
+                                  isScalarReg(reg), false, false);
             opNum++;
         }
 
diff --git a/src/arch/amdgpu/vega/insts/op_encodings.hh b/src/arch/amdgpu/vega/insts/op_encodings.hh
index 109e680..17d69d5 100644
--- a/src/arch/amdgpu/vega/insts/op_encodings.hh
+++ b/src/arch/amdgpu/vega/insts/op_encodings.hh
@@ -414,6 +414,25 @@
             }
         }
 
+        template<int N>
+        void
+        initMemRead(GPUDynInstPtr gpuDynInst, Addr offset)
+        {
+            Wavefront *wf = gpuDynInst->wavefront();
+
+            for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+                if (gpuDynInst->exec_mask[lane]) {
+                    Addr vaddr = gpuDynInst->addr[lane] + offset;
+                    for (int i = 0; i < N; ++i) {
+                        (reinterpret_cast<VecElemU32*>(
+                            gpuDynInst->d_data))[lane * N + i]
+                            = wf->ldsChunk->read<VecElemU32>(
+                                vaddr + i*sizeof(VecElemU32));
+                    }
+                }
+            }
+        }
+
         template<typename T>
         void
         initDualMemRead(GPUDynInstPtr gpuDynInst, Addr offset0, Addr offset1)
@@ -448,6 +467,25 @@
             }
         }
 
+        template<int N>
+        void
+        initMemWrite(GPUDynInstPtr gpuDynInst, Addr offset)
+        {
+            Wavefront *wf = gpuDynInst->wavefront();
+
+            for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+                if (gpuDynInst->exec_mask[lane]) {
+                    Addr vaddr = gpuDynInst->addr[lane] + offset;
+                    for (int i = 0; i < N; ++i) {
+                        wf->ldsChunk->write<VecElemU32>(
+                            vaddr + i*sizeof(VecElemU32),
+                            (reinterpret_cast<VecElemU32*>(
+                                gpuDynInst->d_data))[lane * N + i]);
+                    }
+                }
+            }
+        }
+
         template<typename T>
         void
         initDualMemWrite(GPUDynInstPtr gpuDynInst, Addr offset0, Addr offset1)
@@ -594,6 +632,7 @@
             Addr stride = 0;
             Addr buf_idx = 0;
             Addr buf_off = 0;
+            Addr buffer_offset = 0;
             BufferRsrcDescriptor rsrc_desc;
 
             std::memcpy((void*)&rsrc_desc, s_rsrc_desc.rawDataPtr(),
@@ -616,42 +655,6 @@
 
                     buf_off = v_off[lane] + inst_offset;
 
-
-                    /**
-                     * Range check behavior causes out of range accesses to
-                     * to be treated differently. Out of range accesses return
-                     * 0 for loads and are ignored for stores. For
-                     * non-formatted accesses, this is done on a per-lane
-                     * basis.
-                     */
-                    if (stride == 0 || !rsrc_desc.swizzleEn) {
-                        if (buf_off + stride * buf_idx >=
-                            rsrc_desc.numRecords - s_offset.rawData()) {
-                            DPRINTF(VEGA, "mubuf out-of-bounds condition 1: "
-                                    "lane = %d, buffer_offset = %llx, "
-                                    "const_stride = %llx, "
-                                    "const_num_records = %llx\n",
-                                    lane, buf_off + stride * buf_idx,
-                                    stride, rsrc_desc.numRecords);
-                            oobMask.set(lane);
-                            continue;
-                        }
-                    }
-
-                    if (stride != 0 && rsrc_desc.swizzleEn) {
-                        if (buf_idx >= rsrc_desc.numRecords ||
-                            buf_off >= stride) {
-                            DPRINTF(VEGA, "mubuf out-of-bounds condition 2: "
-                                    "lane = %d, offset = %llx, "
-                                    "index = %llx, "
-                                    "const_num_records = %llx\n",
-                                    lane, buf_off, buf_idx,
-                                    rsrc_desc.numRecords);
-                            oobMask.set(lane);
-                            continue;
-                        }
-                    }
-
                     if (rsrc_desc.swizzleEn) {
                         Addr idx_stride = 8 << rsrc_desc.idxStride;
                         Addr elem_size = 2 << rsrc_desc.elemSize;
@@ -666,12 +669,50 @@
                                 lane, idx_stride, elem_size, idx_msb, idx_lsb,
                                 off_msb, off_lsb);
 
-                        vaddr += ((idx_msb * stride + off_msb * elem_size)
-                            * idx_stride + idx_lsb * elem_size + off_lsb);
+                        buffer_offset =(idx_msb * stride + off_msb * elem_size)
+                            * idx_stride + idx_lsb * elem_size + off_lsb;
                     } else {
-                        vaddr += buf_off + stride * buf_idx;
+                        buffer_offset = buf_off + stride * buf_idx;
                     }
 
+
+                    /**
+                     * Range check behavior causes out of range accesses to
+                     * to be treated differently. Out of range accesses return
+                     * 0 for loads and are ignored for stores. For
+                     * non-formatted accesses, this is done on a per-lane
+                     * basis.
+                     */
+                    if (rsrc_desc.stride == 0 || !rsrc_desc.swizzleEn) {
+                        if (buffer_offset >=
+                            rsrc_desc.numRecords - s_offset.rawData()) {
+                            DPRINTF(VEGA, "mubuf out-of-bounds condition 1: "
+                                    "lane = %d, buffer_offset = %llx, "
+                                    "const_stride = %llx, "
+                                    "const_num_records = %llx\n",
+                                    lane, buf_off + stride * buf_idx,
+                                    stride, rsrc_desc.numRecords);
+                            oobMask.set(lane);
+                            continue;
+                        }
+                    }
+
+                    if (rsrc_desc.stride != 0 && rsrc_desc.swizzleEn) {
+                        if (buf_idx >= rsrc_desc.numRecords ||
+                            buf_off >= stride) {
+                            DPRINTF(VEGA, "mubuf out-of-bounds condition 2: "
+                                    "lane = %d, offset = %llx, "
+                                    "index = %llx, "
+                                    "const_num_records = %llx\n",
+                                    lane, buf_off, buf_idx,
+                                    rsrc_desc.numRecords);
+                            oobMask.set(lane);
+                            continue;
+                        }
+                    }
+
+                    vaddr += buffer_offset;
+
                     DPRINTF(VEGA, "Calculating mubuf address for lane %d: "
                             "vaddr = %llx, base_addr = %llx, "
                             "stride = %llx, buf_idx = %llx, buf_off = %llx\n",
@@ -759,55 +800,140 @@
         void
         initMemRead(GPUDynInstPtr gpuDynInst)
         {
-            initMemReqHelper<T, 1>(gpuDynInst, MemCmd::ReadReq);
+            if (gpuDynInst->executedAs() == enums::SC_GLOBAL) {
+                initMemReqHelper<T, 1>(gpuDynInst, MemCmd::ReadReq);
+            } else if (gpuDynInst->executedAs() == enums::SC_GROUP) {
+                Wavefront *wf = gpuDynInst->wavefront();
+                for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+                    if (gpuDynInst->exec_mask[lane]) {
+                        Addr vaddr = gpuDynInst->addr[lane];
+                        (reinterpret_cast<T*>(gpuDynInst->d_data))[lane]
+                            = wf->ldsChunk->read<T>(vaddr);
+                    }
+                }
+            }
         }
 
         template<int N>
         void
         initMemRead(GPUDynInstPtr gpuDynInst)
         {
-            initMemReqHelper<VecElemU32, N>(gpuDynInst, MemCmd::ReadReq);
+            if (gpuDynInst->executedAs() == enums::SC_GLOBAL) {
+                initMemReqHelper<VecElemU32, N>(gpuDynInst, MemCmd::ReadReq);
+            } else if (gpuDynInst->executedAs() == enums::SC_GROUP) {
+                Wavefront *wf = gpuDynInst->wavefront();
+                for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+                    if (gpuDynInst->exec_mask[lane]) {
+                        Addr vaddr = gpuDynInst->addr[lane];
+                        for (int i = 0; i < N; ++i) {
+                            (reinterpret_cast<VecElemU32*>(
+                                gpuDynInst->d_data))[lane * N + i]
+                                = wf->ldsChunk->read<VecElemU32>(
+                                        vaddr + i*sizeof(VecElemU32));
+                        }
+                    }
+                }
+            }
         }
 
         template<typename T>
         void
         initMemWrite(GPUDynInstPtr gpuDynInst)
         {
-            initMemReqHelper<T, 1>(gpuDynInst, MemCmd::WriteReq);
+            if (gpuDynInst->executedAs() == enums::SC_GLOBAL) {
+                initMemReqHelper<T, 1>(gpuDynInst, MemCmd::WriteReq);
+            } else if (gpuDynInst->executedAs() == enums::SC_GROUP) {
+                Wavefront *wf = gpuDynInst->wavefront();
+                for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+                    if (gpuDynInst->exec_mask[lane]) {
+                        Addr vaddr = gpuDynInst->addr[lane];
+                        wf->ldsChunk->write<T>(vaddr,
+                            (reinterpret_cast<T*>(gpuDynInst->d_data))[lane]);
+                    }
+                }
+            }
         }
 
         template<int N>
         void
         initMemWrite(GPUDynInstPtr gpuDynInst)
         {
-            initMemReqHelper<VecElemU32, N>(gpuDynInst, MemCmd::WriteReq);
+            if (gpuDynInst->executedAs() == enums::SC_GLOBAL) {
+                initMemReqHelper<VecElemU32, N>(gpuDynInst, MemCmd::WriteReq);
+            } else if (gpuDynInst->executedAs() == enums::SC_GROUP) {
+                Wavefront *wf = gpuDynInst->wavefront();
+                for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+                    if (gpuDynInst->exec_mask[lane]) {
+                        Addr vaddr = gpuDynInst->addr[lane];
+                        for (int i = 0; i < N; ++i) {
+                            wf->ldsChunk->write<VecElemU32>(
+                                vaddr + i*sizeof(VecElemU32),
+                                (reinterpret_cast<VecElemU32*>(
+                                    gpuDynInst->d_data))[lane * N + i]);
+                        }
+                    }
+                }
+            }
         }
 
         template<typename T>
         void
         initAtomicAccess(GPUDynInstPtr gpuDynInst)
         {
-            initMemReqHelper<T, 1>(gpuDynInst, MemCmd::SwapReq, true);
+            if (gpuDynInst->executedAs() == enums::SC_GLOBAL) {
+                initMemReqHelper<T, 1>(gpuDynInst, MemCmd::SwapReq, true);
+            } else if (gpuDynInst->executedAs() == enums::SC_GROUP) {
+                Wavefront *wf = gpuDynInst->wavefront();
+                for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
+                    if (gpuDynInst->exec_mask[lane]) {
+                        Addr vaddr = gpuDynInst->addr[lane];
+                        auto amo_op =
+                            gpuDynInst->makeAtomicOpFunctor<T>(
+                                &(reinterpret_cast<T*>(
+                                    gpuDynInst->a_data))[lane],
+                                &(reinterpret_cast<T*>(
+                                    gpuDynInst->x_data))[lane]);
+
+                        T tmp = wf->ldsChunk->read<T>(vaddr);
+                        (*amo_op)(reinterpret_cast<uint8_t *>(&tmp));
+                        wf->ldsChunk->write<T>(vaddr, tmp);
+                        (reinterpret_cast<T*>(gpuDynInst->d_data))[lane] = tmp;
+                    }
+                }
+            }
         }
 
         void
         calcAddr(GPUDynInstPtr gpuDynInst, ConstVecOperandU64 &vaddr,
-                 ScalarRegU32 saddr, ScalarRegU32 offset)
+                 ScalarRegU32 saddr, ScalarRegI32 offset)
         {
+            // Offset is a 13-bit field w/the following meanings:
+            // In Flat instructions, offset is a 12-bit unsigned number
+            // In Global/Scratch instructions, offset is a 13-bit signed number
+            if (isFlat()) {
+                offset = offset & 0xfff;
+            } else {
+                offset = (ScalarRegI32)sext<13>(offset);
+            }
             // If saddr = 0x7f there is no scalar reg to read and address will
             // be a 64-bit address. Otherwise, saddr is the reg index for a
             // scalar reg used as the base address for a 32-bit address.
             if ((saddr == 0x7f && isFlatGlobal()) || isFlat()) {
-                calcAddr64(gpuDynInst, vaddr, offset);
+                calcAddrVgpr(gpuDynInst, vaddr, offset);
             } else {
-                ConstScalarOperandU32 sbase(gpuDynInst, saddr);
+                // Assume we are operating in 64-bit mode and read a pair of
+                // SGPRs for the address base.
+                ConstScalarOperandU64 sbase(gpuDynInst, saddr);
                 sbase.read();
 
-                calcAddr32(gpuDynInst, vaddr, sbase, offset);
+                calcAddrSgpr(gpuDynInst, vaddr, sbase, offset);
             }
 
             if (isFlat()) {
                 gpuDynInst->resolveFlatSegment(gpuDynInst->exec_mask);
+            } else {
+                gpuDynInst->staticInstruction()->executed_as =
+                    enums::SC_GLOBAL;
             }
         }
 
@@ -840,20 +966,23 @@
         void generateGlobalDisassembly();
 
         void
-        calcAddr32(GPUDynInstPtr gpuDynInst, ConstVecOperandU64 &vaddr,
-                   ConstScalarOperandU32 &saddr, ScalarRegU32 offset)
+        calcAddrSgpr(GPUDynInstPtr gpuDynInst, ConstVecOperandU64 &vaddr,
+                     ConstScalarOperandU64 &saddr, ScalarRegI32 offset)
         {
+            // Use SGPR pair as a base address and add VGPR-offset and
+            // instruction offset. The VGPR-offset is always 32-bits so we
+            // mask any upper bits from the vaddr.
             for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
                 if (gpuDynInst->exec_mask[lane]) {
                     gpuDynInst->addr.at(lane) =
-                        (vaddr[lane] + saddr.rawData() + offset) & 0xffffffff;
+                        saddr.rawData() + (vaddr[lane] & 0xffffffff) + offset;
                 }
             }
         }
 
         void
-        calcAddr64(GPUDynInstPtr gpuDynInst, ConstVecOperandU64 &addr,
-                   ScalarRegU32 offset)
+        calcAddrVgpr(GPUDynInstPtr gpuDynInst, ConstVecOperandU64 &addr,
+                     ScalarRegI32 offset)
         {
             for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) {
                 if (gpuDynInst->exec_mask[lane]) {
diff --git a/src/arch/amdgpu/vega/page_size.hh b/src/arch/amdgpu/vega/page_size.hh
new file mode 100644
index 0000000..508c517
--- /dev/null
+++ b/src/arch/amdgpu/vega/page_size.hh
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARCH_AMDGPU_VEGA_PAGE_SIZE_H__
+#define __ARCH_AMDGPU_VEGA_PAGE_SIZE_H__
+
+#include "base/types.hh"
+
+namespace gem5
+{
+namespace VegaISA
+{
+    const Addr PageShift = 12;
+    const Addr PageBytes = 1ULL << PageShift;
+} // namespace VegaISA
+} // namespace gem5
+
+#endif // __ARCH_AMDGPU_VEGA_PAGE_SIZE_H__
diff --git a/src/arch/amdgpu/vega/pagetable.cc b/src/arch/amdgpu/vega/pagetable.cc
new file mode 100644
index 0000000..5ffae24
--- /dev/null
+++ b/src/arch/amdgpu/vega/pagetable.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "arch/amdgpu/vega/pagetable.hh"
+
+#include "sim/serialize.hh"
+
+namespace gem5
+{
+namespace VegaISA
+{
+
+/**
+ * Serialize all of the POD fields in a TLB entry. These fields contain all
+ * of the information needed to handle any of the function calls in the entry.
+ */
+void
+VegaTlbEntry::serialize(CheckpointOut &cp) const
+{
+    SERIALIZE_SCALAR(paddr);
+    SERIALIZE_SCALAR(vaddr);
+    SERIALIZE_SCALAR(logBytes);
+    SERIALIZE_SCALAR(vmid);
+    SERIALIZE_SCALAR(pte);
+    SERIALIZE_SCALAR(lruSeq);
+}
+
+/**
+ * Unserialize all of the POD fields in a TLB entry. These fields contain all
+ * of the information needed to handle any of the function calls in the entry.
+ */
+void
+VegaTlbEntry::unserialize(CheckpointIn &cp)
+{
+    UNSERIALIZE_SCALAR(paddr);
+    UNSERIALIZE_SCALAR(vaddr);
+    UNSERIALIZE_SCALAR(logBytes);
+    UNSERIALIZE_SCALAR(vmid);
+    UNSERIALIZE_SCALAR(pte);
+    UNSERIALIZE_SCALAR(lruSeq);
+}
+
+} // namespace VegaISA
+} // namespace gem5
diff --git a/src/arch/amdgpu/vega/pagetable.hh b/src/arch/amdgpu/vega/pagetable.hh
new file mode 100644
index 0000000..97e1a01
--- /dev/null
+++ b/src/arch/amdgpu/vega/pagetable.hh
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARCH_AMDGPU_VEGA_PAGETABLE_H__
+#define __ARCH_AMDGPU_VEGA_PAGETABLE_H__
+
+#include "arch/amdgpu/vega/page_size.hh"
+#include "base/bitunion.hh"
+#include "base/types.hh"
+#include "sim/serialize.hh"
+
+namespace gem5
+{
+namespace VegaISA
+{
+
+/**
+ * The page table entry is reverse engineered from the macros here:
+ *
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *     drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h#L53
+ */
+BitUnion64(PageTableEntry)
+    Bitfield<58,57> m;
+    Bitfield<56> f;
+    Bitfield<55> l;
+    Bitfield<53,52> sw;
+    Bitfield<51> t;
+    Bitfield<47, 12> ppn;
+    Bitfield<11, 7> fragment;
+    Bitfield<6> w;
+    Bitfield<5> r;
+    Bitfield<4> x;
+    Bitfield<3> z;
+    Bitfield<2> c;
+    Bitfield<1> s;
+    Bitfield<0> v;
+EndBitUnion(PageTableEntry)
+
+BitUnion64(PageDirectoryEntry)
+    Bitfield<63,59> blockFragmentSize;
+    Bitfield<54> p;
+    Bitfield<47, 6> baseAddr;
+    Bitfield<2> c;
+    Bitfield<1> s;
+    Bitfield<0> v;
+EndBitUnion(PageDirectoryEntry)
+
+struct VegaTlbEntry : public Serializable
+{
+    uint16_t vmid;
+
+    // The base of the physical page.
+    Addr paddr;
+
+    // The beginning of the virtual page this entry maps.
+    Addr vaddr;
+    // The size of the page this represents, in address bits.
+    unsigned logBytes;
+
+    PageTableEntry pte;
+
+    // Read permission is always available, assuming it isn't blocked by
+    // other mechanisms.
+    bool writable() { return pte.w; };
+    // Whether the page is cacheable or not.
+    bool uncacheable() { return !pte.c; };
+    // Whether or not memory on this page can be executed.
+    bool noExec() { return !pte.x; };
+
+    // A sequence number to keep track of LRU.
+    uint64_t lruSeq;
+
+    VegaTlbEntry()
+        : vmid(0), paddr(0), vaddr(0), logBytes(PageShift), pte(), lruSeq(0)
+    {}
+
+    VegaTlbEntry(Addr _vmid, Addr _vaddr, Addr _paddr, unsigned _logBytes,
+                 PageTableEntry _pte)
+        : vmid(_vmid), paddr(_paddr), vaddr(_vaddr), logBytes(_logBytes),
+          pte(_pte), lruSeq(0)
+    {}
+
+    // Return the page size in bytes
+    Addr size() const
+    {
+        return (static_cast<Addr>(1) << logBytes);
+    }
+
+    void serialize(CheckpointOut &cp) const override;
+    void unserialize(CheckpointIn &cp) override;
+};
+
+} // namespace VegaISA
+} // namespace gem5
+
+#endif // __ARCH_AMDGPU_VEGA_PAGETABLE_H__
diff --git a/src/arch/amdgpu/vega/pagetable_walker.cc b/src/arch/amdgpu/vega/pagetable_walker.cc
new file mode 100644
index 0000000..bbbaa56
--- /dev/null
+++ b/src/arch/amdgpu/vega/pagetable_walker.cc
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "arch/amdgpu/vega/pagetable_walker.hh"
+
+#include <memory>
+
+#include "arch/amdgpu/vega/faults.hh"
+#include "mem/abstract_mem.hh"
+#include "mem/packet_access.hh"
+
+namespace gem5
+{
+namespace VegaISA
+{
+
+/*
+ * Functional/atomic mode methods
+ */
+Fault
+Walker::startFunctional(Addr base, Addr &addr, unsigned &logBytes,
+                        BaseMMU::Mode mode, bool &isSystem)
+{
+    PageTableEntry pte;
+    Addr vaddr = addr;
+    Fault fault = startFunctional(base, vaddr, pte, logBytes, mode);
+    isSystem = pte.s;
+    addr = ((pte.ppn << PageShift) & ~mask(logBytes))
+         | (vaddr & mask(logBytes));
+
+    return fault;
+}
+
+Fault
+Walker::startFunctional(Addr base, Addr vaddr, PageTableEntry &pte,
+                        unsigned &logBytes, BaseMMU::Mode mode)
+{
+    DPRINTF(GPUPTWalker, "Vega walker walker: %p funcState: %p "
+            "funcState->walker %p\n",
+            this, &funcState, funcState.getWalker());
+    funcState.initState(mode, base, vaddr, true);
+    return funcState.startFunctional(base, vaddr, pte, logBytes);
+}
+
+Fault
+Walker::WalkerState::startFunctional(Addr base, Addr vaddr,
+                                     PageTableEntry &pte, unsigned &logBytes)
+{
+    Fault fault = NoFault;
+    DPRINTF(GPUPTWalker, "Vega walker starting with addr: %#lx "
+            "logical: %#lx\n", vaddr, vaddr >> PageShift);
+
+    assert(!started);
+    started = true;
+
+    do {
+        DPRINTF(GPUPTWalker, "Sending functional read to %#lx\n",
+                read->getAddr());
+
+        auto devmem = walker->system->getDeviceMemory(read);
+        assert(devmem);
+        devmem->access(read);
+
+        fault = stepWalk();
+        assert(fault == NoFault || read == NULL);
+
+        state = nextState;
+    } while (read);
+
+    logBytes = entry.logBytes;
+    pte = entry.pte;
+
+    return fault;
+}
+
+
+/*
+ * Timing mode methods
+ */
+void
+Walker::startTiming(PacketPtr pkt, Addr base, Addr vaddr, BaseMMU::Mode mode)
+{
+    DPRINTF(GPUPTWalker, "Vega walker starting with addr: %#lx "
+            "logical: %#lx\n", vaddr, vaddr >> PageShift);
+
+    WalkerState *newState = new WalkerState(this, pkt);
+
+    newState->initState(mode, base, vaddr);
+    currStates.push_back(newState);
+    DPRINTF(GPUPTWalker, "There are %ld walker states\n", currStates.size());
+
+    newState->startWalk();
+}
+
+void
+Walker::WalkerState::initState(BaseMMU::Mode _mode, Addr baseAddr, Addr vaddr,
+                               bool is_functional)
+{
+    DPRINTF(GPUPTWalker, "Walker::WalkerState::initState\n");
+    DPRINTF(GPUPTWalker, "Walker::WalkerState::initState %p\n", this);
+    DPRINTF(GPUPTWalker, "Walker::WalkerState::initState %d\n", state);
+    assert(state == Ready);
+
+    started = false;
+    mode = _mode;
+    timing = !is_functional;
+    enableNX = true;
+    dataSize = 8; // 64-bit PDEs / PTEs
+    nextState = PDE2;
+
+    DPRINTF(GPUPTWalker, "Setup walk with base %#lx\n", baseAddr);
+
+    // First level in Vega is PDE2. Calculate the address for that PDE using
+    // baseAddr and vaddr.
+    state = PDE2;
+    Addr logical_addr = vaddr >> PageShift;
+    Addr pde2Addr = (((baseAddr >> 6) << 3) + (logical_addr >> 3*9)) << 3;
+    DPRINTF(GPUPTWalker, "Walk PDE2 address is %#lx\n", pde2Addr);
+
+    // Start populating the VegaTlbEntry response
+    entry.vaddr = logical_addr;
+
+    // Prepare the read packet that will be used at each level
+    Request::Flags flags = Request::PHYSICAL;
+
+    RequestPtr request = std::make_shared<Request>(
+        pde2Addr, dataSize, flags, walker->deviceRequestorId);
+
+    read = new Packet(request, MemCmd::ReadReq);
+    read->allocate();
+}
+
+void
+Walker::WalkerState::startWalk()
+{
+    if (!started) {
+        // Read the first PDE to begin
+        DPRINTF(GPUPTWalker, "Sending timing read to %#lx\n",
+                read->getAddr());
+
+        sendPackets();
+        started = true;
+    } else {
+        // This is mostly the same as stepWalk except we update the state and
+        // send the new timing read request.
+        timingFault = stepWalk();
+        assert(timingFault == NoFault || read == NULL);
+
+        state = nextState;
+
+        if (read) {
+            DPRINTF(GPUPTWalker, "Sending timing read to %#lx\n",
+                    read->getAddr());
+            sendPackets();
+        } else {
+            // Set physical page address in entry
+            entry.paddr = bits(entry.pte, 47, entry.logBytes);
+            entry.paddr <<= entry.logBytes;
+
+            // Insert to TLB
+            assert(walker);
+            assert(walker->tlb);
+            walker->tlb->insert(entry.vaddr, entry);
+
+            // Send translation return event
+            walker->walkerResponse(this, entry, tlbPkt);
+        }
+    }
+}
+
+Fault
+Walker::WalkerState::stepWalk()
+{
+    assert(state != Ready && state != Waiting && read);
+    Fault fault = NoFault;
+    PageTableEntry pte = read->getLE<uint64_t>();
+
+    bool uncacheable = !pte.c;
+    Addr nextRead = 0;
+    bool doEndWalk = false;
+
+    walkStateMachine(pte, nextRead, doEndWalk, fault);
+
+    if (doEndWalk) {
+        DPRINTF(GPUPTWalker, "ending walk\n");
+        endWalk();
+    } else {
+        PacketPtr oldRead = read;
+
+        //If we didn't return, we're setting up another read.
+        Request::Flags flags = oldRead->req->getFlags();
+        flags.set(Request::UNCACHEABLE, uncacheable);
+        RequestPtr request = std::make_shared<Request>(
+            nextRead, oldRead->getSize(), flags, walker->deviceRequestorId);
+
+        read = new Packet(request, MemCmd::ReadReq);
+        read->allocate();
+
+        delete oldRead;
+    }
+
+    return fault;
+}
+
+void
+Walker::WalkerState::walkStateMachine(PageTableEntry &pte, Addr &nextRead,
+                                      bool &doEndWalk, Fault &fault)
+{
+    Addr vaddr = entry.vaddr;
+    bool badNX = pte.x && mode == BaseMMU::Execute && enableNX;
+    Addr part1 = 0;
+    Addr part2 = 0;
+    PageDirectoryEntry pde = static_cast<PageDirectoryEntry>(pte);
+
+    // For a four level page table block fragment size should not be needed.
+    // For now issue a panic to prevent strange behavior if it is non-zero.
+    panic_if(pde.blockFragmentSize, "PDE blockFragmentSize must be 0");
+
+    switch(state) {
+      case PDE2:
+        fatal_if(pde.p, "Fragment in PDE2 not implemented");
+
+        // Read the pde1Addr
+        part1 = ((((uint64_t)pte) >> 6) << 3);
+        part2 = offsetFunc(vaddr, 3*9, 2*9);
+        nextRead = ((part1 + part2) << 3) & mask(48);
+        DPRINTF(GPUPTWalker,
+                "Got PDE2 entry %#016x. write:%s->%#016x va:%#016x\n",
+                (uint64_t)pte, pte.w == 0 ? "yes" : "no", nextRead, vaddr);
+        nextState = PDE1;
+        break;
+      case PDE1:
+        fatal_if(pde.p, "Fragment in PDE1 not implemented");
+
+        // Read the pde0Addr
+        part1 = ((((uint64_t)pte) >> 6) << 3);
+        part2 = offsetFunc(vaddr, 2*9, 9);
+        nextRead = ((part1 + part2) << 3) & mask(48);
+        DPRINTF(GPUPTWalker,
+                "Got PDE1 entry %#016x. write:%s->%#016x va: %#016x\n",
+                (uint64_t)pte, pte.w == 0 ? "yes" : "no", nextRead, vaddr);
+        nextState = PDE0;
+        break;
+      case PDE0:
+        if (pde.p) {
+            DPRINTF(GPUPTWalker, "Treating PDE0 as PTE: %#016x frag: %d\n",
+                    (uint64_t)pte, pte.fragment);
+            entry.pte = pte;
+            int fragment = pte.fragment;
+            entry.logBytes = PageShift + std::min(9, fragment);
+            entry.vaddr <<= PageShift;
+            entry.vaddr = entry.vaddr & ~((1 << entry.logBytes) - 1);
+            entry.vaddr = entry.vaddr & ~mask(entry.logBytes);
+            doEndWalk = true;
+        }
+        // Read the PteAddr
+        part1 = ((((uint64_t)pte) >> 6) << 3);
+        part2 = offsetFunc(vaddr, 9, 0);
+        nextRead = ((part1 + part2) << 3) & mask(48);
+        DPRINTF(GPUPTWalker,
+                "Got PDE0 entry %#016x. write:%s->%#016x va:%#016x\n",
+                (uint64_t)pte, pte.w == 0 ? "yes" : "no", nextRead, vaddr);
+        nextState = PTE;
+        break;
+      case PTE:
+        DPRINTF(GPUPTWalker,
+                " PTE entry %#016x. write: %s va: %#016x\n",
+                (uint64_t)pte, pte.w == 0 ? "yes" : "no", vaddr);
+        entry.pte = pte;
+        entry.logBytes = PageShift;
+        entry.vaddr <<= PageShift;
+        entry.vaddr = entry.vaddr & ~mask(entry.logBytes);
+        doEndWalk = true;
+        break;
+      default:
+        panic("Unknown page table walker state %d!\n");
+    }
+
+    if (badNX || !pte.v) {
+        doEndWalk = true;
+        fault = pageFault(pte.v);
+        nextState = state;
+    }
+}
+
+void
+Walker::WalkerState::endWalk()
+{
+    nextState = Ready;
+    delete read;
+    read = NULL;
+    walker->currStates.remove(this);
+}
+
+/**
+ * Port related methods
+ */
+void
+Walker::WalkerState::sendPackets()
+{
+    // If we're already waiting for the port to become available, just return.
+    if (retrying)
+        return;
+
+    if (!walker->sendTiming(this, read)) {
+        DPRINTF(GPUPTWalker, "Timing request for %#lx failed\n",
+                read->getAddr());
+
+        retrying = true;
+    } else {
+        DPRINTF(GPUPTWalker, "Timing request for %#lx successful\n",
+                read->getAddr());
+    }
+}
+
+bool Walker::sendTiming(WalkerState* sending_walker, PacketPtr pkt)
+{
+    auto walker_state = new WalkerSenderState(sending_walker);
+    pkt->pushSenderState(walker_state);
+
+    if (port.sendTimingReq(pkt)) {
+        DPRINTF(GPUPTWalker, "Sending timing read to %#lx from walker %p\n",
+                pkt->getAddr(), sending_walker);
+
+        return true;
+    } else {
+        (void)pkt->popSenderState();
+    }
+
+    return false;
+}
+
+bool
+Walker::WalkerPort::recvTimingResp(PacketPtr pkt)
+{
+    walker->recvTimingResp(pkt);
+
+    return true;
+}
+
+void
+Walker::recvTimingResp(PacketPtr pkt)
+{
+    WalkerSenderState * senderState =
+        safe_cast<WalkerSenderState *>(pkt->popSenderState());
+
+    DPRINTF(GPUPTWalker, "Got response for %#lx from walker %p -- %#lx\n",
+            pkt->getAddr(), senderState->senderWalk, pkt->getLE<uint64_t>());
+    senderState->senderWalk->startWalk();
+
+    delete senderState;
+}
+
+void
+Walker::WalkerPort::recvReqRetry()
+{
+    walker->recvReqRetry();
+}
+
+void
+Walker::recvReqRetry()
+{
+    std::list<WalkerState *>::iterator iter;
+    for (iter = currStates.begin(); iter != currStates.end(); iter++) {
+        WalkerState * walkerState = *(iter);
+        if (walkerState->isRetrying()) {
+            walkerState->retry();
+        }
+    }
+}
+
+void
+Walker::walkerResponse(WalkerState *state, VegaTlbEntry& entry, PacketPtr pkt)
+{
+    tlb->walkerResponse(entry, pkt);
+
+    delete state;
+}
+
+
+/*
+ *  Helper methods
+ */
+bool
+Walker::WalkerState::isRetrying()
+{
+    return retrying;
+}
+
+void
+Walker::WalkerState::retry()
+{
+    retrying = false;
+    sendPackets();
+}
+
+Fault
+Walker::WalkerState::pageFault(bool present)
+{
+    DPRINTF(GPUPTWalker, "Raising page fault.\n");
+    ExceptionCode code;
+    if (mode == BaseMMU::Read)
+        code = ExceptionCode::LOAD_PAGE;
+    else if (mode == BaseMMU::Write)
+        code = ExceptionCode::STORE_PAGE;
+    else
+        code = ExceptionCode::INST_PAGE;
+    if (mode == BaseMMU::Execute && !enableNX)
+        mode = BaseMMU::Read;
+    return std::make_shared<PageFault>(entry.vaddr, code, present, mode, true);
+}
+
+uint64_t
+Walker::WalkerState::offsetFunc(Addr logicalAddr, int top, int lsb)
+{
+    assert(top < 32);
+    assert(lsb < 32);
+    return ((logicalAddr & ((1 << top) - 1)) >> lsb);
+}
+
+
+/**
+ * gem5 methods
+ */
+Port &
+Walker::getPort(const std::string &if_name, PortID idx)
+{
+    if (if_name == "port")
+        return port;
+    else
+        return ClockedObject::getPort(if_name, idx);
+}
+
+} // namespace VegaISA
+} // namespace gem5
diff --git a/src/arch/amdgpu/vega/pagetable_walker.hh b/src/arch/amdgpu/vega/pagetable_walker.hh
new file mode 100644
index 0000000..b00c0a0
--- /dev/null
+++ b/src/arch/amdgpu/vega/pagetable_walker.hh
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_AMDGPU_PAGETABLE_WALKER_HH__
+#define __DEV_AMDGPU_PAGETABLE_WALKER_HH__
+
+#include <vector>
+
+#include "arch/amdgpu/vega/pagetable.hh"
+#include "arch/amdgpu/vega/tlb.hh"
+#include "base/types.hh"
+#include "debug/GPUPTWalker.hh"
+#include "mem/packet.hh"
+#include "params/VegaPagetableWalker.hh"
+#include "sim/clocked_object.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+
+class ThreadContext;
+
+namespace VegaISA
+{
+
+class Walker : public ClockedObject
+{
+  protected:
+    // Port for accessing memory
+    class WalkerPort : public RequestPort
+    {
+      public:
+        WalkerPort(const std::string &_name, Walker * _walker) :
+            RequestPort(_name, _walker), walker(_walker)
+        {}
+
+      protected:
+        Walker *walker;
+
+        bool recvTimingResp(PacketPtr pkt);
+        void recvReqRetry();
+    };
+
+    friend class WalkerPort;
+    WalkerPort port;
+
+    // State to track each walk of the page table
+    class WalkerState
+    {
+        friend class Walker;
+
+      private:
+        enum State
+        {
+            Ready,
+            Waiting,
+            PDE2, PDE1, PDE0, PTE
+        };
+
+      protected:
+        Walker *walker;
+        State state;
+        State nextState;
+        int dataSize;
+        bool enableNX;
+        VegaTlbEntry entry;
+        PacketPtr read;
+        Fault timingFault;
+        BaseMMU::Mode mode;
+        bool retrying;
+        bool started;
+        bool timing;
+        PacketPtr tlbPkt;
+
+      public:
+        WalkerState(Walker *_walker, PacketPtr pkt, bool is_functional = false)
+            : walker(_walker), state(Ready), nextState(Ready), dataSize(8),
+              enableNX(true), retrying(false), started(false), tlbPkt(pkt)
+        {
+            DPRINTF(GPUPTWalker, "Walker::WalkerState %p %p %d\n",
+                    this, walker, state);
+        }
+
+        void initState(BaseMMU::Mode _mode, Addr baseAddr, Addr vaddr,
+                       bool is_functional = false);
+        void startWalk();
+        Fault startFunctional(Addr base, Addr vaddr, PageTableEntry &pte,
+                              unsigned &logBytes);
+
+        bool isRetrying();
+        void retry();
+        std::string name() const { return walker->name(); }
+        Walker* getWalker() const { return walker; }
+
+      private:
+        Fault stepWalk();
+        void stepTimingWalk();
+        void walkStateMachine(PageTableEntry &pte, Addr &nextRead,
+                              bool &doEndWalk, Fault &fault);
+        void sendPackets();
+        void endWalk();
+        Fault pageFault(bool present);
+        uint64_t offsetFunc(Addr logicalAddr, int top, int lsb);
+    };
+
+    friend class WalkerState;
+    // State for timing and atomic accesses (need multiple per walker in
+    // the case of multiple outstanding requests in timing mode)
+    std::list<WalkerState *> currStates;
+    // State for functional accesses (only need one of these per walker)
+    WalkerState funcState;
+
+    struct WalkerSenderState : public Packet::SenderState
+    {
+        WalkerState * senderWalk;
+        WalkerSenderState(WalkerState * _senderWalk) :
+            senderWalk(_senderWalk) {}
+    };
+
+  public:
+    // Kick off the state machine.
+    void startTiming(PacketPtr pkt, Addr base, Addr vaddr, BaseMMU::Mode mode);
+    Fault startFunctional(Addr base, Addr vaddr, PageTableEntry &pte,
+                          unsigned &logBytes, BaseMMU::Mode mode);
+    Fault startFunctional(Addr base, Addr &addr, unsigned &logBytes,
+                          BaseMMU::Mode mode, bool &isSystem);
+
+    Port &getPort(const std::string &if_name,
+                  PortID idx=InvalidPortID) override;
+
+    Addr getBaseAddr() const { return baseAddr; }
+    void setBaseAddr(Addr ta) { baseAddr = ta; }
+
+    void setDevRequestor(RequestorID mid) { deviceRequestorId = mid; }
+    RequestorID getDevRequestor() const { return deviceRequestorId; }
+
+  protected:
+    // The TLB we're supposed to load.
+    GpuTLB *tlb;
+    RequestorID requestorId;
+
+    // Base address set by MAP_PROCESS packet
+    Addr baseAddr;
+    RequestorID deviceRequestorId;
+
+    // Functions for dealing with packets.
+    void recvTimingResp(PacketPtr pkt);
+    void recvReqRetry();
+    bool sendTiming(WalkerState * sendingState, PacketPtr pkt);
+
+    void walkerResponse(WalkerState *state, VegaTlbEntry& entry,
+                        PacketPtr pkt);
+
+    // System pointer for functional accesses
+    System *system;
+
+  public:
+    void setTLB(GpuTLB * _tlb)
+    {
+        assert(tlb == nullptr); // only set it once
+        tlb = _tlb;
+    }
+
+    Walker(const VegaPagetableWalkerParams &p)
+      : ClockedObject(p),
+        port(name() + ".port", this),
+        funcState(this, nullptr, true), tlb(nullptr),
+        requestorId(p.system->getRequestorId(this)),
+        deviceRequestorId(999), system(p.system)
+    {
+        DPRINTF(GPUPTWalker, "Walker::Walker %p\n", this);
+    }
+};
+
+} // namespace VegaISA
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_PAGETABLE_WALKER_HH__
diff --git a/src/arch/amdgpu/vega/tlb.cc b/src/arch/amdgpu/vega/tlb.cc
new file mode 100644
index 0000000..5d9a9e5
--- /dev/null
+++ b/src/arch/amdgpu/vega/tlb.cc
@@ -0,0 +1,999 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "arch/amdgpu/vega/tlb.hh"
+
+#include <cmath>
+#include <cstring>
+
+#include "arch/amdgpu/common/gpu_translation_state.hh"
+#include "arch/amdgpu/vega/faults.hh"
+#include "arch/amdgpu/vega/pagetable_walker.hh"
+#include "debug/GPUPrefetch.hh"
+#include "debug/GPUTLB.hh"
+#include "dev/amdgpu/amdgpu_device.hh"
+
+namespace gem5
+{
+namespace VegaISA
+{
+
+// we have no limit for the number of translations we send
+// downstream as we depend on the limit of the coalescer
+// above us
+GpuTLB::GpuTLB(const VegaGPUTLBParams &p)
+    :  ClockedObject(p), walker(p.walker),
+      gpuDevice(p.gpu_device), size(p.size), stats(this),
+      cleanupEvent([this]{ cleanup(); }, name(), false,
+                   Event::Maximum_Pri)
+{
+    assoc = p.assoc;
+    assert(assoc <= size);
+    numSets = size/assoc;
+    allocationPolicy = p.allocationPolicy;
+    hasMemSidePort = false;
+
+    tlb.assign(size, VegaTlbEntry());
+
+    freeList.resize(numSets);
+    entryList.resize(numSets);
+
+    for (int set = 0; set < numSets; ++set) {
+        for (int way = 0; way < assoc; ++way) {
+            int x = set * assoc + way;
+            freeList[set].push_back(&tlb.at(x));
+        }
+    }
+
+    FA = (size == assoc);
+    setMask = numSets - 1;
+
+    maxCoalescedReqs = p.maxOutstandingReqs;
+
+
+    outstandingReqs = 0;
+    hitLatency = p.hitLatency;
+    missLatency1 = p.missLatency1;
+    missLatency2 = p.missLatency2;
+
+    // create the response ports based on the number of connected ports
+    for (size_t i = 0; i < p.port_cpu_side_ports_connection_count; ++i) {
+        cpuSidePort.push_back(new CpuSidePort(csprintf("%s-port%d",
+                              name(), i), this, i));
+    }
+
+    // create the requestor ports based on the number of connected ports
+    for (size_t i = 0; i < p.port_mem_side_ports_connection_count; ++i) {
+        memSidePort.push_back(new MemSidePort(csprintf("%s-port%d",
+                              name(), i), this, i));
+    }
+
+    // assuming one walker per TLB, set our walker's TLB to this TLB.
+    walker->setTLB(this);
+
+    // gpuDevice should be non-null in full system only and is set by GpuTLB
+    // params from the config file.
+    if (gpuDevice) {
+        gpuDevice->getVM().registerTLB(this);
+    }
+}
+
+GpuTLB::~GpuTLB()
+{
+}
+
+Port &
+GpuTLB::getPort(const std::string &if_name, PortID idx)
+{
+    if (if_name == "cpu_side_ports") {
+        if (idx >= static_cast<PortID>(cpuSidePort.size())) {
+            panic("TLBCoalescer::getPort: unknown index %d\n", idx);
+        }
+
+        return *cpuSidePort[idx];
+    } else if (if_name == "mem_side_ports") {
+        if (idx >= static_cast<PortID>(memSidePort.size())) {
+            panic("TLBCoalescer::getPort: unknown index %d\n", idx);
+        }
+
+        hasMemSidePort = true;
+
+        return *memSidePort[idx];
+    } else {
+        panic("TLBCoalescer::getPort: unknown port %s\n", if_name);
+    }
+}
+
+Fault
+GpuTLB::createPagefault(Addr vaddr, Mode mode)
+{
+    DPRINTF(GPUTLB, "GPUTLB: Raising page fault.\n");
+    ExceptionCode code;
+    if (mode == BaseMMU::Read)
+        code = ExceptionCode::LOAD_PAGE;
+    else if (mode == BaseMMU::Write)
+        code = ExceptionCode::STORE_PAGE;
+    else
+        code = ExceptionCode::INST_PAGE;
+    return std::make_shared<PageFault>(vaddr, code, true, mode, true);
+}
+
+Addr
+GpuTLB::pageAlign(Addr vaddr)
+{
+    Addr pageMask = mask(VegaISA::PageShift);
+    return (vaddr & ~pageMask);
+}
+
+VegaTlbEntry*
+GpuTLB::insert(Addr vpn, VegaTlbEntry &entry)
+{
+    VegaTlbEntry *newEntry = nullptr;
+
+    /**
+     * vpn holds the virtual page address assuming native page size.
+     * However, we need to check the entry size as Vega supports
+     * flexible page sizes of arbitrary size. The set will assume
+     * native page size but the vpn needs to be fixed up to consider
+     * the flexible page size.
+     */
+    Addr real_vpn = vpn & ~(entry.size() - 1);
+
+    /**
+     * Also fix up the ppn as this is used in the math later to compute paddr.
+     */
+    Addr real_ppn = entry.paddr & ~(entry.size() - 1);
+
+    int set = (real_vpn >> VegaISA::PageShift) & setMask;
+
+    DPRINTF(GPUTLB, "Inserted %#lx -> %#lx of size %#lx into set %d\n",
+            real_vpn, real_ppn, entry.size(), set);
+
+    if (!freeList[set].empty()) {
+        newEntry = freeList[set].front();
+        freeList[set].pop_front();
+    } else {
+        newEntry = entryList[set].back();
+        entryList[set].pop_back();
+    }
+
+    *newEntry = entry;
+    newEntry->vaddr = real_vpn;
+    newEntry->paddr = real_ppn;
+    entryList[set].push_front(newEntry);
+
+    return newEntry;
+}
+
+GpuTLB::EntryList::iterator
+GpuTLB::lookupIt(Addr va, bool update_lru)
+{
+    int set = (va >> VegaISA::PageShift) & setMask;
+
+    if (FA) {
+        assert(!set);
+    }
+
+    auto entry = entryList[set].begin();
+    for (; entry != entryList[set].end(); ++entry) {
+        int page_size = (*entry)->size();
+
+        if ((*entry)->vaddr <= va && (*entry)->vaddr + page_size > va) {
+            DPRINTF(GPUTLB, "Matched vaddr %#x to entry starting at %#x "
+                    "with size %#x.\n", va, (*entry)->vaddr, page_size);
+
+            if (update_lru) {
+                entryList[set].push_front(*entry);
+                entryList[set].erase(entry);
+                entry = entryList[set].begin();
+            }
+
+            break;
+        }
+    }
+
+    return entry;
+}
+
+VegaTlbEntry*
+GpuTLB::lookup(Addr va, bool update_lru)
+{
+    int set = (va >> VegaISA::PageShift) & setMask;
+
+    auto entry = lookupIt(va, update_lru);
+
+    if (entry == entryList[set].end())
+        return nullptr;
+    else
+        return *entry;
+}
+
+void
+GpuTLB::invalidateAll()
+{
+    DPRINTF(GPUTLB, "Invalidating all entries.\n");
+
+    for (int i = 0; i < numSets; ++i) {
+        while (!entryList[i].empty()) {
+            VegaTlbEntry *entry = entryList[i].front();
+            entryList[i].pop_front();
+            freeList[i].push_back(entry);
+        }
+    }
+}
+
+void
+GpuTLB::demapPage(Addr va, uint64_t asn)
+{
+
+    int set = (va >> VegaISA::PageShift) & setMask;
+    auto entry = lookupIt(va, false);
+
+    if (entry != entryList[set].end()) {
+        freeList[set].push_back(*entry);
+        entryList[set].erase(entry);
+    }
+}
+
+
+
+/**
+ * TLB_lookup will only perform a TLB lookup returning the TLB entry on a TLB
+ * hit and nullptr on a TLB miss.
+ * Many of the checks about different modes have been converted to
+ * assertions, since these parts of the code are not really used.
+ * On a hit it will update the LRU stack.
+ */
+VegaTlbEntry *
+GpuTLB::tlbLookup(const RequestPtr &req, bool update_stats)
+{
+    Addr vaddr = req->getVaddr();
+    Addr alignedVaddr = pageAlign(vaddr);
+    DPRINTF(GPUTLB, "TLB Lookup for vaddr %#x.\n", vaddr);
+
+    //update LRU stack on a hit
+    VegaTlbEntry *entry = lookup(alignedVaddr, true);
+
+    if (!update_stats) {
+        // functional tlb access for memory initialization
+        // i.e., memory seeding or instr. seeding -> don't update
+        // TLB and stats
+        return entry;
+    }
+
+    stats.localNumTLBAccesses++;
+
+    if (!entry) {
+        stats.localNumTLBMisses++;
+    } else {
+        stats.localNumTLBHits++;
+    }
+
+    return entry;
+}
+
+Walker*
+GpuTLB::getWalker()
+{
+    return walker;
+}
+
+
+void
+GpuTLB::serialize(CheckpointOut &cp) const
+{
+}
+
+void
+GpuTLB::unserialize(CheckpointIn &cp)
+{
+}
+
+/**
+ * Do the TLB lookup for this coalesced request and schedule
+ * another event <TLB access latency> cycles later.
+ */
+
+void
+GpuTLB::issueTLBLookup(PacketPtr pkt)
+{
+    assert(pkt);
+    assert(pkt->senderState);
+
+    /**
+     * The page size is not fixed in Vega and tracking events by VPN could
+     * potentially lead to redundant page walks by using the smallest page
+     * size. The actual VPN can be determined after the first walk is done
+     * and fixed up later.
+     */
+    Addr virt_page_addr = roundDown(pkt->req->getVaddr(),
+                                    VegaISA::PageBytes);
+
+    GpuTranslationState *sender_state =
+            safe_cast<GpuTranslationState*>(pkt->senderState);
+
+    bool update_stats = !sender_state->isPrefetch;
+
+    DPRINTF(GPUTLB, "Translation req. for virt. page addr %#x\n",
+            virt_page_addr);
+
+    int req_cnt = sender_state->reqCnt.back();
+
+    if (update_stats) {
+        stats.accessCycles -= (curCycle() * req_cnt);
+        stats.localCycles -= curCycle();
+        stats.globalNumTLBAccesses += req_cnt;
+    }
+
+    tlbOutcome lookup_outcome = TLB_MISS;
+    const RequestPtr &tmp_req = pkt->req;
+
+    // Access the TLB and figure out if it's a hit or a miss.
+    auto entry = tlbLookup(tmp_req, update_stats);
+
+    if (entry) {
+        lookup_outcome = TLB_HIT;
+        // Put the entry in SenderState
+        VegaTlbEntry *entry = lookup(virt_page_addr, false);
+        assert(entry);
+
+        // Set if this is a system request
+        pkt->req->setSystemReq(entry->pte.s);
+
+        Addr alignedPaddr = pageAlign(entry->paddr);
+        sender_state->tlbEntry =
+            new VegaTlbEntry(1 /* VMID */, virt_page_addr, alignedPaddr,
+                            entry->logBytes, entry->pte);
+
+        if (update_stats) {
+            // the reqCnt has an entry per level, so its size tells us
+            // which level we are in
+            sender_state->hitLevel = sender_state->reqCnt.size();
+            stats.globalNumTLBHits += req_cnt;
+        }
+    } else {
+        if (update_stats)
+            stats.globalNumTLBMisses += req_cnt;
+    }
+
+    /*
+     * We now know the TLB lookup outcome (if it's a hit or a miss), as
+     * well as the TLB access latency.
+     *
+     * We create and schedule a new TLBEvent which will help us take the
+     * appropriate actions (e.g., update TLB on a hit, send request to
+     * lower level TLB on a miss, or start a page walk if this was the
+     * last-level TLB)
+     */
+    TLBEvent *tlb_event =
+        new TLBEvent(this, virt_page_addr, lookup_outcome, pkt);
+
+    if (translationReturnEvent.count(virt_page_addr)) {
+        panic("Virtual Page Address %#x already has a return event\n",
+              virt_page_addr);
+    }
+
+    translationReturnEvent[virt_page_addr] = tlb_event;
+    assert(tlb_event);
+
+    DPRINTF(GPUTLB, "schedule translationReturnEvent @ curTick %d\n",
+            curTick() + cyclesToTicks(Cycles(hitLatency)));
+
+    schedule(tlb_event, curTick() + cyclesToTicks(Cycles(hitLatency)));
+}
+
+GpuTLB::TLBEvent::TLBEvent(GpuTLB* _tlb, Addr _addr,
+    tlbOutcome tlb_outcome, PacketPtr _pkt)
+        : Event(CPU_Tick_Pri), tlb(_tlb), virtPageAddr(_addr),
+          outcome(tlb_outcome), pkt(_pkt)
+{
+}
+
+/**
+ * Do Paging protection checks. If we encounter a page fault, then
+ * an assertion is fired.
+ */
+void
+GpuTLB::pagingProtectionChecks(PacketPtr pkt, VegaTlbEntry * tlb_entry,
+        Mode mode)
+{
+    // Do paging protection checks.
+    bool badWrite = (!tlb_entry->writable());
+
+    if (mode == BaseMMU::Write && badWrite) {
+        // The page must have been present to get into the TLB in
+        // the first place. We'll assume the reserved bits are
+        // fine even though we're not checking them.
+        fatal("Page fault on addr %lx PTE=%#lx", pkt->req->getVaddr(),
+                (uint64_t)tlb_entry->pte);
+    }
+}
+
+void
+GpuTLB::walkerResponse(VegaTlbEntry& entry, PacketPtr pkt)
+{
+    DPRINTF(GPUTLB, "WalkerResponse for %#lx. Entry: (%#lx, %#lx, %#lx)\n",
+            pkt->req->getVaddr(), entry.vaddr, entry.paddr, entry.size());
+
+    Addr virt_page_addr = roundDown(pkt->req->getVaddr(),
+                                    VegaISA::PageBytes);
+
+    Addr page_addr = entry.pte.ppn << VegaISA::PageShift;
+    Addr paddr = insertBits(page_addr, entry.logBytes - 1, 0, entry.vaddr);
+    pkt->req->setPaddr(paddr);
+    pkt->req->setSystemReq(entry.pte.s);
+
+    GpuTranslationState *sender_state =
+        safe_cast<GpuTranslationState*>(pkt->senderState);
+    sender_state->tlbEntry = new VegaTlbEntry(entry);
+
+    handleTranslationReturn(virt_page_addr, TLB_MISS, pkt);
+}
+
+/**
+ * handleTranslationReturn is called on a TLB hit,
+ * when a TLB miss returns or when a page fault returns.
+ * The latter calls handelHit with TLB miss as tlbOutcome.
+ */
+void
+GpuTLB::handleTranslationReturn(Addr virt_page_addr,
+    tlbOutcome tlb_outcome, PacketPtr pkt)
+{
+    assert(pkt);
+    Addr vaddr = pkt->req->getVaddr();
+
+    GpuTranslationState *sender_state =
+        safe_cast<GpuTranslationState*>(pkt->senderState);
+
+    Mode mode = sender_state->tlbMode;
+
+    VegaTlbEntry *local_entry, *new_entry;
+
+    int req_cnt = sender_state->reqCnt.back();
+    bool update_stats = !sender_state->isPrefetch;
+
+    if (update_stats) {
+        stats.accessCycles += (req_cnt * curCycle());
+        stats.localCycles += curCycle();
+    }
+
+    if (tlb_outcome == TLB_HIT) {
+        DPRINTF(GPUTLB, "Translation Done - TLB Hit for addr %#x\n",
+            vaddr);
+        local_entry = safe_cast<VegaTlbEntry *>(sender_state->tlbEntry);
+    } else {
+        DPRINTF(GPUTLB, "Translation Done - TLB Miss for addr %#x\n",
+                vaddr);
+
+        /**
+         * We are returning either from a page walk or from a hit at a
+         * lower TLB level. The senderState should be "carrying" a pointer
+         * to the correct TLBEntry.
+         */
+        new_entry = safe_cast<VegaTlbEntry *>(sender_state->tlbEntry);
+        assert(new_entry);
+        local_entry = new_entry;
+
+        if (allocationPolicy) {
+            assert(new_entry->pte);
+            DPRINTF(GPUTLB, "allocating entry w/ addr %#lx of size %#lx\n",
+                    virt_page_addr, new_entry->size());
+
+            local_entry = insert(virt_page_addr, *new_entry);
+        }
+
+        assert(local_entry);
+    }
+
+    /**
+     * At this point the packet carries an up-to-date tlbEntry pointer
+     * in its senderState.
+     * Next step is to do the paging protection checks.
+     */
+    DPRINTF(GPUTLB, "Entry found with vaddr %#x,  doing protection checks "
+            "while paddr was %#x.\n", local_entry->vaddr,
+            local_entry->paddr);
+
+    pagingProtectionChecks(pkt, local_entry, mode);
+    int page_size = local_entry->size();
+    Addr paddr = local_entry->paddr | (vaddr & (page_size - 1));
+    DPRINTF(GPUTLB, "Translated %#x -> %#x.\n", vaddr, paddr);
+
+    // Since this packet will be sent through the cpu side port, it must be
+    // converted to a response pkt if it is not one already
+    if (pkt->isRequest()) {
+        pkt->makeTimingResponse();
+    }
+
+    pkt->req->setPaddr(paddr);
+
+    if (local_entry->uncacheable()) {
+         pkt->req->setFlags(Request::UNCACHEABLE);
+    }
+
+    //send packet back to coalescer
+    cpuSidePort[0]->sendTimingResp(pkt);
+    //schedule cleanup event
+    cleanupQueue.push(virt_page_addr);
+
+    DPRINTF(GPUTLB, "Scheduled %#lx for cleanup\n", virt_page_addr);
+
+    // schedule this only once per cycle.
+    // The check is required because we might have multiple translations
+    // returning the same cycle
+    // this is a maximum priority event and must be on the same cycle
+    // as the cleanup event in TLBCoalescer to avoid a race with
+    // IssueProbeEvent caused by TLBCoalescer::MemSidePort::recvReqRetry
+    if (!cleanupEvent.scheduled())
+        schedule(cleanupEvent, curTick());
+}
+
+/**
+ * Here we take the appropriate actions based on the result of the
+ * TLB lookup.
+ */
+void
+GpuTLB::translationReturn(Addr virtPageAddr, tlbOutcome outcome,
+                          PacketPtr pkt)
+{
+    DPRINTF(GPUTLB, "Triggered TLBEvent for addr %#x\n", virtPageAddr);
+
+    assert(translationReturnEvent[virtPageAddr]);
+    assert(pkt);
+
+    GpuTranslationState *tmp_sender_state =
+        safe_cast<GpuTranslationState*>(pkt->senderState);
+
+    int req_cnt = tmp_sender_state->reqCnt.back();
+    bool update_stats = !tmp_sender_state->isPrefetch;
+
+
+    if (outcome == TLB_HIT) {
+        handleTranslationReturn(virtPageAddr, TLB_HIT, pkt);
+
+    } else if (outcome == TLB_MISS) {
+
+        DPRINTF(GPUTLB, "This is a TLB miss\n");
+        if (hasMemSidePort) {
+            // the one cyle added here represent the delay from when we get
+            // the reply back till when we propagate it to the coalescer
+            // above.
+
+            /**
+             * There is a TLB below. Send the coalesced request.
+             * We actually send the very first packet of all the
+             * pending packets for this virtual page address.
+             */
+            tmp_sender_state->deviceId = 1;
+            tmp_sender_state->pasId = 0;
+
+            if (!memSidePort[0]->sendTimingReq(pkt)) {
+                DPRINTF(GPUTLB, "Failed sending translation request to "
+                        "lower level TLB for addr %#x\n", virtPageAddr);
+
+                memSidePort[0]->retries.push_back(pkt);
+            } else {
+                DPRINTF(GPUTLB, "Sent translation request to lower level "
+                        "TLB for addr %#x\n", virtPageAddr);
+            }
+        } else {
+            //this is the last level TLB. Start a page walk
+            DPRINTF(GPUTLB, "Last level TLB - start a page walk for "
+                    "addr %#x\n", virtPageAddr);
+
+            if (update_stats)
+                stats.pageTableCycles -= (req_cnt*curCycle());
+
+            TLBEvent *tlb_event = translationReturnEvent[virtPageAddr];
+            assert(tlb_event);
+            tlb_event->updateOutcome(PAGE_WALK);
+            schedule(tlb_event,
+                     curTick() + cyclesToTicks(Cycles(missLatency2)));
+        }
+    } else if (outcome == PAGE_WALK) {
+        if (update_stats)
+            stats.pageTableCycles += (req_cnt*curCycle());
+
+        // Need to access the page table and update the TLB
+        DPRINTF(GPUTLB, "Doing a page walk for address %#x\n",
+                virtPageAddr);
+
+        Addr base = gpuDevice->getVM().getPageTableBase(1);
+        Addr vaddr = pkt->req->getVaddr();
+        walker->setDevRequestor(gpuDevice->vramRequestorId());
+
+        // Do page table walk
+        walker->startTiming(pkt, base, vaddr, BaseMMU::Mode::Read);
+    } else if (outcome == MISS_RETURN) {
+        /** we add an extra cycle in the return path of the translation
+         * requests in between the various TLB levels.
+         */
+        handleTranslationReturn(virtPageAddr, TLB_MISS, pkt);
+    } else {
+        panic("Unexpected TLB outcome %d", outcome);
+    }
+}
+
+void
+GpuTLB::TLBEvent::process()
+{
+    tlb->translationReturn(virtPageAddr, outcome, pkt);
+}
+
+const char*
+GpuTLB::TLBEvent::description() const
+{
+    return "trigger translationDoneEvent";
+}
+
+void
+GpuTLB::TLBEvent::updateOutcome(tlbOutcome _outcome)
+{
+    outcome = _outcome;
+}
+
+Addr
+GpuTLB::TLBEvent::getTLBEventVaddr()
+{
+    return virtPageAddr;
+}
+
+/**
+ * recvTiming receives a coalesced timing request from a TLBCoalescer
+ * and it calls issueTLBLookup()
+ * It only rejects the packet if we have exceeded the max
+ * outstanding number of requests for the TLB
+ */
+bool
+GpuTLB::CpuSidePort::recvTimingReq(PacketPtr pkt)
+{
+    bool ret = false;
+    [[maybe_unused]] Addr virt_page_addr = roundDown(pkt->req->getVaddr(),
+                                                     VegaISA::PageBytes);
+
+    if (tlb->outstandingReqs < tlb->maxCoalescedReqs) {
+        assert(!tlb->translationReturnEvent.count(virt_page_addr));
+        tlb->issueTLBLookup(pkt);
+        // update number of outstanding translation requests
+        tlb->outstandingReqs++;
+        ret = true;
+    } else {
+        DPRINTF(GPUTLB, "Reached maxCoalescedReqs number %d\n",
+                tlb->outstandingReqs);
+        tlb->stats.maxDownstreamReached++;
+        ret = false;
+
+    }
+
+    if (tlb->outstandingReqs > tlb->stats.outstandingReqsMax.value())
+        tlb->stats.outstandingReqsMax = tlb->outstandingReqs;
+
+    return ret;
+}
+
+/**
+ * handleFuncTranslationReturn is called on a TLB hit,
+ * when a TLB miss returns or when a page fault returns.
+ * It updates LRU, inserts the TLB entry on a miss
+ * depending on the allocation policy and does the required
+ * protection checks. It does NOT create a new packet to
+ * update the packet's addr; this is done in hsail-gpu code.
+ */
+void
+GpuTLB::handleFuncTranslationReturn(PacketPtr pkt, tlbOutcome tlb_outcome)
+{
+    GpuTranslationState *sender_state =
+        safe_cast<GpuTranslationState*>(pkt->senderState);
+
+    Mode mode = sender_state->tlbMode;
+    Addr vaddr = pkt->req->getVaddr();
+
+    VegaTlbEntry *local_entry, *new_entry;
+
+    if (tlb_outcome == TLB_HIT) {
+        DPRINTF(GPUTLB, "Functional Translation Done - TLB hit for addr "
+                "%#x\n", vaddr);
+
+        local_entry = safe_cast<VegaTlbEntry *>(sender_state->tlbEntry);
+    } else {
+        DPRINTF(GPUTLB, "Functional Translation Done - TLB miss for addr "
+                "%#x\n", vaddr);
+
+        /**
+         * We are returning either from a page walk or from a hit at a
+         * lower TLB level. The senderState should be "carrying" a pointer
+         * to the correct TLBEntry.
+         */
+        new_entry = safe_cast<VegaTlbEntry *>(sender_state->tlbEntry);
+        assert(new_entry);
+        local_entry = new_entry;
+
+        if (allocationPolicy) {
+            Addr virt_page_addr = roundDown(vaddr, VegaISA::PageBytes);
+
+            DPRINTF(GPUTLB, "allocating entry w/ addr %#lx\n",
+                    virt_page_addr);
+
+            local_entry = insert(virt_page_addr, *new_entry);
+        }
+
+        assert(local_entry);
+    }
+
+    DPRINTF(GPUTLB, "Entry found with vaddr %#x, doing protection checks "
+            "while paddr was %#x.\n", local_entry->vaddr,
+            local_entry->paddr);
+
+    /**
+     * Do paging checks if it's a normal functional access.  If it's for a
+     * prefetch, then sometimes you can try to prefetch something that
+     * won't pass protection. We don't actually want to fault becuase there
+     * is no demand access to deem this a violation.  Just put it in the
+     * TLB and it will fault if indeed a future demand access touches it in
+     * violation.
+     *
+     * This feature could be used to explore security issues around
+     * speculative memory accesses.
+     */
+    if (!sender_state->isPrefetch && sender_state->tlbEntry)
+        pagingProtectionChecks(pkt, local_entry, mode);
+
+    int page_size = local_entry->size();
+    Addr paddr = local_entry->paddr | (vaddr & (page_size - 1));
+    DPRINTF(GPUTLB, "Translated %#x -> %#x.\n", vaddr, paddr);
+
+    pkt->req->setPaddr(paddr);
+
+    if (local_entry->uncacheable())
+         pkt->req->setFlags(Request::UNCACHEABLE);
+}
+
+// This is used for atomic translations. Need to
+// make it all happen during the same cycle.
+void
+GpuTLB::CpuSidePort::recvFunctional(PacketPtr pkt)
+{
+    GpuTranslationState *sender_state =
+        safe_cast<GpuTranslationState*>(pkt->senderState);
+
+    bool update_stats = !sender_state->isPrefetch;
+
+    Addr virt_page_addr = roundDown(pkt->req->getVaddr(),
+                                    VegaISA::PageBytes);
+
+    // do the TLB lookup without updating the stats
+    bool success = tlb->tlbLookup(pkt->req, update_stats);
+    tlbOutcome tlb_outcome = success ? TLB_HIT : TLB_MISS;
+
+    // functional mode means no coalescing
+    // global metrics are the same as the local metrics
+    if (update_stats) {
+        tlb->stats.globalNumTLBAccesses++;
+
+        if (success) {
+            sender_state->hitLevel = sender_state->reqCnt.size();
+            tlb->stats.globalNumTLBHits++;
+        } else {
+            tlb->stats.globalNumTLBMisses++;
+        }
+    }
+
+    if (!success) {
+        if (tlb->hasMemSidePort) {
+            // there is a TLB below -> propagate down the TLB hierarchy
+            tlb->memSidePort[0]->sendFunctional(pkt);
+            // If no valid translation from a prefetch, then just return
+            if (sender_state->isPrefetch && !pkt->req->hasPaddr())
+                return;
+        } else {
+            // Need to access the page table and update the TLB
+            DPRINTF(GPUTLB, "Doing a page walk for address %#x\n",
+                    virt_page_addr);
+
+            Addr vaddr = pkt->req->getVaddr();
+            [[maybe_unused]] Addr alignedVaddr =
+                tlb->pageAlign(virt_page_addr);
+            assert(alignedVaddr == virt_page_addr);
+
+            unsigned logBytes;
+            PageTableEntry pte;
+
+            // Initialize walker state for VMID
+            Addr base = tlb->gpuDevice->getVM().getPageTableBase(1);
+            tlb->walker->setDevRequestor(tlb->gpuDevice->vramRequestorId());
+
+            // Do page table walk
+            Fault fault = tlb->walker->startFunctional(base, vaddr, pte,
+                                                       logBytes,
+                                                       BaseMMU::Mode::Read);
+            if (fault != NoFault) {
+                fatal("Translation fault in TLB at %d!", __LINE__);
+            }
+
+            // PPN is already shifted by fragment so we only shift by native
+            // page size. Fragment is still used via logBytes to select lower
+            // bits from vaddr.
+            Addr page_addr = pte.ppn << PageShift;
+            Addr paddr = insertBits(page_addr, logBytes - 1, 0, vaddr);
+            Addr alignedPaddr = tlb->pageAlign(paddr);
+            pkt->req->setPaddr(paddr);
+            pkt->req->setSystemReq(pte.s);
+
+            if (!sender_state->isPrefetch) {
+                assert(paddr);
+
+                DPRINTF(GPUTLB, "Mapping %#x to %#x\n", vaddr, paddr);
+
+                sender_state->tlbEntry =
+                    new VegaTlbEntry(1 /* VMID */, virt_page_addr,
+                                 alignedPaddr, logBytes, pte);
+            } else {
+                // If this was a prefetch, then do the normal thing if it
+                // was a successful translation.  Otherwise, send an empty
+                // TLB entry back so that it can be figured out as empty
+                // and handled accordingly.
+                if (paddr) {
+                    DPRINTF(GPUTLB, "Mapping %#x to %#x\n", vaddr, paddr);
+
+                    sender_state->tlbEntry =
+                        new VegaTlbEntry(1 /* VMID */, virt_page_addr,
+                                     alignedPaddr, logBytes, pte);
+                } else {
+                    DPRINTF(GPUPrefetch, "Prefetch failed %#x\n", vaddr);
+
+                    sender_state->tlbEntry = nullptr;
+
+                    return;
+                }
+            }
+        }
+    } else {
+        VegaTlbEntry *entry = tlb->lookup(virt_page_addr, update_stats);
+        assert(entry);
+
+        if (sender_state->isPrefetch) {
+            DPRINTF(GPUPrefetch, "Functional Hit for vaddr %#x\n",
+                    entry->vaddr);
+        }
+
+        sender_state->tlbEntry = new VegaTlbEntry(1 /* VMID */, entry->vaddr,
+                                                 entry->paddr, entry->logBytes,
+                                                 entry->pte);
+    }
+
+    // This is the function that would populate pkt->req with the paddr of
+    // the translation. But if no translation happens (i.e Prefetch fails)
+    // then the early returns in the above code wiill keep this function
+    // from executing.
+    tlb->handleFuncTranslationReturn(pkt, tlb_outcome);
+}
+
+void
+GpuTLB::CpuSidePort::recvReqRetry()
+{
+    // The CPUSidePort never sends anything but replies. No retries
+    // expected.
+    panic("recvReqRetry called");
+}
+
+AddrRangeList
+GpuTLB::CpuSidePort::getAddrRanges() const
+{
+    // currently not checked by the requestor
+    AddrRangeList ranges;
+
+    return ranges;
+}
+
+/**
+ * MemSidePort receives the packet back.
+ * We need to call the handleTranslationReturn
+ * and propagate up the hierarchy.
+ */
+bool
+GpuTLB::MemSidePort::recvTimingResp(PacketPtr pkt)
+{
+    Addr virt_page_addr = roundDown(pkt->req->getVaddr(),
+                                    VegaISA::PageBytes);
+
+    DPRINTF(GPUTLB, "MemSidePort recvTiming for virt_page_addr %#x\n",
+            virt_page_addr);
+
+    TLBEvent *tlb_event = tlb->translationReturnEvent[virt_page_addr];
+    assert(tlb_event);
+    assert(virt_page_addr == tlb_event->getTLBEventVaddr());
+
+    tlb_event->updateOutcome(MISS_RETURN);
+    tlb->schedule(tlb_event, curTick()+tlb->clockPeriod());
+
+    return true;
+}
+
+void
+GpuTLB::MemSidePort::recvReqRetry()
+{
+    // No retries should reach the TLB. The retries
+    // should only reach the TLBCoalescer.
+    panic("recvReqRetry called");
+}
+
+void
+GpuTLB::cleanup()
+{
+    while (!cleanupQueue.empty()) {
+        Addr cleanup_addr = cleanupQueue.front();
+        cleanupQueue.pop();
+
+        DPRINTF(GPUTLB, "Deleting return event for %#lx\n", cleanup_addr);
+
+        // delete TLBEvent
+        TLBEvent * old_tlb_event = translationReturnEvent[cleanup_addr];
+        delete old_tlb_event;
+        translationReturnEvent.erase(cleanup_addr);
+
+        // update number of outstanding requests
+        outstandingReqs--;
+    }
+
+    /** the higher level coalescer should retry if it has
+     * any pending requests.
+     */
+    for (int i = 0; i < cpuSidePort.size(); ++i) {
+        cpuSidePort[i]->sendRetryReq();
+    }
+}
+
+GpuTLB::VegaTLBStats::VegaTLBStats(statistics::Group *parent)
+    : statistics::Group(parent),
+      ADD_STAT(maxDownstreamReached, "Number of refused translation requests"),
+      ADD_STAT(outstandingReqsMax, "Maximum count in coalesced request queue"),
+      ADD_STAT(localNumTLBAccesses, "Number of TLB accesses"),
+      ADD_STAT(localNumTLBHits, "Number of TLB hits"),
+      ADD_STAT(localNumTLBMisses, "Number of TLB misses"),
+      ADD_STAT(localTLBMissRate, "TLB miss rate"),
+      ADD_STAT(globalNumTLBAccesses, "Number of TLB accesses"),
+      ADD_STAT(globalNumTLBHits, "Number of TLB hits"),
+      ADD_STAT(globalNumTLBMisses, "Number of TLB misses"),
+      ADD_STAT(globalTLBMissRate, "TLB miss rate"),
+      ADD_STAT(accessCycles, "Cycles spent accessing this TLB level"),
+      ADD_STAT(pageTableCycles, "Cycles spent accessing the page table"),
+      ADD_STAT(localCycles, "Number of cycles spent in queue for all "
+               "incoming reqs"),
+      ADD_STAT(localLatency, "Avg. latency over incoming coalesced reqs")
+{
+    localTLBMissRate = 100 * localNumTLBMisses / localNumTLBAccesses;
+    globalTLBMissRate = 100 * globalNumTLBMisses / globalNumTLBAccesses;
+
+    localLatency = localCycles / localNumTLBAccesses;
+}
+
+} // namespace VegaISA
+} // namespace gem5
diff --git a/src/arch/amdgpu/vega/tlb.hh b/src/arch/amdgpu/vega/tlb.hh
new file mode 100644
index 0000000..c38f591
--- /dev/null
+++ b/src/arch/amdgpu/vega/tlb.hh
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARCH_AMDGPU_VEGA_TLB_HH__
+#define __ARCH_AMDGPU_VEGA_TLB_HH__
+
+#include <list>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "arch/amdgpu/vega/pagetable.hh"
+#include "arch/generic/mmu.hh"
+#include "base/statistics.hh"
+#include "base/trace.hh"
+#include "mem/packet.hh"
+#include "mem/port.hh"
+#include "params/VegaGPUTLB.hh"
+#include "sim/clocked_object.hh"
+
+namespace gem5
+{
+
+class BaseMMU;
+class Packet;
+class AMDGPUDevice;
+class ThreadContext;
+
+namespace VegaISA
+{
+
+class Walker;
+
+class GpuTLB : public ClockedObject
+{
+  public:
+    GpuTLB(const VegaGPUTLBParams &p);
+    ~GpuTLB();
+
+    typedef enum BaseMMU::Mode Mode;
+
+    class Translation
+    {
+      public:
+        virtual ~Translation() { }
+
+        /**
+         * Signal that the translation has been delayed due to a hw page
+         * table walk.
+         */
+        virtual void markDelayed() = 0;
+
+        /**
+         * The memory for this object may be dynamically allocated, and it
+         * may be responsible for cleaning itslef up which will happen in
+         * this function. Once it's called the object is no longer valid.
+         */
+        virtual void finish(Fault fault, const RequestPtr &req,
+                            Mode mode) = 0;
+
+        /** This function is used by the page table walker to determine if
+         * it should translate the a pending request or if the underlying
+         * request has been squashed.
+         * @ return Is the instruction that requested this translation
+         * squashed?
+         */
+        virtual bool squashed() const { return false; }
+    };
+
+    Addr pageAlign(Addr vaddr);
+    void dumpAll();
+    VegaTlbEntry *lookup(Addr va, bool update_lru=true);
+
+    Walker *getWalker();
+    void invalidateAll();
+    void demapPage(Addr va, uint64_t asn);
+
+  protected:
+    typedef std::list<VegaTlbEntry*> EntryList;
+    EntryList::iterator lookupIt(Addr va, bool update_lru=true);
+    Walker *walker;
+    AMDGPUDevice *gpuDevice;
+
+    int size;
+    int assoc;
+    int numSets;
+
+    /**
+     *  true if this is a fully-associative TLB
+     */
+    bool FA;
+    Addr setMask;
+
+    /**
+     * Allocation Policy: true if we always allocate on a hit, false
+     * otherwise. Default is true.
+     */
+    bool allocationPolicy;
+
+    /**
+     * if true, then this is not the last level TLB
+     */
+    bool hasMemSidePort;
+
+    std::vector<VegaTlbEntry> tlb;
+
+    /*
+     * It's a per-set list. As long as we have not reached
+     * the full capacity of the given set, grab an entry from
+     * the freeList.
+     */
+    std::vector<EntryList> freeList;
+
+    /**
+     * An entryList per set is the equivalent of an LRU stack;
+     * it's used to guide replacement decisions. The head of the list
+     * contains the MRU TLB entry of the given set. If the freeList
+     * for this set is empty, the last element of the list
+     * is evicted (i.e., dropped on the floor).
+     */
+    std::vector<EntryList> entryList;
+
+  public:
+    // latencies for a TLB hit, miss and page fault
+    int hitLatency;
+    int missLatency1;
+    int missLatency2;
+
+    struct VegaTLBStats : public statistics::Group
+    {
+        VegaTLBStats(statistics::Group *parent);
+
+        statistics::Scalar maxDownstreamReached;
+        statistics::Scalar outstandingReqsMax;
+
+        // local_stats are as seen from the TLB
+        // without taking into account coalescing
+        statistics::Scalar localNumTLBAccesses;
+        statistics::Scalar localNumTLBHits;
+        statistics::Scalar localNumTLBMisses;
+        statistics::Formula localTLBMissRate;
+
+        // global_stats are as seen from the
+        // CU's perspective taking into account
+        // all coalesced requests.
+        statistics::Scalar globalNumTLBAccesses;
+        statistics::Scalar globalNumTLBHits;
+        statistics::Scalar globalNumTLBMisses;
+        statistics::Formula globalTLBMissRate;
+
+        // from the CU perspective (global)
+        statistics::Scalar accessCycles;
+        statistics::Scalar pageTableCycles;
+
+        // from the perspective of this TLB
+        statistics::Scalar localCycles;
+        statistics::Formula localLatency;
+    } stats;
+
+
+    VegaTlbEntry *insert(Addr vpn, VegaTlbEntry &entry);
+
+    // Checkpointing
+    virtual void serialize(CheckpointOut& cp) const override;
+    virtual void unserialize(CheckpointIn& cp) override;
+    void issueTranslation();
+    enum tlbOutcome {TLB_HIT, TLB_MISS, PAGE_WALK, MISS_RETURN};
+    VegaTlbEntry *tlbLookup(const RequestPtr &req, bool update_stats);
+
+    void walkerResponse(VegaTlbEntry& entry, PacketPtr pkt);
+    void handleTranslationReturn(Addr addr, tlbOutcome outcome,
+                                 PacketPtr pkt);
+
+    void handleFuncTranslationReturn(PacketPtr pkt, tlbOutcome outcome);
+
+    void pagingProtectionChecks(PacketPtr pkt,
+                                VegaTlbEntry *tlb_entry, Mode mode);
+
+    void updatePhysAddresses(Addr virt_page_addr, VegaTlbEntry *tlb_entry,
+                             Addr phys_page_addr);
+
+    void issueTLBLookup(PacketPtr pkt);
+
+    // CpuSidePort is the TLB Port closer to the CPU/CU side
+    class CpuSidePort : public ResponsePort
+    {
+      public:
+        CpuSidePort(const std::string &_name, GpuTLB * gpu_TLB,
+                    PortID _index)
+            : ResponsePort(_name, gpu_TLB), tlb(gpu_TLB), index(_index) { }
+
+      protected:
+        GpuTLB *tlb;
+        int index;
+
+        virtual bool recvTimingReq(PacketPtr pkt);
+        virtual Tick recvAtomic(PacketPtr pkt) { return 0; }
+        virtual void recvFunctional(PacketPtr pkt);
+        virtual void recvRangeChange() { }
+        virtual void recvReqRetry();
+        virtual void recvRespRetry() { panic("recvRespRetry called"); }
+        virtual AddrRangeList getAddrRanges() const;
+    };
+
+    /**
+     * MemSidePort is the TLB Port closer to the memory side
+     * If this is a last level TLB then this port will not be connected.
+     *
+     * Future action item: if we ever do real page walks, then this port
+     * should be connected to a RubyPort.
+     */
+    class MemSidePort : public RequestPort
+    {
+      public:
+        MemSidePort(const std::string &_name, GpuTLB * gpu_TLB,
+                    PortID _index)
+            : RequestPort(_name, gpu_TLB), tlb(gpu_TLB), index(_index) { }
+
+        std::deque<PacketPtr> retries;
+
+      protected:
+        GpuTLB *tlb;
+        int index;
+
+        virtual bool recvTimingResp(PacketPtr pkt);
+        virtual Tick recvAtomic(PacketPtr pkt) { return 0; }
+        virtual void recvFunctional(PacketPtr pkt) { }
+        virtual void recvRangeChange() { }
+        virtual void recvReqRetry();
+    };
+
+    // TLB ports on the cpu Side
+    std::vector<CpuSidePort*> cpuSidePort;
+    // TLB ports on the memory side
+    std::vector<MemSidePort*> memSidePort;
+
+    Port &getPort(const std::string &if_name,
+                  PortID idx=InvalidPortID) override;
+
+    Fault createPagefault(Addr vaddr, Mode mode);
+
+    // maximum number of permitted coalesced requests per cycle
+    int maxCoalescedReqs;
+
+    // Current number of outstandings coalesced requests.
+    // Should be <= maxCoalescedReqs
+    int outstandingReqs;
+
+    /**
+     * A TLBEvent is scheduled after the TLB lookup and helps us take the
+     * appropriate actions:
+     *  (e.g., update TLB on a hit,
+     *  send request to lower level TLB on a miss,
+     *  or start a page walk if this was the last-level TLB).
+     */
+    void translationReturn(Addr virtPageAddr, tlbOutcome outcome,
+                           PacketPtr pkt);
+
+    class TLBEvent : public Event
+    {
+        private:
+            GpuTLB *tlb;
+            Addr virtPageAddr;
+            /**
+             * outcome can be TLB_HIT, TLB_MISS, or PAGE_WALK
+             */
+            tlbOutcome outcome;
+            PacketPtr pkt;
+
+        public:
+            TLBEvent(GpuTLB *_tlb, Addr _addr, tlbOutcome outcome,
+                    PacketPtr _pkt);
+
+            void process();
+            const char *description() const;
+
+            // updateOutcome updates the tlbOutcome of a TLBEvent
+            void updateOutcome(tlbOutcome _outcome);
+            Addr getTLBEventVaddr();
+    };
+
+    std::unordered_map<Addr, TLBEvent*> translationReturnEvent;
+
+    // this FIFO queue keeps track of the virt. page addresses
+    // that are pending cleanup
+    std::queue<Addr> cleanupQueue;
+
+    // the cleanupEvent is scheduled after a TLBEvent triggers in order to
+    // free memory and do the required clean-up
+    void cleanup();
+
+    EventFunctionWrapper cleanupEvent;
+};
+
+} // namespace VegaISA
+
+} // namespace gem5
+
+#endif // __ARCH_AMDGPU_VEGA_TLB_HH__
diff --git a/src/arch/amdgpu/vega/tlb_coalescer.cc b/src/arch/amdgpu/vega/tlb_coalescer.cc
new file mode 100644
index 0000000..d02c9bc
--- /dev/null
+++ b/src/arch/amdgpu/vega/tlb_coalescer.cc
@@ -0,0 +1,696 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "arch/amdgpu/vega/tlb_coalescer.hh"
+
+#include <cstring>
+
+#include "arch/amdgpu/common/gpu_translation_state.hh"
+#include "arch/amdgpu/vega/pagetable.hh"
+#include "arch/generic/mmu.hh"
+#include "base/logging.hh"
+#include "debug/GPUTLB.hh"
+#include "sim/process.hh"
+
+namespace gem5
+{
+
+VegaTLBCoalescer::VegaTLBCoalescer(const VegaTLBCoalescerParams &p)
+    : ClockedObject(p),
+      TLBProbesPerCycle(p.probesPerCycle),
+      coalescingWindow(p.coalescingWindow),
+      disableCoalescing(p.disableCoalescing),
+      probeTLBEvent([this]{ processProbeTLBEvent(); },
+                    "Probe the TLB below",
+                    false, Event::CPU_Tick_Pri),
+      cleanupEvent([this]{ processCleanupEvent(); },
+                   "Cleanup issuedTranslationsTable hashmap",
+                   false, Event::Maximum_Pri),
+      tlb_level(p.tlb_level),
+      maxDownstream(p.maxDownstream),
+      numDownstream(0)
+{
+    // create the response ports based on the number of connected ports
+    for (size_t i = 0; i < p.port_cpu_side_ports_connection_count; ++i) {
+        cpuSidePort.push_back(new CpuSidePort(csprintf("%s-port%d", name(), i),
+                                              this, i));
+    }
+
+    // create the request ports based on the number of connected ports
+    for (size_t i = 0; i < p.port_mem_side_ports_connection_count; ++i) {
+        memSidePort.push_back(new MemSidePort(csprintf("%s-port%d", name(), i),
+                                              this, i));
+    }
+}
+
+Port &
+VegaTLBCoalescer::getPort(const std::string &if_name, PortID idx)
+{
+    if (if_name == "cpu_side_ports") {
+        if (idx >= static_cast<PortID>(cpuSidePort.size())) {
+            panic("VegaTLBCoalescer::getPort: unknown index %d\n", idx);
+        }
+
+        return *cpuSidePort[idx];
+    } else  if (if_name == "mem_side_ports") {
+        if (idx >= static_cast<PortID>(memSidePort.size())) {
+            panic("VegaTLBCoalescer::getPort: unknown index %d\n", idx);
+        }
+
+        return *memSidePort[idx];
+    } else {
+        panic("VegaTLBCoalescer::getPort: unknown port %s\n", if_name);
+    }
+}
+
+/*
+ * This method returns true if the <incoming_pkt>
+ * can be coalesced with <coalesced_pkt> and false otherwise.
+ * A given set of rules is checked.
+ * The rules can potentially be modified based on the TLB level.
+ */
+bool
+VegaTLBCoalescer::canCoalesce(PacketPtr incoming_pkt, PacketPtr coalesced_pkt)
+{
+    if (disableCoalescing)
+        return false;
+
+    GpuTranslationState *incoming_state =
+      safe_cast<GpuTranslationState*>(incoming_pkt->senderState);
+
+    GpuTranslationState *coalesced_state =
+     safe_cast<GpuTranslationState*>(coalesced_pkt->senderState);
+
+    // Rule 1: Coalesce requests only if they
+    // fall within the same virtual page
+    Addr incoming_virt_page_addr = roundDown(incoming_pkt->req->getVaddr(),
+                                             VegaISA::PageBytes);
+
+    Addr coalesced_virt_page_addr = roundDown(coalesced_pkt->req->getVaddr(),
+                                              VegaISA::PageBytes);
+
+    if (incoming_virt_page_addr != coalesced_virt_page_addr)
+        return false;
+
+    //* Rule 2: Coalesce requests only if they
+    // share a TLB Mode, i.e. they are both read
+    // or write requests.
+    BaseMMU::Mode incoming_mode = incoming_state->tlbMode;
+    BaseMMU::Mode coalesced_mode = coalesced_state->tlbMode;
+
+    if (incoming_mode != coalesced_mode)
+        return false;
+
+    // when we can coalesce a packet update the reqCnt
+    // that is the number of packets represented by
+    // this coalesced packet
+    if (!incoming_state->isPrefetch)
+        coalesced_state->reqCnt.back() += incoming_state->reqCnt.back();
+
+    return true;
+}
+
+/*
+ * We need to update the physical addresses of all the translation requests
+ * that were coalesced into the one that just returned.
+ */
+void
+VegaTLBCoalescer::updatePhysAddresses(PacketPtr pkt)
+{
+    Addr virt_page_addr = roundDown(pkt->req->getVaddr(), VegaISA::PageBytes);
+
+    DPRINTF(GPUTLB, "Update phys. addr. for %d coalesced reqs for page %#x\n",
+            issuedTranslationsTable[virt_page_addr].size(), virt_page_addr);
+
+    GpuTranslationState *sender_state =
+        safe_cast<GpuTranslationState*>(pkt->senderState);
+
+    // Make a copy. This gets deleted after the first is sent back on the port
+    assert(sender_state->tlbEntry);
+    VegaISA::VegaTlbEntry tlb_entry =
+        *safe_cast<VegaISA::VegaTlbEntry *>(sender_state->tlbEntry);
+    Addr first_entry_vaddr = tlb_entry.vaddr;
+    Addr first_entry_paddr = tlb_entry.paddr;
+    int page_size = tlb_entry.size();
+    bool uncacheable = tlb_entry.uncacheable();
+    int first_hit_level = sender_state->hitLevel;
+
+    // Get the physical page address of the translated request
+    // Using the page_size specified in the TLBEntry allows us
+    // to support different page sizes.
+    Addr phys_page_paddr = pkt->req->getPaddr();
+    phys_page_paddr &= ~(page_size - 1);
+
+    bool is_system = pkt->req->systemReq();
+
+    for (int i = 0; i < issuedTranslationsTable[virt_page_addr].size(); ++i) {
+        PacketPtr local_pkt = issuedTranslationsTable[virt_page_addr][i];
+        GpuTranslationState *sender_state =
+            safe_cast<GpuTranslationState*>(local_pkt->senderState);
+
+        // we are sending the packet back, so pop the reqCnt associated
+        // with this level in the TLB hiearchy
+        if (!sender_state->isPrefetch) {
+            sender_state->reqCnt.pop_back();
+            localCycles += curCycle();
+        }
+
+        /*
+         * Only the first packet from this coalesced request has been
+         * translated. Grab the translated phys. page addr and update the
+         * physical addresses of the remaining packets with the appropriate
+         * page offsets.
+         */
+        if (i) {
+            Addr paddr = phys_page_paddr;
+            paddr |= (local_pkt->req->getVaddr() & (page_size - 1));
+            local_pkt->req->setPaddr(paddr);
+
+            if (uncacheable)
+                local_pkt->req->setFlags(Request::UNCACHEABLE);
+
+            // update senderState->tlbEntry, so we can insert
+            // the correct TLBEentry in the TLBs above.
+
+            //auto p = sender_state->tc->getProcessPtr();
+            if (sender_state->tlbEntry == NULL) {
+                // not set by lower(l2) coalescer
+                sender_state->tlbEntry =
+                    new VegaISA::VegaTlbEntry(1 /* VMID TODO */,
+                                              first_entry_vaddr,
+                                              first_entry_paddr,
+                                              tlb_entry.logBytes,
+                                              tlb_entry.pte);
+            }
+
+            // update the hitLevel for all uncoalesced reqs
+            // so that each packet knows where it hit
+            // (used for statistics in the CUs)
+            sender_state->hitLevel = first_hit_level;
+        }
+
+        // Copy PTE system bit information to coalesced requests
+        local_pkt->req->setSystemReq(is_system);
+
+        ResponsePort *return_port = sender_state->ports.back();
+        sender_state->ports.pop_back();
+
+        // Translation is done - Convert to a response pkt if necessary and
+        // send the translation back
+        if (local_pkt->isRequest()) {
+            local_pkt->makeTimingResponse();
+        }
+
+        return_port->sendTimingResp(local_pkt);
+    }
+
+    // schedule clean up for end of this cycle
+    // This is a maximum priority event and must be on
+    // the same cycle as GPUTLB cleanup event to prevent
+    // race conditions with an IssueProbeEvent caused by
+    // MemSidePort::recvReqRetry
+    cleanupQueue.push(virt_page_addr);
+
+    if (!cleanupEvent.scheduled())
+        schedule(cleanupEvent, curTick());
+}
+
+// Receive translation requests, create a coalesced request,
+// and send them to the TLB (TLBProbesPerCycle)
+bool
+VegaTLBCoalescer::CpuSidePort::recvTimingReq(PacketPtr pkt)
+{
+    // first packet of a coalesced request
+    PacketPtr first_packet = nullptr;
+    // true if we are able to do coalescing
+    bool didCoalesce = false;
+    // number of coalesced reqs for a given window
+    int coalescedReq_cnt = 0;
+
+    GpuTranslationState *sender_state =
+        safe_cast<GpuTranslationState*>(pkt->senderState);
+
+    bool update_stats = !sender_state->isPrefetch;
+
+    if (coalescer->tlb_level == 1 && coalescer->mustStallCUPort(this))
+        return false;
+
+    // push back the port to remember the path back
+    sender_state->ports.push_back(this);
+
+    if (update_stats) {
+        // if reqCnt is empty then this packet does not represent
+        // multiple uncoalesced reqs(pkts) but just a single pkt.
+        // If it does though then the reqCnt for each level in the
+        // hierarchy accumulates the total number of reqs this packet
+        // represents
+        int req_cnt = 1;
+
+        if (!sender_state->reqCnt.empty())
+            req_cnt = sender_state->reqCnt.back();
+
+        sender_state->reqCnt.push_back(req_cnt);
+
+        // update statistics
+        coalescer->uncoalescedAccesses++;
+        req_cnt = sender_state->reqCnt.back();
+        DPRINTF(GPUTLB, "receiving pkt w/ req_cnt %d\n", req_cnt);
+        coalescer->queuingCycles -= (coalescer->curCycle() * req_cnt);
+        coalescer->localqueuingCycles -= coalescer->curCycle();
+        coalescer->localCycles -= coalescer->curCycle();
+    }
+
+    // Coalesce based on the time the packet arrives at the coalescer (here).
+    if (!sender_state->issueTime)
+        sender_state->issueTime = curTick();
+
+    // The tick index is used as a key to the coalescerFIFO hashmap.
+    // It is shared by all candidates that fall within the
+    // given coalescingWindow.
+    Tick tick_index = sender_state->issueTime / coalescer->coalescingWindow;
+
+    if (coalescer->coalescerFIFO.count(tick_index)) {
+        coalescedReq_cnt = coalescer->coalescerFIFO[tick_index].size();
+    }
+
+    // see if we can coalesce the incoming pkt with another
+    // coalesced request with the same tick_index
+    for (int i = 0; i < coalescedReq_cnt; ++i) {
+        first_packet = coalescer->coalescerFIFO[tick_index][i][0];
+
+        if (coalescer->canCoalesce(pkt, first_packet)) {
+            coalescer->coalescerFIFO[tick_index][i].push_back(pkt);
+
+            DPRINTF(GPUTLB, "Coalesced req %i w/ tick_index %d has %d reqs\n",
+                    i, tick_index,
+                    coalescer->coalescerFIFO[tick_index][i].size());
+
+            didCoalesce = true;
+            break;
+        }
+    }
+
+    // if this is the first request for this tick_index
+    // or we did not manage to coalesce, update stats
+    // and make necessary allocations.
+    if (!coalescedReq_cnt || !didCoalesce) {
+        if (update_stats)
+            coalescer->coalescedAccesses++;
+
+        std::vector<PacketPtr> new_array;
+        new_array.push_back(pkt);
+        coalescer->coalescerFIFO[tick_index].push_back(new_array);
+
+        DPRINTF(GPUTLB, "coalescerFIFO[%d] now has %d coalesced reqs after "
+                "push\n", tick_index,
+                coalescer->coalescerFIFO[tick_index].size());
+    }
+
+    //schedule probeTLBEvent next cycle to send the
+    //coalesced requests to the TLB
+    if (!coalescer->probeTLBEvent.scheduled()) {
+        coalescer->schedule(coalescer->probeTLBEvent,
+                curTick() + coalescer->clockPeriod());
+    }
+
+    return true;
+}
+
+void
+VegaTLBCoalescer::CpuSidePort::recvReqRetry()
+{
+    panic("recvReqRetry called");
+}
+
+void
+VegaTLBCoalescer::CpuSidePort::recvFunctional(PacketPtr pkt)
+{
+
+    GpuTranslationState *sender_state =
+        safe_cast<GpuTranslationState*>(pkt->senderState);
+
+    bool update_stats = !sender_state->isPrefetch;
+
+    if (update_stats)
+        coalescer->uncoalescedAccesses++;
+
+    Addr virt_page_addr = roundDown(pkt->req->getVaddr(), VegaISA::PageBytes);
+    int map_count = coalescer->issuedTranslationsTable.count(virt_page_addr);
+
+    if (map_count) {
+        DPRINTF(GPUTLB, "Warning! Functional access to addr %#x sees timing "
+                "req. pending\n", virt_page_addr);
+    }
+
+    coalescer->memSidePort[0]->sendFunctional(pkt);
+}
+
+AddrRangeList
+VegaTLBCoalescer::CpuSidePort::getAddrRanges() const
+{
+    // currently not checked by the requestor
+    AddrRangeList ranges;
+
+    return ranges;
+}
+
+/*
+ *  a translation completed and returned
+ */
+bool
+VegaTLBCoalescer::MemSidePort::recvTimingResp(PacketPtr pkt)
+{
+    coalescer->updatePhysAddresses(pkt);
+
+    if (coalescer->tlb_level != 1)
+        return true;
+
+
+    coalescer->decrementNumDownstream();
+
+    DPRINTF(GPUTLB,
+            "recvTimingReq: clscr = %p, numDownstream = %d, max = %d\n",
+            coalescer, coalescer->numDownstream, coalescer->maxDownstream);
+
+    coalescer->unstallPorts();
+    return true;
+}
+
+void
+VegaTLBCoalescer::MemSidePort::recvReqRetry()
+{
+    //we've receeived a retry. Schedule a probeTLBEvent
+    if (!coalescer->probeTLBEvent.scheduled())
+        coalescer->schedule(coalescer->probeTLBEvent,
+                curTick() + coalescer->clockPeriod());
+}
+
+void
+VegaTLBCoalescer::MemSidePort::recvFunctional(PacketPtr pkt)
+{
+    fatal("Memory side recvFunctional() not implemented in TLB coalescer.\n");
+}
+
+/*
+ * Here we scan the coalescer FIFO and issue the max
+ * number of permitted probes to the TLB below. We
+ * permit bypassing of coalesced requests for the same
+ * tick_index.
+ *
+ * We do not access the next tick_index unless we've
+ * drained the previous one. The coalesced requests
+ * that are successfully sent are moved to the
+ * issuedTranslationsTable table (the table which keeps
+ * track of the outstanding reqs)
+ */
+void
+VegaTLBCoalescer::processProbeTLBEvent()
+{
+    // number of TLB probes sent so far
+    int sent_probes = 0;
+
+    // It is set to true either when the recvTiming of the TLB below
+    // returns false or when there is another outstanding request for the
+    // same virt. page.
+
+    DPRINTF(GPUTLB, "triggered VegaTLBCoalescer %s\n", __func__);
+
+    if ((tlb_level == 1)
+        && (availDownstreamSlots() == 0)) {
+        DPRINTF(GPUTLB, "IssueProbeEvent - no downstream slots, bail out\n");
+        return;
+    }
+
+    for (auto iter = coalescerFIFO.begin();
+         iter != coalescerFIFO.end();) {
+        int coalescedReq_cnt = iter->second.size();
+        int i = 0;
+        int vector_index = 0;
+
+        DPRINTF(GPUTLB, "coalescedReq_cnt is %d for tick_index %d\n",
+               coalescedReq_cnt, iter->first);
+
+        while (i < coalescedReq_cnt) {
+            ++i;
+            PacketPtr first_packet = iter->second[vector_index][0];
+            //The request to coalescer is origanized as follows.
+            //The coalescerFIFO is a map which is indexed by coalescingWindow
+            // cycle. Only requests that falls in the same coalescingWindow
+            // considered for coalescing. Each entry of a coalescerFIFO is a
+            // vector of vectors. There is one entry for each different virtual
+            // page number and it contains vector of all request that are
+            // coalesced for the same virtual page address
+
+            // compute virtual page address for this request
+            Addr virt_page_addr = roundDown(first_packet->req->getVaddr(),
+                    VegaISA::PageBytes);
+
+            // is there another outstanding request for the same page addr?
+            int pending_reqs =
+                issuedTranslationsTable.count(virt_page_addr);
+
+            if (pending_reqs) {
+                DPRINTF(GPUTLB, "Cannot issue - There are pending reqs for "
+                        "page %#x\n", virt_page_addr);
+
+                ++vector_index;
+                continue;
+            }
+
+            // send the coalesced request for virt_page_addr
+            if (!memSidePort[0]->sendTimingReq(first_packet)) {
+                DPRINTF(GPUTLB,
+                        "Failed to send TLB request for page %#x",
+                        virt_page_addr);
+
+                // No need for a retries queue since we are already
+                // buffering the coalesced request in coalescerFIFO.
+                // Arka:: No point trying to send other requests to TLB at
+                // this point since it is busy. Retries will be called later
+                // by the TLB below
+                return;
+             } else {
+
+                if (tlb_level == 1)
+                    incrementNumDownstream();
+
+                GpuTranslationState *tmp_sender_state =
+                    safe_cast<GpuTranslationState*>(first_packet->senderState);
+
+                bool update_stats = !tmp_sender_state->isPrefetch;
+
+                if (update_stats) {
+                    // req_cnt is total number of packets represented
+                    // by the one we just sent counting all the way from
+                    // the top of TLB hiearchy (i.e., from the CU)
+                    int req_cnt = tmp_sender_state->reqCnt.back();
+                    queuingCycles += (curCycle() * req_cnt);
+
+                    DPRINTF(GPUTLB, "%s sending pkt w/ req_cnt %d\n",
+                            name(), req_cnt);
+
+                    // pkt_cnt is number of packets we coalesced into the one
+                    // we just sent but only at this coalescer level
+                    int pkt_cnt = iter->second[vector_index].size();
+                    localqueuingCycles += (curCycle() * pkt_cnt);
+                }
+
+                DPRINTF(GPUTLB, "Successfully sent TLB request for page %#x\n",
+                       virt_page_addr);
+
+                //copy coalescedReq to issuedTranslationsTable
+                issuedTranslationsTable[virt_page_addr]
+                    = iter->second[vector_index];
+
+                //erase the entry of this coalesced req
+                iter->second.erase(iter->second.begin() + vector_index);
+
+                if (iter->second.empty())
+                    assert( i == coalescedReq_cnt );
+
+                sent_probes++;
+
+                if (sent_probes == TLBProbesPerCycle ||
+                    ((tlb_level == 1) && (!availDownstreamSlots()))) {
+                    //Before returning make sure that empty vectors are taken
+                    // out. Not a big issue though since a later invocation
+                    // will take it out anyway.
+                    if (iter->second.empty())
+                        coalescerFIFO.erase(iter);
+
+                    //schedule probeTLBEvent next cycle to send the
+                    //coalesced requests to the TLB
+                    if (!probeTLBEvent.scheduled()) {
+                        schedule(probeTLBEvent,
+                                 cyclesToTicks(curCycle() + Cycles(1)));
+                    }
+                    return;
+                }
+            }
+        }
+
+        //if there are no more coalesced reqs for this tick_index
+        //erase the hash_map with the first iterator
+        if (iter->second.empty()) {
+            coalescerFIFO.erase(iter++);
+        } else {
+            ++iter;
+        }
+    }
+}
+
+void
+VegaTLBCoalescer::processCleanupEvent()
+{
+    while (!cleanupQueue.empty()) {
+        Addr cleanup_addr = cleanupQueue.front();
+        cleanupQueue.pop();
+        issuedTranslationsTable.erase(cleanup_addr);
+
+        DPRINTF(GPUTLB, "Cleanup - Delete coalescer entry with key %#x\n",
+                cleanup_addr);
+    }
+}
+
+void
+VegaTLBCoalescer::regStats()
+{
+    ClockedObject::regStats();
+
+    uncoalescedAccesses
+        .name(name() + ".uncoalesced_accesses")
+        .desc("Number of uncoalesced TLB accesses")
+        ;
+
+    coalescedAccesses
+        .name(name() + ".coalesced_accesses")
+        .desc("Number of coalesced TLB accesses")
+        ;
+
+    queuingCycles
+        .name(name() + ".queuing_cycles")
+        .desc("Number of cycles spent in queue")
+        ;
+
+    localqueuingCycles
+        .name(name() + ".local_queuing_cycles")
+        .desc("Number of cycles spent in queue for all incoming reqs")
+        ;
+
+   localCycles
+        .name(name() + ".local_cycles")
+        .desc("Number of cycles spent in queue for all incoming reqs")
+        ;
+
+    localLatency
+        .name(name() + ".local_latency")
+        .desc("Avg. latency over all incoming pkts")
+        ;
+
+    latency
+        .name(name() + ".latency")
+        .desc("Avg. latency over all incoming pkts")
+        ;
+
+    localLatency = localqueuingCycles / uncoalescedAccesses;
+    latency = localCycles / uncoalescedAccesses;
+}
+
+void
+VegaTLBCoalescer::insertStalledPortIfNotMapped(CpuSidePort *port)
+{
+    assert(tlb_level == 1);
+    if (stalledPortsMap.count(port) != 0)
+        return; // we already know this port is stalled
+
+    stalledPortsMap[port] = port;
+    stalledPortsQueue.push(port);
+    DPRINTF(GPUTLB,
+            "insertStalledPortIfNotMapped: port %p, mapSz = %d, qsz = %d\n",
+            port, stalledPortsMap.size(), stalledPortsQueue.size());
+}
+
+bool
+VegaTLBCoalescer::mustStallCUPort(CpuSidePort *port)
+{
+    assert(tlb_level == 1);
+
+    DPRINTF(GPUTLB, "mustStallCUPort: downstream = %d, max = %d\n",
+            numDownstream, maxDownstream);
+
+    if (availDownstreamSlots() == 0 || numDownstream == maxDownstream) {
+        warn("RED ALERT - VegaTLBCoalescer::mustStallCUPort\n");
+        insertStalledPortIfNotMapped(port);
+        return true;
+    }
+    else
+        return false;
+}
+
+void
+VegaTLBCoalescer::unstallPorts()
+{
+    assert(tlb_level == 1);
+    if (!stalledPorts() || availDownstreamSlots() == 0)
+        return;
+
+    DPRINTF(GPUTLB, "unstallPorts()\n");
+    /*
+     * this check is needed because we can be called from recvTiiningResponse()
+     * or, synchronously due to having called sendRetry, from recvTimingReq()
+     */
+    if (availDownstreamSlots() == 0) // can happen if retry sent 1 downstream
+        return;
+    /*
+     *  Consider this scenario
+     *        1) max downstream is reached
+     *        2) port1 tries to send a req, cant => stalledPortsQueue = [port1]
+     *        3) port2 tries to send a req, cant => stalledPortsQueue = [port1,
+     *              port2]
+     *        4) a request completes and we remove port1 from both data
+     *              structures & call
+     *             sendRetry => stalledPortsQueue = [port2]
+     *        5) port1 sends one req downstream and a second is rejected
+     *             => stalledPortsQueue = [port2, port1]
+     *
+     *        so we round robin and each stalled port can send 1 req on retry
+     */
+    assert(availDownstreamSlots() == 1);
+    auto port = stalledPortsQueue.front();
+    DPRINTF(GPUTLB, "sending retry for port = %p(%s)\n", port, port->name());
+    stalledPortsQueue.pop();
+    auto iter = stalledPortsMap.find(port);
+    assert(iter != stalledPortsMap.end());
+    stalledPortsMap.erase(iter);
+    port->sendRetryReq(); // cu will synchronously call recvTimingReq
+}
+
+} // namespace gem5
diff --git a/src/arch/amdgpu/vega/tlb_coalescer.hh b/src/arch/amdgpu/vega/tlb_coalescer.hh
new file mode 100644
index 0000000..da73b2e
--- /dev/null
+++ b/src/arch/amdgpu/vega/tlb_coalescer.hh
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARCH_AMDGPU_VEGA_TLB_COALESCER_HH__
+#define __ARCH_AMDGPU_VEGA_TLB_COALESCER_HH__
+
+#include <list>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "arch/amdgpu/vega/tlb.hh"
+#include "arch/isa.hh"
+#include "base/statistics.hh"
+#include "mem/port.hh"
+#include "mem/request.hh"
+#include "params/VegaTLBCoalescer.hh"
+#include "sim/clocked_object.hh"
+
+namespace gem5
+{
+
+class Packet;
+class ThreadContext;
+
+/**
+ * The VegaTLBCoalescer is a ClockedObject sitting on the front side (CPUSide)
+ * of each TLB. It receives packets and issues coalesced requests to the
+ * TLB below it. It controls how requests are coalesced (the rules)
+ * and the permitted number of TLB probes per cycle (i.e., how many
+ * coalesced requests it feeds the TLB per cycle).
+ */
+class VegaTLBCoalescer : public ClockedObject
+{
+  public:
+    VegaTLBCoalescer(const VegaTLBCoalescerParams &p);
+    ~VegaTLBCoalescer() { }
+
+    // Number of TLB probes per cycle. Parameterizable - default 2.
+    int TLBProbesPerCycle;
+
+    // Consider coalescing across that many ticks.
+    // Paraemterizable - default 1.
+    int coalescingWindow;
+
+    // Each coalesced request consists of multiple packets
+    // that all fall within the same virtual page
+    typedef std::vector<PacketPtr> coalescedReq;
+
+    // disables coalescing when true
+    bool disableCoalescing;
+
+    /*
+     * This is a hash map with <tick_index> as a key.
+     * It contains a vector of coalescedReqs per <tick_index>.
+     * Requests are buffered here until they can be issued to
+     * the TLB, at which point they are copied to the
+     * issuedTranslationsTable hash map.
+     *
+     * In terms of coalescing, we coalesce requests in a given
+     * window of x cycles by using tick_index = issueTime/x as a
+     * key, where x = coalescingWindow. issueTime is the issueTime
+     * of the pkt from the ComputeUnit's perspective, but another
+     * option is to change it to curTick(), so we coalesce based
+     * on the receive time.
+     */
+    typedef std::map<Tick, std::vector<coalescedReq>> CoalescingFIFO;
+
+    CoalescingFIFO coalescerFIFO;
+
+    /*
+     * issuedTranslationsTable: a hash_map indexed by virtual page
+     * address. Each hash_map entry has a vector of PacketPtr associated
+     * with it denoting the different packets that share an outstanding
+     * coalesced translation request for the same virtual page.
+     *
+     * The rules that determine which requests we can coalesce are
+     * specified in the canCoalesce() method.
+     */
+    typedef std::unordered_map<Addr, coalescedReq> CoalescingTable;
+
+    CoalescingTable issuedTranslationsTable;
+
+    // number of packets the coalescer receives
+    statistics::Scalar uncoalescedAccesses;
+    // number packets the coalescer send to the TLB
+    statistics::Scalar coalescedAccesses;
+
+    // Number of cycles the coalesced requests spend waiting in
+    // coalescerFIFO. For each packet the coalescer receives we take into
+    // account the number of all uncoalesced requests this pkt "represents"
+    statistics::Scalar queuingCycles;
+
+    // On average how much time a request from the
+    // uncoalescedAccesses that reaches the TLB
+    // spends waiting?
+    statistics::Scalar localqueuingCycles;
+    statistics::Scalar localCycles;
+  // localqueuingCycles/uncoalescedAccesses
+    statistics::Formula localLatency;
+   // latency of a request to be completed
+    statistics::Formula latency;
+
+    bool canCoalesce(PacketPtr pkt1, PacketPtr pkt2);
+    void updatePhysAddresses(PacketPtr pkt);
+    void regStats() override;
+
+    class CpuSidePort : public ResponsePort
+    {
+      public:
+        CpuSidePort(const std::string &_name, VegaTLBCoalescer *tlb_coalescer,
+                    PortID _index)
+            : ResponsePort(_name, tlb_coalescer), coalescer(tlb_coalescer),
+              index(_index) { }
+
+      protected:
+        VegaTLBCoalescer *coalescer;
+        int index;
+
+        virtual bool recvTimingReq(PacketPtr pkt);
+        virtual Tick recvAtomic(PacketPtr pkt) { return 0; }
+        virtual void recvFunctional(PacketPtr pkt);
+        virtual void recvRangeChange() { }
+        virtual void recvReqRetry();
+
+        virtual void
+        recvRespRetry()
+        {
+            fatal("recvRespRetry() is not implemented in the TLB "
+                "coalescer.\n");
+        }
+
+        virtual AddrRangeList getAddrRanges() const;
+    };
+
+    class MemSidePort : public RequestPort
+    {
+      public:
+        MemSidePort(const std::string &_name, VegaTLBCoalescer *tlb_coalescer,
+                    PortID _index)
+            : RequestPort(_name, tlb_coalescer), coalescer(tlb_coalescer),
+              index(_index) { }
+
+        std::deque<PacketPtr> retries;
+
+      protected:
+        VegaTLBCoalescer *coalescer;
+        int index;
+
+        virtual bool recvTimingResp(PacketPtr pkt);
+        virtual Tick recvAtomic(PacketPtr pkt) { return 0; }
+        virtual void recvFunctional(PacketPtr pkt);
+        virtual void recvRangeChange() { }
+        virtual void recvReqRetry();
+
+        virtual void
+        recvRespRetry()
+        {
+            fatal("recvRespRetry() not implemented in TLB coalescer");
+        }
+    };
+
+    // Coalescer response ports on the cpu Side
+    std::vector<CpuSidePort*> cpuSidePort;
+    // Coalescer request ports on the memory side
+    std::vector<MemSidePort*> memSidePort;
+
+    Port &getPort(const std::string &if_name,
+                  PortID idx=InvalidPortID) override;
+
+    void processProbeTLBEvent();
+    /// This event issues the TLB probes
+    EventFunctionWrapper probeTLBEvent;
+
+    void processCleanupEvent();
+    /// The cleanupEvent is scheduled after a TLBEvent triggers
+    /// in order to free memory and do the required clean-up
+    EventFunctionWrapper cleanupEvent;
+
+    int tlb_level;
+    int maxDownstream;
+    unsigned int numDownstream;
+    CpuSidePort *stalledPort;
+    std::queue<CpuSidePort *> stalledPortsQueue;
+    // enforce uniqueness in queue
+    std::map<CpuSidePort *, CpuSidePort *> stalledPortsMap;
+
+    unsigned int availDownstreamSlots() {
+        assert(tlb_level == 1);
+        return maxDownstream - numDownstream;
+    }
+
+    void insertStalledPortIfNotMapped(CpuSidePort *);
+    bool mustStallCUPort(CpuSidePort *);
+
+    bool stalledPorts() {
+        assert(tlb_level == 1);
+      return stalledPortsQueue.size() > 0;
+    }
+
+    void decrementNumDownstream() {
+        assert(tlb_level == 1);
+        assert(numDownstream > 0);
+        numDownstream--;
+    }
+
+    void incrementNumDownstream() {
+        assert(tlb_level == 1);
+        assert(maxDownstream >= numDownstream);
+        numDownstream++;
+    }
+
+    void unstallPorts();
+
+
+    // this FIFO queue keeps track of the virt. page
+    // addresses that are pending cleanup
+    std::queue<Addr> cleanupQueue;
+};
+
+} // namespace gem5
+
+#endif // __ARCH_AMDGPU_VEGA_TLB_COALESCER_HH__
diff --git a/src/arch/arm/ArmCPU.py b/src/arch/arm/ArmCPU.py
new file mode 100644
index 0000000..c55d99b
--- /dev/null
+++ b/src/arch/arm/ArmCPU.py
@@ -0,0 +1,78 @@
+# Copyright 2021 Google, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from m5.proxy import Self
+
+from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU
+from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU
+from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU
+from m5.objects.BaseO3CPU import BaseO3CPU
+from m5.objects.BaseO3Checker import BaseO3Checker
+from m5.objects.BaseMinorCPU import BaseMinorCPU
+from m5.objects.ArmDecoder import ArmDecoder
+from m5.objects.ArmMMU import ArmMMU
+from m5.objects.ArmInterrupts import ArmInterrupts
+from m5.objects.ArmISA import ArmISA
+
+class ArmCPU:
+    ArchDecoder = ArmDecoder
+    ArchMMU = ArmMMU
+    ArchInterrupts = ArmInterrupts
+    ArchISA = ArmISA
+
+class ArmAtomicSimpleCPU(BaseAtomicSimpleCPU, ArmCPU):
+    mmu = ArmMMU()
+
+class ArmNonCachingSimpleCPU(BaseNonCachingSimpleCPU, ArmCPU):
+    mmu = ArmMMU()
+
+class ArmTimingSimpleCPU(BaseTimingSimpleCPU, ArmCPU):
+    mmu = ArmMMU()
+
+class ArmO3Checker(BaseO3Checker, ArmCPU):
+    mmu = ArmMMU()
+
+class ArmO3CPU(BaseO3CPU, ArmCPU):
+    mmu = ArmMMU()
+
+    # For x86, each CC reg is used to hold only a subset of the
+    # flags, so we need 4-5 times the number of CC regs as
+    # physical integer regs to be sure we don't run out.  In
+    # typical real machines, CC regs are not explicitly renamed
+    # (it's a side effect of int reg renaming), so they should
+    # never be the bottleneck here.
+    numPhysCCRegs = Self.numPhysIntRegs * 5
+
+    def addCheckerCpu(self):
+        self.checker = ArmO3Checker(workload=self.workload,
+                                    exitOnError=False,
+                                    updateOnError=True,
+                                    warnOnlyOnLoadError=True)
+        self.checker.mmu.itb.size = self.mmu.itb.size
+        self.checker.mmu.dtb.size = self.mmu.dtb.size
+        self.checker.cpu_id = self.cpu_id
+
+class ArmMinorCPU(BaseMinorCPU, ArmCPU):
+    mmu = ArmMMU()
diff --git a/src/arch/arm/ArmDecoder.py b/src/arch/arm/ArmDecoder.py
index 9f01af6..a5c16f5 100644
--- a/src/arch/arm/ArmDecoder.py
+++ b/src/arch/arm/ArmDecoder.py
@@ -1,3 +1,15 @@
+# Copyright (c) 2021 Arm Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
 # Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -23,9 +35,13 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.params import *
 from m5.objects.InstDecoder import InstDecoder
 
 class ArmDecoder(InstDecoder):
     type = 'ArmDecoder'
     cxx_class = 'gem5::ArmISA::Decoder'
     cxx_header = "arch/arm/decoder.hh"
+
+    dvm_enabled = Param.Bool(False,
+        "Does the decoder implement DVM operations")
diff --git a/src/arch/arm/ArmFsWorkload.py b/src/arch/arm/ArmFsWorkload.py
index 974600b..43bbd16 100644
--- a/src/arch/arm/ArmFsWorkload.py
+++ b/src/arch/arm/ArmFsWorkload.py
@@ -58,6 +58,9 @@
     dtb_filename = Param.String("",
         "File that contains the Device Tree Blob. Don't use DTB if empty.")
     dtb_addr = Param.Addr(0, "DTB or ATAGS address")
+    initrd_filename = Param.String("",
+        "File that contains the initial ramdisk. Don't use initrd if empty.")
+    initrd_addr = Param.Addr(0, "initrd/initramfs address")
     cpu_release_addr = Param.Addr(0, "cpu-release-addr property")
 
     machine_type = Param.ArmMachineType('DTOnly',
diff --git a/src/arch/arm/ArmSystem.py b/src/arch/arm/ArmSystem.py
index 9755e0d..f9df791 100644
--- a/src/arch/arm/ArmSystem.py
+++ b/src/arch/arm/ArmSystem.py
@@ -103,7 +103,7 @@
 
 class Armv8(ArmRelease):
     extensions = [
-        'LPAE'
+        'LPAE', 'VIRTUALIZATION', 'SECURITY'
     ]
 
 class ArmDefaultRelease(Armv8):
diff --git a/util/stats/__init__.py b/src/arch/arm/AtomicSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/arm/AtomicSimpleCPU.py
index b3f54da..c3a25ba 100644
--- a/util/stats/__init__.py
+++ b/src/arch/arm/AtomicSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.ArmCPU import ArmAtomicSimpleCPU
+
+AtomicSimpleCPU = ArmAtomicSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/arm/MinorCPU.py
similarity index 93%
copy from util/stats/__init__.py
copy to src/arch/arm/MinorCPU.py
index b3f54da..bac0197 100644
--- a/util/stats/__init__.py
+++ b/src/arch/arm/MinorCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.ArmCPU import ArmMinorCPU
+
+MinorCPU = ArmMinorCPU
diff --git a/util/stats/__init__.py b/src/arch/arm/NonCachingSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/arm/NonCachingSimpleCPU.py
index b3f54da..bfad3ba 100644
--- a/util/stats/__init__.py
+++ b/src/arch/arm/NonCachingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.ArmCPU import ArmNonCachingSimpleCPU
+
+NonCachingSimpleCPU = ArmNonCachingSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/arm/O3CPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/arm/O3CPU.py
index b3f54da..54782e7 100644
--- a/util/stats/__init__.py
+++ b/src/arch/arm/O3CPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.ArmCPU import ArmO3CPU
+
+O3CPU = ArmO3CPU
+
+# Deprecated
+DerivO3CPU = O3CPU
diff --git a/util/stats/__init__.py b/src/arch/arm/O3Checker.py
similarity index 92%
copy from util/stats/__init__.py
copy to src/arch/arm/O3Checker.py
index b3f54da..0ac7ab4 100644
--- a/util/stats/__init__.py
+++ b/src/arch/arm/O3Checker.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.ArmCPU import ArmO3Checker
+
+O3Checker = ArmO3Checker
diff --git a/src/arch/arm/SConscript b/src/arch/arm/SConscript
index b138212..bc720d1 100644
--- a/src/arch/arm/SConscript
+++ b/src/arch/arm/SConscript
@@ -40,7 +40,14 @@
 
 Import('*')
 
-GTest('aapcs64.test', 'aapcs64.test.cc', '../../base/debug.cc')
+# The GTest function does not have a 'tags' parameter. We therefore apply this
+# guard to ensure this test is only built when ARM is compiled.
+#
+# Note: This will need reconfigured for multi-isa. E.g., if this is
+# incorporated: https://gem5-review.googlesource.com/c/public/gem5/+/52491
+if env['TARGET_ISA'] == 'arm':
+    GTest('aapcs64.test', 'aapcs64.test.cc', '../../base/debug.cc')
+
 Source('decoder.cc', tags='arm isa')
 Source('faults.cc', tags='arm isa')
 Source('htm.cc', tags='arm isa')
@@ -61,7 +68,7 @@
 Source('insts/fplib.cc', tags='arm isa')
 Source('insts/crypto.cc', tags='arm isa')
 Source('insts/tme64.cc', tags='arm isa')
-if env['PROTOCOL'] == 'MESI_Three_Level_HTM':
+if env['CONF']['PROTOCOL'] == 'MESI_Three_Level_HTM':
     Source('insts/tme64ruby.cc', tags='arm isa')
 else:
     Source('insts/tme64classic.cc', tags='arm isa')
@@ -112,6 +119,14 @@
     tags='arm isa')
 SimObject('ArmPMU.py', sim_objects=['ArmPMU'], tags='arm isa')
 
+SimObject('ArmCPU.py', sim_objects=[], tags='arm isa')
+SimObject('AtomicSimpleCPU.py', sim_objects=[], tags='arm isa')
+SimObject('TimingSimpleCPU.py', sim_objects=[], tags='arm isa')
+SimObject('NonCachingSimpleCPU.py', sim_objects=[], tags='arm isa')
+SimObject('O3CPU.py', sim_objects=[], tags='arm isa')
+SimObject('O3Checker.py', sim_objects=[], tags='arm isa')
+SimObject('MinorCPU.py', sim_objects=[], tags='arm isa')
+
 DebugFlag('Arm', tags='arm isa')
 DebugFlag('ArmTme', 'Transactional Memory Extension', tags='arm isa')
 DebugFlag('Semihosting', tags='arm isa')
diff --git a/util/stats/__init__.py b/src/arch/arm/TimingSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/arm/TimingSimpleCPU.py
index b3f54da..8a20a36 100644
--- a/util/stats/__init__.py
+++ b/src/arch/arm/TimingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.ArmCPU import ArmTimingSimpleCPU
+
+TimingSimpleCPU = ArmTimingSimpleCPU
diff --git a/src/arch/arm/aapcs32.hh b/src/arch/arm/aapcs32.hh
index beaaa7f..ff52039 100644
--- a/src/arch/arm/aapcs32.hh
+++ b/src/arch/arm/aapcs32.hh
@@ -64,7 +64,7 @@
         Addr retAddr=0;
 
         explicit State(const ThreadContext *tc) :
-            nsaa(tc->readIntReg(ArmISA::INTREG_SPX))
+            nsaa(tc->getReg(ArmISA::int_reg::Spx))
         {}
     };
 };
@@ -150,7 +150,7 @@
     {
         uint32_t val = std::is_signed_v<Integer> ?
                 sext<sizeof(Integer) * 8>(i) : i;
-        tc->setIntReg(ArmISA::INTREG_R0, val);
+        tc->setReg(ArmISA::int_reg::R0, val);
     }
 };
 
@@ -161,7 +161,7 @@
     static void
     store(ThreadContext *tc, const Integer &i)
     {
-        tc->setIntReg(ArmISA::INTREG_R0, (uint32_t)i);
+        tc->setReg(ArmISA::int_reg::R0, (uint32_t)i);
     }
 };
 
@@ -173,11 +173,11 @@
     store(ThreadContext *tc, const Integer &i)
     {
         if (ArmISA::byteOrder(tc) == ByteOrder::little) {
-            tc->setIntReg(ArmISA::INTREG_R0, (uint32_t)(i >> 0));
-            tc->setIntReg(ArmISA::INTREG_R1, (uint32_t)(i >> 32));
+            tc->setReg(ArmISA::int_reg::R0, (uint32_t)(i >> 0));
+            tc->setReg(ArmISA::int_reg::R1, (uint32_t)(i >> 32));
         } else {
-            tc->setIntReg(ArmISA::INTREG_R0, (uint32_t)(i >> 32));
-            tc->setIntReg(ArmISA::INTREG_R1, (uint32_t)(i >> 0));
+            tc->setReg(ArmISA::int_reg::R0, (uint32_t)(i >> 32));
+            tc->setReg(ArmISA::int_reg::R1, (uint32_t)(i >> 0));
         }
     }
 };
@@ -191,7 +191,7 @@
     get(ThreadContext *tc, Aapcs32::State &state)
     {
         if (state.ncrn <= state.MAX_CRN) {
-            return tc->readIntReg(state.ncrn++);
+            return tc->getReg(RegId(IntRegClass, state.ncrn++));
         }
 
         // Max out the ncrn since we effectively exhausted it.
@@ -216,11 +216,11 @@
                 state.ncrn + 1 <= state.MAX_CRN) {
             Integer low, high;
             if (ArmISA::byteOrder(tc) == ByteOrder::little) {
-                low = tc->readIntReg(state.ncrn++) & mask(32);
-                high = tc->readIntReg(state.ncrn++) & mask(32);
+                low = tc->getReg(RegId(IntRegClass, state.ncrn++)) & mask(32);
+                high = tc->getReg(RegId(IntRegClass, state.ncrn++)) & mask(32);
             } else {
-                high = tc->readIntReg(state.ncrn++) & mask(32);
-                low = tc->readIntReg(state.ncrn++) & mask(32);
+                high = tc->getReg(RegId(IntRegClass, state.ncrn++)) & mask(32);
+                low = tc->getReg(RegId(IntRegClass, state.ncrn++)) & mask(32);
             }
             return low | (high << 32);
         }
@@ -284,7 +284,7 @@
             uint32_t val;
             memcpy((void *)&val, (void *)&cp, sizeof(Composite));
             val = gtoh(val, ArmISA::byteOrder(tc));
-            tc->setIntReg(ArmISA::INTREG_R0, val);
+            tc->setReg(ArmISA::int_reg::R0, val);
         } else {
             VPtr<Composite> cp(state.retAddr, tc);
             *cp = htog(composite, ArmISA::byteOrder(tc));
@@ -295,7 +295,7 @@
     prepare(ThreadContext *tc, Aapcs32::State &state)
     {
         if (sizeof(Composite) > sizeof(uint32_t))
-            state.retAddr = tc->readIntReg(state.ncrn++);
+            state.retAddr = tc->getReg(RegId(IntRegClass, state.ncrn++));
     }
 };
 
@@ -316,7 +316,7 @@
         if (bytes <= chunk_size) {
             if (state.ncrn++ <= state.MAX_CRN) {
                 alignas(alignof(Composite)) uint32_t val =
-                    tc->readIntReg(state.ncrn++);
+                    tc->getReg(RegId(IntRegClass, state.ncrn++));
                 val = htog(val, ArmISA::byteOrder(tc));
                 return gtoh(*(Composite *)&val, ArmISA::byteOrder(tc));
             }
@@ -328,7 +328,7 @@
         if (state.ncrn + regs - 1 <= state.MAX_CRN) {
             alignas(alignof(Composite)) uint8_t buf[bytes];
             for (int i = 0; i < regs; i++) {
-                Chunk val = tc->readIntReg(state.ncrn++);
+                Chunk val = tc->getReg(RegId(IntRegClass, state.ncrn++));
                 val = htog(val, ArmISA::byteOrder(tc));
                 size_t to_copy = std::min<size_t>(bytes, chunk_size);
                 memcpy(buf + i * chunk_size, &val, to_copy);
@@ -342,7 +342,7 @@
 
             int offset = 0;
             while (state.ncrn <= state.MAX_CRN) {
-                Chunk val = tc->readIntReg(state.ncrn++);
+                Chunk val = tc->getReg(RegId(IntRegClass, state.ncrn++));
                 val = htog(val, ArmISA::byteOrder(tc));
                 size_t to_copy = std::min<size_t>(bytes, chunk_size);
                 memcpy(buf + offset, &val, to_copy);
@@ -478,11 +478,8 @@
         auto bytes = floatToBits(f);
         auto *vec_elems = static_cast<ArmISA::VecElem *>(&bytes);
         constexpr int chunks = sizeof(Float) / sizeof(ArmISA::VecElem);
-        for (int chunk = 0; chunk < chunks; chunk++) {
-            int reg = chunk / ArmISA::NumVecElemPerVecReg;
-            int elem = chunk % ArmISA::NumVecElemPerVecReg;
-            tc->setVecElem(RegId(VecElemClass, reg, elem), vec_elems[chunk]);
-        }
+        for (int chunk = 0; chunk < chunks; chunk++)
+            tc->setReg(RegId(VecElemClass, chunk), vec_elems[chunk]);
     };
 };
 
@@ -505,11 +502,8 @@
         auto *vec_elems = static_cast<ArmISA::VecElem *>(&result);
 
         constexpr int chunks = sizeof(Float) / sizeof(ArmISA::VecElem);
-        for (int chunk = 0; chunk < chunks; chunk++) {
-            int reg = chunk / ArmISA::NumVecElemPerVecReg;
-            int elem = chunk % ArmISA::NumVecElemPerVecReg;
-            vec_elems[chunk] = tc->readVecElem(RegId(VecElemClass, reg, elem));
-        }
+        for (int chunk = 0; chunk < chunks; chunk++)
+            vec_elems[chunk] = tc->getReg(RegId(VecElemClass, chunk));
 
         return bitsToFloat(result);
     }
@@ -578,7 +572,8 @@
                 const int lane = index % lane_per_reg;
 
                 RegId id(VecRegClass, reg);
-                auto val = tc->readVecReg(id);
+                ArmISA::VecRegContainer val;
+                tc->getReg(id, &val);
                 ha[i] = val.as<Elem>()[lane];
             }
             return ha;
@@ -625,9 +620,10 @@
             const int lane = i % lane_per_reg;
 
             RegId id(VecRegClass, reg);
-            auto val = tc->readVecReg(id);
+            ArmISA::VecRegContainer val;
+            tc->getReg(id, &val);
             val.as<Elem>()[lane] = ha[i];
-            tc->setVecReg(id, val);
+            tc->setReg(id, &val);
         }
     }
 
diff --git a/src/arch/arm/aapcs64.hh b/src/arch/arm/aapcs64.hh
index 7f2d162..fe58fef 100644
--- a/src/arch/arm/aapcs64.hh
+++ b/src/arch/arm/aapcs64.hh
@@ -61,7 +61,7 @@
         static const int MAX_SRN = 7;
 
         explicit State(const ThreadContext *tc) :
-            nsaa(tc->readIntReg(ArmISA::INTREG_SPX))
+            nsaa(tc->getReg(ArmISA::int_reg::Spx))
         {}
     };
 };
@@ -202,7 +202,9 @@
     {
         if (state.nsrn <= state.MAX_SRN) {
             RegId id(VecRegClass, state.nsrn++);
-            return tc->readVecReg(id).as<Float>()[0];
+            ArmISA::VecRegContainer vc;
+            tc->getReg(id, &vc);
+            return vc.as<Float>()[0];
         }
 
         return loadFromStack<Float>(tc, state);
@@ -217,9 +219,10 @@
     store(ThreadContext *tc, const Float &f)
     {
         RegId id(VecRegClass, 0);
-        auto reg = tc->readVecReg(id);
+        ArmISA::VecRegContainer reg;
+        tc->getReg(id, &reg);
         reg.as<Float>()[0] = f;
-        tc->setVecReg(id, reg);
+        tc->setReg(id, &reg);
     }
 };
 
@@ -238,7 +241,7 @@
     get(ThreadContext *tc, Aapcs64::State &state)
     {
         if (state.ngrn <= state.MAX_GRN)
-            return tc->readIntReg(state.ngrn++);
+            return tc->getReg(RegId(IntRegClass, state.ngrn++));
 
         // Max out ngrn since we've effectively saturated it.
         state.ngrn = state.MAX_GRN + 1;
@@ -259,8 +262,8 @@
             state.ngrn++;
 
         if (sizeof(Integer) == 16 && state.ngrn + 1 <= state.MAX_GRN) {
-            Integer low = tc->readIntReg(state.ngrn++);
-            Integer high = tc->readIntReg(state.ngrn++);
+            Integer low = tc->getReg(RegId(IntRegClass, state.ngrn++));
+            Integer high = tc->getReg(RegId(IntRegClass, state.ngrn++));
             high = high << 64;
             return high | low;
         }
@@ -279,7 +282,7 @@
     static void
     store(ThreadContext *tc, const Integer &i)
     {
-        tc->setIntReg(0, i);
+        tc->setReg(ArmISA::int_reg::X0, i);
     }
 };
 
@@ -290,8 +293,8 @@
     static void
     store(ThreadContext *tc, const Integer &i)
     {
-        tc->setIntReg(0, (uint64_t)i);
-        tc->setIntReg(1, (uint64_t)(i >> 64));
+        tc->setReg(ArmISA::int_reg::X0, (uint64_t)i);
+        tc->setReg(ArmISA::int_reg::X1, (uint64_t)(i >> 64));
     }
 };
 
@@ -379,7 +382,7 @@
         if (state.ngrn + regs - 1 <= state.MAX_GRN) {
             alignas(alignof(Composite)) uint8_t buf[bytes];
             for (int i = 0; i < regs; i++) {
-                Chunk val = tc->readIntReg(state.ngrn++);
+                Chunk val = tc->getReg(RegId(IntRegClass, state.ngrn++));
                 val = htog(val, ArmISA::byteOrder(tc));
                 size_t to_copy = std::min<size_t>(bytes, chunk_size);
                 memcpy(buf + i * chunk_size, &val, to_copy);
@@ -403,7 +406,7 @@
     store(ThreadContext *tc, const Composite &c)
     {
         if (sizeof(Composite) > 16) {
-            Addr addr = tc->readIntReg(ArmISA::INTREG_X8);
+            Addr addr = tc->getReg(ArmISA::int_reg::X8);
             VPtr<Composite> composite(addr, tc);
             *composite = htog(c, ArmISA::byteOrder(tc));
             return;
@@ -426,7 +429,7 @@
             memcpy(&val, buf, to_copy);
             val = gtoh(val, ArmISA::byteOrder(tc));
 
-            tc->setIntReg(i, val);
+            tc->setReg(ArmISA::int_reg::x(i), val);
 
             bytes -= to_copy;
             buf += to_copy;
diff --git a/src/arch/arm/decoder.cc b/src/arch/arm/decoder.cc
index 9d90537..4385744 100644
--- a/src/arch/arm/decoder.cc
+++ b/src/arch/arm/decoder.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014,2018 ARM Limited
+ * Copyright (c) 2012-2014,2018, 2021 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -55,7 +55,9 @@
 GenericISA::BasicDecodeCache<Decoder, ExtMachInst> Decoder::defaultCache;
 
 Decoder::Decoder(const ArmDecoderParams &params)
-    : InstDecoder(params, &data), data(0), fpscrLen(0), fpscrStride(0),
+    : InstDecoder(params, &data),
+      dvmEnabled(params.dvm_enabled),
+      data(0), fpscrLen(0), fpscrStride(0),
       decoderFlavor(dynamic_cast<ISA *>(params.isa)->decoderFlavor())
 {
     reset();
@@ -63,6 +65,13 @@
     // Initialize SVE vector length
     sveLen = (dynamic_cast<ISA *>(params.isa)
             ->getCurSveVecLenInBitsAtReset() >> 7) - 1;
+
+    if (dvmEnabled) {
+        warn_once(
+            "DVM Ops instructions are micro-architecturally "
+            "modelled as loads. This will tamper the effective "
+            "number of loads stat\n");
+    }
 }
 
 void
diff --git a/src/arch/arm/decoder.hh b/src/arch/arm/decoder.hh
index 62d6f54..fdabe6c 100644
--- a/src/arch/arm/decoder.hh
+++ b/src/arch/arm/decoder.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014 ARM Limited
+ * Copyright (c) 2013-2014, 2021 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -62,6 +62,10 @@
 class ISA;
 class Decoder : public InstDecoder
 {
+  public: // Public decoder parameters
+    /** True if the decoder should emit DVM Ops (treated as Loads) */
+    const bool dvmEnabled;
+
   protected:
     //The extended machine instruction being generated
     ExtMachInst emi;
diff --git a/src/arch/arm/fastmodel/CortexA76/FastModelCortexA76.py b/src/arch/arm/fastmodel/CortexA76/FastModelCortexA76.py
index c7d46ff..3f98162 100644
--- a/src/arch/arm/fastmodel/CortexA76/FastModelCortexA76.py
+++ b/src/arch/arm/fastmodel/CortexA76/FastModelCortexA76.py
@@ -31,6 +31,8 @@
 from m5.objects.ArmISA import ArmISA
 from m5.objects.FastModel import AmbaInitiatorSocket, AmbaTargetSocket
 from m5.objects.FastModelGIC import Gicv3CommsTargetSocket
+from m5.objects.ResetPort import ResetResponsePort
+from m5.objects.IntPin import IntSinkPin
 from m5.objects.Gic import ArmPPI
 from m5.objects.Iris import IrisBaseCPU
 from m5.objects.SystemC import SystemC_ScModule
@@ -46,6 +48,10 @@
     evs = Parent.evs
 
     redistributor = Gicv3CommsTargetSocket('GIC communication target')
+    core_reset = IntSinkPin('Raising this signal will put the core into ' \
+                            'reset mode.')
+    poweron_reset = IntSinkPin('Power on reset. Initializes all the ' \
+                               'processor logic, including debug logic.')
 
     CFGEND = Param.Bool(False, "Endianness configuration at reset.  "\
             "0, little endian. 1, big endian.")
@@ -163,6 +169,11 @@
             "Non-secure physical timer event")
 
     amba = AmbaInitiatorSocket(64, 'AMBA initiator socket')
+    top_reset = IntSinkPin('A single cluster-wide power on reset signal for ' \
+            'all resettable registers in DynamIQ.')
+    dbg_reset = IntSinkPin('Initialize the shared debug APB, Cross Trigger ' \
+            'Interface (CTI), and Cross Trigger Matrix (CTM) logic.')
+    model_reset = ResetResponsePort('A reset port to reset the whole cluster.')
 
     # These parameters are described in "Fast Models Reference Manual" section
     # 3.4.19, "ARMCortexA7x1CT".
diff --git a/src/arch/arm/fastmodel/CortexA76/SConscript b/src/arch/arm/fastmodel/CortexA76/SConscript
index 016f613..f3c6606 100644
--- a/src/arch/arm/fastmodel/CortexA76/SConscript
+++ b/src/arch/arm/fastmodel/CortexA76/SConscript
@@ -25,9 +25,6 @@
 
 Import('*')
 
-if not env['USE_ARM_FASTMODEL']:
-    Return()
-
 protocol_dir = Dir('..').Dir('protocol')
 
 for name in ('x1', 'x2', 'x3', 'x4'):
@@ -39,7 +36,8 @@
 
 SimObject('FastModelCortexA76.py', sim_objects=[
     'FastModelCortexA76', 'FastModelCortexA76Cluster'] +
-    [f'FastModelScxEvsCortexA76x{num}' for num in (1, 2, 3, 4)])
-Source('cortex_a76.cc')
-Source('evs.cc')
-Source('thread_context.cc')
+    [f'FastModelScxEvsCortexA76x{num}' for num in (1, 2, 3, 4)],
+    tags='arm fastmodel')
+Source('cortex_a76.cc', tags='arm fastmodel')
+Source('evs.cc', tags='arm fastmodel')
+Source('thread_context.cc', tags='arm fastmodel')
diff --git a/src/arch/arm/fastmodel/CortexA76/cortex_a76.cc b/src/arch/arm/fastmodel/CortexA76/cortex_a76.cc
index e77e734..9280a04 100644
--- a/src/arch/arm/fastmodel/CortexA76/cortex_a76.cc
+++ b/src/arch/arm/fastmodel/CortexA76/cortex_a76.cc
@@ -104,7 +104,8 @@
 Port &
 CortexA76::getPort(const std::string &if_name, PortID idx)
 {
-    if (if_name == "redistributor")
+    if (if_name == "redistributor" || if_name == "core_reset" ||
+        if_name == "poweron_reset")
         return cluster->getEvs()->gem5_getPort(if_name, num);
     else
         return Base::getPort(if_name, idx);
@@ -199,7 +200,8 @@
 Port &
 CortexA76Cluster::getPort(const std::string &if_name, PortID idx)
 {
-    if (if_name == "amba") {
+    if (if_name == "amba" || if_name == "top_reset" ||
+        if_name == "dbg_reset" || if_name == "model_reset") {
         return evs->gem5_getPort(if_name, idx);
     } else {
         return SimObject::getPort(if_name, idx);
diff --git a/src/arch/arm/fastmodel/CortexA76/evs.cc b/src/arch/arm/fastmodel/CortexA76/evs.cc
index 935d139..1c06935 100644
--- a/src/arch/arm/fastmodel/CortexA76/evs.cc
+++ b/src/arch/arm/fastmodel/CortexA76/evs.cc
@@ -71,9 +71,30 @@
 }
 
 template <class Types>
+void
+ScxEvsCortexA76<Types>::requestReset()
+{
+    // Reset all cores.
+    for (auto &poweron_reset : this->poweron_reset) {
+        poweron_reset->signal_out.set_state(0, true);
+        poweron_reset->signal_out.set_state(0, false);
+    }
+    // Reset DSU.
+    this->top_reset.signal_out.set_state(0, true);
+    this->top_reset.signal_out.set_state(0, false);
+    // Reset debug APB.
+    this->dbg_reset.signal_out.set_state(0, true);
+    this->dbg_reset.signal_out.set_state(0, false);
+}
+
+template <class Types>
 ScxEvsCortexA76<Types>::ScxEvsCortexA76(
         const sc_core::sc_module_name &mod_name, const Params &p) :
-    Base(mod_name), amba(Base::amba, p.name + ".amba", -1),
+    Base(mod_name),
+    amba(Base::amba, p.name + ".amba", -1),
+    top_reset(p.name + ".top_reset", 0),
+    dbg_reset(p.name + ".dbg_reset", 0),
+    model_reset(p.name + ".model_reset", -1, this),
     params(p)
 {
     for (int i = 0; i < CoreCount; i++) {
@@ -93,6 +114,10 @@
                 new SignalReceiver(csprintf("cntpnsirq[%d]", i)));
         rvbaraddr.emplace_back(new SignalInitiator<uint64_t>(
                     csprintf("rvbaraddr[%d]", i).c_str()));
+        core_reset.emplace_back(
+                new SignalSender(csprintf("core_reset[%d]", i), 0));
+        poweron_reset.emplace_back(
+                new SignalSender(csprintf("poweron_reset[%d]", i), 0));
 
         Base::cnthpirq[i].bind(cnthpirq[i]->signal_in);
         Base::cnthvirq[i].bind(cnthvirq[i]->signal_in);
@@ -104,8 +129,13 @@
         Base::vcpumntirq[i].bind(vcpumntirq[i]->signal_in);
         Base::cntpnsirq[i].bind(cntpnsirq[i]->signal_in);
         rvbaraddr[i]->bind(Base::rvbaraddr[i]);
+        core_reset[i]->signal_out.bind(Base::core_reset[i]);
+        poweron_reset[i]->signal_out.bind(Base::poweron_reset[i]);
     }
 
+    top_reset.signal_out.bind(Base::top_reset);
+    dbg_reset.signal_out.bind(Base::dbg_reset);
+
     clockRateControl.bind(this->clock_rate_s);
     periphClockRateControl.bind(this->periph_clock_rate_s);
 }
@@ -156,8 +186,18 @@
 {
     if (if_name == "redistributor")
         return *redist.at(idx);
+    else if (if_name == "core_reset")
+        return *core_reset.at(idx);
+    else if (if_name == "poweron_reset")
+        return *poweron_reset.at(idx);
     else if (if_name == "amba")
         return amba;
+    else if (if_name == "top_reset")
+        return top_reset;
+    else if (if_name == "dbg_reset")
+        return dbg_reset;
+    else if (if_name == "model_reset")
+        return model_reset;
     else
         return Base::gem5_getPort(if_name, idx);
 }
diff --git a/src/arch/arm/fastmodel/CortexA76/evs.hh b/src/arch/arm/fastmodel/CortexA76/evs.hh
index 7c834d0..081e80f 100644
--- a/src/arch/arm/fastmodel/CortexA76/evs.hh
+++ b/src/arch/arm/fastmodel/CortexA76/evs.hh
@@ -32,8 +32,10 @@
 
 #include "arch/arm/fastmodel/amba_ports.hh"
 #include "arch/arm/fastmodel/common/signal_receiver.hh"
+#include "arch/arm/fastmodel/common/signal_sender.hh"
 #include "arch/arm/fastmodel/iris/cpu.hh"
 #include "arch/arm/fastmodel/protocol/exported_clock_rate_control.hh"
+#include "dev/reset_port.hh"
 #include "mem/port_proxy.hh"
 #include "params/FastModelScxEvsCortexA76x1.hh"
 #include "params/FastModelScxEvsCortexA76x2.hh"
@@ -90,6 +92,14 @@
     std::vector<std::unique_ptr<SignalReceiver>> vcpumntirq;
     std::vector<std::unique_ptr<SignalReceiver>> cntpnsirq;
     std::vector<std::unique_ptr<SignalInitiator<uint64_t>>> rvbaraddr;
+    std::vector<std::unique_ptr<SignalSender>> core_reset;
+    std::vector<std::unique_ptr<SignalSender>> poweron_reset;
+
+    SignalSender top_reset;
+
+    SignalSender dbg_reset;
+
+    ResetResponsePort<ScxEvsCortexA76> model_reset;
 
     CortexA76Cluster *gem5CpuCluster;
 
@@ -119,6 +129,8 @@
     void setCluster(SimObject *cluster) override;
 
     void setResetAddr(int core, Addr addr, bool secure) override;
+
+    void requestReset();
 };
 
 struct ScxEvsCortexA76x1Types
diff --git a/src/arch/arm/fastmodel/CortexA76/thread_context.cc b/src/arch/arm/fastmodel/CortexA76/thread_context.cc
index 735e06d..672f3b7 100644
--- a/src/arch/arm/fastmodel/CortexA76/thread_context.cc
+++ b/src/arch/arm/fastmodel/CortexA76/thread_context.cc
@@ -102,7 +102,7 @@
 
     auto *non_const_this = const_cast<CortexA76TC *>(this);
 
-    if (idx == ArmISA::INTREG_R13_MON || idx == ArmISA::INTREG_R14_MON) {
+    if (idx == ArmISA::int_reg::R13Mon || idx == ArmISA::int_reg::R14Mon) {
         orig_cpsr = readMiscRegNoEffect(ArmISA::MISCREG_CPSR);
         ArmISA::CPSR new_cpsr = orig_cpsr;
         new_cpsr.mode = ArmISA::MODE_MON;
@@ -111,7 +111,7 @@
 
     RegVal val = ThreadContext::readIntRegFlat(idx);
 
-    if (idx == ArmISA::INTREG_R13_MON || idx == ArmISA::INTREG_R14_MON) {
+    if (idx == ArmISA::int_reg::R13Mon || idx == ArmISA::int_reg::R14Mon) {
         non_const_this->setMiscReg(ArmISA::MISCREG_CPSR, orig_cpsr);
     }
 
@@ -123,7 +123,7 @@
 {
     ArmISA::CPSR orig_cpsr;
 
-    if (idx == ArmISA::INTREG_R13_MON || idx == ArmISA::INTREG_R14_MON) {
+    if (idx == ArmISA::int_reg::R13Mon || idx == ArmISA::int_reg::R14Mon) {
         orig_cpsr = readMiscRegNoEffect(ArmISA::MISCREG_CPSR);
         ArmISA::CPSR new_cpsr = orig_cpsr;
         new_cpsr.mode = ArmISA::MODE_MON;
@@ -132,7 +132,7 @@
 
     ThreadContext::setIntRegFlat(idx, val);
 
-    if (idx == ArmISA::INTREG_R13_MON || idx == ArmISA::INTREG_R14_MON) {
+    if (idx == ArmISA::int_reg::R13Mon || idx == ArmISA::int_reg::R14Mon) {
         setMiscReg(ArmISA::MISCREG_CPSR, orig_cpsr);
     }
 }
@@ -142,10 +142,10 @@
 {
     RegVal result = Iris::ThreadContext::readCCRegFlat(idx);
     switch (idx) {
-      case ArmISA::CCREG_NZ:
+      case ArmISA::cc_reg::Nz:
         result = ((ArmISA::CPSR)result).nz;
         break;
-      case ArmISA::CCREG_FP:
+      case ArmISA::cc_reg::Fp:
         result = bits(result, 31, 28);
         break;
       default:
@@ -158,14 +158,14 @@
 CortexA76TC::setCCRegFlat(RegIndex idx, RegVal val)
 {
     switch (idx) {
-      case ArmISA::CCREG_NZ:
+      case ArmISA::cc_reg::Nz:
         {
             ArmISA::CPSR cpsr = readMiscRegNoEffect(ArmISA::MISCREG_CPSR);
             cpsr.nz = val;
             val = cpsr;
         }
         break;
-      case ArmISA::CCREG_FP:
+      case ArmISA::cc_reg::Fp:
         {
             ArmISA::FPSCR fpscr = readMiscRegNoEffect(ArmISA::MISCREG_FPSCR);
             val = insertBits(fpscr, 31, 28, val);
@@ -267,7 +267,7 @@
         { ArmISA::MISCREG_DBGOSLAR, "DBGOSLAR" },
         // ArmISA::MISCREG_DBGOSLSR?
         // ArmISA::MISCREG_DBGOSDLR?
-        { ArmISA::MISCREG_DBGPRCR, "DBGPRCR_EL1" }, //XXX verify
+        // ArmISA::MISCREG_DBGPRCR?
         // ArmISA::MISCREG_DBGDSAR?
         { ArmISA::MISCREG_DBGCLAIMSET, "DBGCLAIMSET" },
         { ArmISA::MISCREG_DBGCLAIMCLR, "DBGCLAIMCLR" },
@@ -283,119 +283,119 @@
 
         // AArch32 CP15 registers (system control)
         { ArmISA::MISCREG_MIDR, "MIDR" },
-        { ArmISA::MISCREG_CTR, "CTR" },
-        { ArmISA::MISCREG_TCMTR, "TCMTR" },
-        { ArmISA::MISCREG_TLBTR, "TLBTR" },
-        { ArmISA::MISCREG_MPIDR, "MPIDR" },
-        { ArmISA::MISCREG_REVIDR, "REVIDR" },
-        { ArmISA::MISCREG_ID_PFR0, "ID_PFR0" },
-        { ArmISA::MISCREG_ID_PFR1, "ID_PFR1" },
-        { ArmISA::MISCREG_ID_DFR0, "ID_DFR0" },
-        { ArmISA::MISCREG_ID_AFR0, "ID_AFR0" },
-        { ArmISA::MISCREG_ID_MMFR0, "ID_MMFR0" },
-        { ArmISA::MISCREG_ID_MMFR1, "ID_MMFR1" },
-        { ArmISA::MISCREG_ID_MMFR2, "ID_MMFR2" },
-        { ArmISA::MISCREG_ID_MMFR3, "ID_MMFR3" },
-        { ArmISA::MISCREG_ID_MMFR4, "ID_MMFR4" },
-        { ArmISA::MISCREG_ID_ISAR0, "ID_ISAR0" },
-        { ArmISA::MISCREG_ID_ISAR1, "ID_ISAR1" },
-        { ArmISA::MISCREG_ID_ISAR2, "ID_ISAR2" },
-        { ArmISA::MISCREG_ID_ISAR3, "ID_ISAR3" },
-        { ArmISA::MISCREG_ID_ISAR4, "ID_ISAR4" },
-        { ArmISA::MISCREG_ID_ISAR5, "ID_ISAR5" },
-        { ArmISA::MISCREG_ID_ISAR6, "ID_ISAR6" },
-        { ArmISA::MISCREG_CCSIDR, "CCSIDR" },
-        { ArmISA::MISCREG_CLIDR, "CLIDR" },
-        { ArmISA::MISCREG_AIDR, "AIDR" },
-        { ArmISA::MISCREG_CSSELR, "CSSELR_EL1" }, //XXX verify
+        // ArmISA::MISCREG_CTR?
+        // ArmISA::MISCREG_TCMTR?
+        // ArmISA::MISCREG_TLBTR?
+        // ArmISA::MISCREG_MPIDR?
+        // ArmISA::MISCREG_REVIDR?
+        // ArmISA::MISCREG_ID_PFR0?
+        // ArmISA::MISCREG_ID_PFR1?
+        // ArmISA::MISCREG_ID_DFR0?
+        // ArmISA::MISCREG_ID_AFR0?
+        // ArmISA::MISCREG_ID_MMFR0?
+        // ArmISA::MISCREG_ID_MMFR1?
+        // ArmISA::MISCREG_ID_MMFR2?
+        // ArmISA::MISCREG_ID_MMFR3?
+        // ArmISA::MISCREG_ID_MMFR4?
+        // ArmISA::MISCREG_ID_ISAR0?
+        // ArmISA::MISCREG_ID_ISAR1?
+        // ArmISA::MISCREG_ID_ISAR2?
+        // ArmISA::MISCREG_ID_ISAR3?
+        // ArmISA::MISCREG_ID_ISAR4?
+        // ArmISA::MISCREG_ID_ISAR5?
+        // ArmISA::MISCREG_ID_ISAR6?
+        // ArmISA::MISCREG_CCSIDR?
+        // ArmISA::MISCREG_CLIDR?
+        // ArmISA::MISCREG_AIDR?
+        // ArmISA::MISCREG_CSSELR?
         // ArmISA::MISCREG_CSSELR_NS?
         // ArmISA::MISCREG_CSSELR_S?
-        { ArmISA::MISCREG_VPIDR, "VPIDR" },
-        { ArmISA::MISCREG_VMPIDR, "VMPIDR" },
+        // ArmISA::MISCREG_VPIDR?
+        // ArmISA::MISCREG_VMPIDR?,
         // ArmISA::MISCREG_SCTLR?
         // ArmISA::MISCREG_SCTLR_NS?
         // ArmISA::MISCREG_SCTLR_S?
         // ArmISA::MISCREG_ACTLR?
         // ArmISA::MISCREG_ACTLR_NS?
         // ArmISA::MISCREG_ACTLR_S?
-        { ArmISA::MISCREG_CPACR, "CPACR" },
+        // ArmISA::MISCREG_CPACR?
         { ArmISA::MISCREG_SCR, "SCR" },
         { ArmISA::MISCREG_SDER, "SDER" },
-        { ArmISA::MISCREG_NSACR, "NSACR" },
-        { ArmISA::MISCREG_HSCTLR, "HSCTLR" },
-        { ArmISA::MISCREG_HACTLR, "HACTLR" },
-        { ArmISA::MISCREG_HCR, "HCR" },
-        { ArmISA::MISCREG_HDCR, "HDCR" },
-        { ArmISA::MISCREG_HCPTR, "HCPTR" },
-        { ArmISA::MISCREG_HSTR, "HSTR_EL2" }, //XXX verify
-        { ArmISA::MISCREG_HACR, "HACR" },
+        // ArmISA::MISCREG_NSACR?
+        // ArmISA::MISCREG_HSCTLR?
+        // ArmISA::MISCREG_HACTLR?
+        // ArmISA::MISCREG_HCR?
+        // ArmISA::MISCREG_HDCR?
+        // ArmISA::MISCREG_HCPTR?
+        // ArmISA::MISCREG_HSTR?
+        // ArmISA::MISCREG_HACR?
         // ArmISA::MISCREG_TTBR0?
-        { ArmISA::MISCREG_TTBR0_NS, "NS_TTBR0" }, //XXX verify
+        // ArmISA::MISCREG_TTBR0_NS?
         // ArmISA::MISCREG_TTBR0_S?
         // ArmISA::MISCREG_TTBR1?
-        { ArmISA::MISCREG_TTBR1_NS, "NS_TTBR1" }, //XXX verify
+        // ArmISA::MISCREG_TTBR1_NS?
         // ArmISA::MISCREG_TTBR1_S?
         // ArmISA::MISCREG_TTBCR?
-        { ArmISA::MISCREG_TTBCR_NS, "NS_TTBCR" }, //XXX verify
+        // ArmISA::MISCREG_TTBCR_NS?
         // ArmISA::MISCREG_TTBCR_S?
         // ArmISA::MISCREG_HTCR?
         // ArmISA::MISCREG_VTCR?
         // ArmISA::MISCREG_DACR?
-        { ArmISA::MISCREG_DACR_NS, "NS_DACR" }, //XXX verify
+        // ArmISA::MISCREG_DACR_NS?
         // ArmISA::MISCREG_DACR_S?
         // ArmISA::MISCREG_DFSR?
-        { ArmISA::MISCREG_DFSR_NS, "NS_DFSR" }, //XXX verify
+        // ArmISA::MISCREG_DFSR_NS?
         // ArmISA::MISCREG_DFSR_S?
         // ArmISA::MISCREG_IFSR?
-        { ArmISA::MISCREG_IFSR_NS, "NS_IFSR" },
+        // ArmISA::MISCREG_IFSR_NS?
         // ArmISA::MISCREG_IFSR_S?
-        { ArmISA::MISCREG_ADFSR, "ADFSR" },
+        // ArmISA::MISCREG_ADFSR?
         // ArmISA::MISCREG_ADFSR_NS?
         // ArmISA::MISCREG_ADFSR_S?
-        { ArmISA::MISCREG_AIFSR, "AIFSR" },
+        // ArmISA::MISCREG_AIFSR?
         // ArmISA::MISCREG_AIFSR_NS?
         // ArmISA::MISCREG_AIFSR_S?
         // ArmISA::MISCREG_HADFSR?
         // ArmISA::MISCREG_HAIFSR?
-        { ArmISA::MISCREG_HSR, "HSR" },
+        // ArmISA::MISCREG_HSR?
         // ArmISA::MISCREG_DFAR?
-        { ArmISA::MISCREG_DFAR_NS, "NS_DFAR" }, //XXX verify
+        // ArmISA::MISCREG_DFAR_NS?
         // ArmISA::MISCREG_DFAR_S?
         // ArmISA::MISCREG_IFAR?
-        { ArmISA::MISCREG_IFAR_NS, "NS_IFAR" }, //XXX verify
+        // ArmISA::MISCREG_IFAR_NS?
         // ArmISA::MISCREG_IFAR_S?
-        { ArmISA::MISCREG_HDFAR, "HDFAR" },
-        { ArmISA::MISCREG_HIFAR, "HIFAR" },
-        { ArmISA::MISCREG_HPFAR, "HPFAR" },
-        { ArmISA::MISCREG_ICIALLUIS, "ICIALLUIS" },
+        // ArmISA::MISCREG_HDFAR?
+        // ArmISA::MISCREG_HIFAR?
+        // ArmISA::MISCREG_HPFAR?
+        // ArmISA::MISCREG_ICIALLUIS?
         // ArmISA::MISCREG_BPIALLIS?
         // ArmISA::MISCREG_PAR?
-        { ArmISA::MISCREG_PAR_NS, "NS_PAR" }, //XXX verify
+        // ArmISA::MISCREG_PAR_NS?
         // ArmISA::MISCREG_PAR_S?
-        { ArmISA::MISCREG_ICIALLU, "ICIALLU" },
-        { ArmISA::MISCREG_ICIMVAU, "ICIMVAU" },
+        // ArmISA::MISCREG_ICIALLU?
+        // ArmISA::MISCREG_ICIMVAU?
         // ArmISA::MISCREG_CP15ISB?
         // ArmISA::MISCREG_BPIALL?
         // ArmISA::MISCREG_BPIMVA?
-        { ArmISA::MISCREG_DCIMVAC, "DCIMVAC" },
-        { ArmISA::MISCREG_DCISW, "DCISW" },
-        { ArmISA::MISCREG_ATS1CPR, "ATS1CPR" },
-        { ArmISA::MISCREG_ATS1CPW, "ATS1CPW" },
-        { ArmISA::MISCREG_ATS1CUR, "ATS1CUR" },
-        { ArmISA::MISCREG_ATS1CUW, "ATS1CUW" },
-        { ArmISA::MISCREG_ATS12NSOPR, "ATS12NSOPR" },
-        { ArmISA::MISCREG_ATS12NSOPW, "ATS12NSOPW" },
-        { ArmISA::MISCREG_ATS12NSOUR, "ATS12NSOUR" },
-        { ArmISA::MISCREG_ATS12NSOUW, "ATS12NSOUW" },
-        { ArmISA::MISCREG_DCCMVAC, "DCCMVAC" },
-        { ArmISA::MISCREG_DCCSW, "DCCSW" },
+        // ArmISA::MISCREG_DCIMVAC?
+        // ArmISA::MISCREG_DCISW?
+        // ArmISA::MISCREG_ATS1CPR?
+        // ArmISA::MISCREG_ATS1CPW?
+        // ArmISA::MISCREG_ATS1CUR?
+        // ArmISA::MISCREG_ATS1CUW?
+        // ArmISA::MISCREG_ATS12NSOPR?
+        // ArmISA::MISCREG_ATS12NSOPW?
+        // ArmISA::MISCREG_ATS12NSOUR?
+        // ArmISA::MISCREG_ATS12NSOUW?
+        // ArmISA::MISCREG_DCCMVAC?
+        // ArmISA::MISCREG_DCCSW?
         // ArmISA::MISCREG_CP15DSB?
         // ArmISA::MISCREG_CP15DMB?
-        { ArmISA::MISCREG_DCCMVAU, "DCCMVAU" },
+        // ArmISA::MISCREG_DCCMVAU?
         // ArmISA::MISCREG_DCCIMVAC?
-        { ArmISA::MISCREG_DCCISW, "DCCISW" },
-        { ArmISA::MISCREG_ATS1HR, "ATS1HR" },
-        { ArmISA::MISCREG_ATS1HW, "ATS1HW" },
+        // ArmISA::MISCREG_DCCISW?
+        // ArmISA::MISCREG_ATS1HR?
+        // ArmISA::MISCREG_ATS1HW?
         // ArmISA::MISCREG_TLBIALLIS?
         // ArmISA::MISCREG_TLBIMVAIS?
         // ArmISA::MISCREG_TLBIASIDIS?
@@ -437,7 +437,7 @@
         { ArmISA::MISCREG_PMCCNTR, "PMCCNTR" },
         { ArmISA::MISCREG_PMXEVTYPER, "PMXEVTYPER" },
         { ArmISA::MISCREG_PMCCFILTR, "PMCCFILTR" },
-        { ArmISA::MISCREG_PMXEVCNTR, "PMXEVCNTR_EL0" }, //XXX verify
+        { ArmISA::MISCREG_PMXEVCNTR, "PMXEVCNTR" },
         { ArmISA::MISCREG_PMUSERENR, "PMUSERENR" },
         { ArmISA::MISCREG_PMINTENSET, "PMINTENSET" },
         { ArmISA::MISCREG_PMINTENCLR, "PMINTENCLR" },
@@ -445,50 +445,50 @@
         // ArmISA::MISCREG_L2CTLR?
         // ArmISA::MISCREG_L2ECTLR?
         // ArmISA::MISCREG_PRRR?
-        { ArmISA::MISCREG_PRRR_NS, "NS_PRRR" }, //XXX verify
+        // ArmISA::MISCREG_PRRR_NS?
         // ArmISA::MISCREG_PRRR_S?
         // ArmISA::MISCREG_MAIR0?
         // ArmISA::MISCREG_MAIR0_NS?
         // ArmISA::MISCREG_MAIR0_S?
         // ArmISA::MISCREG_NMRR?
-        { ArmISA::MISCREG_NMRR_NS, "NS_NMRR" }, //XXX verify
+        // ArmISA::MISCREG_NMRR_NS?
         // ArmISA::MISCREG_NMRR_S?
         // ArmISA::MISCREG_MAIR1?
         // ArmISA::MISCREG_MAIR1_NS?
         // ArmISA::MISCREG_MAIR1_S?
         // ArmISA::MISCREG_AMAIR0?
-        { ArmISA::MISCREG_AMAIR0_NS, "NS_AMAIR0" }, //XXX verify
+        // ArmISA::MISCREG_AMAIR0_N?
         // ArmISA::MISCREG_AMAIR0_S?
         // ArmISA::MISCREG_AMAIR1?
-        { ArmISA::MISCREG_AMAIR1_NS, "NS_AMAIR1" }, //XXX verify
+        // ArmISA::MISCREG_AMAIR1_NS?
         // ArmISA::MISCREG_AMAIR1_S?
-        { ArmISA::MISCREG_HMAIR0, "HMAIR0" },
-        { ArmISA::MISCREG_HMAIR1, "HMAIR1" },
-        { ArmISA::MISCREG_HAMAIR0, "HAMAIR0" },
-        { ArmISA::MISCREG_HAMAIR1, "HAMAIR1" },
+        // ArmISA::MISCREG_HMAIR0?
+        // ArmISA::MISCREG_HMAIR1?
+        // ArmISA::MISCREG_HAMAIR0?
+        // ArmISA::MISCREG_HAMAIR1?
         // ArmISA::MISCREG_VBAR?
-        { ArmISA::MISCREG_VBAR_NS, "NS_VBAR" }, //XXX verify
+        // ArmISA::MISCREG_VBAR_NS?
         // ArmISA::MISCREG_VBAR_S?
         { ArmISA::MISCREG_MVBAR, "MVBAR" },
-        { ArmISA::MISCREG_RMR, "RMR" },
-        { ArmISA::MISCREG_ISR, "ISR" },
-        { ArmISA::MISCREG_HVBAR, "HVBAR" },
-        { ArmISA::MISCREG_FCSEIDR, "FCSEIDR" },
+        // ArmISA::MISCREG_RMR?
+        // ArmISA::MISCREG_ISR?
+        // ArmISA::MISCREG_HVBAR?
+        // ArmISA::MISCREG_FCSEIDR?
         // ArmISA::MISCREG_CONTEXTIDR?
-        { ArmISA::MISCREG_CONTEXTIDR_NS, "NS_CONTEXTIDR" }, //XXX verify
+        // ArmISA::MISCREG_CONTEXTIDR_NS?
         // ArmISA::MISCREG_CONTEXTIDR_S?
         // ArmISA::MISCREG_TPIDRURW?
-        { ArmISA::MISCREG_TPIDRURW_NS, "NS_TPIDRURW" }, //XXX verify
+        // ArmISA::MISCREG_TPIDRURW_NS?
         // ArmISA::MISCREG_TPIDRURW_S?
         // ArmISA::MISCREG_TPIDRURO?
-        { ArmISA::MISCREG_TPIDRURO_NS, "NS_TPIDRURO" }, //XXX verify
+        // ArmISA::MISCREG_TPIDRURO_NS?
         // ArmISA::MISCREG_TPIDRURO_S?
         // ArmISA::MISCREG_TPIDRPRW?
-        { ArmISA::MISCREG_TPIDRPRW_NS, "NS_TPIDRPRW" }, //XXX verify
-        /// ArmISA::MISCREG_TPIDRPRW_S?
-        { ArmISA::MISCREG_HTPIDR, "HTPIDR" },
+        // ArmISA::MISCREG_TPIDRPRW_NS?
+        // ArmISA::MISCREG_TPIDRPRW_S?
+        // ArmISA::MISCREG_HTPIDR?
         { ArmISA::MISCREG_CNTFRQ, "CNTFRQ" },
-        { ArmISA::MISCREG_CNTKCTL, "CNTKCTL" },
+        // ArmISA::MISCREG_CNTKCTL?
         { ArmISA::MISCREG_CNTP_TVAL, "CNTP_TVAL" },
         // ArmISA::MISCREG_CNTP_TVAL_NS?
         // ArmISA::MISCREG_CNTP_TVAL_S?
@@ -497,9 +497,9 @@
         // ArmISA::MISCREG_CNTP_CTL_S?
         { ArmISA::MISCREG_CNTV_TVAL, "CNTV_TVAL" },
         { ArmISA::MISCREG_CNTV_CTL, "CNTV_CTL" },
-        { ArmISA::MISCREG_CNTHCTL, "CNTHCTL" },
-        { ArmISA::MISCREG_CNTHP_TVAL, "CNTHP_TVAL" },
-        { ArmISA::MISCREG_CNTHP_CTL, "CNTHP_CTL" },
+        // ArmISA::MISCREG_CNTHCTL?
+        // ArmISA::MISCREG_CNTHP_TVAL?
+        // ArmISA::MISCREG_CNTHP_CTL?
         // ArmISA::MISCREG_IL1DATA0?
         // ArmISA::MISCREG_IL1DATA1?
         // ArmISA::MISCREG_IL1DATA2?
@@ -509,11 +509,11 @@
         // ArmISA::MISCREG_DL1DATA2?
         // ArmISA::MISCREG_DL1DATA3?
         // ArmISA::MISCREG_DL1DATA4?
-        { ArmISA::MISCREG_RAMINDEX, "RAMIDX" }, //XXX verify
+        // ArmISA::MISCREG_RAMINDEX?
         // ArmISA::MISCREG_L2ACTLR?
         // ArmISA::MISCREG_CBAR?
-        { ArmISA::MISCREG_HTTBR, "HTTBR" },
-        { ArmISA::MISCREG_VTTBR, "VTTBR" },
+        // ArmISA::MISCREG_HTTBR?
+        // ArmISA::MISCREG_VTTBR?
         { ArmISA::MISCREG_CNTPCT, "CNTPCT" },
         { ArmISA::MISCREG_CNTVCT, "CNTVCT" },
         { ArmISA::MISCREG_CNTP_CVAL, "CNTP_CVAL" },
@@ -521,9 +521,9 @@
         // ArmISA::MISCREG_CNTP_CVAL_S?
         { ArmISA::MISCREG_CNTV_CVAL, "CNTV_CVAL" },
         { ArmISA::MISCREG_CNTVOFF, "CNTVOFF" },
-        { ArmISA::MISCREG_CNTHP_CVAL, "CNTHP_CVAL" },
+        // ArmISA::MISCREG_CNTHP_CVAL?
         // ArmISA::MISCREG_CPUMERRSR?
-        { ArmISA::MISCREG_L2MERRSR, "L2MERRSR" },
+        // ArmISA::MISCREG_L2MERRSR?
 
         // AArch64 registers (Op0=2)
         { ArmISA::MISCREG_MDCCINT_EL1, "MDCCINT_EL1" },
@@ -807,7 +807,7 @@
         { ArmISA::MISCREG_CPUACTLR_EL1, "CPUACTLR_EL1" },
         { ArmISA::MISCREG_CPUECTLR_EL1, "CPUECTLR_EL1" },
         // ArmISA::MISCREG_CPUMERRSR_EL1?
-        { ArmISA::MISCREG_L2MERRSR_EL1, "L2MERRSR_EL1" },
+        // ArmISA::MISCREG_L2MERRSR_EL1?
         // ArmISA::MISCREG_CBAR_EL1?
         { ArmISA::MISCREG_CONTEXTIDR_EL2, "CONTEXTIDR_EL2" },
 
@@ -832,107 +832,107 @@
 });
 
 Iris::ThreadContext::IdxNameMap CortexA76TC::intReg32IdxNameMap({
-        { ArmISA::INTREG_R0, "R0" },
-        { ArmISA::INTREG_R1, "R1" },
-        { ArmISA::INTREG_R2, "R2" },
-        { ArmISA::INTREG_R3, "R3" },
-        { ArmISA::INTREG_R4, "R4" },
-        { ArmISA::INTREG_R5, "R5" },
-        { ArmISA::INTREG_R6, "R6" },
-        { ArmISA::INTREG_R7, "R7" },
-        { ArmISA::INTREG_R8, "R8" },
-        { ArmISA::INTREG_R9, "R9" },
-        { ArmISA::INTREG_R10, "R10" },
-        { ArmISA::INTREG_R11, "R11" },
-        { ArmISA::INTREG_R12, "R12" },
-        { ArmISA::INTREG_R13, "R13" },
-        { ArmISA::INTREG_R14, "R14" },
-        { ArmISA::INTREG_R15, "R15" }
+        { ArmISA::int_reg::R0, "R0" },
+        { ArmISA::int_reg::R1, "R1" },
+        { ArmISA::int_reg::R2, "R2" },
+        { ArmISA::int_reg::R3, "R3" },
+        { ArmISA::int_reg::R4, "R4" },
+        { ArmISA::int_reg::R5, "R5" },
+        { ArmISA::int_reg::R6, "R6" },
+        { ArmISA::int_reg::R7, "R7" },
+        { ArmISA::int_reg::R8, "R8" },
+        { ArmISA::int_reg::R9, "R9" },
+        { ArmISA::int_reg::R10, "R10" },
+        { ArmISA::int_reg::R11, "R11" },
+        { ArmISA::int_reg::R12, "R12" },
+        { ArmISA::int_reg::R13, "R13" },
+        { ArmISA::int_reg::R14, "R14" },
+        { ArmISA::int_reg::R15, "R15" }
 });
 
 Iris::ThreadContext::IdxNameMap CortexA76TC::intReg64IdxNameMap({
-        { ArmISA::INTREG_X0, "X0" },
-        { ArmISA::INTREG_X1, "X1" },
-        { ArmISA::INTREG_X2, "X2" },
-        { ArmISA::INTREG_X3, "X3" },
-        { ArmISA::INTREG_X4, "X4" },
-        { ArmISA::INTREG_X5, "X5" },
-        { ArmISA::INTREG_X6, "X6" },
-        { ArmISA::INTREG_X7, "X7" },
-        { ArmISA::INTREG_X8, "X8" },
-        { ArmISA::INTREG_X9, "X9" },
-        { ArmISA::INTREG_X10, "X10" },
-        { ArmISA::INTREG_X11, "X11" },
-        { ArmISA::INTREG_X12, "X12" },
-        { ArmISA::INTREG_X13, "X13" },
-        { ArmISA::INTREG_X14, "X14" },
-        { ArmISA::INTREG_X15, "X15" },
-        { ArmISA::INTREG_X16, "X16" },
-        { ArmISA::INTREG_X17, "X17" },
-        { ArmISA::INTREG_X18, "X18" },
-        { ArmISA::INTREG_X19, "X19" },
-        { ArmISA::INTREG_X20, "X20" },
-        { ArmISA::INTREG_X21, "X21" },
-        { ArmISA::INTREG_X22, "X22" },
-        { ArmISA::INTREG_X23, "X23" },
-        { ArmISA::INTREG_X24, "X24" },
-        { ArmISA::INTREG_X25, "X25" },
-        { ArmISA::INTREG_X26, "X26" },
-        { ArmISA::INTREG_X27, "X27" },
-        { ArmISA::INTREG_X28, "X28" },
-        { ArmISA::INTREG_X29, "X29" },
-        { ArmISA::INTREG_X30, "X30" },
-        { ArmISA::INTREG_SPX, "SP" },
+        { ArmISA::int_reg::X0, "X0" },
+        { ArmISA::int_reg::X1, "X1" },
+        { ArmISA::int_reg::X2, "X2" },
+        { ArmISA::int_reg::X3, "X3" },
+        { ArmISA::int_reg::X4, "X4" },
+        { ArmISA::int_reg::X5, "X5" },
+        { ArmISA::int_reg::X6, "X6" },
+        { ArmISA::int_reg::X7, "X7" },
+        { ArmISA::int_reg::X8, "X8" },
+        { ArmISA::int_reg::X9, "X9" },
+        { ArmISA::int_reg::X10, "X10" },
+        { ArmISA::int_reg::X11, "X11" },
+        { ArmISA::int_reg::X12, "X12" },
+        { ArmISA::int_reg::X13, "X13" },
+        { ArmISA::int_reg::X14, "X14" },
+        { ArmISA::int_reg::X15, "X15" },
+        { ArmISA::int_reg::X16, "X16" },
+        { ArmISA::int_reg::X17, "X17" },
+        { ArmISA::int_reg::X18, "X18" },
+        { ArmISA::int_reg::X19, "X19" },
+        { ArmISA::int_reg::X20, "X20" },
+        { ArmISA::int_reg::X21, "X21" },
+        { ArmISA::int_reg::X22, "X22" },
+        { ArmISA::int_reg::X23, "X23" },
+        { ArmISA::int_reg::X24, "X24" },
+        { ArmISA::int_reg::X25, "X25" },
+        { ArmISA::int_reg::X26, "X26" },
+        { ArmISA::int_reg::X27, "X27" },
+        { ArmISA::int_reg::X28, "X28" },
+        { ArmISA::int_reg::X29, "X29" },
+        { ArmISA::int_reg::X30, "X30" },
+        { ArmISA::int_reg::Spx, "SP" },
 });
 
 Iris::ThreadContext::IdxNameMap CortexA76TC::flattenedIntIdxNameMap({
-        { ArmISA::INTREG_R0, "X0" },
-        { ArmISA::INTREG_R1, "X1" },
-        { ArmISA::INTREG_R2, "X2" },
-        { ArmISA::INTREG_R3, "X3" },
-        { ArmISA::INTREG_R4, "X4" },
-        { ArmISA::INTREG_R5, "X5" },
-        { ArmISA::INTREG_R6, "X6" },
-        { ArmISA::INTREG_R7, "X7" },
-        { ArmISA::INTREG_R8, "X8" },
-        { ArmISA::INTREG_R9, "X9" },
-        { ArmISA::INTREG_R10, "X10" },
-        { ArmISA::INTREG_R11, "X11" },
-        { ArmISA::INTREG_R12, "X12" },
-        { ArmISA::INTREG_R13, "X13" },
-        { ArmISA::INTREG_R14, "X14" },
+        { ArmISA::int_reg::R0, "X0" },
+        { ArmISA::int_reg::R1, "X1" },
+        { ArmISA::int_reg::R2, "X2" },
+        { ArmISA::int_reg::R3, "X3" },
+        { ArmISA::int_reg::R4, "X4" },
+        { ArmISA::int_reg::R5, "X5" },
+        { ArmISA::int_reg::R6, "X6" },
+        { ArmISA::int_reg::R7, "X7" },
+        { ArmISA::int_reg::R8, "X8" },
+        { ArmISA::int_reg::R9, "X9" },
+        { ArmISA::int_reg::R10, "X10" },
+        { ArmISA::int_reg::R11, "X11" },
+        { ArmISA::int_reg::R12, "X12" },
+        { ArmISA::int_reg::R13, "X13" },
+        { ArmISA::int_reg::R14, "X14" },
         // Skip PC.
-        { ArmISA::INTREG_R13_SVC, "X19" },
-        { ArmISA::INTREG_R14_SVC, "X18" },
-        { ArmISA::INTREG_R13_MON, "R13" }, // Need to be in monitor mode?
-        { ArmISA::INTREG_R14_MON, "R14" }, // Need to be in monitor mode?
-        { ArmISA::INTREG_R13_HYP, "X15" },
-        { ArmISA::INTREG_R13_ABT, "X21" },
-        { ArmISA::INTREG_R14_ABT, "X20" },
-        { ArmISA::INTREG_R13_UND, "X23" },
-        { ArmISA::INTREG_R14_UND, "X22" },
-        { ArmISA::INTREG_R13_IRQ, "X17" },
-        { ArmISA::INTREG_R14_IRQ, "X16" },
-        { ArmISA::INTREG_R8_FIQ, "X24" },
-        { ArmISA::INTREG_R9_FIQ, "X25" },
-        { ArmISA::INTREG_R10_FIQ, "X26" },
-        { ArmISA::INTREG_R11_FIQ, "X27" },
-        { ArmISA::INTREG_R12_FIQ, "X28" },
-        { ArmISA::INTREG_R13_FIQ, "X29" },
-        { ArmISA::INTREG_R14_FIQ, "X30" },
+        { ArmISA::int_reg::R13Svc, "X19" },
+        { ArmISA::int_reg::R14Svc, "X18" },
+        { ArmISA::int_reg::R13Mon, "R13" }, // Need to be in monitor mode?
+        { ArmISA::int_reg::R14Mon, "R14" }, // Need to be in monitor mode?
+        { ArmISA::int_reg::R13Hyp, "X15" },
+        { ArmISA::int_reg::R13Abt, "X21" },
+        { ArmISA::int_reg::R14Abt, "X20" },
+        { ArmISA::int_reg::R13Und, "X23" },
+        { ArmISA::int_reg::R14Und, "X22" },
+        { ArmISA::int_reg::R13Irq, "X17" },
+        { ArmISA::int_reg::R14Irq, "X16" },
+        { ArmISA::int_reg::R8Fiq, "X24" },
+        { ArmISA::int_reg::R9Fiq, "X25" },
+        { ArmISA::int_reg::R10Fiq, "X26" },
+        { ArmISA::int_reg::R11Fiq, "X27" },
+        { ArmISA::int_reg::R12Fiq, "X28" },
+        { ArmISA::int_reg::R13Fiq, "X29" },
+        { ArmISA::int_reg::R14Fiq, "X30" },
         // Skip zero, ureg0-2, and dummy regs.
-        { ArmISA::INTREG_SP0, "SP_EL0" },
-        { ArmISA::INTREG_SP1, "SP_EL1" },
-        { ArmISA::INTREG_SP2, "SP_EL2" },
-        { ArmISA::INTREG_SP3, "SP_EL3" },
+        { ArmISA::int_reg::Sp0, "SP_EL0" },
+        { ArmISA::int_reg::Sp1, "SP_EL1" },
+        { ArmISA::int_reg::Sp2, "SP_EL2" },
+        { ArmISA::int_reg::Sp3, "SP_EL3" },
 });
 
 Iris::ThreadContext::IdxNameMap CortexA76TC::ccRegIdxNameMap({
-        { ArmISA::CCREG_NZ, "CPSR" },
-        { ArmISA::CCREG_C, "CPSR.C" },
-        { ArmISA::CCREG_V, "CPSR.V" },
-        { ArmISA::CCREG_GE, "CPSR.GE" },
-        { ArmISA::CCREG_FP, "FPSCR" },
+        { ArmISA::cc_reg::Nz, "CPSR" },
+        { ArmISA::cc_reg::C, "CPSR.C" },
+        { ArmISA::cc_reg::V, "CPSR.V" },
+        { ArmISA::cc_reg::Ge, "CPSR.GE" },
+        { ArmISA::cc_reg::Fp, "FPSCR" },
 });
 
 Iris::ThreadContext::IdxNameMap CortexA76TC::vecRegIdxNameMap({
diff --git a/src/arch/arm/fastmodel/CortexA76/x1/x1.lisa b/src/arch/arm/fastmodel/CortexA76/x1/x1.lisa
index b83efc6..5cadfac 100644
--- a/src/arch/arm/fastmodel/CortexA76/x1/x1.lisa
+++ b/src/arch/arm/fastmodel/CortexA76/x1/x1.lisa
@@ -60,6 +60,12 @@
         // Core reset addrs.
         self.rvbaraddr => core.rvbaraddr;
 
+        // Reset signals.
+        self.core_reset => core.reset;
+        self.poweron_reset => core.cpuporeset;
+        self.top_reset => core.sporeset;
+        self.dbg_reset => core.presetdbg;
+
         // Clocks.
         clock1Hz.clk_out => clockDiv.clk_in;
         clock1Hz.clk_out => clockDivPeriph.clk_in;
@@ -100,4 +106,8 @@
     master port<Signal> vcpumntirq[1];
     master port<Signal> cntpnsirq[1];
     slave port<Value_64> rvbaraddr[1];
+    slave port<Signal> core_reset[1];
+    slave port<Signal> poweron_reset[1];
+    slave port<Signal> top_reset;
+    slave port<Signal> dbg_reset;
 }
diff --git a/src/arch/arm/fastmodel/CortexA76/x1/x1.sgproj b/src/arch/arm/fastmodel/CortexA76/x1/x1.sgproj
index ff839685..f3fd0db 100644
--- a/src/arch/arm/fastmodel/CortexA76/x1/x1.sgproj
+++ b/src/arch/arm/fastmodel/CortexA76/x1/x1.sgproj
@@ -8,13 +8,12 @@
     ADDITIONAL_COMPILER_SETTINGS = "-O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
     TARGET_SYSTEMC_AUTO = "1";
 
diff --git a/src/arch/arm/fastmodel/CortexA76/x2/x2.lisa b/src/arch/arm/fastmodel/CortexA76/x2/x2.lisa
index 4ab0b07..5805fd5 100644
--- a/src/arch/arm/fastmodel/CortexA76/x2/x2.lisa
+++ b/src/arch/arm/fastmodel/CortexA76/x2/x2.lisa
@@ -60,6 +60,12 @@
         // Core reset addrs.
         self.rvbaraddr => core.rvbaraddr;
 
+        // Reset signals.
+        self.core_reset => core.reset;
+        self.poweron_reset => core.cpuporeset;
+        self.top_reset => core.sporeset;
+        self.dbg_reset => core.presetdbg;
+
         // Clocks.
         clock1Hz.clk_out => clockDiv.clk_in;
         clock1Hz.clk_out => clockDivPeriph.clk_in;
@@ -100,4 +106,8 @@
     master port<Signal> vcpumntirq[2];
     master port<Signal> cntpnsirq[2];
     slave port<Value_64> rvbaraddr[2];
+    slave port<Signal> core_reset[2];
+    slave port<Signal> poweron_reset[2];
+    slave port<Signal> top_reset;
+    slave port<Signal> dbg_reset;
 }
diff --git a/src/arch/arm/fastmodel/CortexA76/x2/x2.sgproj b/src/arch/arm/fastmodel/CortexA76/x2/x2.sgproj
index 8ecb76f..abec8ad 100644
--- a/src/arch/arm/fastmodel/CortexA76/x2/x2.sgproj
+++ b/src/arch/arm/fastmodel/CortexA76/x2/x2.sgproj
@@ -8,13 +8,12 @@
     ADDITIONAL_COMPILER_SETTINGS = "-march=core2 -O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
     TARGET_SYSTEMC_AUTO = "1";
 
diff --git a/src/arch/arm/fastmodel/CortexA76/x3/x3.lisa b/src/arch/arm/fastmodel/CortexA76/x3/x3.lisa
index 7625769..991f8b8 100644
--- a/src/arch/arm/fastmodel/CortexA76/x3/x3.lisa
+++ b/src/arch/arm/fastmodel/CortexA76/x3/x3.lisa
@@ -60,6 +60,12 @@
         // Core reset addrs.
         self.rvbaraddr => core.rvbaraddr;
 
+        // Reset signals.
+        self.core_reset => core.reset;
+        self.poweron_reset => core.cpuporeset;
+        self.top_reset => core.sporeset;
+        self.dbg_reset => core.presetdbg;
+
         // Clocks.
         clock1Hz.clk_out => clockDiv.clk_in;
         clock1Hz.clk_out => clockDivPeriph.clk_in;
@@ -100,4 +106,8 @@
     master port<Signal> vcpumntirq[3];
     master port<Signal> cntpnsirq[3];
     slave port<Value_64> rvbaraddr[3];
+    slave port<Signal> core_reset[3];
+    slave port<Signal> poweron_reset[3];
+    slave port<Signal> top_reset;
+    slave port<Signal> dbg_reset;
 }
diff --git a/src/arch/arm/fastmodel/CortexA76/x3/x3.sgproj b/src/arch/arm/fastmodel/CortexA76/x3/x3.sgproj
index 36cfec7..666b1dc 100644
--- a/src/arch/arm/fastmodel/CortexA76/x3/x3.sgproj
+++ b/src/arch/arm/fastmodel/CortexA76/x3/x3.sgproj
@@ -8,13 +8,12 @@
     ADDITIONAL_COMPILER_SETTINGS = "-march=core2 -O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
     TARGET_SYSTEMC_AUTO = "1";
 
diff --git a/src/arch/arm/fastmodel/CortexA76/x4/x4.lisa b/src/arch/arm/fastmodel/CortexA76/x4/x4.lisa
index 3e67719..f8c50fc 100644
--- a/src/arch/arm/fastmodel/CortexA76/x4/x4.lisa
+++ b/src/arch/arm/fastmodel/CortexA76/x4/x4.lisa
@@ -60,6 +60,12 @@
         // Core reset addrs.
         self.rvbaraddr => core.rvbaraddr;
 
+        // Reset signals.
+        self.core_reset => core.reset;
+        self.poweron_reset => core.cpuporeset;
+        self.top_reset => core.sporeset;
+        self.dbg_reset => core.presetdbg;
+
         // Clocks.
         clock1Hz.clk_out => clockDiv.clk_in;
         clock1Hz.clk_out => clockDivPeriph.clk_in;
@@ -100,4 +106,8 @@
     master port<Signal> vcpumntirq[4];
     master port<Signal> cntpnsirq[4];
     slave port<Value_64> rvbaraddr[4];
+    slave port<Signal> core_reset[4];
+    slave port<Signal> poweron_reset[4];
+    slave port<Signal> top_reset;
+    slave port<Signal> dbg_reset;
 }
diff --git a/src/arch/arm/fastmodel/CortexA76/x4/x4.sgproj b/src/arch/arm/fastmodel/CortexA76/x4/x4.sgproj
index 291256b..e3c9063 100644
--- a/src/arch/arm/fastmodel/CortexA76/x4/x4.sgproj
+++ b/src/arch/arm/fastmodel/CortexA76/x4/x4.sgproj
@@ -8,13 +8,12 @@
     ADDITIONAL_COMPILER_SETTINGS = "-march=core2 -O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
     TARGET_SYSTEMC_AUTO = "1";
 
diff --git a/src/arch/arm/fastmodel/CortexR52/FastModelCortexR52.py b/src/arch/arm/fastmodel/CortexR52/FastModelCortexR52.py
index 710584d..c9e21e6 100644
--- a/src/arch/arm/fastmodel/CortexR52/FastModelCortexR52.py
+++ b/src/arch/arm/fastmodel/CortexR52/FastModelCortexR52.py
@@ -30,6 +30,7 @@
 from m5.objects.ArmInterrupts import ArmInterrupts
 from m5.objects.ArmISA import ArmISA
 from m5.objects.FastModel import AmbaInitiatorSocket, AmbaTargetSocket
+from m5.objects.ResetPort import ResetResponsePort
 from m5.objects.IntPin import IntSinkPin, VectorIntSinkPin
 from m5.objects.Iris import IrisBaseCPU
 from m5.objects.SystemC import SystemC_ScModule
@@ -114,6 +115,9 @@
 
     ext_slave = AmbaTargetSocket(64, 'AMBA target socket')
     top_reset = IntSinkPin('This signal resets timer and interrupt controller.')
+    dbg_reset = IntSinkPin('Initialize the shared debug APB, Cross Trigger ' \
+            'Interface (CTI), and Cross Trigger Matrix (CTM) logic.')
+    model_reset = ResetResponsePort('A reset port to reset the whole cluster.')
 
     CLUSTER_ID = Param.UInt16(0, "CLUSTER_ID[15:8] equivalent to " \
             "CFGMPIDRAFF2, CLUSTER_ID[7:0] equivalent to CFGMPIDRAFF1")
diff --git a/src/arch/arm/fastmodel/CortexR52/SConscript b/src/arch/arm/fastmodel/CortexR52/SConscript
index c1dc1fe..bf3df74 100644
--- a/src/arch/arm/fastmodel/CortexR52/SConscript
+++ b/src/arch/arm/fastmodel/CortexR52/SConscript
@@ -25,9 +25,6 @@
 
 Import('*')
 
-if not env['USE_ARM_FASTMODEL']:
-    Return()
-
 protocol_dir = Dir('..').Dir('protocol')
 
 for name in ('x1', 'x2', 'x3', 'x4'):
@@ -40,7 +37,8 @@
 
 SimObject('FastModelCortexR52.py', sim_objects=[
     'FastModelCortexR52', 'FastModelCortexR52Cluster'] +
-    [f'FastModelScxEvsCortexR52x{num}' for num in (1, 2, 3, 4)])
-Source('cortex_r52.cc')
-Source('evs.cc')
-Source('thread_context.cc')
+    [f'FastModelScxEvsCortexR52x{num}' for num in (1, 2, 3, 4)],
+    tags='arm fastmodel')
+Source('cortex_r52.cc', tags='arm fastmodel')
+Source('evs.cc', tags='arm fastmodel')
+Source('thread_context.cc', tags='arm fastmodel')
diff --git a/src/arch/arm/fastmodel/CortexR52/cortex_r52.cc b/src/arch/arm/fastmodel/CortexR52/cortex_r52.cc
index 85a4c2a..be83082 100644
--- a/src/arch/arm/fastmodel/CortexR52/cortex_r52.cc
+++ b/src/arch/arm/fastmodel/CortexR52/cortex_r52.cc
@@ -158,7 +158,8 @@
 {
     if (if_name == "spi") {
         return evs->gem5_getPort(if_name, idx);
-    } else if (if_name == "ext_slave" || if_name == "top_reset") {
+    } else if (if_name == "ext_slave" || if_name == "top_reset" ||
+               if_name == "dbg_reset" || if_name == "model_reset") {
         assert(idx == InvalidPortID);
         return evs->gem5_getPort(if_name, idx);
     } else {
diff --git a/src/arch/arm/fastmodel/CortexR52/evs.cc b/src/arch/arm/fastmodel/CortexR52/evs.cc
index 720f1cc..6887c6c 100644
--- a/src/arch/arm/fastmodel/CortexR52/evs.cc
+++ b/src/arch/arm/fastmodel/CortexR52/evs.cc
@@ -96,9 +96,11 @@
 ScxEvsCortexR52<Types>::ScxEvsCortexR52(
         const sc_core::sc_module_name &mod_name, const Params &p) :
     Base(mod_name),
-    params(p),
     ext_slave(Base::ext_slave, p.name + ".ext_slave", -1),
-    top_reset(p.name + ".top_reset", 0)
+    top_reset(p.name + ".top_reset", 0),
+    dbg_reset(p.name + ".dbg_reset", 0),
+    model_reset(p.name + ".model_reset", -1, this),
+    params(p)
 {
     for (int i = 0; i < CoreCount; i++)
         corePins.emplace_back(new CorePins(this, i));
@@ -109,6 +111,7 @@
     }
 
     top_reset.signal_out.bind(Base::top_reset);
+    dbg_reset.signal_out.bind(Base::dbg_reset);
 
     clockRateControl.bind(this->clock_rate_s);
     signalInterrupt.bind(this->signal_interrupt);
@@ -144,6 +147,10 @@
         return this->ext_slave;
     } else if (if_name == "top_reset") {
         return this->top_reset;
+    } else if (if_name == "dbg_reset") {
+        return this->dbg_reset;
+    } else if (if_name == "model_reset") {
+        return this->model_reset;
     } else if (if_name == "spi") {
         return *this->spis.at(idx);
     } else if (if_name.substr(0, 3) == "ppi") {
diff --git a/src/arch/arm/fastmodel/CortexR52/evs.hh b/src/arch/arm/fastmodel/CortexR52/evs.hh
index fa9d7fe..535d678 100644
--- a/src/arch/arm/fastmodel/CortexR52/evs.hh
+++ b/src/arch/arm/fastmodel/CortexR52/evs.hh
@@ -37,6 +37,7 @@
 #include "arch/arm/fastmodel/protocol/exported_clock_rate_control.hh"
 #include "arch/arm/fastmodel/protocol/signal_interrupt.hh"
 #include "dev/intpin.hh"
+#include "dev/reset_port.hh"
 #include "mem/port_proxy.hh"
 #include "params/FastModelScxEvsCortexR52x1.hh"
 #include "params/FastModelScxEvsCortexR52x2.hh"
@@ -119,14 +120,18 @@
 
     std::vector<std::unique_ptr<ClstrInt>> spis;
 
-    CortexR52Cluster *gem5CpuCluster;
-
-    const Params &params;
-
     AmbaTarget ext_slave;
 
     SignalSender top_reset;
 
+    SignalSender dbg_reset;
+
+    ResetResponsePort<ScxEvsCortexR52> model_reset;
+
+    CortexR52Cluster *gem5CpuCluster;
+
+    const Params &params;
+
   public:
     ScxEvsCortexR52(const Params &p) : ScxEvsCortexR52(p.name.c_str(), p) {}
     ScxEvsCortexR52(const sc_core::sc_module_name &mod_name, const Params &p);
@@ -143,6 +148,22 @@
         this->signalInterrupt->spi(num, false);
     }
 
+    void
+    requestReset()
+    {
+        // Reset all cores.
+        for (auto &core_pin : corePins) {
+            core_pin->poweron_reset.signal_out.set_state(0, true);
+            core_pin->poweron_reset.signal_out.set_state(0, false);
+        }
+        // Reset L2 system.
+        this->top_reset.signal_out.set_state(0, true);
+        this->top_reset.signal_out.set_state(0, false);
+        // Reset debug APB.
+        this->dbg_reset.signal_out.set_state(0, true);
+        this->dbg_reset.signal_out.set_state(0, false);
+    }
+
     Port &gem5_getPort(const std::string &if_name, int idx) override;
 
     void
diff --git a/src/arch/arm/fastmodel/CortexR52/thread_context.cc b/src/arch/arm/fastmodel/CortexR52/thread_context.cc
index 269baf5..f3e1709 100644
--- a/src/arch/arm/fastmodel/CortexR52/thread_context.cc
+++ b/src/arch/arm/fastmodel/CortexR52/thread_context.cc
@@ -104,10 +104,10 @@
 {
     RegVal result = Iris::ThreadContext::readCCRegFlat(idx);
     switch (idx) {
-      case ArmISA::CCREG_NZ:
+      case ArmISA::cc_reg::Nz:
         result = ((ArmISA::CPSR)result).nz;
         break;
-      case ArmISA::CCREG_FP:
+      case ArmISA::cc_reg::Fp:
         result = bits(result, 31, 28);
         break;
       default:
@@ -120,14 +120,14 @@
 CortexR52TC::setCCRegFlat(RegIndex idx, RegVal val)
 {
     switch (idx) {
-      case ArmISA::CCREG_NZ:
+      case ArmISA::cc_reg::Nz:
         {
             ArmISA::CPSR cpsr = readMiscRegNoEffect(ArmISA::MISCREG_CPSR);
             cpsr.nz = val;
             val = cpsr;
         }
         break;
-      case ArmISA::CCREG_FP:
+      case ArmISA::cc_reg::Fp:
         {
             ArmISA::FPSCR fpscr = readMiscRegNoEffect(ArmISA::MISCREG_FPSCR);
             val = insertBits(fpscr, 31, 28, val);
@@ -792,30 +792,30 @@
 });
 
 Iris::ThreadContext::IdxNameMap CortexR52TC::intReg32IdxNameMap({
-        { ArmISA::INTREG_R0, "R0" },
-        { ArmISA::INTREG_R1, "R1" },
-        { ArmISA::INTREG_R2, "R2" },
-        { ArmISA::INTREG_R3, "R3" },
-        { ArmISA::INTREG_R4, "R4" },
-        { ArmISA::INTREG_R5, "R5" },
-        { ArmISA::INTREG_R6, "R6" },
-        { ArmISA::INTREG_R7, "R7" },
-        { ArmISA::INTREG_R8, "R8" },
-        { ArmISA::INTREG_R9, "R9" },
-        { ArmISA::INTREG_R10, "R10" },
-        { ArmISA::INTREG_R11, "R11" },
-        { ArmISA::INTREG_R12, "R12" },
-        { ArmISA::INTREG_R13, "R13" },
-        { ArmISA::INTREG_R14, "R14" },
-        { ArmISA::INTREG_R15, "R15" }
+        { ArmISA::int_reg::R0, "R0" },
+        { ArmISA::int_reg::R1, "R1" },
+        { ArmISA::int_reg::R2, "R2" },
+        { ArmISA::int_reg::R3, "R3" },
+        { ArmISA::int_reg::R4, "R4" },
+        { ArmISA::int_reg::R5, "R5" },
+        { ArmISA::int_reg::R6, "R6" },
+        { ArmISA::int_reg::R7, "R7" },
+        { ArmISA::int_reg::R8, "R8" },
+        { ArmISA::int_reg::R9, "R9" },
+        { ArmISA::int_reg::R10, "R10" },
+        { ArmISA::int_reg::R11, "R11" },
+        { ArmISA::int_reg::R12, "R12" },
+        { ArmISA::int_reg::R13, "R13" },
+        { ArmISA::int_reg::R14, "R14" },
+        { ArmISA::int_reg::R15, "R15" }
 });
 
 Iris::ThreadContext::IdxNameMap CortexR52TC::ccRegIdxNameMap({
-        { ArmISA::CCREG_NZ, "CPSR" },
-        { ArmISA::CCREG_C, "CPSR.C" },
-        { ArmISA::CCREG_V, "CPSR.V" },
-        { ArmISA::CCREG_GE, "CPSR.GE" },
-        { ArmISA::CCREG_FP, "FPSCR" },
+        { ArmISA::cc_reg::Nz, "CPSR" },
+        { ArmISA::cc_reg::C, "CPSR.C" },
+        { ArmISA::cc_reg::V, "CPSR.V" },
+        { ArmISA::cc_reg::Ge, "CPSR.GE" },
+        { ArmISA::cc_reg::Fp, "FPSCR" },
 });
 
 std::vector<iris::MemorySpaceId> CortexR52TC::bpSpaceIds;
diff --git a/src/arch/arm/fastmodel/CortexR52/x1/x1.lisa b/src/arch/arm/fastmodel/CortexR52/x1/x1.lisa
index 36f3279..2a7299d 100644
--- a/src/arch/arm/fastmodel/CortexR52/x1/x1.lisa
+++ b/src/arch/arm/fastmodel/CortexR52/x1/x1.lisa
@@ -45,9 +45,12 @@
         core.flash_m => self.flash;
         core.pvbus_core_m => self.amba;
         self.ext_slave => core.ext_slave_s;
+
+        // Reset signals.
         self.core_reset => core.reset;
         self.poweron_reset => core.cpuporeset;
         self.top_reset => core.topreset;
+        self.dbg_reset => core.presetdbg;
         self.halt => core.cpuhalt;
 
         // Clocks.
@@ -77,6 +80,7 @@
     slave port<Signal> poweron_reset[1];
     slave port<Signal> halt[1];
     slave port<Signal> top_reset;
+    slave port<Signal> dbg_reset;
     slave port<Value_64> cfgvectable[1];
 
     slave port<ExportedClockRateControl> clock_rate_s
diff --git a/src/arch/arm/fastmodel/CortexR52/x1/x1.sgproj b/src/arch/arm/fastmodel/CortexR52/x1/x1.sgproj
index 4e8d35f..9d2a574 100644
--- a/src/arch/arm/fastmodel/CortexR52/x1/x1.sgproj
+++ b/src/arch/arm/fastmodel/CortexR52/x1/x1.sgproj
@@ -8,13 +8,12 @@
     ADDITIONAL_COMPILER_SETTINGS = "-O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
     TARGET_SYSTEMC_AUTO = "1";
 
diff --git a/src/arch/arm/fastmodel/CortexR52/x2/x2.lisa b/src/arch/arm/fastmodel/CortexR52/x2/x2.lisa
index 492d289..9100a5b 100644
--- a/src/arch/arm/fastmodel/CortexR52/x2/x2.lisa
+++ b/src/arch/arm/fastmodel/CortexR52/x2/x2.lisa
@@ -45,9 +45,12 @@
         core.flash_m => self.flash;
         core.pvbus_core_m => self.amba;
         self.ext_slave => core.ext_slave_s;
+
+        // Reset signals.
         self.core_reset => core.reset;
         self.poweron_reset => core.cpuporeset;
         self.top_reset => core.topreset;
+        self.dbg_reset => core.presetdbg;
         self.halt => core.cpuhalt;
 
         // Clocks.
@@ -78,6 +81,7 @@
     slave port<Signal> poweron_reset[2];
     slave port<Signal> halt[2];
     slave port<Signal> top_reset;
+    slave port<Signal> dbg_reset;
     slave port<Value_64> cfgvectable[2];
 
     slave port<ExportedClockRateControl> clock_rate_s
diff --git a/src/arch/arm/fastmodel/CortexR52/x2/x2.sgproj b/src/arch/arm/fastmodel/CortexR52/x2/x2.sgproj
index ad3d40a..e103170 100644
--- a/src/arch/arm/fastmodel/CortexR52/x2/x2.sgproj
+++ b/src/arch/arm/fastmodel/CortexR52/x2/x2.sgproj
@@ -8,13 +8,12 @@
     ADDITIONAL_COMPILER_SETTINGS = "-march=core2 -O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
     TARGET_SYSTEMC_AUTO = "1";
 
diff --git a/src/arch/arm/fastmodel/CortexR52/x3/x3.lisa b/src/arch/arm/fastmodel/CortexR52/x3/x3.lisa
index ed4837c..bb8d153 100644
--- a/src/arch/arm/fastmodel/CortexR52/x3/x3.lisa
+++ b/src/arch/arm/fastmodel/CortexR52/x3/x3.lisa
@@ -45,9 +45,12 @@
         core.flash_m => self.flash;
         core.pvbus_core_m => self.amba;
         self.ext_slave => core.ext_slave_s;
+
+        // Reset signals.
         self.core_reset => core.reset;
         self.poweron_reset => core.cpuporeset;
         self.top_reset => core.topreset;
+        self.dbg_reset => core.presetdbg;
         self.halt => core.cpuhalt;
 
         // Clocks.
@@ -79,6 +82,7 @@
     slave port<Signal> poweron_reset[3];
     slave port<Signal> halt[3];
     slave port<Signal> top_reset;
+    slave port<Signal> dbg_reset;
     slave port<Value_64> cfgvectable[3];
 
     slave port<ExportedClockRateControl> clock_rate_s
diff --git a/src/arch/arm/fastmodel/CortexR52/x3/x3.sgproj b/src/arch/arm/fastmodel/CortexR52/x3/x3.sgproj
index a5d269e..0c92809 100644
--- a/src/arch/arm/fastmodel/CortexR52/x3/x3.sgproj
+++ b/src/arch/arm/fastmodel/CortexR52/x3/x3.sgproj
@@ -8,13 +8,12 @@
     ADDITIONAL_COMPILER_SETTINGS = "-march=core2 -O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
     TARGET_SYSTEMC_AUTO = "1";
 
diff --git a/src/arch/arm/fastmodel/CortexR52/x4/x4.lisa b/src/arch/arm/fastmodel/CortexR52/x4/x4.lisa
index 73680b1..5b278dd 100644
--- a/src/arch/arm/fastmodel/CortexR52/x4/x4.lisa
+++ b/src/arch/arm/fastmodel/CortexR52/x4/x4.lisa
@@ -45,9 +45,12 @@
         core.flash_m => self.flash;
         core.pvbus_core_m => self.amba;
         self.ext_slave => core.ext_slave_s;
+
+        // Reset signals.
         self.core_reset => core.reset;
         self.poweron_reset => core.cpuporeset;
         self.top_reset => core.topreset;
+        self.dbg_reset => core.presetdbg;
         self.halt => core.cpuhalt;
 
         // Clocks.
@@ -80,6 +83,7 @@
     slave port<Signal> poweron_reset[4];
     slave port<Signal> halt[4];
     slave port<Signal> top_reset;
+    slave port<Signal> dbg_reset;
     slave port<Value_64> cfgvectable[4];
 
     slave port<ExportedClockRateControl> clock_rate_s
diff --git a/src/arch/arm/fastmodel/CortexR52/x4/x4.sgproj b/src/arch/arm/fastmodel/CortexR52/x4/x4.sgproj
index d33f850..6a145fd 100644
--- a/src/arch/arm/fastmodel/CortexR52/x4/x4.sgproj
+++ b/src/arch/arm/fastmodel/CortexR52/x4/x4.sgproj
@@ -8,13 +8,12 @@
     ADDITIONAL_COMPILER_SETTINGS = "-march=core2 -O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
     TARGET_SYSTEMC_AUTO = "1";
 
diff --git a/src/arch/arm/fastmodel/GIC/FastModelGIC.py b/src/arch/arm/fastmodel/GIC/FastModelGIC.py
index 5a551df..8a94d59 100644
--- a/src/arch/arm/fastmodel/GIC/FastModelGIC.py
+++ b/src/arch/arm/fastmodel/GIC/FastModelGIC.py
@@ -41,6 +41,7 @@
 
 from m5.objects.FastModel import AmbaInitiatorSocket, AmbaTargetSocket
 from m5.objects.Gic import BaseGic
+from m5.objects.IntPin import VectorIntSourcePin
 from m5.objects.SystemC import SystemC_ScModule
 
 GICV3_COMMS_TARGET_ROLE = 'GICV3 COMMS TARGET'
@@ -473,6 +474,8 @@
     redistributor = VectorGicv3CommsInitiatorSocket(
             'GIC communication initiator')
 
+    wake_request = VectorIntSourcePin('GIC wake request initiator')
+
     # Used for DTB autogeneration
     _state = FdtState(addr_cells=2, size_cells=2, interrupt_cells=3)
 
diff --git a/src/arch/arm/fastmodel/GIC/GIC.lisa b/src/arch/arm/fastmodel/GIC/GIC.lisa
index 9e4d917..34b09c8 100644
--- a/src/arch/arm/fastmodel/GIC/GIC.lisa
+++ b/src/arch/arm/fastmodel/GIC/GIC.lisa
@@ -53,6 +53,9 @@
         // For the CPU interface.
         gic.redistributor_m => self.redistributor;
 
+        // Outgoing wake requests.
+        gic.wake_request => self.wake_request;
+
         // Internal ports for PPI and SPI programmatic access.
         self.ppi_0 => gic.ppi_in_0;
         self.ppi_1 => gic.ppi_in_1;
@@ -324,6 +327,8 @@
 
     master port<GICv3Comms> redistributor[256];
 
+    master port<Signal> wake_request[256];
+
     #define setPPI(C) \
           case C: ppi_##C[num].setValue(state); \
           break
diff --git a/src/arch/arm/fastmodel/GIC/GIC.sgproj b/src/arch/arm/fastmodel/GIC/GIC.sgproj
index c835356..aa5e6ae 100644
--- a/src/arch/arm/fastmodel/GIC/GIC.sgproj
+++ b/src/arch/arm/fastmodel/GIC/GIC.sgproj
@@ -8,14 +8,14 @@
     ADDITIONAL_COMPILER_SETTINGS = "-O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function -I../../../../../";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
+    TARGET_SYSTEMC_AUTO = "1";
 }
 files
 {
diff --git a/src/arch/arm/fastmodel/GIC/SConscript b/src/arch/arm/fastmodel/GIC/SConscript
index 2a95145..c56ddd5 100644
--- a/src/arch/arm/fastmodel/GIC/SConscript
+++ b/src/arch/arm/fastmodel/GIC/SConscript
@@ -25,13 +25,11 @@
 
 Import('*')
 
-if not env['USE_ARM_FASTMODEL']:
-    Return()
-
 protocol_dir = Dir('..').Dir('protocol')
 
 ArmFastModelComponent(File('GIC.sgproj'), File('GIC.lisa'),
                       protocol_dir.File('SignalInterruptProtocol.lisa')
                       ).prepare_env(env)
-SimObject('FastModelGIC.py', sim_objects=['SCFastModelGIC', 'FastModelGIC'])
-Source('gic.cc')
+SimObject('FastModelGIC.py', sim_objects=['SCFastModelGIC', 'FastModelGIC'],
+        tags='arm fastmodel')
+Source('gic.cc', tags='arm fastmodel')
diff --git a/src/arch/arm/fastmodel/GIC/gic.cc b/src/arch/arm/fastmodel/GIC/gic.cc
index d1b5980..fbe863a 100644
--- a/src/arch/arm/fastmodel/GIC/gic.cc
+++ b/src/arch/arm/fastmodel/GIC/gic.cc
@@ -77,6 +77,12 @@
 {
     signalInterrupt.bind(signal_interrupt);
 
+    for (int i = 0; i < wake_request.size(); i++) {
+        wakeRequests.emplace_back(
+            new SignalReceiver(csprintf("%s.wakerequest[%d]", name(), i)));
+        wake_request[i].bind(wakeRequests[i]->signal_in);
+    }
+
     set_parameter("gic.enabled", params.enabled);
     set_parameter("gic.has-gicv3", params.has_gicv3);
     set_parameter("gic.has-gicv4.1", params.has_gicv4_1);
@@ -306,7 +312,18 @@
     ambaS(params.sc_gic->amba_s, params.name + ".amba_s", -1),
     redistributors(params.port_redistributor_connection_count),
     scGIC(params.sc_gic)
-{}
+{
+    for (int i = 0; i < params.port_wake_request_connection_count; i++) {
+        wakeRequestPorts.emplace_back(new IntSourcePin<GIC>(
+            csprintf("%s.wakerequestport[%d]", name(), i), i, this));
+        auto handler = [this, i](bool status)
+        {
+            auto &port = wakeRequestPorts[i];
+            status ? port->raise() : port->lower();
+        };
+        scGIC->wakeRequests[i]->onChange(handler);
+    }
+}
 
 Port &
 GIC::getPort(const std::string &if_name, PortID idx)
@@ -323,6 +340,8 @@
                                                    name(), idx), idx));
         }
         return *ptr;
+    } else if (if_name == "wake_request") {
+        return *wakeRequestPorts.at(idx);
     } else {
         return BaseGic::getPort(if_name, idx);
     }
diff --git a/src/arch/arm/fastmodel/GIC/gic.hh b/src/arch/arm/fastmodel/GIC/gic.hh
index 27eccd4..33a172d 100644
--- a/src/arch/arm/fastmodel/GIC/gic.hh
+++ b/src/arch/arm/fastmodel/GIC/gic.hh
@@ -36,7 +36,9 @@
 #include <memory>
 
 #include "arch/arm/fastmodel/amba_ports.hh"
+#include "arch/arm/fastmodel/common/signal_receiver.hh"
 #include "dev/arm/base_gic.hh"
+#include "dev/intpin.hh"
 #include "params/FastModelGIC.hh"
 #include "params/SCFastModelGIC.hh"
 #include "scx_evs_GIC.h"
@@ -92,6 +94,8 @@
 
     SignalInterruptInitiatorSocket signalInterrupt;
 
+    std::vector<std::unique_ptr<SignalReceiver>> wakeRequests;
+
     void before_end_of_elaboration() override;
 
     void
@@ -118,6 +122,7 @@
     AmbaInitiator ambaM;
     AmbaTarget ambaS;
     std::vector<std::unique_ptr<TlmGicInitiator>> redistributors;
+    std::vector<std::unique_ptr<IntSourcePin<GIC>>> wakeRequestPorts;
 
     SCGIC *scGIC;
 
diff --git a/src/arch/arm/fastmodel/PL330_DMAC/PL330.sgproj b/src/arch/arm/fastmodel/PL330_DMAC/PL330.sgproj
index 31eef35..d59849c 100644
--- a/src/arch/arm/fastmodel/PL330_DMAC/PL330.sgproj
+++ b/src/arch/arm/fastmodel/PL330_DMAC/PL330.sgproj
@@ -8,13 +8,12 @@
     ADDITIONAL_COMPILER_SETTINGS = "-O3 -Wall -std=c++14 -Wno-deprecated -Wno-unused-function -I../../../../../";
     ADDITIONAL_LINKER_SETTINGS = "-Wl,--no-undefined";
     BUILD_DIR = "./gcc";
-    COMPILER = "gcc-6.4";
+    COMPILER = "gcc-7.3";
     CONFIG_DESCRIPTION = "";
     CONFIG_NAME = "gcc";
     PLATFORM = "Linux64";
     PREPROCESSOR_DEFINES = "NDEBUG";
     SIMGEN_COMMAND_LINE = "--num-comps-file 50";
-    TARGET_MAXVIEW = "0";
     TARGET_SYSTEMC = "1";
     TARGET_SYSTEMC_AUTO = "1";
 }
diff --git a/src/arch/arm/fastmodel/PL330_DMAC/SConscript b/src/arch/arm/fastmodel/PL330_DMAC/SConscript
index 3e41e05..e93b45c 100644
--- a/src/arch/arm/fastmodel/PL330_DMAC/SConscript
+++ b/src/arch/arm/fastmodel/PL330_DMAC/SConscript
@@ -25,13 +25,11 @@
 
 Import('*')
 
-if not env['USE_ARM_FASTMODEL']:
-    Return()
-
 protocol_dir = Dir('..').Dir('protocol')
 
 ArmFastModelComponent(File('PL330.sgproj'), File('PL330.lisa'),
                       protocol_dir.File('SignalInterruptProtocol.lisa')
                       ).prepare_env(env)
-SimObject('FastModelPL330.py', sim_objects=['FastModelPL330'])
-Source('pl330.cc')
+SimObject('FastModelPL330.py', sim_objects=['FastModelPL330'],
+        tags='arm fastmodel')
+Source('pl330.cc', tags='arm fastmodel')
diff --git a/src/arch/arm/fastmodel/PL330_DMAC/pl330.cc b/src/arch/arm/fastmodel/PL330_DMAC/pl330.cc
index dc3f9ec..e582404 100644
--- a/src/arch/arm/fastmodel/PL330_DMAC/pl330.cc
+++ b/src/arch/arm/fastmodel/PL330_DMAC/pl330.cc
@@ -246,7 +246,7 @@
             port = suffix[0] - '0';
         } else if (suffix.size() == 2 && isdigit(suffix[0]) &&
                 isdigit(suffix[1])) {
-            port = (suffix[1] - '0') * 10 + (suffix[0] - '0');
+            port = (suffix[0] - '0') * 10 + (suffix[1] - '0');
         }
         if (port != -1 && port < irqPort.size())
             return *irqPort[port].at(idx);
diff --git a/src/arch/arm/fastmodel/SConscript b/src/arch/arm/fastmodel/SConscript
index 63b1b16..35c0da1 100644
--- a/src/arch/arm/fastmodel/SConscript
+++ b/src/arch/arm/fastmodel/SConscript
@@ -36,6 +36,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from itertools import cycle
+import logging
 import shlex
 
 Import('*')
@@ -46,27 +47,27 @@
 
 import os.path
 
-if env['USE_ARM_FASTMODEL']:
-    if not env['USE_SYSTEMC']:
+if env['CONF']['USE_ARM_FASTMODEL']:
+    if env['CONF']['USE_SYSTEMC']:
+        env.TagImplies('arm fastmodel', 'arm isa')
+    else:
         warning('ARM Fast Models require systemc support')
-        env['USE_ARM_FASTMODEL'] = False
-        Return()
-
-if not env['USE_ARM_FASTMODEL']:
-    Return()
+        env['CONF']['USE_ARM_FASTMODEL'] = False
 
 
 systemc_home = Dir('#/src/systemc/ext/systemc_home')
 env['ENV']['SYSTEMC_HOME'] = systemc_home.abspath
 
 def extract_var(name):
-    if name not in env:
-        error('Error: %s is not set' % name)
-    print('%s = %s' % (name, env[name]))
+    val = env['CONF'].get(name, None)
+    if val is None:
+        error(f'{name} is not set')
+    if env['CONF']['USE_ARM_FASTMODEL']:
+        print(f'{name} = {val}')
     # Make sure the value of this variable shows up as an environment variable
     # for commands scons runs.
-    env['ENV'][name] = env[name]
-    return env[name]
+    env['ENV'][name] = val
+    return val
 
 pvlib_home, maxcore_home, armlmd_license_file = \
     list(map(extract_var, ('PVLIB_HOME', 'MAXCORE_HOME',
@@ -74,14 +75,21 @@
 
 pvlib_home = Dir(pvlib_home)
 maxcore_home = Dir(maxcore_home)
-armlmd_license_file = File(armlmd_license_file)
 
-
-pvlib_flavor = env['PVLIB_FLAVOR']
+pvlib_flavor = env['CONF']['PVLIB_FLAVOR']
 pvlib_lib_dir = pvlib_home.Dir('lib').Dir(pvlib_flavor)
 
 simulation_engine_name = 'libMAXCOREInitSimulationEngine.3.so'
-simulation_engine_lib = pvlib_lib_dir.File(simulation_engine_name)
+simulation_engine_lib = env.Command(
+            Dir(env['BUILDDIR']).File(simulation_engine_name),
+            pvlib_lib_dir.File(simulation_engine_name),
+            MakeAction("cp ${SOURCE} ${TARGET}", Transform('COPY')))
+
+arm_singleton_registry_name = 'arm_singleton_registry.so'
+arm_singleton_registry_lib = env.Command(
+            Dir(env['BUILDDIR']).File(arm_singleton_registry_name),
+            pvlib_lib_dir.File(arm_singleton_registry_name),
+            MakeAction("cp ${SOURCE} ${TARGET}", Transform('COPY')))
 
 
 def staticify(env, name):
@@ -95,10 +103,10 @@
         full_name = Dir(path).File(static_name).get_abspath()
         if os.path.isfile(full_name):
             return File(full_name)
-
+    if env['CONF']['USE_ARM_FASTMODEL']:
+        warning("Failed to find FM static lib: " + name)
     return name
 
-
 # Adjust the build environment to support building in Fast Models.
 
 env.Append(CCFLAGS='-pthread')
@@ -121,25 +129,39 @@
     pvlib_home.Dir('Iris').Dir(pvlib_flavor),
 )
 env.Append(LIBPATH=lib_paths)
-env.Append(RPATH=lib_paths)
 
-fm_libs = (
+# Per ARM's 11.16 release note, a platform build with simgen automatically
+# copies libraries into the build target directory along with the other
+# dependencies. Therefore, we only need to add each simgen result into rpath and
+# no other shared librarires are required here.
+fm_static_libs = (
     'components',
     'pvbus',
     'armctmodel',
     'fmruntime',
     'IrisSupport',
 )
-env.Append(LIBS=list(staticify(env, lib) for lib in fm_libs))
-system_libs = (
-    'atomic',
-    'dl',
-    'rt',
-)
-env.Append(LIBS=system_libs)
+for lib in fm_static_libs:
+    SourceLib(staticify(env, lib), tags='arm fastmodel')
 
+SourceLib('atomic', tags='arm fastmodel')
+SourceLib('dl', tags='arm fastmodel')
+SourceLib('rt', tags='arm fastmodel')
 
 class ProjectFileParser(Grammar):
+    def __init__(self):
+        self.log = logging.getLogger('fm_proj_ply')
+        if GetOption('verbose'):
+            self.log.setLevel(logging.DEBUG)
+
+        self.yacc_kwargs['write_tables'] = False
+
+        self.yacc_kwargs['debuglog'] = self.log
+        self.yacc_kwargs['errorlog'] = self.log
+
+        self.lex_kwargs['debuglog'] = self.log
+        self.lex_kwargs['errorlog'] = self.log
+
     class Param(object):
         def __init__(self, is_object):
             self.is_object = is_object
@@ -246,12 +268,46 @@
         t[0] = t[1]
 
 
-license_count = int(env['ARMLMD_LICENSE_COUNT'])
+# If fast model is disabled, ARMLMD_LICENSE_COUNT will be 0 which will break
+# the cycle() iterator below. The fast model components won't be built, but
+# they still need to be set up successfully with valid license slots.
+license_count = max(int(env['CONF']['ARMLMD_LICENSE_COUNT']), 1)
 arm_licenses = list((Value(object()) for i in range(license_count)))
 license_cycle = cycle(arm_licenses)
 
+# HACK: Make sure the gic protocol headers are somewhere we can find them.
+# These should start out alongside other headers fast model provides which we
+# are already able to include, but unfortunately they're in the examples
+# directory.
+gicv3_comms_headers = (
+        'gicv3_comms_base.h', 'gicv3_comms_if.h', 'gicv3_comms_sockets.h')
+examples_common_dir = pvlib_home.Dir('examples/SystemCExport/Common')
+gic_protocol_path = 'Protocols/GICv3Comms'
+gic_protocol_dest = Dir(env['BUILDDIR']).Dir(gic_protocol_path)
+gic_protocol_src = examples_common_dir.Dir(gic_protocol_path)
+
+for header in gicv3_comms_headers:
+    Command(gic_protocol_dest.File(header), gic_protocol_src.File(header),
+            Copy('${TARGET}', '${SOURCE}'))
+# Since we are copying the source files to a different directory, Scons's
+# dependency scanner does not pick up the dependency between these files.
+# Specify them manually.
+env.Depends(gic_protocol_dest.File('gicv3_comms_base.h'),
+            gic_protocol_dest.File('gicv3_comms_if.h'))
+env.Depends(gic_protocol_dest.File('gicv3_comms_sockets.h'),
+            gic_protocol_dest.File('gicv3_comms_if.h'))
+
+common_headers = ('lisa_protocol_types.h', 'tlm_has_get_protocol_types.h')
+for header in common_headers:
+    header_target = gic_protocol_dest.Dir('include').File(header)
+    header_src = examples_common_dir.Dir('include').File(header)
+    Command(header_target, header_src, Copy('${TARGET}', '${SOURCE}'))
+
 class ArmFastModelComponent(object):
-    def __init__(self, project_file, *extra_deps):
+    def __init__(self, project_file, *extra_deps, tags=None):
+        if not tags:
+            tags = ['arm fastmodel']
+        self.tags = tags
         project_file = File(project_file)
         project_file_dir = project_file.Dir('.')
 
@@ -262,6 +318,8 @@
         tlc = None
         # Config name.
         config_name = None
+        # simgen_arg
+        simgen_command_line = ''
 
         # Scan for particular properties of the project.
         for param in proj.params:
@@ -271,6 +329,12 @@
                 elif param.name == 'ACTIVE_CONFIG_LINUX':
                     config_name = param.value
 
+        for param in proj.params:
+            if param.name == config_name:
+                for sub_param in param.params:
+                    if sub_param.name == 'SIMGEN_COMMAND_LINE':
+                        simgen_command_line = sub_param.value
+
         assert tlc is not None and config_name is not None
 
         simgen_dir = project_file_dir.Dir(config_name)
@@ -309,17 +373,26 @@
         self.headerpaths = [gen_dir]
         self.libs = static_lib_nodes + shared_libs
         self.libpaths = [simgen_dir]
-        self.rpaths = [simgen_dir]
+        # Simgen also puts required share library under the project folder.
+        self.rpaths = [simgen_dir, project_file_dir]
         self.log = gen_dir.File('build_%s.log' % tlc)
-        self.simgen_cmd = env.subst('${SIMGEN} -p %s --configuration %s -b ' +
-            '--verbose off --num-build-cpus 100 --build-dir %s >%s') % \
+        self.simgen_cmd = env.subst('${CONF["SIMGEN"]} -p %s '
+            '--configuration %s -b --verbose off --num-build-cpus %d %s '
+            '--build-dir %s >%s') % \
             (shlex.quote(project_file.srcnode().abspath),
              shlex.quote(config_name),
+             GetOption('num_jobs'),
+             simgen_command_line,
              shlex.quote(simgen_dir.abspath),
              shlex.quote(self.log.abspath))
 
         sources = [project_file]
         sources.extend(extra_deps)
+        # The simgen-generated .lisa files may #include these gicv3 files, but
+        # SCons does not detect this dependency since they are generated files.
+        # Add the dependencies manually.
+        sources.extend([gic_protocol_dest.File('gicv3_comms_sockets.h'),
+                        gic_protocol_dest.File('gicv3_comms_base.h')])
         env.Command(lib_nodes + self.headers + [self.log], sources,
                     Action(self.simgen_builder, Transform('SIMGEN')))
         # Distribute simgen actions among ARM license slots. All actions which
@@ -331,11 +404,18 @@
         # but the difference is probably not significant.
         env.SideEffect(next(license_cycle), lib_nodes[0])
 
+        # We need a copy of the simulation engine lib and arm_singleton_registry
+        # (introduced in 11.16) alongside the executable.
+        Depends(lib_nodes[0], simulation_engine_lib)
+        Depends(lib_nodes[0], arm_singleton_registry_lib)
+
     def prepare_env(self, env):
         env.Append(LIBPATH=self.libpaths)
         env.AddLocalRPATH(*self.rpaths)
         env.Append(CPPPATH=self.headerpaths)
-        env.Prepend(LIBS=self.libs)
+        # Put these libraries earlier in the list by setting priority.
+        for lib in self.libs:
+            SourceLib(lib, priority=1, tags=self.tags)
 
     def simgen_builder(self, target, source, env):
         cmd = self.simgen_cmd
@@ -350,31 +430,10 @@
 
 Export('ArmFastModelComponent')
 
-PySource('m5', 'arm_fast_model.py')
-Source('fastmodel.cc')
+PySource('m5', 'arm_fast_model.py', tags='arm fastmodel')
+Source('fastmodel.cc', tags='arm fastmodel')
 
 SimObject('FastModel.py', sim_objects=[
-    'AmbaToTlmBridge64', 'AmbaFromTlmBridge64'])
-Source('amba_to_tlm_bridge.cc')
-Source('amba_from_tlm_bridge.cc')
-
-# HACK: Make sure the gic protocol headers are somewhere we can find them.
-# These should start out alongside other headers fast model provides which we
-# are already able to include, but unfortunately they're in the examples
-# directory.
-gicv3_comms_headers = (
-        'gicv3_comms_base.h', 'gicv3_comms_if.h', 'gicv3_comms_sockets.h')
-examples_common_dir = pvlib_home.Dir('examples/SystemCExport/Common')
-gic_protocol_path = 'Protocols/GICv3Comms'
-gic_protocol_dest = Dir(env['BUILDDIR']).Dir(gic_protocol_path)
-gic_protocol_src = examples_common_dir.Dir(gic_protocol_path)
-
-for header in gicv3_comms_headers:
-    Command(gic_protocol_dest.File(header), gic_protocol_src.File(header),
-            Copy('${TARGET}', '${SOURCE}'))
-
-common_headers = ('lisa_protocol_types.h', 'tlm_has_get_protocol_types.h')
-for header in common_headers:
-    header_target = gic_protocol_dest.Dir('include').File(header)
-    header_src = examples_common_dir.Dir('include').File(header)
-    Command(header_target, header_src, Copy('${TARGET}', '${SOURCE}'))
+    'AmbaToTlmBridge64', 'AmbaFromTlmBridge64'], tags='arm fastmodel')
+Source('amba_to_tlm_bridge.cc', tags='arm fastmodel')
+Source('amba_from_tlm_bridge.cc', tags='arm fastmodel')
diff --git a/src/arch/arm/fastmodel/SConsopts b/src/arch/arm/fastmodel/SConsopts
index 74165fe..d3f8980 100644
--- a/src/arch/arm/fastmodel/SConsopts
+++ b/src/arch/arm/fastmodel/SConsopts
@@ -36,7 +36,7 @@
     ('PVLIB_HOME', 'Fast Model portfolio directory',
      os.environ.get('PVLIB_HOME', '')),
     ('PVLIB_FLAVOR', 'What build flavor of the Fast Model pvlib to use',
-     'Linux64_GCC-6.4'),
+     'Linux64_GCC-7.3'),
     ('MAXCORE_HOME', 'Fast Model tools directory',
      os.environ.get('MAXCORE_HOME', '')),
     ('ARMLMD_LICENSE_FILE', 'ARM license file location',
@@ -45,9 +45,3 @@
      'The maximum number of ARM licenses to use concurrently', 1),
     ('SIMGEN', 'simgen executable', os.environ.get('SIMGEN', default_simgen)),
 )
-
-export_vars.extend([
-        'ARMLMD_LICENSE_FILE',
-        'PVLIB_HOME',
-        'PVLIB_FLAVOR',
-])
diff --git a/src/arch/arm/fastmodel/arm_fast_model.py b/src/arch/arm/fastmodel/arm_fast_model.py
index c9d1113..f11443d 100644
--- a/src/arch/arm/fastmodel/arm_fast_model.py
+++ b/src/arch/arm/fastmodel/arm_fast_model.py
@@ -42,21 +42,9 @@
 # 7.6 of the Fast Models User Guide.
 
 def scx_initialize(id):
-    # Change our working directory to where the simulation engine library
-    # is so that the fast model code can find it. It looks in the binary
-    # directory and in the current working directory, and we can change
-    # the later. This avoids having to make copies of that library all
-    # over the place.
-    cwd = os.getcwd()
-    os.chdir(os.path.join(buildEnv['PVLIB_HOME'], 'lib',
-                          buildEnv['PVLIB_FLAVOR']))
-
     # Actually run scx_initialize.
     _m5.arm_fast_model.scx_initialize(id)
 
-    # Restore the previous working directory.
-    os.chdir(cwd)
-
 def scx_load_application(instance, application):
     _m5.arm_fast_model.scx_load_application(instance, application)
 
diff --git a/src/arch/arm/fastmodel/iris/Iris.py b/src/arch/arm/fastmodel/iris/Iris.py
index 4979715..51eb394 100644
--- a/src/arch/arm/fastmodel/iris/Iris.py
+++ b/src/arch/arm/fastmodel/iris/Iris.py
@@ -66,7 +66,12 @@
     cxx_class = 'gem5::Iris::ISA'
     cxx_header = 'arch/arm/fastmodel/iris/isa.hh'
 
-class IrisBaseCPU(BaseCPU):
+class IrisCPU():
+    ArchMMU = IrisMMU
+    ArchInterrupts = IrisInterrupts
+    ArchISA = IrisISA
+
+class IrisBaseCPU(BaseCPU, IrisCPU):
     type = 'IrisBaseCPU'
     abstract = True
     cxx_class = 'gem5::Iris::BaseCPU'
@@ -97,6 +102,3 @@
             self.isa = [ IrisISA() for i in range(self.numThreads) ]
         else:
             assert(len(self.isa) == int(self.numThreads))
-
-    def createInterruptController(self):
-        self.interrupts = [ IrisInterrupts() for i in range(self.numThreads) ]
diff --git a/src/arch/arm/fastmodel/iris/SConscript b/src/arch/arm/fastmodel/iris/SConscript
index 6635ca0..3387719 100644
--- a/src/arch/arm/fastmodel/iris/SConscript
+++ b/src/arch/arm/fastmodel/iris/SConscript
@@ -25,14 +25,12 @@
 
 Import('*')
 
-if not env['USE_ARM_FASTMODEL']:
-    Return()
-
 SimObject('Iris.py', sim_objects=[
-    'IrisTLB', 'IrisMMU', 'IrisInterrupts', 'IrisISA', 'IrisBaseCPU'])
-Source('cpu.cc')
-Source('interrupts.cc')
-Source('isa.cc')
-Source('tlb.cc')
+    'IrisTLB', 'IrisMMU', 'IrisInterrupts', 'IrisISA', 'IrisBaseCPU'],
+    tags='arm fastmodel')
+Source('cpu.cc', tags='arm fastmodel')
+Source('interrupts.cc', tags='arm fastmodel')
+Source('isa.cc', tags='arm fastmodel')
+Source('tlb.cc', tags='arm fastmodel')
 
-Source('thread_context.cc')
+Source('thread_context.cc', tags='arm fastmodel')
diff --git a/src/arch/arm/fastmodel/iris/thread_context.cc b/src/arch/arm/fastmodel/iris/thread_context.cc
index 16c8250..eb7ba68 100644
--- a/src/arch/arm/fastmodel/iris/thread_context.cc
+++ b/src/arch/arm/fastmodel/iris/thread_context.cc
@@ -181,7 +181,7 @@
     const auto &space_ids = getBpSpaceIds();
     for (auto sid: space_ids) {
         BpId id;
-        call().breakpoint_set_code(_instId, id, pc, sid, 0, true);
+        call().breakpoint_set_code(_instId, id, pc, sid, 0);
         it->second->ids.push_back(id);
     }
 }
@@ -606,6 +606,181 @@
 }
 
 RegVal
+ThreadContext::getReg(const RegId &reg) const
+{
+    RegVal val;
+    getReg(reg, &val);
+    return val;
+}
+
+void
+ThreadContext::setReg(const RegId &reg, RegVal val)
+{
+    setReg(reg, &val);
+}
+
+void
+ThreadContext::getReg(const RegId &reg, void *val) const
+{
+    const RegIndex idx = reg.index();
+    const RegClassType type = reg.classValue();
+    switch (type) {
+      case IntRegClass:
+        *(RegVal *)val = readIntReg(idx);
+        break;
+      case FloatRegClass:
+        *(RegVal *)val = readFloatReg(idx);
+        break;
+      case VecRegClass:
+        *(ArmISA::VecRegContainer *)val = readVecReg(reg);
+        break;
+      case VecElemClass:
+        *(RegVal *)val = readVecElem(reg);
+        break;
+      case VecPredRegClass:
+        *(ArmISA::VecPredRegContainer *)val = readVecPredReg(reg);
+        break;
+      case CCRegClass:
+        *(RegVal *)val = readCCReg(idx);
+        break;
+      case MiscRegClass:
+        panic("MiscRegs should not be read with getReg.");
+      default:
+        panic("Unrecognized register class type %d.", type);
+    }
+}
+
+void
+ThreadContext::setReg(const RegId &reg, const void *val)
+{
+    const RegIndex idx = reg.index();
+    const RegClassType type = reg.classValue();
+    switch (type) {
+      case IntRegClass:
+        setIntReg(idx, *(RegVal *)val);
+        break;
+      case FloatRegClass:
+        setFloatReg(idx, *(RegVal *)val);
+        break;
+      case VecRegClass:
+        setVecReg(reg, *(ArmISA::VecRegContainer *)val);
+        break;
+      case VecElemClass:
+        setVecElem(reg, *(RegVal *)val);
+        break;
+      case VecPredRegClass:
+        setVecPredReg(reg, *(ArmISA::VecPredRegContainer *)val);
+        break;
+      case CCRegClass:
+        setCCReg(idx, *(RegVal *)val);
+        break;
+      case MiscRegClass:
+        panic("MiscRegs should not be read with getReg.");
+      default:
+        panic("Unrecognized register class type %d.", type);
+    }
+}
+
+void *
+ThreadContext::getWritableReg(const RegId &reg)
+{
+    const RegClassType type = reg.classValue();
+    switch (type) {
+      case VecRegClass:
+        return &getWritableVecReg(reg);
+      case VecPredRegClass:
+        return &getWritableVecPredReg(reg);
+      default:
+        panic("Unrecognized register class type %d.", type);
+    }
+}
+
+RegVal
+ThreadContext::getRegFlat(const RegId &reg) const
+{
+    RegVal val;
+    getRegFlat(reg, &val);
+    return val;
+}
+
+void
+ThreadContext::setRegFlat(const RegId &reg, RegVal val)
+{
+    setRegFlat(reg, &val);
+}
+
+void
+ThreadContext::getRegFlat(const RegId &reg, void *val) const
+{
+    const RegIndex idx = reg.index();
+    const RegClassType type = reg.classValue();
+    switch (type) {
+      case IntRegClass:
+        *(RegVal *)val = readIntRegFlat(idx);
+        break;
+      case VecRegClass:
+        *(ArmISA::VecRegContainer *)val = readVecRegFlat(idx);
+        break;
+      case VecElemClass:
+        *(RegVal *)val = readVecElemFlat(idx);
+        break;
+      case VecPredRegClass:
+        *(ArmISA::VecPredRegContainer *)val = readVecPredRegFlat(idx);
+        break;
+      case CCRegClass:
+        *(RegVal *)val = readCCRegFlat(idx);
+        break;
+      case MiscRegClass:
+        panic("MiscRegs should not be read with getReg.");
+      default:
+        panic("Unrecognized register class type %d.", type);
+    }
+}
+
+void
+ThreadContext::setRegFlat(const RegId &reg, const void *val)
+{
+    const RegIndex idx = reg.index();
+    const RegClassType type = reg.classValue();
+    switch (type) {
+      case IntRegClass:
+        setIntRegFlat(idx, *(RegVal *)val);
+        break;
+      case VecRegClass:
+        setVecRegFlat(idx, *(ArmISA::VecRegContainer *)val);
+        break;
+      case VecElemClass:
+        setVecElemFlat(idx, *(RegVal *)val);
+        break;
+      case VecPredRegClass:
+        setVecPredRegFlat(idx, *(ArmISA::VecPredRegContainer *)val);
+        break;
+      case CCRegClass:
+        setCCRegFlat(idx, *(RegVal *)val);
+        break;
+      case MiscRegClass:
+        panic("MiscRegs should not be read with getReg.");
+      default:
+        panic("Unrecognized register class type %d.", type);
+    }
+}
+
+void *
+ThreadContext::getWritableRegFlat(const RegId &reg)
+{
+    const RegIndex idx = reg.index();
+    const RegClassType type = reg.classValue();
+    switch (type) {
+      case VecRegClass:
+        return &getWritableVecRegFlat(idx);
+      case VecPredRegClass:
+        return &getWritableVecPredRegFlat(idx);
+      default:
+        panic("Unrecognized register class type %d.", type);
+    }
+}
+
+RegVal
 ThreadContext::readIntReg(RegIndex reg_idx) const
 {
     ArmISA::CPSR cpsr = readMiscRegNoEffect(ArmISA::MISCREG_CPSR);
@@ -735,7 +910,7 @@
     return reg;
 }
 
-const ArmISA::VecPredRegContainer &
+ArmISA::VecPredRegContainer
 ThreadContext::readVecPredRegFlat(RegIndex idx) const
 {
     return readVecPredReg(RegId(VecPredRegClass, idx));
diff --git a/src/arch/arm/fastmodel/iris/thread_context.hh b/src/arch/arm/fastmodel/iris/thread_context.hh
index 9a1eaba..2feb25e 100644
--- a/src/arch/arm/fastmodel/iris/thread_context.hh
+++ b/src/arch/arm/fastmodel/iris/thread_context.hh
@@ -220,7 +220,7 @@
     System *getSystemPtr() override { return _cpu->system; }
 
     BaseISA *
-    getIsaPtr() override
+    getIsaPtr() const override
     {
         return _isa;
     }
@@ -279,70 +279,65 @@
     //
     // New accessors for new decoder.
     //
-    RegVal readIntReg(RegIndex reg_idx) const override;
+    RegVal getReg(const RegId &reg) const override;
+    void getReg(const RegId &reg, void *val) const override;
+    void *getWritableReg(const RegId &reg) override;
 
-    RegVal
-    readFloatReg(RegIndex reg_idx) const override
+    void setReg(const RegId &reg, RegVal val) override;
+    void setReg(const RegId &reg, const void *val) override;
+
+    virtual RegVal readIntReg(RegIndex reg_idx) const;
+
+    virtual const ArmISA::VecRegContainer &readVecReg(const RegId &reg) const;
+    virtual ArmISA::VecRegContainer &
+    getWritableVecReg(const RegId &reg)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
 
-    const ArmISA::VecRegContainer &readVecReg(const RegId &reg) const override;
-    ArmISA::VecRegContainer &
-    getWritableVecReg(const RegId &reg) override
+    virtual RegVal
+    readVecElem(const RegId &reg) const
     {
         panic("%s not implemented.", __FUNCTION__);
     }
 
-    RegVal
-    readVecElem(const RegId &reg) const override
+    virtual const ArmISA::VecPredRegContainer &
+        readVecPredReg(const RegId &reg) const;
+    virtual ArmISA::VecPredRegContainer &
+    getWritableVecPredReg(const RegId &reg)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
 
-    const ArmISA::VecPredRegContainer &
-        readVecPredReg(const RegId &reg) const override;
-    ArmISA::VecPredRegContainer &
-    getWritableVecPredReg(const RegId &reg) override
-    {
-        panic("%s not implemented.", __FUNCTION__);
-    }
-
-    RegVal
-    readCCReg(RegIndex reg_idx) const override
+    virtual RegVal
+    readCCReg(RegIndex reg_idx) const
     {
         return readCCRegFlat(reg_idx);
     }
 
-    void setIntReg(RegIndex reg_idx, RegVal val) override;
+    virtual void setIntReg(RegIndex reg_idx, RegVal val);
 
-    void
-    setFloatReg(RegIndex reg_idx, RegVal val) override
+    virtual void
+    setVecReg(const RegId &reg, const ArmISA::VecRegContainer &val)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
 
-    void
-    setVecReg(const RegId &reg, const ArmISA::VecRegContainer &val) override
+    virtual void
+    setVecElem(const RegId& reg, RegVal val)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
 
-    void
-    setVecElem(const RegId& reg, RegVal val) override
-    {
-        panic("%s not implemented.", __FUNCTION__);
-    }
-
-    void
+    virtual void
     setVecPredReg(const RegId &reg,
-                  const ArmISA::VecPredRegContainer &val) override
+                  const ArmISA::VecPredRegContainer &val)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
 
-    void
-    setCCReg(RegIndex reg_idx, RegVal val) override
+    virtual void
+    setCCReg(RegIndex reg_idx, RegVal val)
     {
         setCCRegFlat(reg_idx, val);
     }
@@ -398,59 +393,54 @@
      * serialization code to access all registers.
      */
 
-    RegVal readIntRegFlat(RegIndex idx) const override;
-    void setIntRegFlat(RegIndex idx, uint64_t val) override;
+    RegVal getRegFlat(const RegId &reg) const override;
+    void getRegFlat(const RegId &reg, void *val) const override;
+    void *getWritableRegFlat(const RegId &reg) override;
 
-    RegVal
-    readFloatRegFlat(RegIndex idx) const override
+    void setRegFlat(const RegId &reg, RegVal val) override;
+    void setRegFlat(const RegId &reg, const void *val) override;
+
+    virtual RegVal readIntRegFlat(RegIndex idx) const;
+    virtual void setIntRegFlat(RegIndex idx, uint64_t val);
+
+    virtual const ArmISA::VecRegContainer &readVecRegFlat(RegIndex idx) const;
+    virtual ArmISA::VecRegContainer &
+    getWritableVecRegFlat(RegIndex idx)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
-    void
-    setFloatRegFlat(RegIndex idx, RegVal val) override
+    virtual void
+    setVecRegFlat(RegIndex idx, const ArmISA::VecRegContainer &val)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
 
-    const ArmISA::VecRegContainer &readVecRegFlat(RegIndex idx) const override;
-    ArmISA::VecRegContainer &
-    getWritableVecRegFlat(RegIndex idx) override
+    virtual RegVal
+    readVecElemFlat(RegIndex idx) const
     {
         panic("%s not implemented.", __FUNCTION__);
     }
-    void
-    setVecRegFlat(RegIndex idx, const ArmISA::VecRegContainer &val) override
+    virtual void
+    setVecElemFlat(RegIndex idx, RegVal val)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
 
-    RegVal
-    readVecElemFlat(RegIndex idx, const ElemIndex& elemIdx) const override
+    virtual ArmISA::VecPredRegContainer readVecPredRegFlat(RegIndex idx) const;
+    virtual ArmISA::VecPredRegContainer &
+    getWritableVecPredRegFlat(RegIndex idx)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
-    void
-    setVecElemFlat(RegIndex idx, const ElemIndex &elemIdx, RegVal val) override
-    {
-        panic("%s not implemented.", __FUNCTION__);
-    }
-
-    const ArmISA::VecPredRegContainer &
-        readVecPredRegFlat(RegIndex idx) const override;
-    ArmISA::VecPredRegContainer &
-    getWritableVecPredRegFlat(RegIndex idx) override
-    {
-        panic("%s not implemented.", __FUNCTION__);
-    }
-    void
+    virtual void
     setVecPredRegFlat(RegIndex idx,
-            const ArmISA::VecPredRegContainer &val) override
+            const ArmISA::VecPredRegContainer &val)
     {
         panic("%s not implemented.", __FUNCTION__);
     }
 
-    RegVal readCCRegFlat(RegIndex idx) const override;
-    void setCCRegFlat(RegIndex idx, RegVal val) override;
+    virtual RegVal readCCRegFlat(RegIndex idx) const;
+    virtual void setCCRegFlat(RegIndex idx, RegVal val);
     /** @} */
 
     // hardware transactional memory
diff --git a/src/cpu/o3/O3Checker.py b/src/arch/arm/fastmodel/reset_controller/FastModelResetControllerExample.py
similarity index 73%
copy from src/cpu/o3/O3Checker.py
copy to src/arch/arm/fastmodel/reset_controller/FastModelResetControllerExample.py
index c343cd6..b9327f4 100644
--- a/src/cpu/o3/O3Checker.py
+++ b/src/arch/arm/fastmodel/reset_controller/FastModelResetControllerExample.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2007 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -25,9 +24,17 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from m5.params import *
-from m5.objects.CheckerCPU import CheckerCPU
+from m5.proxy import *
 
-class O3Checker(CheckerCPU):
-    type = 'O3Checker'
-    cxx_class = 'gem5::o3::Checker'
-    cxx_header = 'cpu/o3/checker.hh'
+from m5.objects.Device import BasicPioDevice
+from m5.objects.IntPin import IntSourcePin
+from m5.objects.Iris import IrisBaseCPU
+
+class FastModelResetControllerExample(BasicPioDevice):
+    type = 'FastModelResetControllerExample'
+    cxx_class = 'gem5::fastmodel::ResetControllerExample'
+    cxx_header = 'arch/arm/fastmodel/reset_controller/example.hh'
+
+    cpu = Param.IrisBaseCPU('target cpu')
+    reset = IntSourcePin('reset pin')
+    halt = IntSourcePin('halt pin')
diff --git a/src/cpu/simple/SConsopts b/src/arch/arm/fastmodel/reset_controller/SConscript
similarity index 87%
rename from src/cpu/simple/SConsopts
rename to src/arch/arm/fastmodel/reset_controller/SConscript
index f12fee2..8e5de8e 100644
--- a/src/cpu/simple/SConsopts
+++ b/src/arch/arm/fastmodel/reset_controller/SConscript
@@ -1,7 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -28,4 +25,7 @@
 
 Import('*')
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+SimObject('FastModelResetControllerExample.py', sim_objects=[
+    'FastModelResetControllerExample'], tags='arm fastmodel')
+
+Source('example.cc', tags='arm fastmodel')
diff --git a/src/arch/arm/fastmodel/reset_controller/example.cc b/src/arch/arm/fastmodel/reset_controller/example.cc
new file mode 100644
index 0000000..33769ac
--- /dev/null
+++ b/src/arch/arm/fastmodel/reset_controller/example.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "arch/arm/fastmodel/reset_controller/example.hh"
+
+#include <algorithm>
+
+#include "base/logging.hh"
+
+namespace gem5
+{
+namespace fastmodel
+{
+
+ResetControllerExample::CorePins::CorePins(const std::string &module_name)
+    : reset(module_name + ".reset", 0, this),
+      halt(module_name + ".halt", 0, this)
+{}
+
+ResetControllerExample::Registers::Registers(
+    const std::string &module_name, Iris::BaseCPU *c, CorePins *p)
+    : RegisterBankLE(module_name, 0), cpu(c), pins(p),
+      nsrvbar(module_name + ".nsrvbar"),
+      rvbar(module_name + ".rvbar"),
+      reset(module_name + ".reset"),
+      halt(module_name + ".halt")
+{
+      panic_if(cpu == nullptr, "ResetControllerExample needs a target cpu.");
+      nsrvbar.writer(
+          [this] (auto &reg, auto val)
+          {
+              cpu->setResetAddr(val, false);
+          });
+      rvbar.writer(
+          [this] (auto &reg, auto val)
+          {
+              cpu->setResetAddr(val, true);
+          });
+      reset.writer(
+          [this] (auto &reg, auto val)
+          {
+              panic_if(!pins->reset.isConnected(),
+                       "%s is not connected.", pins->reset.name());
+
+              if (val)
+                  pins->reset.raise();
+              else
+                  pins->reset.lower();
+          });
+      halt.writer(
+          [this] (auto &reg, auto val)
+          {
+              panic_if(!pins->halt.isConnected(),
+                       "%s is not connected.", pins->halt.name());
+
+              if (val)
+                  pins->halt.raise();
+              else
+                  pins->halt.lower();
+          });
+
+      addRegisters({
+          nsrvbar,
+          rvbar,
+          reset,
+          halt,
+      });
+}
+
+ResetControllerExample::ResetControllerExample(const Params &p)
+    : BasicPioDevice(p, 0x20),
+      pins(p.name + ".pins"),
+      registers(p.name  + ".registers", p.cpu, &pins)
+{}
+
+Tick
+ResetControllerExample::read(PacketPtr pkt)
+{
+    pkt->makeResponse();
+    auto data = pkt->getPtr<uint8_t>();
+    auto size = pkt->getSize();
+    std::fill(data, data + size, 0);
+    return pioDelay;
+}
+
+Tick
+ResetControllerExample::write(PacketPtr pkt)
+{
+    pkt->makeResponse();
+    size_t size = pkt->getSize();
+    if (size != 4 && size != 8) {
+        pkt->setBadAddress();
+    } else {
+        auto addr = pkt->getAddr() - pioAddr;
+        registers.write(addr, pkt->getPtr<void>(), size);
+    }
+    return pioDelay;
+}
+
+Port &
+ResetControllerExample::getPort(const std::string &if_name, PortID idx)
+{
+    if (if_name == "reset")
+        return pins.reset;
+    else if (if_name == "halt")
+        return pins.halt;
+
+    return BasicPioDevice::getPort(if_name, idx);
+}
+
+} // namespace fastmodel
+} // namespace gem5
diff --git a/src/arch/arm/fastmodel/reset_controller/example.hh b/src/arch/arm/fastmodel/reset_controller/example.hh
new file mode 100644
index 0000000..2805d6f
--- /dev/null
+++ b/src/arch/arm/fastmodel/reset_controller/example.hh
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARCH_ARM_FASTMODEL_RESET_CONTROLLER_EXAMPLE_HH__
+#define __ARCH_ARM_FASTMODEL_RESET_CONTROLLER_EXAMPLE_HH__
+
+#include <string>
+
+#include "arch/arm/fastmodel/iris/cpu.hh"
+#include "dev/intpin.hh"
+#include "dev/io_device.hh"
+#include "dev/reg_bank.hh"
+#include "mem/packet_access.hh"
+#include "params/FastModelResetControllerExample.hh"
+
+namespace gem5
+{
+
+namespace fastmodel
+{
+
+class ResetControllerExample : public BasicPioDevice
+{
+  private:
+    struct CorePins
+    {
+        using CoreInt = IntSourcePin<CorePins>;
+        CoreInt reset;
+        CoreInt halt;
+
+        explicit CorePins(const std::string &);
+    };
+
+    class Registers : public RegisterBankLE
+    {
+      private:
+        Iris::BaseCPU *cpu;
+        CorePins *pins;
+
+        Register64 nsrvbar;
+        Register64 rvbar;
+        Register32 reset;
+        Register32 halt;
+
+      public:
+        Registers(const std::string &, Iris::BaseCPU *, CorePins *);
+    };
+
+    CorePins pins;
+    Registers registers;
+
+  public:
+    using Params = FastModelResetControllerExampleParams;
+    explicit ResetControllerExample(const Params &);
+
+    Tick read(PacketPtr pkt) override;
+    Tick write(PacketPtr pkt) override;
+    Port &getPort(const std::string &, PortID = InvalidPortID) override;
+};
+
+} // namespace fastmodel
+} // namespace gem5
+
+#endif  // __ARCH_ARM_FASTMODEL_RESET_CONTROLLER_EXAMPLE_HH__
diff --git a/src/arch/arm/faults.cc b/src/arch/arm/faults.cc
index ba5bcc9..1009738 100644
--- a/src/arch/arm/faults.cc
+++ b/src/arch/arm/faults.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2014, 2016-2019 ARM Limited
+ * Copyright (c) 2010, 2012-2014, 2016-2019, 2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -397,42 +397,31 @@
 void
 ArmFault::setSyndrome(ThreadContext *tc, MiscRegIndex syndrome_reg)
 {
-    uint32_t value;
+    ESR esr = 0;
     uint32_t exc_class = (uint32_t) ec(tc);
-    uint32_t issVal = iss();
+    uint32_t iss_val = iss();
 
     assert(!from64 || ArmSystem::highestELIs64(tc));
 
-    value = exc_class << 26;
+    esr.ec = exc_class;
+    esr.il = il(tc);
 
-    // HSR.IL not valid for Prefetch Aborts (0x20, 0x21) and Data Aborts (0x24,
-    // 0x25) for which the ISS information is not valid (ARMv7).
-    // @todo: ARMv8 revises AArch32 functionality: when HSR.IL is not
-    // valid it is treated as RES1.
-    if (to64) {
-        value |= 1 << 25;
-    } else if ((bits(exc_class, 5, 3) != 4) ||
-               (bits(exc_class, 2) && bits(issVal, 24))) {
-        if (!machInst.thumb || machInst.bigThumb)
-            value |= 1 << 25;
-    }
     // Condition code valid for EC[5:4] nonzero
     if (!from64 && ((bits(exc_class, 5, 4) == 0) &&
                     (bits(exc_class, 3, 0) != 0))) {
+
         if (!machInst.thumb) {
-            uint32_t      cond;
-            ConditionCode condCode = (ConditionCode) (uint32_t) machInst.condCode;
+            ConditionCode cond_code = (ConditionCode) (uint32_t) machInst.condCode;
             // If its on unconditional instruction report with a cond code of
             // 0xE, ie the unconditional code
-            cond  = (condCode == COND_UC) ? COND_AL : condCode;
-            value |= cond << 20;
-            value |= 1    << 24;
+            esr.cond_iss.cv = 1;
+            esr.cond_iss.cond = (cond_code == COND_UC) ? COND_AL : cond_code;
         }
-        value |= bits(issVal, 19, 0);
+        esr.cond_iss.iss = bits(iss_val, 19, 0);
     } else {
-        value |= issVal;
+        esr.iss = iss_val;
     }
-    tc->setMiscReg(syndrome_reg, value);
+    tc->setMiscReg(syndrome_reg, esr);
 }
 
 void
@@ -464,8 +453,7 @@
         toEL = fromEL;
 
     // Check for Set Priviledge Access Never, if PAN is supported
-    AA64MMFR1 mmfr1 = tc->readMiscReg(MISCREG_ID_AA64MMFR1_EL1);
-    if (mmfr1.pan) {
+    if (HaveExt(tc, ArmExtension::FEAT_PAN)) {
         if (toEL == EL1) {
             const SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR_EL1);
             span = !sctlr.span;
@@ -528,10 +516,10 @@
     SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR);
     SCR scr = tc->readMiscReg(MISCREG_SCR);
     CPSR saved_cpsr = tc->readMiscReg(MISCREG_CPSR);
-    saved_cpsr.nz = tc->readCCReg(CCREG_NZ);
-    saved_cpsr.c = tc->readCCReg(CCREG_C);
-    saved_cpsr.v = tc->readCCReg(CCREG_V);
-    saved_cpsr.ge = tc->readCCReg(CCREG_GE);
+    saved_cpsr.nz = tc->getReg(cc_reg::Nz);
+    saved_cpsr.c = tc->getReg(cc_reg::C);
+    saved_cpsr.v = tc->getReg(cc_reg::V);
+    saved_cpsr.ge = tc->getReg(cc_reg::Ge);
 
     [[maybe_unused]] Addr cur_pc = tc->pcState().as<PCState>().pc();
     ITSTATE it = tc->pcState().as<PCState>().itstate();
@@ -594,7 +582,7 @@
         tc->setMiscReg(MISCREG_ELR_HYP, cur_pc +
                 (saved_cpsr.t ? thumbPcOffset(true)  : armPcOffset(true)));
     } else {
-        tc->setIntReg(INTREG_LR, cur_pc +
+        tc->setReg(int_reg::Lr, cur_pc +
                 (saved_cpsr.t ? thumbPcOffset(false) : armPcOffset(false)));
     }
 
@@ -631,7 +619,7 @@
 
     Addr new_pc = getVector(tc);
     DPRINTF(Faults, "Invoking Fault:%s cpsr:%#x PC:%#x lr:%#x newVec: %#x "
-            "%s\n", name(), cpsr, cur_pc, tc->readIntReg(INTREG_LR),
+            "%s\n", name(), cpsr, cur_pc, tc->getReg(int_reg::Lr),
             new_pc, arm_inst ? csprintf("inst: %#x", arm_inst->encoding()) :
             std::string());
     PCState pc(new_pc);
@@ -673,9 +661,9 @@
     // Save process state into SPSR_ELx
     CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
     CPSR spsr = cpsr;
-    spsr.nz = tc->readCCReg(CCREG_NZ);
-    spsr.c = tc->readCCReg(CCREG_C);
-    spsr.v = tc->readCCReg(CCREG_V);
+    spsr.nz = tc->getReg(cc_reg::Nz);
+    spsr.c = tc->getReg(cc_reg::C);
+    spsr.v = tc->getReg(cc_reg::V);
     spsr.ss = isResetSPSR() ? 0: cpsr.ss;
     if (from64) {
         // Force some bitfields to 0
@@ -686,7 +674,7 @@
         spsr.it2 = 0;
         spsr.t = 0;
     } else {
-        spsr.ge = tc->readCCReg(CCREG_GE);
+        spsr.ge = tc->getReg(cc_reg::Ge);
         ITSTATE it = tc->pcState().as<PCState>().itstate();
         spsr.it2 = it.top6;
         spsr.it1 = it.bottom2;
@@ -935,8 +923,8 @@
 }
 
 
-HypervisorCall::HypervisorCall(ExtMachInst _machInst, uint32_t _imm) :
-        ArmFaultVals<HypervisorCall>(_machInst, _imm)
+HypervisorCall::HypervisorCall(ExtMachInst mach_inst, uint32_t _imm) :
+        ArmFaultVals<HypervisorCall>(mach_inst, _imm)
 {
     bStep = true;
 }
@@ -1250,18 +1238,6 @@
 }
 
 template<class T>
-uint32_t
-AbortFault<T>::iss() const
-{
-    uint32_t val;
-
-    val  = srcEncoded & 0x3F;
-    val |= write << 6;
-    val |= s1ptw << 7;
-    return (val);
-}
-
-template<class T>
 bool
 AbortFault<T>::isMMUFault() const
 {
@@ -1311,6 +1287,18 @@
     }
 }
 
+uint32_t
+PrefetchAbort::iss() const
+{
+    ESR esr = 0;
+    auto& iss = esr.instruction_abort_iss;
+
+    iss.ifsc = srcEncoded & 0x3F;
+    iss.s1ptw = s1ptw;
+
+    return iss;
+}
+
 bool
 PrefetchAbort::routeToMonitor(ThreadContext *tc) const
 {
@@ -1368,6 +1356,12 @@
 }
 
 bool
+DataAbort::il(ThreadContext *tc) const
+{
+    return !isv? true : AbortFault<DataAbort>::il(tc);
+}
+
+bool
 DataAbort::routeToMonitor(ThreadContext *tc) const
 {
     SCR scr = 0;
@@ -1389,7 +1383,7 @@
 
     bool amo = hcr.amo;
     if (hcr.tge == 1)
-        amo =  (!HaveVirtHostExt(tc) || hcr.e2h == 0);
+        amo =  (!HaveExt(tc, ArmExtension::FEAT_VHE) || hcr.e2h == 0);
 
     // if in Hyp mode then stay in Hyp mode
     toHyp = fromEL == EL2 ||
@@ -1406,28 +1400,29 @@
 uint32_t
 DataAbort::iss() const
 {
-    uint32_t val;
+    ESR esr = 0;
+    auto& iss = esr.data_abort_iss;
 
-    // Add on the data abort specific fields to the generic abort ISS value
-    val  = AbortFault<DataAbort>::iss();
-
-    val |= cm << 8;
+    iss.dfsc = srcEncoded & 0x3F;
+    iss.wnr = write;
+    iss.s1ptw = s1ptw;
+    iss.cm = cm;
 
     // ISS is valid if not caused by a stage 1 page table walk, and when taken
     // to AArch64 only when directed to EL2
     if (!s1ptw && stage2 && (!to64 || toEL == EL2)) {
-        val |= isv << 24;
+        iss.isv = isv;
         if (isv) {
-            val |= sas << 22;
-            val |= sse << 21;
-            val |= srt << 16;
+            iss.sas = sas;
+            iss.sse = sse;
+            iss.srt = srt;
             // AArch64 only. These assignments are safe on AArch32 as well
             // because these vars are initialized to false
-            val |= sf << 15;
-            val |= ar << 14;
+            iss.sf = sf;
+            iss.ar = ar;
         }
     }
-    return (val);
+    return iss;
 }
 
 void
@@ -1613,8 +1608,8 @@
 }
 
 
-SoftwareBreakpoint::SoftwareBreakpoint(ExtMachInst _mach_inst, uint32_t _iss)
-    : ArmFaultVals<SoftwareBreakpoint>(_mach_inst, _iss)
+SoftwareBreakpoint::SoftwareBreakpoint(ExtMachInst mach_inst, uint32_t _iss)
+    : ArmFaultVals<SoftwareBreakpoint>(mach_inst, _iss)
 {}
 
 bool
@@ -1633,8 +1628,8 @@
     return from64 ? EC_SOFTWARE_BREAKPOINT_64 : vals.ec;
 }
 
-HardwareBreakpoint::HardwareBreakpoint(Addr _vaddr,  uint32_t _iss)
-    : ArmFaultVals<HardwareBreakpoint>(0x0, _iss), vAddr(_vaddr)
+HardwareBreakpoint::HardwareBreakpoint(Addr vaddr,  uint32_t _iss)
+    : ArmFaultVals<HardwareBreakpoint>(0x0, _iss), vAddr(vaddr)
 {}
 
 bool
@@ -1683,23 +1678,20 @@
 
 }
 
-Watchpoint::Watchpoint(ExtMachInst _mach_inst, Addr _vaddr,
+Watchpoint::Watchpoint(ExtMachInst mach_inst, Addr _vaddr,
                        bool _write, bool _cm)
-    : ArmFaultVals<Watchpoint>(_mach_inst), vAddr(_vaddr),
+    : ArmFaultVals<Watchpoint>(mach_inst), vAddr(_vaddr),
       write(_write), cm(_cm)
 {}
 
 uint32_t
 Watchpoint::iss() const
 {
-    uint32_t iss = 0x0022;
-// NV
-//    if (toEL == EL2)
-//        iss |= 0x02000;
-    if (cm)
-        iss |= 0x00100;
-    if (write)
-        iss |= 0x00040;
+    ESR esr = 0;
+    auto& iss = esr.watchpoint_iss;
+    iss.dfsc = 0b100010;
+    iss.cm = cm;
+    iss.wnr = write;
     return iss;
 }
 
@@ -1747,9 +1739,9 @@
             return EC_WATCHPOINT_LOWER_EL;
 }
 
-SoftwareStepFault::SoftwareStepFault(ExtMachInst _mach_inst, bool is_ldx,
+SoftwareStepFault::SoftwareStepFault(ExtMachInst mach_inst, bool is_ldx,
                                      bool _stepped)
-    : ArmFaultVals<SoftwareStepFault>(_mach_inst), isldx(is_ldx),
+    : ArmFaultVals<SoftwareStepFault>(mach_inst), isldx(is_ldx),
                                       stepped(_stepped)
 {
     bStep = true;
@@ -1778,21 +1770,17 @@
 uint32_t
 SoftwareStepFault::iss() const
 {
-    uint32_t iss= 0x0022;
-    if (stepped) {
-        iss |= 0x1000000;
-    }
-
-    if (isldx) {
-        iss |= 0x40;
-    }
-
+    ESR esr = 0;
+    auto& iss = esr.software_step_iss;
+    iss.ifsc = 0b100010;
+    iss.isv = stepped;
+    iss.ex = isldx;
     return iss;
-
 }
 
 void
-ArmSev::invoke(ThreadContext *tc, const StaticInstPtr &inst) {
+ArmSev::invoke(ThreadContext *tc, const StaticInstPtr &inst)
+{
     DPRINTF(Faults, "Invoking ArmSev Fault\n");
     if (!FullSystem)
         return;
diff --git a/src/arch/arm/faults.hh b/src/arch/arm/faults.hh
index 688faad..6c0cda6 100644
--- a/src/arch/arm/faults.hh
+++ b/src/arch/arm/faults.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2013, 2016-2019 ARM Limited
+ * Copyright (c) 2010, 2012-2013, 2016-2019, 2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -193,26 +193,27 @@
         // (exceptions taken in HYP mode or in AArch64 state)
         const ExceptionClass ec;
 
-        FaultVals(const FaultName& name_, const FaultOffset& offset_,
-                const uint16_t& currELTOffset_, const uint16_t& currELHOffset_,
-                const uint16_t& lowerEL64Offset_,
-                const uint16_t& lowerEL32Offset_,
-                const OperatingMode& nextMode_, const uint8_t& armPcOffset_,
-                const uint8_t& thumbPcOffset_, const uint8_t& armPcElrOffset_,
-                const uint8_t& thumbPcElrOffset_, const bool& hypTrappable_,
-                const bool& abortDisable_, const bool& fiqDisable_,
-                const ExceptionClass& ec_)
-        : name(name_), offset(offset_), currELTOffset(currELTOffset_),
-          currELHOffset(currELHOffset_), lowerEL64Offset(lowerEL64Offset_),
-          lowerEL32Offset(lowerEL32Offset_), nextMode(nextMode_),
-          armPcOffset(armPcOffset_), thumbPcOffset(thumbPcOffset_),
-          armPcElrOffset(armPcElrOffset_), thumbPcElrOffset(thumbPcElrOffset_),
-          hypTrappable(hypTrappable_), abortDisable(abortDisable_),
-          fiqDisable(fiqDisable_), ec(ec_) {}
+        FaultVals(const FaultName& name_, FaultOffset offset_,
+                  uint16_t curr_elt_offset, uint16_t curr_elh_offset,
+                  uint16_t lower_el64_offset,
+                  uint16_t lower_el32_offset,
+                  OperatingMode next_mode, uint8_t arm_pc_offset,
+                  uint8_t thumb_pc_offset, uint8_t arm_pc_elr_offset,
+                  uint8_t thumb_pc_elr_offset, bool hyp_trappable,
+                  bool abort_disable, bool fiq_disable,
+                  ExceptionClass ec_)
+        : name(name_), offset(offset_), currELTOffset(curr_elt_offset),
+          currELHOffset(curr_elh_offset), lowerEL64Offset(lower_el64_offset),
+          lowerEL32Offset(lower_el32_offset), nextMode(next_mode),
+          armPcOffset(arm_pc_offset), thumbPcOffset(thumb_pc_offset),
+          armPcElrOffset(arm_pc_elr_offset),
+          thumbPcElrOffset(thumb_pc_elr_offset),
+          hypTrappable(hyp_trappable), abortDisable(abort_disable),
+          fiqDisable(fiq_disable), ec(ec_) {}
     };
 
-    ArmFault(ExtMachInst _machInst = 0, uint32_t _iss = 0) :
-        machInst(_machInst), issRaw(_iss), bStep(false), from64(false),
+    ArmFault(ExtMachInst mach_inst = 0, uint32_t _iss = 0) :
+        machInst(mach_inst), issRaw(_iss), bStep(false), from64(false),
         to64(false), fromEL(EL0), toEL(EL0), fromMode(MODE_UNDEFINED),
         faultUpdated(false), hypRouted(false), span(false) {}
 
@@ -241,15 +242,16 @@
     virtual OperatingMode nextMode() = 0;
     virtual bool routeToMonitor(ThreadContext *tc) const = 0;
     virtual bool routeToHyp(ThreadContext *tc) const { return false; }
-    virtual uint8_t armPcOffset(bool isHyp) = 0;
-    virtual uint8_t thumbPcOffset(bool isHyp) = 0;
+    virtual uint8_t armPcOffset(bool is_hyp) = 0;
+    virtual uint8_t thumbPcOffset(bool is_hyp) = 0;
     virtual uint8_t armPcElrOffset() = 0;
     virtual uint8_t thumbPcElrOffset() = 0;
     virtual bool abortDisable(ThreadContext *tc) = 0;
     virtual bool fiqDisable(ThreadContext *tc) = 0;
     virtual ExceptionClass ec(ThreadContext *tc) const = 0;
-    virtual uint32_t vectorCatchFlag() const { return 0x0; }
+    virtual bool il(ThreadContext *tc) const = 0;
     virtual uint32_t iss() const = 0;
+    virtual uint32_t vectorCatchFlag() const { return 0x0; }
     virtual bool isStage2() const { return false; }
     virtual FSR getFsr(ThreadContext *tc) const { return 0; }
     virtual void setSyndrome(ThreadContext *tc, MiscRegIndex syndrome_reg);
@@ -264,30 +266,48 @@
     static FaultVals vals;
 
   public:
-    ArmFaultVals<T>(ExtMachInst _machInst = 0, uint32_t _iss = 0) :
-        ArmFault(_machInst, _iss) {}
+    ArmFaultVals<T>(ExtMachInst mach_inst = 0, uint32_t _iss = 0) :
+        ArmFault(mach_inst, _iss) {}
     FaultName name() const override { return vals.name; }
     FaultOffset offset(ThreadContext *tc) override;
 
     FaultOffset offset64(ThreadContext *tc) override;
 
     OperatingMode nextMode() override { return vals.nextMode; }
-    virtual bool routeToMonitor(ThreadContext *tc) const override {
+
+    virtual bool
+    routeToMonitor(ThreadContext *tc) const override
+    {
         return false;
     }
-    uint8_t armPcOffset(bool isHyp) override {
-        return isHyp ? vals.armPcElrOffset
-                     : vals.armPcOffset;
+
+    uint8_t
+    armPcOffset(bool is_hyp) override
+    {
+        return is_hyp ? vals.armPcElrOffset
+                      : vals.armPcOffset;
     }
-    uint8_t thumbPcOffset(bool isHyp) override {
-        return isHyp ? vals.thumbPcElrOffset
-                     : vals.thumbPcOffset;
+
+    uint8_t
+    thumbPcOffset(bool is_hyp) override
+    {
+        return is_hyp ? vals.thumbPcElrOffset
+                      : vals.thumbPcOffset;
     }
+
     uint8_t armPcElrOffset() override { return vals.armPcElrOffset; }
     uint8_t thumbPcElrOffset() override { return vals.thumbPcElrOffset; }
     bool abortDisable(ThreadContext* tc) override { return vals.abortDisable; }
     bool fiqDisable(ThreadContext* tc) override { return vals.fiqDisable; }
+
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override { return vals.ec; }
+    bool
+    il(ThreadContext *tc) const override
+    {
+        // ESR.IL = 1 if exception cause is unknown (EC = 0)
+        return ec(tc) == EC_UNKNOWN || !machInst.thumb || machInst.bigThumb;
+    }
     uint32_t iss() const override { return issRaw; }
 };
 
@@ -310,17 +330,17 @@
     const char *mnemonic;
 
   public:
-    UndefinedInstruction(ExtMachInst _machInst,
+    UndefinedInstruction(ExtMachInst mach_inst,
                          bool _unknown,
                          const char *_mnemonic = NULL,
                          bool _disabled = false) :
-        ArmFaultVals<UndefinedInstruction>(_machInst),
+        ArmFaultVals<UndefinedInstruction>(mach_inst),
         unknown(_unknown), disabled(_disabled),
         overrideEc(EC_INVALID), mnemonic(_mnemonic)
     {}
-    UndefinedInstruction(ExtMachInst _machInst, uint32_t _iss,
+    UndefinedInstruction(ExtMachInst mach_inst, uint32_t _iss,
             ExceptionClass _overrideEc, const char *_mnemonic = NULL) :
-        ArmFaultVals<UndefinedInstruction>(_machInst, _iss),
+        ArmFaultVals<UndefinedInstruction>(mach_inst, _iss),
         unknown(false), disabled(true), overrideEc(_overrideEc),
         mnemonic(_mnemonic)
     {}
@@ -328,9 +348,11 @@
     void invoke(ThreadContext *tc, const StaticInstPtr &inst =
                 nullStaticInstPtr) override;
     bool routeToHyp(ThreadContext *tc) const override;
+    uint32_t vectorCatchFlag() const override { return 0x02000002; }
+
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override;
     uint32_t iss() const override;
-    uint32_t vectorCatchFlag() const override { return 0x02000002; }
 };
 
 class SupervisorCall : public ArmFaultVals<SupervisorCall>
@@ -338,9 +360,9 @@
   protected:
     ExceptionClass overrideEc;
   public:
-    SupervisorCall(ExtMachInst _machInst, uint32_t _iss,
+    SupervisorCall(ExtMachInst mach_inst, uint32_t _iss,
                    ExceptionClass _overrideEc = EC_INVALID) :
-        ArmFaultVals<SupervisorCall>(_machInst, _iss),
+        ArmFaultVals<SupervisorCall>(mach_inst, _iss),
         overrideEc(_overrideEc)
     {
         bStep = true;
@@ -349,25 +371,29 @@
     void invoke(ThreadContext *tc, const StaticInstPtr &inst =
                 nullStaticInstPtr) override;
     bool routeToHyp(ThreadContext *tc) const override;
+    uint32_t vectorCatchFlag() const override { return 0x04000404; }
+
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override;
     uint32_t iss() const override;
-    uint32_t vectorCatchFlag() const override { return 0x04000404; }
 };
 
 class SecureMonitorCall : public ArmFaultVals<SecureMonitorCall>
 {
   public:
-    SecureMonitorCall(ExtMachInst _machInst) :
-        ArmFaultVals<SecureMonitorCall>(_machInst)
+    SecureMonitorCall(ExtMachInst mach_inst) :
+        ArmFaultVals<SecureMonitorCall>(mach_inst)
     {
         bStep = true;
     }
 
     void invoke(ThreadContext *tc, const StaticInstPtr &inst =
                 nullStaticInstPtr) override;
+    uint32_t vectorCatchFlag() const override { return 0x00000400; }
+
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override;
     uint32_t iss() const override;
-    uint32_t vectorCatchFlag() const override { return 0x00000400; }
 };
 
 class SupervisorTrap : public ArmFaultVals<SupervisorTrap>
@@ -377,15 +403,17 @@
     ExceptionClass overrideEc;
 
   public:
-    SupervisorTrap(ExtMachInst _machInst, uint32_t _iss,
+    SupervisorTrap(ExtMachInst mach_inst, uint32_t _iss,
                    ExceptionClass _overrideEc = EC_INVALID) :
-        ArmFaultVals<SupervisorTrap>(_machInst, _iss),
+        ArmFaultVals<SupervisorTrap>(mach_inst, _iss),
         overrideEc(_overrideEc)
     {}
 
     bool routeToHyp(ThreadContext *tc) const override;
-    uint32_t iss() const override;
+
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override;
+    uint32_t iss() const override;
 };
 
 class SecureMonitorTrap : public ArmFaultVals<SecureMonitorTrap>
@@ -395,24 +423,27 @@
     ExceptionClass overrideEc;
 
   public:
-    SecureMonitorTrap(ExtMachInst _machInst, uint32_t _iss,
+    SecureMonitorTrap(ExtMachInst mach_inst, uint32_t _iss,
                       ExceptionClass _overrideEc = EC_INVALID) :
-        ArmFaultVals<SecureMonitorTrap>(_machInst, _iss),
+        ArmFaultVals<SecureMonitorTrap>(mach_inst, _iss),
         overrideEc(_overrideEc)
     {}
 
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override;
 };
 
 class HypervisorCall : public ArmFaultVals<HypervisorCall>
 {
   public:
-    HypervisorCall(ExtMachInst _machInst, uint32_t _imm);
+    HypervisorCall(ExtMachInst mach_inst, uint32_t _imm);
 
     bool routeToHyp(ThreadContext *tc) const override;
     bool routeToMonitor(ThreadContext *tc) const override;
-    ExceptionClass ec(ThreadContext *tc) const override;
     uint32_t vectorCatchFlag() const override { return 0xFFFFFFFF; }
+
+    /** Syndrome methods */
+    ExceptionClass ec(ThreadContext *tc) const override;
 };
 
 class HypervisorTrap : public ArmFaultVals<HypervisorTrap>
@@ -422,12 +453,13 @@
     ExceptionClass overrideEc;
 
   public:
-    HypervisorTrap(ExtMachInst _machInst, uint32_t _iss,
+    HypervisorTrap(ExtMachInst mach_inst, uint32_t _iss,
                    ExceptionClass _overrideEc = EC_INVALID) :
-      ArmFaultVals<HypervisorTrap>(_machInst, _iss),
+      ArmFaultVals<HypervisorTrap>(mach_inst, _iss),
       overrideEc(_overrideEc)
     {}
 
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override;
 };
 
@@ -476,7 +508,6 @@
     FSR getFsr(ThreadContext *tc) const override;
     uint8_t getFaultStatusCode(ThreadContext *tc) const;
     bool abortDisable(ThreadContext *tc) override;
-    uint32_t iss() const override;
     bool isStage2() const override { return stage2; }
     void annotate(ArmFault::AnnotationIDs id, uint64_t val) override;
     void setSyndrome(ThreadContext *tc, MiscRegIndex syndrome_reg) override;
@@ -497,11 +528,15 @@
                 _source, _stage2, _tranMethod, _debug)
     {}
 
-    ExceptionClass ec(ThreadContext *tc) const override;
     // @todo: external aborts should be routed if SCR.EA == 1
     bool routeToMonitor(ThreadContext *tc) const override;
     bool routeToHyp(ThreadContext *tc) const override;
     uint32_t vectorCatchFlag() const override { return 0x08000808; }
+
+    /** Syndrome methods */
+    ExceptionClass ec(ThreadContext *tc) const override;
+    bool il(ThreadContext *tc) const override { return true; }
+    uint32_t iss() const override;
 };
 
 class DataAbort : public AbortFault<DataAbort>
@@ -529,13 +564,16 @@
         isv(false), sas (0), sse(0), srt(0), cm(0), sf(false), ar(false)
     {}
 
-    ExceptionClass ec(ThreadContext *tc) const override;
     // @todo: external aborts should be routed if SCR.EA == 1
     bool routeToMonitor(ThreadContext *tc) const override;
     bool routeToHyp(ThreadContext *tc) const override;
-    uint32_t iss() const override;
     void annotate(AnnotationIDs id, uint64_t val) override;
     uint32_t vectorCatchFlag() const override { return 0x10001010; }
+
+    /** Syndrome methods */
+    ExceptionClass ec(ThreadContext *tc) const override;
+    bool il(ThreadContext *tc) const override;
+    uint32_t iss() const override;
 };
 
 class VirtualDataAbort : public AbortFault<VirtualDataAbort>
@@ -591,11 +629,14 @@
     /// The unaligned value of the PC
     Addr faultPC;
   public:
-    PCAlignmentFault(Addr _faultPC) : faultPC(_faultPC)
+    PCAlignmentFault(Addr fault_pc) : faultPC(fault_pc)
     {}
     void invoke(ThreadContext *tc, const StaticInstPtr &inst =
                 nullStaticInstPtr) override;
     bool routeToHyp(ThreadContext *tc) const override;
+
+    /** Syndrome methods */
+    bool il(ThreadContext *tc) const override { return true; }
 };
 
 /// Stack pointer alignment fault (AArch64 only)
@@ -604,6 +645,9 @@
   public:
     SPAlignmentFault();
     bool routeToHyp(ThreadContext *tc) const override;
+
+    /** Syndrome methods */
+    bool il(ThreadContext *tc) const override { return true; }
 };
 
 /// System error (AArch64 only)
@@ -615,15 +659,19 @@
                 nullStaticInstPtr) override;
     bool routeToMonitor(ThreadContext *tc) const override;
     bool routeToHyp(ThreadContext *tc) const override;
+
+    /** Syndrome methods */
+    bool il(ThreadContext *tc) const override { return true; }
 };
 
-/// System error (AArch64 only)
+/// Software Breakpoint (AArch64 only)
 class SoftwareBreakpoint : public ArmFaultVals<SoftwareBreakpoint>
 {
   public:
-    SoftwareBreakpoint(ExtMachInst _mach_inst, uint32_t _iss);
-
+    SoftwareBreakpoint(ExtMachInst mach_inst, uint32_t _iss);
     bool routeToHyp(ThreadContext *tc) const override;
+
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override;
 };
 
@@ -636,7 +684,10 @@
                 nullStaticInstPtr) override;
     HardwareBreakpoint(Addr _vaddr, uint32_t _iss);
     bool routeToHyp(ThreadContext *tc) const override;
+
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override;
+    bool il(ThreadContext *tc) const override { return true; }
 };
 
 class Watchpoint : public ArmFaultVals<Watchpoint>
@@ -647,13 +698,16 @@
     bool cm;
 
   public:
-    Watchpoint(ExtMachInst _mach_inst, Addr _vaddr, bool _write, bool _cm);
+    Watchpoint(ExtMachInst mach_inst, Addr vaddr, bool _write, bool _cm);
     void invoke(ThreadContext *tc, const StaticInstPtr &inst =
                 nullStaticInstPtr) override;
     bool routeToHyp(ThreadContext *tc) const override;
-    uint32_t iss() const override;
-    ExceptionClass ec(ThreadContext *tc) const override;
     void annotate(AnnotationIDs id, uint64_t val) override;
+
+    /** Syndrome methods */
+    ExceptionClass ec(ThreadContext *tc) const override;
+    bool il(ThreadContext *tc) const override { return true; }
+    uint32_t iss() const override;
 };
 
 class SoftwareStepFault : public ArmFaultVals<SoftwareStepFault>
@@ -663,17 +717,20 @@
     bool stepped;
 
   public:
-    SoftwareStepFault(ExtMachInst _mach_inst, bool is_ldx, bool stepped);
+    SoftwareStepFault(ExtMachInst mach_inst, bool is_ldx, bool stepped);
     bool routeToHyp(ThreadContext *tc) const override;
-    uint32_t iss() const override;
+
+    /** Syndrome methods */
     ExceptionClass ec(ThreadContext *tc) const override;
+    bool il(ThreadContext *tc) const override { return true; }
+    uint32_t iss() const override;
 };
 
 // A fault that flushes the pipe, excluding the faulting instructions
 class ArmSev : public ArmFaultVals<ArmSev>
 {
   public:
-    ArmSev () {}
+    ArmSev() {}
     void invoke(ThreadContext *tc, const StaticInstPtr &inst =
                 nullStaticInstPtr) override;
 };
@@ -683,8 +740,10 @@
 {
   public:
     IllegalInstSetStateFault();
-
     bool routeToHyp(ThreadContext *tc) const override;
+
+    /** Syndrome methods */
+    bool il(ThreadContext *tc) const override { return true; }
 };
 
 /*
diff --git a/src/arch/arm/freebsd/fs_workload.cc b/src/arch/arm/freebsd/fs_workload.cc
index fe04fd4..5e6b954 100644
--- a/src/arch/arm/freebsd/fs_workload.cc
+++ b/src/arch/arm/freebsd/fs_workload.cc
@@ -115,9 +115,9 @@
 
     // Kernel boot requirements to set up r0, r1 and r2 in ARMv7
     for (auto *tc: system->threads) {
-        tc->setIntReg(0, 0);
-        tc->setIntReg(1, params().machine_type);
-        tc->setIntReg(2, params().dtb_addr);
+        tc->setReg(int_reg::R0, (RegVal)0);
+        tc->setReg(int_reg::R1, params().machine_type);
+        tc->setReg(int_reg::R2, params().dtb_addr);
     }
 }
 
diff --git a/src/arch/arm/freebsd/se_workload.cc b/src/arch/arm/freebsd/se_workload.cc
index 66e587e..b8b222d 100644
--- a/src/arch/arm/freebsd/se_workload.cc
+++ b/src/arch/arm/freebsd/se_workload.cc
@@ -157,9 +157,9 @@
     process->Process::syscall(tc);
 
     if (dynamic_cast<ArmProcess64 *>(process))
-        syscallDescs64.get(tc->readIntReg(INTREG_X8))->doSyscall(tc);
+        syscallDescs64.get(tc->getReg(int_reg::X8))->doSyscall(tc);
     else
-        syscallDescs32.get(tc->readIntReg(INTREG_R7))->doSyscall(tc);
+        syscallDescs32.get(tc->getReg(int_reg::R7))->doSyscall(tc);
 }
 
 } // namespace ArmISA
diff --git a/src/arch/arm/freebsd/se_workload.hh b/src/arch/arm/freebsd/se_workload.hh
index 4ec5090..b944dbd 100644
--- a/src/arch/arm/freebsd/se_workload.hh
+++ b/src/arch/arm/freebsd/se_workload.hh
@@ -84,15 +84,15 @@
     {
         RegVal val;
         if (ret.successful()) {
-            tc->setCCReg(ArmISA::CCREG_C, 0);
+            tc->setReg(ArmISA::cc_reg::C, (RegVal)0);
             val = ret.returnValue();
         } else {
-            tc->setCCReg(ArmISA::CCREG_C, 1);
+            tc->setReg(ArmISA::cc_reg::C, 1);
             val = ret.encodedValue();
         }
-        tc->setIntReg(ArmISA::ReturnValueReg, val);
+        tc->setReg(ArmISA::ReturnValueReg, val);
         if (ret.count() > 1)
-            tc->setIntReg(ArmISA::SyscallPseudoReturnReg, ret.value2());
+            tc->setReg(ArmISA::SyscallPseudoReturnReg, ret.value2());
     }
 };
 
diff --git a/src/arch/arm/fs_workload.cc b/src/arch/arm/fs_workload.cc
index 6a79aa0..367db20 100644
--- a/src/arch/arm/fs_workload.cc
+++ b/src/arch/arm/fs_workload.cc
@@ -59,9 +59,9 @@
 {
     PCState new_pc = tc->pcState().as<PCState>();
     if (inAArch64(tc)) {
-        new_pc.set(tc->readIntReg(INTREG_X30));
+        new_pc.set(tc->getReg(int_reg::X30));
     } else {
-        new_pc.set(tc->readIntReg(ReturnAddressReg) & ~1ULL);
+        new_pc.set(tc->getReg(ReturnAddressReg) & ~1ULL);
     }
 
     CheckerCPU *checker = tc->getCheckerCpuPtr();
@@ -130,11 +130,11 @@
                  "gic_cpu_addr must be set with bootloader");
 
         for (auto *tc: arm_sys->threads) {
-            tc->setIntReg(3, kernelEntry);
+            tc->setReg(int_reg::R3, kernelEntry);
             if (is_gic_v2)
-                tc->setIntReg(4, arm_sys->params().gic_cpu_addr);
+                tc->setReg(int_reg::R4, arm_sys->params().gic_cpu_addr);
             if (getArch() == loader::Arm)
-                tc->setIntReg(5, params().cpu_release_addr);
+                tc->setReg(int_reg::R5, params().cpu_release_addr);
         }
         inform("Using kernel entry physical address at %#x\n", kernelEntry);
     } else {
@@ -144,7 +144,7 @@
     }
 }
 
-    loader::ObjectFile *
+loader::ObjectFile *
 FsWorkload::getBootLoader(loader::ObjectFile *const obj)
 {
     if (obj) {
diff --git a/src/arch/arm/htm.cc b/src/arch/arm/htm.cc
index 84ef4a1..2549fe3 100644
--- a/src/arch/arm/htm.cc
+++ b/src/arch/arm/htm.cc
@@ -71,22 +71,22 @@
 void
 ArmISA::HTMCheckpoint::save(ThreadContext *tc)
 {
-    sp = tc->readIntReg(INTREG_SPX);
+    sp = tc->getReg(int_reg::Spx);
     // below should be enabled on condition that GICV3 is enabled
     //tme_checkpoint->iccPmrEl1 = tc->readMiscReg(MISCREG_ICC_PMR_EL1);
     nzcv = tc->readMiscReg(MISCREG_NZCV);
     daif = tc->readMiscReg(MISCREG_DAIF);
-    for (auto n = 0; n < NUM_ARCH_INTREGS; n++) {
-        x[n] = tc->readIntReg(n);
+    for (auto n = 0; n < int_reg::NumArchRegs; n++) {
+        x[n] = tc->getReg(RegId(IntRegClass, n));
     }
     // TODO first detect if FP is enabled at this EL
     for (auto n = 0; n < NumVecRegs; n++) {
         RegId idx = RegId(VecRegClass, n);
-        z[n] = tc->readVecReg(idx);
+        tc->getReg(idx, &z[n]);
     }
     for (auto n = 0; n < NumVecPredRegs; n++) {
         RegId idx = RegId(VecPredRegClass, n);
-        p[n] = tc->readVecPredReg(idx);
+        tc->getReg(idx, &p[n]);
     }
     fpcr = tc->readMiscReg(MISCREG_FPCR);
     fpsr = tc->readMiscReg(MISCREG_FPSR);
@@ -98,22 +98,22 @@
 void
 ArmISA::HTMCheckpoint::restore(ThreadContext *tc, HtmFailureFaultCause cause)
 {
-    tc->setIntReg(INTREG_SPX, sp);
+    tc->setReg(int_reg::Spx, sp);
     // below should be enabled on condition that GICV3 is enabled
     //tc->setMiscReg(MISCREG_ICC_PMR_EL1, tme_checkpoint->iccPmrEl1);
     tc->setMiscReg(MISCREG_NZCV, nzcv);
     tc->setMiscReg(MISCREG_DAIF, daif);
-    for (auto n = 0; n < NUM_ARCH_INTREGS; n++) {
-        tc->setIntReg(n, x[n]);
+    for (auto n = 0; n < int_reg::NumArchRegs; n++) {
+        tc->setReg(RegId(IntRegClass, n), x[n]);
     }
     // TODO first detect if FP is enabled at this EL
     for (auto n = 0; n < NumVecRegs; n++) {
         RegId idx = RegId(VecRegClass, n);
-        tc->setVecReg(idx, z[n]);
+        tc->setReg(idx, &z[n]);
     }
     for (auto n = 0; n < NumVecPredRegs; n++) {
         RegId idx = RegId(VecPredRegClass, n);
-        tc->setVecPredReg(idx, p[n]);
+        tc->setReg(idx, &p[n]);
     }
     tc->setMiscReg(MISCREG_FPCR, fpcr);
     tc->setMiscReg(MISCREG_FPSR, fpsr);
@@ -158,7 +158,7 @@
         replaceBits(error_code, 15, 1);
     if (interrupt)
         replaceBits(error_code, 23, 1);
-    tc->setIntReg(rt, error_code);
+    tc->setReg(RegId(IntRegClass, rt), error_code);
 
     // set next PC
     pcstateckpt.uReset();
diff --git a/src/arch/arm/htm.hh b/src/arch/arm/htm.hh
index 7323687..2242bdc 100644
--- a/src/arch/arm/htm.hh
+++ b/src/arch/arm/htm.hh
@@ -74,7 +74,7 @@
   private:
     uint8_t rt; // TSTART destination register
     Addr nPc; // Fallback instruction address
-    std::array<RegVal, NUM_ARCH_INTREGS> x; // General purpose registers
+    std::array<RegVal, int_reg::NumArchRegs> x; // General purpose registers
     std::array<VecRegContainer, NumVecRegs> z; // Vector registers
     std::array<VecPredRegContainer, NumVecRegs> p; // Predicate registers
     Addr sp; // Stack Pointer at current EL
diff --git a/src/arch/arm/insts/branch.hh b/src/arch/arm/insts/branch.hh
index 252fb50..56c60cb 100644
--- a/src/arch/arm/insts/branch.hh
+++ b/src/arch/arm/insts/branch.hh
@@ -82,11 +82,11 @@
 class BranchReg : public PredOp
 {
   protected:
-    IntRegIndex op1;
+    RegIndex op1;
 
   public:
     BranchReg(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-              IntRegIndex _op1) :
+              RegIndex _op1) :
         PredOp(mnem, _machInst, __opClass), op1(_op1)
     {}
 
@@ -99,7 +99,7 @@
 {
   public:
     BranchRegCond(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                  IntRegIndex _op1, ConditionCode _condCode) :
+                  RegIndex _op1, ConditionCode _condCode) :
         BranchReg(mnem, _machInst, __opClass, _op1)
     {
         // Only update if this isn't part of an IT block
@@ -112,12 +112,12 @@
 class BranchRegReg : public PredOp
 {
   protected:
-    IntRegIndex op1;
-    IntRegIndex op2;
+    RegIndex op1;
+    RegIndex op2;
 
   public:
     BranchRegReg(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                 IntRegIndex _op1, IntRegIndex _op2) :
+                 RegIndex _op1, RegIndex _op2) :
         PredOp(mnem, _machInst, __opClass), op1(_op1), op2(_op2)
     {}
 
@@ -130,11 +130,11 @@
 {
   protected:
     int32_t imm;
-    IntRegIndex op1;
+    RegIndex op1;
 
   public:
     BranchImmReg(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                 int32_t _imm, IntRegIndex _op1) :
+                 int32_t _imm, RegIndex _op1) :
         PredOp(mnem, _machInst, __opClass), imm(_imm), op1(_op1)
     {}
 };
diff --git a/src/arch/arm/insts/branch64.cc b/src/arch/arm/insts/branch64.cc
index 4c9552d..f4d0686 100644
--- a/src/arch/arm/insts/branch64.cc
+++ b/src/arch/arm/insts/branch64.cc
@@ -121,7 +121,7 @@
 {
     std::stringstream ss;
     printMnemonic(ss, "", false);
-    if (op1 != INTREG_X30)
+    if (op1 != int_reg::X30)
         printIntReg(ss, op1);
     return ss.str();
 }
@@ -132,7 +132,7 @@
 {
     std::stringstream ss;
     printMnemonic(ss, "", false);
-    if (op1 != INTREG_X30)
+    if (op1 != int_reg::X30)
         printIntReg(ss, op1);
     return ss.str();
 }
diff --git a/src/arch/arm/insts/branch64.hh b/src/arch/arm/insts/branch64.hh
index 551ade7..3babd70 100644
--- a/src/arch/arm/insts/branch64.hh
+++ b/src/arch/arm/insts/branch64.hh
@@ -87,12 +87,12 @@
 class BranchRegReg64 : public ArmStaticInst
 {
   protected:
-    IntRegIndex op1;
-    IntRegIndex op2;
+    RegIndex op1;
+    RegIndex op2;
 
   public:
     BranchRegReg64(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _op1, IntRegIndex _op2) :
+                RegIndex _op1, RegIndex _op2) :
         ArmStaticInst(mnem, _machInst, __opClass), op1(_op1), op2(_op2)
     {}
 
@@ -104,11 +104,11 @@
 class BranchReg64 : public ArmStaticInst
 {
   protected:
-    IntRegIndex op1;
+    RegIndex op1;
 
   public:
     BranchReg64(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _op1) :
+                RegIndex _op1) :
         ArmStaticInst(mnem, _machInst, __opClass), op1(_op1)
     {}
 
@@ -121,7 +121,7 @@
 {
   public:
     BranchRet64(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _op1) :
+                RegIndex _op1) :
         BranchReg64(mnem, _machInst, __opClass, _op1)
     {}
 
@@ -134,7 +134,7 @@
 {
   public:
     BranchRetA64(const char *mnem, ExtMachInst _machInst, OpClass __opClass) :
-        BranchRegReg64(mnem, _machInst, __opClass, INTREG_X30, INTREG_SPX)
+        BranchRegReg64(mnem, _machInst, __opClass, int_reg::X30, int_reg::Spx)
     {}
 
     std::string generateDisassembly(
@@ -157,11 +157,11 @@
 class BranchEretA64 : public ArmStaticInst
 {
   protected:
-    IntRegIndex op1;
+    RegIndex op1;
 
   public:
     BranchEretA64(const char *mnem, ExtMachInst _machInst, OpClass __opClass) :
-        ArmStaticInst(mnem, _machInst, __opClass), op1(INTREG_SPX)
+        ArmStaticInst(mnem, _machInst, __opClass), op1(int_reg::Spx)
     {}
 
     std::string generateDisassembly(
@@ -172,11 +172,11 @@
 {
   protected:
     int64_t imm;
-    IntRegIndex op1;
+    RegIndex op1;
 
   public:
     BranchImmReg64(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                   int64_t _imm, IntRegIndex _op1) :
+                   int64_t _imm, RegIndex _op1) :
         ArmStaticInst(mnem, _machInst, __opClass), imm(_imm), op1(_op1)
     {}
 
@@ -196,12 +196,12 @@
   protected:
     int64_t imm1;
     int64_t imm2;
-    IntRegIndex op1;
+    RegIndex op1;
 
   public:
     BranchImmImmReg64(const char *mnem, ExtMachInst _machInst,
                       OpClass __opClass, int64_t _imm1, int64_t _imm2,
-                      IntRegIndex _op1) :
+                      RegIndex _op1) :
         ArmStaticInst(mnem, _machInst, __opClass),
         imm1(_imm1), imm2(_imm2), op1(_op1)
     {}
diff --git a/src/arch/arm/insts/data64.cc b/src/arch/arm/insts/data64.cc
index 3c2a6c1..9efe54d 100644
--- a/src/arch/arm/insts/data64.cc
+++ b/src/arch/arm/insts/data64.cc
@@ -49,7 +49,7 @@
 {
     std::stringstream ss;
     printDataInst(ss, true, false, /*XXX not really s*/ false, dest, op1,
-                  INTREG_ZERO, INTREG_ZERO, 0, LSL, imm);
+                  int_reg::Zero, int_reg::Zero, 0, LSL, imm);
     return ss.str();
 }
 
@@ -70,7 +70,7 @@
 {
     std::stringstream ss;
     printDataInst(ss, false, true, /*XXX not really s*/ false, dest, op1,
-                  op2, INTREG_ZERO, shiftAmt, shiftType, 0);
+                  op2, int_reg::Zero, shiftAmt, shiftType, 0);
     return ss.str();
 }
 
@@ -80,7 +80,7 @@
 {
     std::stringstream ss;
     printDataInst(ss, false, true, /*XXX not really s*/ false, dest, op1,
-                  op2, INTREG_ZERO, shiftAmt, LSL, 0);
+                  op2, int_reg::Zero, shiftAmt, LSL, 0);
     return ss.str();
 }
 
diff --git a/src/arch/arm/insts/data64.hh b/src/arch/arm/insts/data64.hh
index d83602f..2dea9f1 100644
--- a/src/arch/arm/insts/data64.hh
+++ b/src/arch/arm/insts/data64.hh
@@ -50,11 +50,11 @@
 class DataXImmOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1;
+    RegIndex dest, op1;
     uint64_t imm;
 
     DataXImmOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-               IntRegIndex _dest, IntRegIndex _op1, uint64_t _imm) :
+               RegIndex _dest, RegIndex _op1, uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm(_imm)
     {}
@@ -66,11 +66,11 @@
 class DataXImmOnlyOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
     uint64_t imm;
 
     DataXImmOnlyOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                   IntRegIndex _dest, uint64_t _imm) :
+                   RegIndex _dest, uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), imm(_imm)
     {}
@@ -82,12 +82,12 @@
 class DataXSRegOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     int32_t shiftAmt;
     ArmShiftType shiftType;
 
     DataXSRegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                RegIndex _dest, RegIndex _op1, RegIndex _op2,
                 int32_t _shiftAmt, ArmShiftType _shiftType) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2),
@@ -101,12 +101,12 @@
 class DataXERegOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     ArmExtendType extendType;
     int32_t shiftAmt;
 
     DataXERegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                RegIndex _dest, RegIndex _op1, RegIndex _op2,
                 ArmExtendType _extendType, int32_t _shiftAmt) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2),
@@ -120,10 +120,10 @@
 class DataX1RegOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1;
+    RegIndex dest, op1;
 
     DataX1RegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _op1) :
+                RegIndex _dest, RegIndex _op1) :
         ArmStaticInst(mnem, _machInst, __opClass), dest(_dest), op1(_op1)
     {}
 
@@ -134,11 +134,11 @@
 class DataX1RegImmOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1;
+    RegIndex dest, op1;
     uint64_t imm;
 
     DataX1RegImmOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                   IntRegIndex _dest, IntRegIndex _op1, uint64_t _imm) :
+                   RegIndex _dest, RegIndex _op1, uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass), dest(_dest), op1(_op1),
         imm(_imm)
     {}
@@ -150,11 +150,11 @@
 class DataX1Reg2ImmOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1;
+    RegIndex dest, op1;
     uint64_t imm1, imm2;
 
     DataX1Reg2ImmOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                    IntRegIndex _dest, IntRegIndex _op1, uint64_t _imm1,
+                    RegIndex _dest, RegIndex _op1, uint64_t _imm1,
                     uint64_t _imm2) :
         ArmStaticInst(mnem, _machInst, __opClass), dest(_dest), op1(_op1),
         imm1(_imm1), imm2(_imm2)
@@ -167,10 +167,10 @@
 class DataX2RegOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
 
     DataX2RegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2) :
+                RegIndex _dest, RegIndex _op1, RegIndex _op2) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2)
     {}
@@ -182,11 +182,11 @@
 class DataX2RegImmOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     uint64_t imm;
 
     DataX2RegImmOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
                    uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), imm(_imm)
@@ -199,11 +199,11 @@
 class DataX3RegOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2, op3;
+    RegIndex dest, op1, op2, op3;
 
     DataX3RegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-                IntRegIndex _op3) :
+                RegIndex _dest, RegIndex _op1, RegIndex _op2,
+                RegIndex _op3) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), op3(_op3)
     {}
@@ -215,13 +215,13 @@
 class DataXCondCompImmOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex op1;
+    RegIndex op1;
     uint64_t imm;
     ConditionCode condCode;
     uint8_t defCc;
 
     DataXCondCompImmOp(const char *mnem, ExtMachInst _machInst,
-                      OpClass __opClass, IntRegIndex _op1, uint64_t _imm,
+                      OpClass __opClass, RegIndex _op1, uint64_t _imm,
                       ConditionCode _condCode, uint8_t _defCc) :
         ArmStaticInst(mnem, _machInst, __opClass),
         op1(_op1), imm(_imm), condCode(_condCode), defCc(_defCc)
@@ -234,12 +234,12 @@
 class DataXCondCompRegOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex op1, op2;
+    RegIndex op1, op2;
     ConditionCode condCode;
     uint8_t defCc;
 
     DataXCondCompRegOp(const char *mnem, ExtMachInst _machInst,
-                       OpClass __opClass, IntRegIndex _op1, IntRegIndex _op2,
+                       OpClass __opClass, RegIndex _op1, RegIndex _op2,
                        ConditionCode _condCode, uint8_t _defCc) :
         ArmStaticInst(mnem, _machInst, __opClass),
         op1(_op1), op2(_op2), condCode(_condCode), defCc(_defCc)
@@ -252,11 +252,11 @@
 class DataXCondSelOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     ConditionCode condCode;
 
     DataXCondSelOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
                    ConditionCode _condCode) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), condCode(_condCode)
diff --git a/src/arch/arm/insts/macromem.cc b/src/arch/arm/insts/macromem.cc
index db274e5..effed08 100644
--- a/src/arch/arm/insts/macromem.cc
+++ b/src/arch/arm/insts/macromem.cc
@@ -55,7 +55,7 @@
 {
 
 MacroMemOp::MacroMemOp(const char *mnem, ExtMachInst machInst,
-                       OpClass __opClass, IntRegIndex rn,
+                       OpClass __opClass, RegIndex rn,
                        bool index, bool up, bool user, bool writeback,
                        bool load, uint32_t reglist) :
     PredMacroOp(mnem, machInst, __opClass)
@@ -98,13 +98,13 @@
     // Add 0 to Rn and stick it in ureg0.
     // This is equivalent to a move.
     if (copy_base)
-        *uop++ = new MicroAddiUop(machInst, INTREG_UREG0, rn, 0);
+        *uop++ = new MicroAddiUop(machInst, int_reg::Ureg0, rn, 0);
 
     unsigned reg = 0;
     while (mem_ops != 0) {
         // Do load operations in pairs if possible
         if (load && mem_ops >= 2 &&
-            !(mem_ops == 2 && bits(regs,INTREG_PC) && exception_ret)) {
+            !(mem_ops == 2 && bits(regs, int_reg::Pc) && exception_ret)) {
             // 64-bit memory operation
             // Find 2 set register bits (clear them after finding)
             unsigned reg_idx1;
@@ -113,22 +113,22 @@
             // Find the first register
             while (!bits(regs, reg)) reg++;
             replaceBits(regs, reg, 0);
-            reg_idx1 = force_user ? intRegInMode(MODE_USER, reg) : reg;
+            reg_idx1 = force_user ? int_reg::regInMode(MODE_USER, reg) : reg;
 
             // Find the second register
             while (!bits(regs, reg)) reg++;
             replaceBits(regs, reg, 0);
-            reg_idx2 = force_user ? intRegInMode(MODE_USER, reg) : reg;
+            reg_idx2 = force_user ? int_reg::regInMode(MODE_USER, reg) : reg;
 
             // Load into temp reg if necessary
-            if (reg_idx2 == INTREG_PC && pc_temp)
-                reg_idx2 = INTREG_UREG1;
+            if (reg_idx2 == int_reg::Pc && pc_temp)
+                reg_idx2 = int_reg::Ureg1;
 
             // Actually load both registers from memory
             *uop = new MicroLdr2Uop(machInst, reg_idx1, reg_idx2,
-                    copy_base ? INTREG_UREG0 : rn, up, addr);
+                    copy_base ? int_reg::Ureg0 : rn, up, addr);
 
-            if (!writeback && reg_idx2 == INTREG_PC) {
+            if (!writeback && reg_idx2 == int_reg::Pc) {
                 // No writeback if idx==pc, set appropriate flags
                 (*uop)->setFlag(StaticInst::IsControl);
                 (*uop)->setFlag(StaticInst::IsIndirectControl);
@@ -148,27 +148,27 @@
             unsigned reg_idx;
             while (!bits(regs, reg)) reg++;
             replaceBits(regs, reg, 0);
-            reg_idx = force_user ? intRegInMode(MODE_USER, reg) : reg;
+            reg_idx = force_user ? int_reg::regInMode(MODE_USER, reg) : reg;
 
             if (load) {
-                if (writeback && reg_idx == INTREG_PC) {
+                if (writeback && reg_idx == int_reg::Pc) {
                     // If this instruction changes the PC and performs a
                     // writeback, ensure the pc load/branch is the last uop.
                     // Load into a temp reg here.
-                    *uop = new MicroLdrUop(machInst, INTREG_UREG1,
-                            copy_base ? INTREG_UREG0 : rn, up, addr);
-                } else if (reg_idx == INTREG_PC && exception_ret) {
+                    *uop = new MicroLdrUop(machInst, int_reg::Ureg1,
+                            copy_base ? int_reg::Ureg0 : rn, up, addr);
+                } else if (reg_idx == int_reg::Pc && exception_ret) {
                     // Special handling for exception return
                     *uop = new MicroLdrRetUop(machInst, reg_idx,
-                            copy_base ? INTREG_UREG0 : rn, up, addr);
+                            copy_base ? int_reg::Ureg0 : rn, up, addr);
                 } else {
                     // standard single load uop
                     *uop = new MicroLdrUop(machInst, reg_idx,
-                            copy_base ? INTREG_UREG0 : rn, up, addr);
+                            copy_base ? int_reg::Ureg0 : rn, up, addr);
                 }
 
                 // Loading pc as last operation?  Set appropriate flags.
-                if (!writeback && reg_idx == INTREG_PC) {
+                if (!writeback && reg_idx == int_reg::Pc) {
                     (*uop)->setFlag(StaticInst::IsControl);
                     (*uop)->setFlag(StaticInst::IsIndirectControl);
 
@@ -200,9 +200,10 @@
         // Write PC after address writeback?
         if (pc_temp) {
             if (exception_ret) {
-                *uop = new MicroUopRegMovRet(machInst, 0, INTREG_UREG1);
+                *uop = new MicroUopRegMovRet(machInst, 0, int_reg::Ureg1);
             } else {
-                *uop = new MicroUopRegMov(machInst, INTREG_PC, INTREG_UREG1);
+                *uop = new MicroUopRegMov(
+                        machInst, int_reg::Pc, int_reg::Ureg1);
             }
             (*uop)->setFlag(StaticInst::IsControl);
             (*uop)->setFlag(StaticInst::IsIndirectControl);
@@ -212,7 +213,7 @@
             else
                 (*uop)->setFlag(StaticInst::IsUncondControl);
 
-            if (rn == INTREG_SP)
+            if (rn == int_reg::Sp)
                 (*uop)->setFlag(StaticInst::IsReturn);
 
             ++uop;
@@ -244,7 +245,7 @@
                      uint32_t size, bool fp, bool load, bool noAlloc,
                      bool signExt, bool exclusive, bool acrel,
                      int64_t imm, AddrMode mode,
-                     IntRegIndex rn, IntRegIndex rt, IntRegIndex rt2) :
+                     RegIndex rn, RegIndex rt, RegIndex rt2) :
     PredMacroOp(mnem, machInst, __opClass)
 {
     bool post = (mode == AddrMd_PostIndex);
@@ -263,7 +264,7 @@
     rn = makeSP(rn);
 
     if (!post) {
-        *uop++ = new MicroAddXiSpAlignUop(machInst, INTREG_UREG0, rn,
+        *uop++ = new MicroAddXiSpAlignUop(machInst, int_reg::Ureg0, rn,
                 post ? 0 : imm);
     }
 
@@ -271,67 +272,84 @@
         if (size == 16) {
             if (load) {
                 *uop++ = new MicroLdFp16Uop(machInst, rt,
-                        post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
                 *uop++ = new MicroLdFp16Uop(machInst, rt2,
-                        post ? rn : INTREG_UREG0, 16, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 16, noAlloc, exclusive,
+                        acrel);
             } else {
                 *uop++ = new MicroStrQBFpXImmUop(machInst, rt,
-                        post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
                 *uop++ = new MicroStrQTFpXImmUop(machInst, rt,
-                        post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
                 *uop++ = new MicroStrQBFpXImmUop(machInst, rt2,
-                        post ? rn : INTREG_UREG0, 16, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 16, noAlloc, exclusive,
+                        acrel);
                 *uop++ = new MicroStrQTFpXImmUop(machInst, rt2,
-                        post ? rn : INTREG_UREG0, 16, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 16, noAlloc, exclusive,
+                        acrel);
             }
         } else if (size == 8) {
             if (load) {
                 *uop++ = new MicroLdPairFp8Uop(machInst, rt, rt2,
-                        post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
             } else {
                 *uop++ = new MicroStrFpXImmUop(machInst, rt,
-                        post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
                 *uop++ = new MicroStrFpXImmUop(machInst, rt2,
-                        post ? rn : INTREG_UREG0, 8, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 8, noAlloc, exclusive,
+                        acrel);
             }
         } else if (size == 4) {
             if (load) {
                 *uop++ = new MicroLdrDFpXImmUop(machInst, rt, rt2,
-                        post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
             } else {
                 *uop++ = new MicroStrDFpXImmUop(machInst, rt, rt2,
-                        post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
             }
         }
     } else {
         if (size == 8) {
             if (load) {
                 *uop++ = new MicroLdPairUop(machInst, rt, rt2,
-                        post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
             } else {
-                *uop++ = new MicroStrXImmUop(machInst, rt, post ? rn : INTREG_UREG0,
-                        0, noAlloc, exclusive, acrel);
-                *uop++ = new MicroStrXImmUop(machInst, rt2, post ? rn : INTREG_UREG0,
-                        size, noAlloc, exclusive, acrel);
+                *uop++ = new MicroStrXImmUop(machInst, rt,
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
+                *uop++ = new MicroStrXImmUop(machInst, rt2,
+                        post ? rn : int_reg::Ureg0, size, noAlloc, exclusive,
+                        acrel);
             }
         } else if (size == 4) {
             if (load) {
                 if (signExt) {
                     *uop++ = new MicroLdrDSXImmUop(machInst, rt, rt2,
-                            post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                            post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                            acrel);
                 } else {
                     *uop++ = new MicroLdrDUXImmUop(machInst, rt, rt2,
-                            post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                            post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                            acrel);
                 }
             } else {
                 *uop++ = new MicroStrDXImmUop(machInst, rt, rt2,
-                        post ? rn : INTREG_UREG0, 0, noAlloc, exclusive, acrel);
+                        post ? rn : int_reg::Ureg0, 0, noAlloc, exclusive,
+                        acrel);
             }
         }
     }
 
     if (writeback) {
-        *uop++ = new MicroAddXiUop(machInst, rn, post ? rn : INTREG_UREG0,
+        *uop++ = new MicroAddXiUop(machInst, rn, post ? rn : int_reg::Ureg0,
                                    post ? imm : 0);
     }
 
@@ -346,8 +364,8 @@
 }
 
 BigFpMemImmOp::BigFpMemImmOp(const char *mnem, ExtMachInst machInst,
-                             OpClass __opClass, bool load, IntRegIndex dest,
-                             IntRegIndex base, int64_t imm) :
+                             OpClass __opClass, bool load, RegIndex dest,
+                             RegIndex base, int64_t imm) :
     PredMacroOp(mnem, machInst, __opClass)
 {
     numMicroops = load ? 1 : 2;
@@ -367,8 +385,8 @@
 }
 
 BigFpMemPostOp::BigFpMemPostOp(const char *mnem, ExtMachInst machInst,
-                               OpClass __opClass, bool load, IntRegIndex dest,
-                               IntRegIndex base, int64_t imm) :
+                               OpClass __opClass, bool load, RegIndex dest,
+                               RegIndex base, int64_t imm) :
     PredMacroOp(mnem, machInst, __opClass)
 {
     numMicroops = load ? 2 : 3;
@@ -393,8 +411,8 @@
 }
 
 BigFpMemPreOp::BigFpMemPreOp(const char *mnem, ExtMachInst machInst,
-                             OpClass __opClass, bool load, IntRegIndex dest,
-                             IntRegIndex base, int64_t imm) :
+                             OpClass __opClass, bool load, RegIndex dest,
+                             RegIndex base, int64_t imm) :
     PredMacroOp(mnem, machInst, __opClass)
 {
     numMicroops = load ? 2 : 3;
@@ -419,8 +437,8 @@
 }
 
 BigFpMemRegOp::BigFpMemRegOp(const char *mnem, ExtMachInst machInst,
-                             OpClass __opClass, bool load, IntRegIndex dest,
-                             IntRegIndex base, IntRegIndex offset,
+                             OpClass __opClass, bool load, RegIndex dest,
+                             RegIndex base, RegIndex offset,
                              ArmExtendType type, int64_t imm) :
     PredMacroOp(mnem, machInst, __opClass)
 {
@@ -445,7 +463,7 @@
 }
 
 BigFpMemLitOp::BigFpMemLitOp(const char *mnem, ExtMachInst machInst,
-                             OpClass __opClass, IntRegIndex dest,
+                             OpClass __opClass, RegIndex dest,
                              int64_t imm) :
     PredMacroOp(mnem, machInst, __opClass)
 {
@@ -1125,8 +1143,8 @@
     PredMacroOp(mnem, machInst, __opClass)
 {
     RegIndex vx = NumVecV8ArchRegs;
-    RegIndex rnsp = (RegIndex) makeSP((IntRegIndex) rn);
-    bool baseIsSP = isSP((IntRegIndex) rnsp);
+    RegIndex rnsp = (RegIndex) makeSP((RegIndex) rn);
+    bool baseIsSP = isSP((RegIndex) rnsp);
 
     numMicroops = wb ? 1 : 0;
 
@@ -1162,7 +1180,7 @@
     // 64-bit general register OR as '11111' for an immediate value equal to
     // the total number of bytes transferred (i.e. 8, 16, 24, 32, 48 or 64)
     if (wb) {
-        if (rm != ((RegIndex) INTREG_X31)) {
+        if (rm != int_reg::X31) {
             microOps[uopIdx++] = new MicroAddXERegUop(machInst, rnsp, rnsp, rm,
                                                       UXTX, 0);
         } else {
@@ -1210,8 +1228,8 @@
     PredMacroOp(mnem, machInst, __opClass)
 {
     RegIndex vx = NumVecV8ArchRegs;
-    RegIndex rnsp = (RegIndex) makeSP((IntRegIndex) rn);
-    bool baseIsSP = isSP((IntRegIndex) rnsp);
+    RegIndex rnsp = (RegIndex) makeSP((RegIndex) rn);
+    bool baseIsSP = isSP((RegIndex) rnsp);
 
     numMicroops = wb ? 1 : 0;
 
@@ -1270,7 +1288,7 @@
     // 64-bit general register OR as '11111' for an immediate value equal to
     // the total number of bytes transferred (i.e. 8, 16, 24, 32, 48 or 64)
     if (wb) {
-        if (rm != ((RegIndex) INTREG_X31)) {
+        if (rm != int_reg::X31) {
             microOps[uopIdx++] = new MicroAddXERegUop(machInst, rnsp, rnsp, rm,
                                                       UXTX, 0);
         } else {
@@ -1299,8 +1317,8 @@
 
 {
     RegIndex vx = NumVecV8ArchRegs;
-    RegIndex rnsp = (RegIndex) makeSP((IntRegIndex) rn);
-    bool baseIsSP = isSP((IntRegIndex) rnsp);
+    RegIndex rnsp = (RegIndex) makeSP((RegIndex) rn);
+    bool baseIsSP = isSP((RegIndex) rnsp);
 
     numMicroops = wb ? 1 : 0;
 
@@ -1338,7 +1356,7 @@
     // 64-bit general register OR as '11111' for an immediate value equal to
     // the total number of bytes transferred (i.e. 8, 16, 24, 32, 48 or 64)
     if (wb) {
-        if (rm != ((RegIndex) INTREG_X31)) {
+        if (rm != int_reg::X31) {
             microOps[uopIdx++] = new MicroAddXERegUop(machInst, rnsp, rnsp, rm,
                                                       UXTX, 0);
         } else {
@@ -1372,8 +1390,8 @@
     wb(false), replicate(false)
 {
     RegIndex vx = NumVecV8ArchRegs;
-    RegIndex rnsp = (RegIndex) makeSP((IntRegIndex) rn);
-    bool baseIsSP = isSP((IntRegIndex) rnsp);
+    RegIndex rnsp = (RegIndex) makeSP((RegIndex) rn);
+    bool baseIsSP = isSP((RegIndex) rnsp);
 
     numMicroops = wb ? 1 : 0;
 
@@ -1417,7 +1435,7 @@
     // 64-bit general register OR as '11111' for an immediate value equal to
     // the total number of bytes transferred (i.e. 8, 16, 24, 32, 48 or 64)
     if (wb) {
-        if (rm != ((RegIndex) INTREG_X31)) {
+        if (rm != int_reg::X31) {
             microOps[uopIdx++] = new MicroAddXERegUop(machInst, rnsp, rnsp, rm,
                                                       UXTX, 0);
         } else {
@@ -1436,7 +1454,7 @@
 }
 
 MacroVFPMemOp::MacroVFPMemOp(const char *mnem, ExtMachInst machInst,
-                             OpClass __opClass, IntRegIndex rn,
+                             OpClass __opClass, RegIndex rn,
                              RegIndex vd, bool single, bool up,
                              bool writeback, bool load, uint32_t offset) :
     PredMacroOp(mnem, machInst, __opClass)
@@ -1560,7 +1578,7 @@
     printIntReg(ss, ura);
     ccprintf(ss, ", ");
     printIntReg(ss, urb);
-    printExtendOperand(false, ss, (IntRegIndex)urc, type, shiftAmt);
+    printExtendOperand(false, ss, (RegIndex)urc, type, shiftAmt);
     return ss.str();
 }
 
diff --git a/src/arch/arm/insts/macromem.hh b/src/arch/arm/insts/macromem.hh
index 90f12d2..429dced 100644
--- a/src/arch/arm/insts/macromem.hh
+++ b/src/arch/arm/insts/macromem.hh
@@ -287,10 +287,10 @@
 class MicroSetPCCPSR : public MicroOp
 {
     protected:
-    IntRegIndex ura, urb, urc;
+    RegIndex ura, urb, urc;
 
     MicroSetPCCPSR(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-                   IntRegIndex _ura, IntRegIndex _urb, IntRegIndex _urc)
+                   RegIndex _ura, RegIndex _urb, RegIndex _urc)
         : MicroOp(mnem, machInst, __opClass),
           ura(_ura), urb(_urb), urc(_urc)
     {
@@ -463,7 +463,7 @@
 {
   protected:
     MacroMemOp(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-               IntRegIndex rn, bool index, bool up, bool user,
+               RegIndex rn, bool index, bool up, bool user,
                bool writeback, bool load, uint32_t reglist);
 };
 
@@ -484,43 +484,43 @@
     PairMemOp(const char *mnem, ExtMachInst machInst, OpClass __opClass,
               uint32_t size, bool fp, bool load, bool noAlloc, bool signExt,
               bool exclusive, bool acrel, int64_t imm, AddrMode mode,
-              IntRegIndex rn, IntRegIndex rt, IntRegIndex rt2);
+              RegIndex rn, RegIndex rt, RegIndex rt2);
 };
 
 class BigFpMemImmOp : public PredMacroOp
 {
   protected:
     BigFpMemImmOp(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-                  bool load, IntRegIndex dest, IntRegIndex base, int64_t imm);
+                  bool load, RegIndex dest, RegIndex base, int64_t imm);
 };
 
 class BigFpMemPostOp : public PredMacroOp
 {
   protected:
     BigFpMemPostOp(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-                   bool load, IntRegIndex dest, IntRegIndex base, int64_t imm);
+                   bool load, RegIndex dest, RegIndex base, int64_t imm);
 };
 
 class BigFpMemPreOp : public PredMacroOp
 {
   protected:
     BigFpMemPreOp(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-                  bool load, IntRegIndex dest, IntRegIndex base, int64_t imm);
+                  bool load, RegIndex dest, RegIndex base, int64_t imm);
 };
 
 class BigFpMemRegOp : public PredMacroOp
 {
   protected:
     BigFpMemRegOp(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-                  bool load, IntRegIndex dest, IntRegIndex base,
-                  IntRegIndex offset, ArmExtendType type, int64_t imm);
+                  bool load, RegIndex dest, RegIndex base,
+                  RegIndex offset, ArmExtendType type, int64_t imm);
 };
 
 class BigFpMemLitOp : public PredMacroOp
 {
   protected:
     BigFpMemLitOp(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-                  IntRegIndex dest, int64_t imm);
+                  RegIndex dest, int64_t imm);
 };
 
 /**
@@ -570,7 +570,7 @@
 {
   protected:
     MacroVFPMemOp(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-                  IntRegIndex rn, RegIndex vd, bool single, bool up,
+                  RegIndex rn, RegIndex vd, bool single, bool up,
                   bool writeback, bool load, uint32_t offset);
 };
 
diff --git a/src/arch/arm/insts/mem.cc b/src/arch/arm/insts/mem.cc
index aed9933..590c30e 100644
--- a/src/arch/arm/insts/mem.cc
+++ b/src/arch/arm/insts/mem.cc
@@ -119,7 +119,7 @@
         printMnemonic(ss, "ib");
         break;
     }
-    printIntReg(ss, INTREG_SP);
+    printIntReg(ss, int_reg::Sp);
     if (wb) {
         ss << "!";
     }
diff --git a/src/arch/arm/insts/mem.hh b/src/arch/arm/insts/mem.hh
index d5ef424..42dfac3 100644
--- a/src/arch/arm/insts/mem.hh
+++ b/src/arch/arm/insts/mem.hh
@@ -98,20 +98,20 @@
         IncrementBefore
     };
   protected:
-    IntRegIndex base;
+    RegIndex base;
     AddrMode mode;
     bool wb;
-    IntRegIndex ura, urb, urc;
+    RegIndex ura, urb, urc;
     static const unsigned numMicroops = 3;
 
     StaticInstPtr *uops;
 
     RfeOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-          IntRegIndex _base, AddrMode _mode, bool _wb)
+          RegIndex _base, AddrMode _mode, bool _wb)
         : MightBeMicro(mnem, _machInst, __opClass),
           base(_base), mode(_mode), wb(_wb),
-          ura(INTREG_UREG0), urb(INTREG_UREG1),
-          urc(INTREG_UREG2),
+          ura(int_reg::Ureg0), urb(int_reg::Ureg1),
+          urc(int_reg::Ureg2),
           uops(NULL)
     {}
 
@@ -186,15 +186,15 @@
 
   protected:
 
-    IntRegIndex dest;
-    IntRegIndex base;
+    RegIndex dest;
+    RegIndex base;
     bool add;
     static const unsigned numMicroops = 3;
 
     StaticInstPtr *uops;
 
     Memory(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-           IntRegIndex _dest, IntRegIndex _base, bool _add)
+           RegIndex _dest, RegIndex _base, bool _add)
         : MightBeMicro(mnem, _machInst, __opClass),
           dest(_dest), base(_base), add(_add), uops(NULL)
     {}
@@ -232,7 +232,7 @@
     int32_t imm;
 
     MemoryImm(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-              IntRegIndex _dest, IntRegIndex _base, bool _add, int32_t _imm)
+              RegIndex _dest, RegIndex _base, bool _add, int32_t _imm)
         : Memory(mnem, _machInst, __opClass, _dest, _base, _add), imm(_imm)
     {}
 
@@ -249,10 +249,10 @@
 class MemoryExImm : public MemoryImm
 {
   protected:
-    IntRegIndex result;
+    RegIndex result;
 
     MemoryExImm(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _result, IntRegIndex _dest, IntRegIndex _base,
+                RegIndex _result, RegIndex _dest, RegIndex _base,
                 bool _add, int32_t _imm)
         : MemoryImm(mnem, _machInst, __opClass, _dest, _base, _add, _imm),
                     result(_result)
@@ -271,11 +271,11 @@
 class MemoryDImm : public MemoryImm
 {
   protected:
-    IntRegIndex dest2;
+    RegIndex dest2;
 
     MemoryDImm(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-              IntRegIndex _dest, IntRegIndex _dest2,
-              IntRegIndex _base, bool _add, int32_t _imm)
+              RegIndex _dest, RegIndex _dest2,
+              RegIndex _base, bool _add, int32_t _imm)
         : MemoryImm(mnem, _machInst, __opClass, _dest, _base, _add, _imm),
           dest2(_dest2)
     {}
@@ -292,11 +292,11 @@
 class MemoryExDImm : public MemoryDImm
 {
   protected:
-    IntRegIndex result;
+    RegIndex result;
 
     MemoryExDImm(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                 IntRegIndex _result, IntRegIndex _dest, IntRegIndex _dest2,
-                 IntRegIndex _base, bool _add, int32_t _imm)
+                 RegIndex _result, RegIndex _dest, RegIndex _dest2,
+                 RegIndex _base, bool _add, int32_t _imm)
         : MemoryDImm(mnem, _machInst, __opClass, _dest, _dest2,
                      _base, _add, _imm), result(_result)
     {}
@@ -316,12 +316,12 @@
   protected:
     int32_t shiftAmt;
     ArmShiftType shiftType;
-    IntRegIndex index;
+    RegIndex index;
 
     MemoryReg(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-              IntRegIndex _dest, IntRegIndex _base, bool _add,
+              RegIndex _dest, RegIndex _base, bool _add,
               int32_t _shiftAmt, ArmShiftType _shiftType,
-              IntRegIndex _index)
+              RegIndex _index)
         : Memory(mnem, _machInst, __opClass, _dest, _base, _add),
           shiftAmt(_shiftAmt), shiftType(_shiftType), index(_index)
     {}
@@ -332,13 +332,13 @@
 class MemoryDReg : public MemoryReg
 {
   protected:
-    IntRegIndex dest2;
+    RegIndex dest2;
 
     MemoryDReg(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-               IntRegIndex _dest, IntRegIndex _dest2,
-               IntRegIndex _base, bool _add,
+               RegIndex _dest, RegIndex _dest2,
+               RegIndex _base, bool _add,
                int32_t _shiftAmt, ArmShiftType _shiftType,
-               IntRegIndex _index)
+               RegIndex _index)
         : MemoryReg(mnem, _machInst, __opClass, _dest, _base, _add,
                     _shiftAmt, _shiftType, _index),
           dest2(_dest2)
@@ -358,38 +358,38 @@
 {
   protected:
     MemoryOffset(const char *mnem, ExtMachInst _machInst,
-                 OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
+                 OpClass __opClass, RegIndex _dest, RegIndex _base,
                  bool _add, int32_t _imm)
         : Base(mnem, _machInst, __opClass, _dest, _base, _add, _imm)
     {}
 
     MemoryOffset(const char *mnem, ExtMachInst _machInst,
-                 OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
+                 OpClass __opClass, RegIndex _dest, RegIndex _base,
                  bool _add, int32_t _shiftAmt, ArmShiftType _shiftType,
-                 IntRegIndex _index)
+                 RegIndex _index)
         : Base(mnem, _machInst, __opClass, _dest, _base, _add,
                 _shiftAmt, _shiftType, _index)
     {}
 
     MemoryOffset(const char *mnem, ExtMachInst _machInst,
-                 OpClass __opClass, IntRegIndex _dest, IntRegIndex _dest2,
-                 IntRegIndex _base, bool _add, int32_t _imm)
+                 OpClass __opClass, RegIndex _dest, RegIndex _dest2,
+                 RegIndex _base, bool _add, int32_t _imm)
         : Base(mnem, _machInst, __opClass, _dest, _dest2, _base, _add, _imm)
     {}
 
     MemoryOffset(const char *mnem, ExtMachInst _machInst,
-                 OpClass __opClass, IntRegIndex _result,
-                 IntRegIndex _dest, IntRegIndex _dest2,
-                 IntRegIndex _base, bool _add, int32_t _imm)
+                 OpClass __opClass, RegIndex _result,
+                 RegIndex _dest, RegIndex _dest2,
+                 RegIndex _base, bool _add, int32_t _imm)
         : Base(mnem, _machInst, __opClass, _result,
                 _dest, _dest2, _base, _add, _imm)
     {}
 
     MemoryOffset(const char *mnem, ExtMachInst _machInst,
-                 OpClass __opClass, IntRegIndex _dest, IntRegIndex _dest2,
-                 IntRegIndex _base, bool _add,
+                 OpClass __opClass, RegIndex _dest, RegIndex _dest2,
+                 RegIndex _base, bool _add,
                  int32_t _shiftAmt, ArmShiftType _shiftType,
-                 IntRegIndex _index)
+                 RegIndex _index)
         : Base(mnem, _machInst, __opClass, _dest, _dest2, _base, _add,
                 _shiftAmt, _shiftType, _index)
     {}
@@ -409,38 +409,38 @@
 {
   protected:
     MemoryPreIndex(const char *mnem, ExtMachInst _machInst,
-                   OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
+                   OpClass __opClass, RegIndex _dest, RegIndex _base,
                    bool _add, int32_t _imm)
         : Base(mnem, _machInst, __opClass, _dest, _base, _add, _imm)
     {}
 
     MemoryPreIndex(const char *mnem, ExtMachInst _machInst,
-                   OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
+                   OpClass __opClass, RegIndex _dest, RegIndex _base,
                    bool _add, int32_t _shiftAmt, ArmShiftType _shiftType,
-                   IntRegIndex _index)
+                   RegIndex _index)
         : Base(mnem, _machInst, __opClass, _dest, _base, _add,
                 _shiftAmt, _shiftType, _index)
     {}
 
     MemoryPreIndex(const char *mnem, ExtMachInst _machInst,
-                   OpClass __opClass, IntRegIndex _dest, IntRegIndex _dest2,
-                   IntRegIndex _base, bool _add, int32_t _imm)
+                   OpClass __opClass, RegIndex _dest, RegIndex _dest2,
+                   RegIndex _base, bool _add, int32_t _imm)
         : Base(mnem, _machInst, __opClass, _dest, _dest2, _base, _add, _imm)
     {}
 
     MemoryPreIndex(const char *mnem, ExtMachInst _machInst,
-                   OpClass __opClass, IntRegIndex _result,
-                   IntRegIndex _dest, IntRegIndex _dest2,
-                   IntRegIndex _base, bool _add, int32_t _imm)
+                   OpClass __opClass, RegIndex _result,
+                   RegIndex _dest, RegIndex _dest2,
+                   RegIndex _base, bool _add, int32_t _imm)
         : Base(mnem, _machInst, __opClass, _result,
                 _dest, _dest2, _base, _add, _imm)
     {}
 
     MemoryPreIndex(const char *mnem, ExtMachInst _machInst,
-                   OpClass __opClass, IntRegIndex _dest, IntRegIndex _dest2,
-                   IntRegIndex _base, bool _add,
+                   OpClass __opClass, RegIndex _dest, RegIndex _dest2,
+                   RegIndex _base, bool _add,
                    int32_t _shiftAmt, ArmShiftType _shiftType,
-                   IntRegIndex _index)
+                   RegIndex _index)
         : Base(mnem, _machInst, __opClass, _dest, _dest2, _base, _add,
                 _shiftAmt, _shiftType, _index)
     {}
@@ -460,38 +460,38 @@
 {
   protected:
     MemoryPostIndex(const char *mnem, ExtMachInst _machInst,
-                    OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
+                    OpClass __opClass, RegIndex _dest, RegIndex _base,
                     bool _add, int32_t _imm)
         : Base(mnem, _machInst, __opClass, _dest, _base, _add, _imm)
     {}
 
     MemoryPostIndex(const char *mnem, ExtMachInst _machInst,
-                    OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
+                    OpClass __opClass, RegIndex _dest, RegIndex _base,
                     bool _add, int32_t _shiftAmt, ArmShiftType _shiftType,
-                    IntRegIndex _index)
+                    RegIndex _index)
         : Base(mnem, _machInst, __opClass, _dest, _base, _add,
                 _shiftAmt, _shiftType, _index)
     {}
 
     MemoryPostIndex(const char *mnem, ExtMachInst _machInst,
-                    OpClass __opClass, IntRegIndex _dest, IntRegIndex _dest2,
-                    IntRegIndex _base, bool _add, int32_t _imm)
+                    OpClass __opClass, RegIndex _dest, RegIndex _dest2,
+                    RegIndex _base, bool _add, int32_t _imm)
         : Base(mnem, _machInst, __opClass, _dest, _dest2, _base, _add, _imm)
     {}
 
     MemoryPostIndex(const char *mnem, ExtMachInst _machInst,
-                    OpClass __opClass, IntRegIndex _result,
-                    IntRegIndex _dest, IntRegIndex _dest2,
-                    IntRegIndex _base, bool _add, int32_t _imm)
+                    OpClass __opClass, RegIndex _result,
+                    RegIndex _dest, RegIndex _dest2,
+                    RegIndex _base, bool _add, int32_t _imm)
         : Base(mnem, _machInst, __opClass, _result,
                 _dest, _dest2, _base, _add, _imm)
     {}
 
     MemoryPostIndex(const char *mnem, ExtMachInst _machInst,
-                    OpClass __opClass, IntRegIndex _dest, IntRegIndex _dest2,
-                    IntRegIndex _base, bool _add,
+                    OpClass __opClass, RegIndex _dest, RegIndex _dest2,
+                    RegIndex _base, bool _add,
                     int32_t _shiftAmt, ArmShiftType _shiftType,
-                    IntRegIndex _index)
+                    RegIndex _index)
         : Base(mnem, _machInst, __opClass, _dest, _dest2, _base, _add,
                 _shiftAmt, _shiftType, _index)
     {}
diff --git a/src/arch/arm/insts/mem64.hh b/src/arch/arm/insts/mem64.hh
index 53c0527..da91887 100644
--- a/src/arch/arm/insts/mem64.hh
+++ b/src/arch/arm/insts/mem64.hh
@@ -52,7 +52,7 @@
 class SysDC64 : public MiscRegOp64
 {
   protected:
-    IntRegIndex base;
+    RegIndex base;
     MiscRegIndex dest;
     uint64_t imm;
 
@@ -60,7 +60,7 @@
     mutable Addr faultAddr;
 
     SysDC64(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-            IntRegIndex _base, MiscRegIndex _dest, uint64_t _imm)
+            RegIndex _base, MiscRegIndex _dest, uint64_t _imm)
         : MiscRegOp64(mnem, _machInst, __opClass, false),
           base(_base), dest(_dest), imm(_imm), faultAddr(0)
     {}
@@ -116,8 +116,8 @@
 
   protected:
 
-    IntRegIndex dest;
-    IntRegIndex base;
+    RegIndex dest;
+    RegIndex base;
     /// True if the base register is SP (used for SP alignment checking).
     bool baseIsSP;
     static const unsigned numMicroops = 3;
@@ -125,7 +125,7 @@
     StaticInstPtr *uops;
 
     Memory64(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-             IntRegIndex _dest, IntRegIndex _base)
+             RegIndex _dest, RegIndex _base)
         : MightBeMicro64(mnem, _machInst, __opClass),
           dest(_dest), base(_base), uops(NULL), memAccessFlags(0)
     {
@@ -158,7 +158,7 @@
     int64_t imm;
 
     MemoryImm64(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _base, int64_t _imm)
+                RegIndex _dest, RegIndex _base, int64_t _imm)
         : Memory64(mnem, _machInst, __opClass, _dest, _base), imm(_imm)
     {}
 
@@ -169,10 +169,10 @@
 class MemoryDImm64 : public MemoryImm64
 {
   protected:
-    IntRegIndex dest2;
+    RegIndex dest2;
 
     MemoryDImm64(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _dest2, IntRegIndex _base,
+                RegIndex _dest, RegIndex _dest2, RegIndex _base,
                 int64_t _imm)
         : MemoryImm64(mnem, _machInst, __opClass, _dest, _base, _imm),
           dest2(_dest2)
@@ -185,11 +185,11 @@
 class MemoryDImmEx64 : public MemoryDImm64
 {
   protected:
-    IntRegIndex result;
+    RegIndex result;
 
     MemoryDImmEx64(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                 IntRegIndex _result, IntRegIndex _dest, IntRegIndex _dest2,
-                 IntRegIndex _base, int32_t _imm)
+                 RegIndex _result, RegIndex _dest, RegIndex _dest2,
+                 RegIndex _base, int32_t _imm)
         : MemoryDImm64(mnem, _machInst, __opClass, _dest, _dest2,
                      _base, _imm), result(_result)
     {}
@@ -202,7 +202,7 @@
 {
   protected:
     MemoryPreIndex64(const char *mnem, ExtMachInst _machInst,
-                     OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
+                     OpClass __opClass, RegIndex _dest, RegIndex _base,
                      int64_t _imm)
         : MemoryImm64(mnem, _machInst, __opClass, _dest, _base, _imm)
     {}
@@ -215,7 +215,7 @@
 {
   protected:
     MemoryPostIndex64(const char *mnem, ExtMachInst _machInst,
-                      OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
+                      OpClass __opClass, RegIndex _dest, RegIndex _base,
                       int64_t _imm)
         : MemoryImm64(mnem, _machInst, __opClass, _dest, _base, _imm)
     {}
@@ -227,13 +227,13 @@
 class MemoryReg64 : public Memory64
 {
   protected:
-    IntRegIndex offset;
+    RegIndex offset;
     ArmExtendType type;
     uint64_t shiftAmt;
 
     MemoryReg64(const char *mnem, ExtMachInst _machInst,
-                OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
-                IntRegIndex _offset, ArmExtendType _type,
+                OpClass __opClass, RegIndex _dest, RegIndex _base,
+                RegIndex _offset, ArmExtendType _type,
                 uint64_t _shiftAmt)
         : Memory64(mnem, _machInst, __opClass, _dest, _base),
           offset(_offset), type(_type), shiftAmt(_shiftAmt)
@@ -247,7 +247,7 @@
 {
   protected:
     MemoryRaw64(const char *mnem, ExtMachInst _machInst,
-                OpClass __opClass, IntRegIndex _dest, IntRegIndex _base)
+                OpClass __opClass, RegIndex _dest, RegIndex _base)
         : Memory64(mnem, _machInst, __opClass, _dest, _base)
     {}
 
@@ -258,11 +258,11 @@
 class MemoryEx64 : public Memory64
 {
   protected:
-    IntRegIndex result;
+    RegIndex result;
 
     MemoryEx64(const char *mnem, ExtMachInst _machInst,
-               OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
-               IntRegIndex _result)
+               OpClass __opClass, RegIndex _dest, RegIndex _base,
+               RegIndex _result)
         : Memory64(mnem, _machInst, __opClass, _dest, _base), result(_result)
     {}
 
@@ -276,8 +276,8 @@
     int64_t imm;
 
     MemoryLiteral64(const char *mnem, ExtMachInst _machInst,
-                    OpClass __opClass, IntRegIndex _dest, int64_t _imm)
-        : Memory64(mnem, _machInst, __opClass, _dest, INTREG_ZERO), imm(_imm)
+                    OpClass __opClass, RegIndex _dest, int64_t _imm)
+        : Memory64(mnem, _machInst, __opClass, _dest, int_reg::Zero), imm(_imm)
     {}
 
     std::string generateDisassembly(
@@ -287,17 +287,17 @@
 class MemoryAtomicPair64 : public Memory64
 {
   protected:
-    IntRegIndex dest2;
-    IntRegIndex result;
-    IntRegIndex result2;
+    RegIndex dest2;
+    RegIndex result;
+    RegIndex result2;
 
     MemoryAtomicPair64(const char *mnem, ExtMachInst _machInst,
-                       OpClass __opClass, IntRegIndex _dest, IntRegIndex _base,
-                       IntRegIndex _result)
+                       OpClass __opClass, RegIndex _dest, RegIndex _base,
+                       RegIndex _result)
         : Memory64(mnem, _machInst, __opClass, _dest, _base),
-          dest2((IntRegIndex)(_dest + (IntRegIndex)(1))),
+          dest2((RegIndex)(_dest + (RegIndex)(1))),
           result(_result),
-          result2((IntRegIndex)(_result + (IntRegIndex)(1)))
+          result2((RegIndex)(_result + (RegIndex)(1)))
     {}
 
     std::string generateDisassembly(
diff --git a/src/arch/arm/insts/misc.cc b/src/arch/arm/insts/misc.cc
index 4bb02c9..06a712e 100644
--- a/src/arch/arm/insts/misc.cc
+++ b/src/arch/arm/insts/misc.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2013, 2017-2018 ARM Limited
+ * Copyright (c) 2010, 2012-2013, 2017-2018, 2021 Arm Limited
  * Copyright (c) 2013 Advanced Micro Devices, Inc.
  * All rights reserved
  *
@@ -37,6 +37,7 @@
  */
 
 #include "arch/arm/insts/misc.hh"
+#include "arch/arm/tlbi_op.hh"
 
 #include "cpu/reg_class.hh"
 
@@ -338,7 +339,7 @@
     printMnemonic(ss);
     printIntReg(ss, dest);
     ccprintf(ss, ", #%d, ", imm);
-    printShiftOperand(ss, op1, true, shiftAmt, INTREG_ZERO, shiftType);
+    printShiftOperand(ss, op1, true, shiftAmt, int_reg::Zero, shiftType);
     printIntReg(ss, op1);
     return ss.str();
 }
@@ -362,14 +363,7 @@
 Fault
 McrMrcMiscInst::execute(ExecContext *xc, Trace::InstRecord *traceData) const
 {
-    bool hypTrap = mcrMrc15TrapToHyp(miscReg, xc->tcBase(), iss);
-
-    if (hypTrap) {
-        return std::make_shared<HypervisorTrap>(machInst, iss,
-                                                EC_TRAPPED_CP15_MCR_MRC);
-    } else {
-        return NoFault;
-    }
+    return mcrMrc15Trap(miscReg, machInst, xc->tcBase(), iss);
 }
 
 std::string
@@ -388,11 +382,9 @@
 Fault
 McrMrcImplDefined::execute(ExecContext *xc, Trace::InstRecord *traceData) const
 {
-    bool hypTrap = mcrMrc15TrapToHyp(miscReg, xc->tcBase(), iss);
-
-    if (hypTrap) {
-        return std::make_shared<HypervisorTrap>(machInst, iss,
-                                                EC_TRAPPED_CP15_MCR_MRC);
+    Fault fault = mcrMrc15Trap(miscReg, machInst, xc->tcBase(), iss);
+    if (fault != NoFault) {
+        return fault;
     } else {
         return std::make_shared<UndefinedInstruction>(machInst, false,
                                                       mnemonic);
@@ -406,4 +398,287 @@
     return csprintf("%-10s (implementation defined)", mnemonic);
 }
 
+void
+TlbiOp::performTlbi(ExecContext *xc, MiscRegIndex dest_idx, RegVal value) const
+{
+    ThreadContext* tc = xc->tcBase();
+    auto isa = static_cast<ArmISA::ISA *>(tc->getIsaPtr());
+    auto release = isa->getRelease();
+
+    switch (dest_idx) {
+      case MISCREG_TLBIALL: // TLBI all entries, EL0&1,
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIALL tlbiOp(EL1, secure);
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate All, Inner Shareable
+      case MISCREG_TLBIALLIS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIALL tlbiOp(EL1, secure);
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // Instruction TLB Invalidate All
+      case MISCREG_ITLBIALL:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            ITLBIALL tlbiOp(EL1, secure);
+            tlbiOp(tc);
+            return;
+        }
+      // Data TLB Invalidate All
+      case MISCREG_DTLBIALL:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            DTLBIALL tlbiOp(EL1, secure);
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate by VA
+      // mcr tlbimval(is) is invalidating all matching entries
+      // regardless of the level of lookup, since in gem5 we cache
+      // in the tlb the last level of lookup only.
+      case MISCREG_TLBIMVA:
+      case MISCREG_TLBIMVAL:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVA tlbiOp(EL1,
+                           secure,
+                           mbits(value, 31, 12),
+                           bits(value, 7,0));
+
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate by VA, Inner Shareable
+      case MISCREG_TLBIMVAIS:
+      case MISCREG_TLBIMVALIS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVA tlbiOp(EL1,
+                           secure,
+                           mbits(value, 31, 12),
+                           bits(value, 7,0));
+
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // TLB Invalidate by ASID match
+      case MISCREG_TLBIASID:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIASID tlbiOp(EL1,
+                            secure,
+                            bits(value, 7,0));
+
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate by ASID match, Inner Shareable
+      case MISCREG_TLBIASIDIS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIASID tlbiOp(EL1,
+                            secure,
+                            bits(value, 7,0));
+
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // mcr tlbimvaal(is) is invalidating all matching entries
+      // regardless of the level of lookup, since in gem5 we cache
+      // in the tlb the last level of lookup only.
+      // TLB Invalidate by VA, All ASID
+      case MISCREG_TLBIMVAA:
+      case MISCREG_TLBIMVAAL:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVAA tlbiOp(EL1, secure,
+                            mbits(value, 31,12));
+
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate by VA, All ASID, Inner Shareable
+      case MISCREG_TLBIMVAAIS:
+      case MISCREG_TLBIMVAALIS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVAA tlbiOp(EL1, secure,
+                            mbits(value, 31,12));
+
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // mcr tlbimvalh(is) is invalidating all matching entries
+      // regardless of the level of lookup, since in gem5 we cache
+      // in the tlb the last level of lookup only.
+      // TLB Invalidate by VA, Hyp mode
+      case MISCREG_TLBIMVAH:
+      case MISCREG_TLBIMVALH:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVAA tlbiOp(EL2, secure,
+                            mbits(value, 31,12));
+
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate by VA, Hyp mode, Inner Shareable
+      case MISCREG_TLBIMVAHIS:
+      case MISCREG_TLBIMVALHIS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVAA tlbiOp(EL2, secure,
+                            mbits(value, 31,12));
+
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // mcr tlbiipas2l(is) is invalidating all matching entries
+      // regardless of the level of lookup, since in gem5 we cache
+      // in the tlb the last level of lookup only.
+      // TLB Invalidate by Intermediate Physical Address, Stage 2
+      case MISCREG_TLBIIPAS2:
+      case MISCREG_TLBIIPAS2L:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIIPA tlbiOp(EL1,
+                           secure,
+                           static_cast<Addr>(bits(value, 35, 0)) << 12);
+
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate by Intermediate Physical Address, Stage 2,
+      // Inner Shareable
+      case MISCREG_TLBIIPAS2IS:
+      case MISCREG_TLBIIPAS2LIS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIIPA tlbiOp(EL1,
+                           secure,
+                           static_cast<Addr>(bits(value, 35, 0)) << 12);
+
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // Instruction TLB Invalidate by VA
+      case MISCREG_ITLBIMVA:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            ITLBIMVA tlbiOp(EL1,
+                            secure,
+                            mbits(value, 31, 12),
+                            bits(value, 7,0));
+
+            tlbiOp(tc);
+            return;
+        }
+      // Data TLB Invalidate by VA
+      case MISCREG_DTLBIMVA:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            DTLBIMVA tlbiOp(EL1,
+                            secure,
+                            mbits(value, 31, 12),
+                            bits(value, 7,0));
+
+            tlbiOp(tc);
+            return;
+        }
+      // Instruction TLB Invalidate by ASID match
+      case MISCREG_ITLBIASID:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            ITLBIASID tlbiOp(EL1,
+                             secure,
+                             bits(value, 7,0));
+
+            tlbiOp(tc);
+            return;
+        }
+      // Data TLB Invalidate by ASID match
+      case MISCREG_DTLBIASID:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            DTLBIASID tlbiOp(EL1,
+                             secure,
+                             bits(value, 7,0));
+
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate All, Non-Secure Non-Hyp
+      case MISCREG_TLBIALLNSNH:
+        {
+            TLBIALLN tlbiOp(EL1);
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate All, Non-Secure Non-Hyp, Inner Shareable
+      case MISCREG_TLBIALLNSNHIS:
+        {
+            TLBIALLN tlbiOp(EL1);
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // TLB Invalidate All, Hyp mode
+      case MISCREG_TLBIALLH:
+        {
+            TLBIALLN tlbiOp(EL2);
+            tlbiOp(tc);
+            return;
+        }
+      // TLB Invalidate All, Hyp mode, Inner Shareable
+      case MISCREG_TLBIALLHIS:
+        {
+            TLBIALLN tlbiOp(EL2);
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      default:
+        panic("Unrecognized TLBIOp\n");
+    }
+}
+
 } // namespace gem5
diff --git a/src/arch/arm/insts/misc.hh b/src/arch/arm/insts/misc.hh
index 263434a..d9f24b9 100644
--- a/src/arch/arm/insts/misc.hh
+++ b/src/arch/arm/insts/misc.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2013, 2017-2018 ARM Limited
+ * Copyright (c) 2010, 2012-2013, 2017-2018, 2021 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -46,10 +46,10 @@
 class MrsOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
 
     MrsOp(const char *mnem, ArmISA::ExtMachInst _machInst, OpClass __opClass,
-            ArmISA::IntRegIndex _dest) :
+            RegIndex _dest) :
         ArmISA::PredOp(mnem, _machInst, __opClass), dest(_dest)
     {}
 
@@ -87,10 +87,10 @@
 class MsrRegOp : public MsrBase
 {
   protected:
-    ArmISA::IntRegIndex op1;
+    RegIndex op1;
 
     MsrRegOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-             OpClass __opClass, ArmISA::IntRegIndex _op1, uint8_t _byteMask) :
+             OpClass __opClass, RegIndex _op1, uint8_t _byteMask) :
         MsrBase(mnem, _machInst, __opClass, _byteMask), op1(_op1)
     {}
 
@@ -102,13 +102,13 @@
 {
   protected:
     ArmISA::MiscRegIndex op1;
-    ArmISA::IntRegIndex dest;
-    ArmISA::IntRegIndex dest2;
+    RegIndex dest;
+    RegIndex dest2;
     uint32_t imm;
 
     MrrcOp(const char *mnem, ArmISA::ExtMachInst _machInst, OpClass __opClass,
-           ArmISA::MiscRegIndex _op1, ArmISA::IntRegIndex _dest,
-           ArmISA::IntRegIndex _dest2, uint32_t _imm) :
+           ArmISA::MiscRegIndex _op1, RegIndex _dest, RegIndex _dest2,
+           uint32_t _imm) :
         ArmISA::PredOp(mnem, _machInst, __opClass), op1(_op1), dest(_dest),
         dest2(_dest2), imm(_imm)
     {}
@@ -120,13 +120,13 @@
 class McrrOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex op1;
-    ArmISA::IntRegIndex op2;
+    RegIndex op1;
+    RegIndex op2;
     ArmISA::MiscRegIndex dest;
     uint32_t    imm;
 
     McrrOp(const char *mnem, ArmISA::ExtMachInst _machInst, OpClass __opClass,
-           ArmISA::IntRegIndex _op1, ArmISA::IntRegIndex _op2,
+           RegIndex _op1, RegIndex _op2,
            ArmISA::MiscRegIndex _dest, uint32_t _imm) :
         ArmISA::PredOp(mnem, _machInst, __opClass), op1(_op1), op2(_op2),
         dest(_dest), imm(_imm)
@@ -153,11 +153,11 @@
 class RegImmOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
     uint64_t imm;
 
     RegImmOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-             OpClass __opClass, ArmISA::IntRegIndex _dest, uint64_t _imm) :
+             OpClass __opClass, RegIndex _dest, uint64_t _imm) :
         ArmISA::PredOp(mnem, _machInst, __opClass), dest(_dest), imm(_imm)
     {}
 
@@ -168,12 +168,12 @@
 class RegRegOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
-    ArmISA::IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
 
     RegRegOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-             OpClass __opClass, ArmISA::IntRegIndex _dest,
-             ArmISA::IntRegIndex _op1) :
+             OpClass __opClass, RegIndex _dest,
+             RegIndex _op1) :
         ArmISA::PredOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1)
     {}
 
@@ -184,10 +184,10 @@
 class RegOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
 
     RegOp(const char *mnem, ArmISA::ExtMachInst _machInst, OpClass __opClass,
-             ArmISA::IntRegIndex _dest) :
+             RegIndex _dest) :
         ArmISA::PredOp(mnem, _machInst, __opClass), dest(_dest)
     {}
 
@@ -198,13 +198,13 @@
 class RegImmRegOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
     uint64_t imm;
-    ArmISA::IntRegIndex op1;
+    RegIndex op1;
 
     RegImmRegOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-                OpClass __opClass, ArmISA::IntRegIndex _dest, uint64_t _imm,
-                ArmISA::IntRegIndex _op1) :
+                OpClass __opClass, RegIndex _dest, uint64_t _imm,
+                RegIndex _op1) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), imm(_imm), op1(_op1)
     {}
@@ -216,14 +216,14 @@
 class RegRegRegImmOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
-    ArmISA::IntRegIndex op1;
-    ArmISA::IntRegIndex op2;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
     uint64_t imm;
 
     RegRegRegImmOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-                   OpClass __opClass, ArmISA::IntRegIndex _dest,
-                   ArmISA::IntRegIndex _op1, ArmISA::IntRegIndex _op2,
+                   OpClass __opClass, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2,
                    uint64_t _imm) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), imm(_imm)
@@ -236,15 +236,15 @@
 class RegRegRegRegOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
-    ArmISA::IntRegIndex op1;
-    ArmISA::IntRegIndex op2;
-    ArmISA::IntRegIndex op3;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
+    RegIndex op3;
 
     RegRegRegRegOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-                   OpClass __opClass, ArmISA::IntRegIndex _dest,
-                   ArmISA::IntRegIndex _op1, ArmISA::IntRegIndex _op2,
-                   ArmISA::IntRegIndex _op3) :
+                   OpClass __opClass, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2,
+                   RegIndex _op3) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), op3(_op3)
     {}
@@ -256,13 +256,13 @@
 class RegRegRegOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
-    ArmISA::IntRegIndex op1;
-    ArmISA::IntRegIndex op2;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
 
     RegRegRegOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-                OpClass __opClass, ArmISA::IntRegIndex _dest,
-                ArmISA::IntRegIndex _op1, ArmISA::IntRegIndex _op2) :
+                OpClass __opClass, RegIndex _dest,
+                RegIndex _op1, RegIndex _op2) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2)
     {}
@@ -274,13 +274,13 @@
 class RegRegImmOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
-    ArmISA::IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
     uint64_t imm;
 
     RegRegImmOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-                OpClass __opClass, ArmISA::IntRegIndex _dest,
-                ArmISA::IntRegIndex _op1, uint64_t _imm) :
+                OpClass __opClass, RegIndex _dest,
+                RegIndex _op1, uint64_t _imm) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm(_imm)
     {}
@@ -293,12 +293,12 @@
 {
   protected:
     ArmISA::MiscRegIndex dest;
-    ArmISA::IntRegIndex op1;
+    RegIndex op1;
     uint64_t imm;
 
     MiscRegRegImmOp(const char *mnem, ArmISA::ExtMachInst _machInst,
                     OpClass __opClass, ArmISA::MiscRegIndex _dest,
-                    ArmISA::IntRegIndex _op1, uint64_t _imm) :
+                    RegIndex _op1, uint64_t _imm) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm(_imm)
     {}
@@ -310,12 +310,12 @@
 class RegMiscRegImmOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
     ArmISA::MiscRegIndex op1;
     uint64_t imm;
 
     RegMiscRegImmOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-                    OpClass __opClass, ArmISA::IntRegIndex _dest,
+                    OpClass __opClass, RegIndex _dest,
                     ArmISA::MiscRegIndex _op1, uint64_t _imm) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm(_imm)
@@ -328,12 +328,12 @@
 class RegImmImmOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
     uint64_t imm1;
     uint64_t imm2;
 
     RegImmImmOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-                OpClass __opClass, ArmISA::IntRegIndex _dest,
+                OpClass __opClass, RegIndex _dest,
                 uint64_t _imm1, uint64_t _imm2) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), imm1(_imm1), imm2(_imm2)
@@ -346,14 +346,14 @@
 class RegRegImmImmOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
-    ArmISA::IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
     uint64_t imm1;
     uint64_t imm2;
 
     RegRegImmImmOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-                   OpClass __opClass, ArmISA::IntRegIndex _dest,
-                   ArmISA::IntRegIndex _op1, uint64_t _imm1, uint64_t _imm2) :
+                   OpClass __opClass, RegIndex _dest,
+                   RegIndex _op1, uint64_t _imm1, uint64_t _imm2) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm1(_imm1), imm2(_imm2)
     {}
@@ -365,15 +365,15 @@
 class RegImmRegShiftOp : public ArmISA::PredOp
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
     uint64_t imm;
-    ArmISA::IntRegIndex op1;
+    RegIndex op1;
     int32_t shiftAmt;
     ArmISA::ArmShiftType shiftType;
 
     RegImmRegShiftOp(const char *mnem, ArmISA::ExtMachInst _machInst,
-                     OpClass __opClass, ArmISA::IntRegIndex _dest,
-                     uint64_t _imm, ArmISA::IntRegIndex _op1,
+                     OpClass __opClass, RegIndex _dest,
+                     uint64_t _imm, RegIndex _op1,
                      int32_t _shiftAmt, ArmISA::ArmShiftType _shiftType) :
         ArmISA::PredOp(mnem, _machInst, __opClass),
         dest(_dest), imm(_imm), op1(_op1),
@@ -439,6 +439,19 @@
 
 };
 
+class TlbiOp : public MiscRegRegImmOp
+{
+  protected:
+    TlbiOp(const char *mnem, ArmISA::ExtMachInst _machInst,
+           OpClass __opClass, ArmISA::MiscRegIndex _dest,
+           RegIndex _op1, uint64_t _imm) :
+        MiscRegRegImmOp(mnem, _machInst, __opClass, _dest, _op1, _imm)
+    {}
+
+    void performTlbi(ExecContext *xc,
+                     ArmISA::MiscRegIndex dest_idx, RegVal value) const;
+};
+
 } // namespace gem5
 
 #endif
diff --git a/src/arch/arm/insts/misc64.cc b/src/arch/arm/insts/misc64.cc
index a0ef0f8..5bbb739 100644
--- a/src/arch/arm/insts/misc64.cc
+++ b/src/arch/arm/insts/misc64.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2013,2017-2020 ARM Limited
+ * Copyright (c) 2011-2013,2017-2021 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -38,6 +38,8 @@
 #include "arch/arm/insts/misc64.hh"
 #include "arch/arm/isa.hh"
 
+#include "arch/arm/tlbi_op.hh"
+
 namespace gem5
 {
 
@@ -888,4 +890,358 @@
     return ss.str();
 }
 
+void
+TlbiOp64::performTlbi(ExecContext *xc, MiscRegIndex dest_idx, RegVal value) const
+{
+    ThreadContext* tc = xc->tcBase();
+    auto isa = static_cast<ArmISA::ISA *>(tc->getIsaPtr());
+    auto release = isa->getRelease();
+
+    bool asid_16bits = ArmSystem::haveLargeAsid64(tc);
+
+    switch (dest_idx) {
+      // AArch64 TLB Invalidate All, EL3
+      case MISCREG_TLBI_ALLE3:
+        {
+            TLBIALLEL tlbiOp(EL3, true);
+            tlbiOp(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate All, EL3, Inner Shareable
+      case MISCREG_TLBI_ALLE3IS:
+        {
+            TLBIALLEL tlbiOp(EL3, true);
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate All, EL2
+      case MISCREG_TLBI_ALLE2:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIALLEL tlbiOp(EL2, secure);
+            tlbiOp(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate All, EL2, Inner Shareable
+      case MISCREG_TLBI_ALLE2IS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIALLEL tlbiOp(EL2, secure);
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate All, EL1
+      case MISCREG_TLBI_ALLE1:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIALLEL tlbiOp(EL1, secure);
+            tlbiOp(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate All, EL1, Inner Shareable
+      case MISCREG_TLBI_ALLE1IS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIALLEL tlbiOp(EL1, secure);
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      case MISCREG_TLBI_VMALLS12E1:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIVMALL tlbiOp(EL1, secure, true);
+            tlbiOp(tc);
+            return;
+        }
+      case MISCREG_TLBI_VMALLE1:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            ExceptionLevel target_el = EL1;
+            if (EL2Enabled(tc)) {
+                HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+                if (hcr.tge && hcr.e2h) {
+                    target_el = EL2;
+                }
+            }
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIVMALL tlbiOp(target_el, secure, false);
+            tlbiOp(tc);
+            return;
+        }
+      case MISCREG_TLBI_VMALLS12E1IS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIVMALL tlbiOp(EL1, secure, true);
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      case MISCREG_TLBI_VMALLE1IS:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            ExceptionLevel target_el = EL1;
+            if (EL2Enabled(tc)) {
+                HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+                if (hcr.tge && hcr.e2h) {
+                    target_el = EL2;
+                }
+            }
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIVMALL tlbiOp(target_el, secure, false);
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // VAEx(IS) and VALEx(IS) are the same because TLBs
+      // only store entries
+      // from the last level of translation table walks
+      // AArch64 TLB Invalidate by VA, EL3
+      case MISCREG_TLBI_VAE3_Xt:
+      case MISCREG_TLBI_VALE3_Xt:
+        {
+
+            TLBIMVAA tlbiOp(EL3, true,
+                            static_cast<Addr>(bits(value, 43, 0)) << 12);
+            tlbiOp(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate by VA, EL3, Inner Shareable
+      case MISCREG_TLBI_VAE3IS_Xt:
+      case MISCREG_TLBI_VALE3IS_Xt:
+        {
+            TLBIMVAA tlbiOp(EL3, true,
+                            static_cast<Addr>(bits(value, 43, 0)) << 12);
+
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate by VA, EL2
+      case MISCREG_TLBI_VAE2_Xt:
+      case MISCREG_TLBI_VALE2_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+            HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+
+            if (hcr.e2h) {
+                // The asid will only be used when e2h == 1
+                auto asid = asid_16bits ? bits(value, 63, 48) :
+                                          bits(value, 55, 48);
+
+                TLBIMVA tlbiOp(EL2, secure,
+                               static_cast<Addr>(bits(value, 43, 0)) << 12,
+                               asid);
+                tlbiOp(tc);
+            } else {
+                TLBIMVAA tlbiOp(EL2, secure,
+                                static_cast<Addr>(bits(value, 43, 0)) << 12);
+                tlbiOp(tc);
+            }
+            return;
+        }
+      // AArch64 TLB Invalidate by VA, EL2, Inner Shareable
+      case MISCREG_TLBI_VAE2IS_Xt:
+      case MISCREG_TLBI_VALE2IS_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+            HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+
+            if (hcr.e2h) {
+                // The asid will only be used when e2h == 1
+                auto asid = asid_16bits ? bits(value, 63, 48) :
+                                          bits(value, 55, 48);
+
+                TLBIMVA tlbiOp(EL2, secure,
+                               static_cast<Addr>(bits(value, 43, 0)) << 12,
+                               asid);
+                tlbiOp.broadcast(tc);
+            } else {
+                TLBIMVAA tlbiOp(EL2, secure,
+                                static_cast<Addr>(bits(value, 43, 0)) << 12);
+                tlbiOp.broadcast(tc);
+            }
+            return;
+        }
+      // AArch64 TLB Invalidate by VA, EL1
+      case MISCREG_TLBI_VAE1_Xt:
+      case MISCREG_TLBI_VALE1_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+            auto asid = asid_16bits ? bits(value, 63, 48) :
+                                      bits(value, 55, 48);
+
+            ExceptionLevel target_el = EL1;
+            if (EL2Enabled(tc)) {
+                HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+                if (hcr.tge && hcr.e2h) {
+                    target_el = EL2;
+                }
+            }
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVA tlbiOp(target_el, secure,
+                           static_cast<Addr>(bits(value, 43, 0)) << 12,
+                           asid);
+
+            tlbiOp(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate by VA, EL1, Inner Shareable
+      case MISCREG_TLBI_VAE1IS_Xt:
+      case MISCREG_TLBI_VALE1IS_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+            auto asid = asid_16bits ? bits(value, 63, 48) :
+                                      bits(value, 55, 48);
+
+            ExceptionLevel target_el = EL1;
+            if (EL2Enabled(tc)) {
+                HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+                if (hcr.tge && hcr.e2h) {
+                    target_el = EL2;
+                }
+            }
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVA tlbiOp(target_el, secure,
+                            static_cast<Addr>(bits(value, 43, 0)) << 12,
+                            asid);
+
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate by ASID, EL1
+      case MISCREG_TLBI_ASIDE1_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+            auto asid = asid_16bits ? bits(value, 63, 48) :
+                                      bits(value, 55, 48);
+
+            ExceptionLevel target_el = EL1;
+            if (EL2Enabled(tc)) {
+                HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+                if (hcr.tge && hcr.e2h) {
+                    target_el = EL2;
+                }
+            }
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIASID tlbiOp(target_el, secure, asid);
+            tlbiOp(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate by ASID, EL1, Inner Shareable
+      case MISCREG_TLBI_ASIDE1IS_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+            auto asid = asid_16bits ? bits(value, 63, 48) :
+                                      bits(value, 55, 48);
+
+            ExceptionLevel target_el = EL1;
+            if (EL2Enabled(tc)) {
+                HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+                if (hcr.tge && hcr.e2h) {
+                    target_el = EL2;
+                }
+            }
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIASID tlbiOp(target_el, secure, asid);
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // VAAE1(IS) and VAALE1(IS) are the same because TLBs only store
+      // entries from the last level of translation table walks
+      // AArch64 TLB Invalidate by VA, All ASID, EL1
+      case MISCREG_TLBI_VAAE1_Xt:
+      case MISCREG_TLBI_VAALE1_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            ExceptionLevel target_el = EL1;
+            if (EL2Enabled(tc)) {
+                HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+                if (hcr.tge && hcr.e2h) {
+                    target_el = EL2;
+                }
+            }
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVAA tlbiOp(target_el, secure,
+                static_cast<Addr>(bits(value, 43, 0)) << 12);
+
+            tlbiOp(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate by VA, All ASID, EL1, Inner Shareable
+      case MISCREG_TLBI_VAAE1IS_Xt:
+      case MISCREG_TLBI_VAALE1IS_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            ExceptionLevel target_el = EL1;
+            if (EL2Enabled(tc)) {
+                HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
+                if (hcr.tge && hcr.e2h) {
+                    target_el = EL2;
+                }
+            }
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIMVAA tlbiOp(target_el, secure,
+                static_cast<Addr>(bits(value, 43, 0)) << 12);
+
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate by Intermediate Physical Address,
+      // Stage 2, EL1
+      case MISCREG_TLBI_IPAS2E1_Xt:
+      case MISCREG_TLBI_IPAS2LE1_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIIPA tlbiOp(EL1, secure,
+                           static_cast<Addr>(bits(value, 35, 0)) << 12);
+
+            tlbiOp(tc);
+            return;
+        }
+      // AArch64 TLB Invalidate by Intermediate Physical Address,
+      // Stage 2, EL1, Inner Shareable
+      case MISCREG_TLBI_IPAS2E1IS_Xt:
+      case MISCREG_TLBI_IPAS2LE1IS_Xt:
+        {
+            SCR scr = tc->readMiscReg(MISCREG_SCR);
+
+            bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
+            TLBIIPA tlbiOp(EL1, secure,
+                           static_cast<Addr>(bits(value, 35, 0)) << 12);
+
+            tlbiOp.broadcast(tc);
+            return;
+        }
+      default:
+        panic("Invalid TLBI\n");
+    }
+}
+
 } // namespace gem5
diff --git a/src/arch/arm/insts/misc64.hh b/src/arch/arm/insts/misc64.hh
index e38f43a..5e166f5 100644
--- a/src/arch/arm/insts/misc64.hh
+++ b/src/arch/arm/insts/misc64.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2013,2017-2019 ARM Limited
+ * Copyright (c) 2011-2013,2017-2019, 2021 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -60,14 +60,14 @@
 class RegRegImmImmOp64 : public ArmISA::ArmStaticInst
 {
   protected:
-    ArmISA::IntRegIndex dest;
-    ArmISA::IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
     uint64_t imm1;
     uint64_t imm2;
 
     RegRegImmImmOp64(const char *mnem, ArmISA::ExtMachInst _machInst,
-                     OpClass __opClass, ArmISA::IntRegIndex _dest,
-                     ArmISA::IntRegIndex _op1, uint64_t _imm1,
+                     OpClass __opClass, RegIndex _dest,
+                     RegIndex _op1, uint64_t _imm1,
                      int64_t _imm2) :
         ArmISA::ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm1(_imm1), imm2(_imm2)
@@ -80,14 +80,14 @@
 class RegRegRegImmOp64 : public ArmISA::ArmStaticInst
 {
   protected:
-    ArmISA::IntRegIndex dest;
-    ArmISA::IntRegIndex op1;
-    ArmISA::IntRegIndex op2;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
     uint64_t imm;
 
     RegRegRegImmOp64(const char *mnem, ArmISA::ExtMachInst _machInst,
-                     OpClass __opClass, ArmISA::IntRegIndex _dest,
-                     ArmISA::IntRegIndex _op1, ArmISA::IntRegIndex _op2,
+                     OpClass __opClass, RegIndex _dest,
+                     RegIndex _op1, RegIndex _op2,
                      uint64_t _imm) :
         ArmISA::ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), imm(_imm)
@@ -177,12 +177,12 @@
 {
   protected:
     ArmISA::MiscRegIndex dest;
-    ArmISA::IntRegIndex op1;
+    RegIndex op1;
     uint32_t imm;
 
     MiscRegRegImmOp64(const char *mnem, ArmISA::ExtMachInst _machInst,
                       OpClass __opClass, ArmISA::MiscRegIndex _dest,
-                      ArmISA::IntRegIndex _op1, uint32_t _imm) :
+                      RegIndex _op1, uint32_t _imm) :
         MiscRegOp64(mnem, _machInst, __opClass, false),
         dest(_dest), op1(_op1), imm(_imm)
     {}
@@ -194,12 +194,12 @@
 class RegMiscRegImmOp64 : public MiscRegOp64
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
     ArmISA::MiscRegIndex op1;
     uint32_t imm;
 
     RegMiscRegImmOp64(const char *mnem, ArmISA::ExtMachInst _machInst,
-                      OpClass __opClass, ArmISA::IntRegIndex _dest,
+                      OpClass __opClass, RegIndex _dest,
                       ArmISA::MiscRegIndex _op1, uint32_t _imm) :
         MiscRegOp64(mnem, _machInst, __opClass, true),
         dest(_dest), op1(_op1), imm(_imm)
@@ -240,10 +240,10 @@
 class RegNone : public ArmISA::ArmStaticInst
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
 
     RegNone(const char *mnem, ArmISA::ExtMachInst _machInst,
-            OpClass __opClass, ArmISA::IntRegIndex _dest) :
+            OpClass __opClass, RegIndex _dest) :
         ArmISA::ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest)
     {}
@@ -252,6 +252,19 @@
         Addr pc, const loader::SymbolTable *symtab) const;
 };
 
+class TlbiOp64 : public MiscRegRegImmOp64
+{
+  protected:
+    TlbiOp64(const char *mnem, ArmISA::ExtMachInst _machInst,
+             OpClass __opClass, ArmISA::MiscRegIndex _dest,
+             RegIndex _op1, uint32_t _imm) :
+        MiscRegRegImmOp64(mnem, _machInst, __opClass, _dest, _op1, _imm)
+    {}
+
+    void performTlbi(ExecContext *xc,
+                     ArmISA::MiscRegIndex idx, RegVal value) const;
+};
+
 } // namespace gem5
 
 #endif
diff --git a/src/arch/arm/insts/mult.hh b/src/arch/arm/insts/mult.hh
index 5939b56..11c2d96 100644
--- a/src/arch/arm/insts/mult.hh
+++ b/src/arch/arm/insts/mult.hh
@@ -53,10 +53,10 @@
 class Mult3 : public PredOp
 {
   protected:
-    IntRegIndex reg0, reg1, reg2;
+    RegIndex reg0, reg1, reg2;
 
     Mult3(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-          IntRegIndex _reg0, IntRegIndex _reg1, IntRegIndex _reg2) :
+          RegIndex _reg0, RegIndex _reg1, RegIndex _reg2) :
         PredOp(mnem, _machInst, __opClass),
         reg0(_reg0), reg1(_reg1), reg2(_reg2)
     {}
@@ -68,11 +68,11 @@
 class Mult4 : public Mult3
 {
   protected:
-    IntRegIndex reg3;
+    RegIndex reg3;
 
     Mult4(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-          IntRegIndex _reg0, IntRegIndex _reg1,
-          IntRegIndex _reg2, IntRegIndex _reg3) :
+          RegIndex _reg0, RegIndex _reg1,
+          RegIndex _reg2, RegIndex _reg3) :
         Mult3(mnem, _machInst, __opClass, _reg0, _reg1, _reg2), reg3(_reg3)
     {}
 };
diff --git a/src/arch/arm/insts/pred_inst.cc b/src/arch/arm/insts/pred_inst.cc
index 517d511..c5fee01 100644
--- a/src/arch/arm/insts/pred_inst.cc
+++ b/src/arch/arm/insts/pred_inst.cc
@@ -54,10 +54,7 @@
     uint32_t imm = machInst.imm;
     imm = (imm << (32 - rotate)) | (imm >> rotate);
     printDataInst(ss, false, machInst.opcode4 == 0, machInst.sField,
-            (IntRegIndex)(uint32_t)machInst.rd,
-            (IntRegIndex)(uint32_t)machInst.rn,
-            (IntRegIndex)(uint32_t)machInst.rm,
-            (IntRegIndex)(uint32_t)machInst.rs,
+            machInst.rd, machInst.rn, machInst.rm, machInst.rs,
             machInst.shiftSize, (ArmShiftType)(uint32_t)machInst.shift,
             imm);
     return ss.str();
@@ -69,10 +66,7 @@
 {
     std::stringstream ss;
     printDataInst(ss, true, machInst.opcode4 == 0, machInst.sField,
-            (IntRegIndex)(uint32_t)machInst.rd,
-            (IntRegIndex)(uint32_t)machInst.rn,
-            (IntRegIndex)(uint32_t)machInst.rm,
-            (IntRegIndex)(uint32_t)machInst.rs,
+            machInst.rd, machInst.rn, machInst.rm, machInst.rs,
             machInst.shiftSize, (ArmShiftType)(uint32_t)machInst.shift,
             imm);
     return ss.str();
@@ -84,7 +78,7 @@
 {
     std::stringstream ss;
     printDataInst(ss, true, false, /*XXX not really s*/ false, dest, op1,
-                  INTREG_ZERO, INTREG_ZERO, 0, LSL, imm);
+                  int_reg::Zero, int_reg::Zero, 0, LSL, imm);
     return ss.str();
 }
 
@@ -94,7 +88,7 @@
 {
     std::stringstream ss;
     printDataInst(ss, false, true, /*XXX not really s*/ false, dest, op1,
-                  op2, INTREG_ZERO, shiftAmt, shiftType, 0);
+                  op2, int_reg::Zero, shiftAmt, shiftType, 0);
     return ss.str();
 }
 
diff --git a/src/arch/arm/insts/pred_inst.hh b/src/arch/arm/insts/pred_inst.hh
index a4a6567..29581a9 100644
--- a/src/arch/arm/insts/pred_inst.hh
+++ b/src/arch/arm/insts/pred_inst.hh
@@ -283,14 +283,14 @@
 class DataImmOp : public PredOp
 {
   protected:
-    IntRegIndex dest, op1;
+    RegIndex dest, op1;
     uint32_t imm;
     // Whether the carry flag should be modified if that's an option for
     // this instruction.
     bool rotC;
 
     DataImmOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-              IntRegIndex _dest, IntRegIndex _op1, uint32_t _imm, bool _rotC) :
+              RegIndex _dest, RegIndex _op1, uint32_t _imm, bool _rotC) :
         PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm(_imm), rotC(_rotC)
     {}
@@ -302,12 +302,12 @@
 class DataRegOp : public PredOp
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     int32_t shiftAmt;
     ArmShiftType shiftType;
 
     DataRegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-              IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+              RegIndex _dest, RegIndex _op1, RegIndex _op2,
               int32_t _shiftAmt, ArmShiftType _shiftType) :
         PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2),
@@ -321,12 +321,12 @@
 class DataRegRegOp : public PredOp
 {
   protected:
-    IntRegIndex dest, op1, op2, shift;
+    RegIndex dest, op1, op2, shift;
     ArmShiftType shiftType;
 
     DataRegRegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                 IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-                 IntRegIndex _shift, ArmShiftType _shiftType) :
+                 RegIndex _dest, RegIndex _op1, RegIndex _op2,
+                 RegIndex _shift, ArmShiftType _shiftType) :
         PredOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), shift(_shift),
         shiftType(_shiftType)
diff --git a/src/arch/arm/insts/static_inst.cc b/src/arch/arm/insts/static_inst.cc
index f3d646d..4367920 100644
--- a/src/arch/arm/insts/static_inst.cc
+++ b/src/arch/arm/insts/static_inst.cc
@@ -302,17 +302,17 @@
     if (opWidth == 0)
         opWidth = intWidth;
     if (aarch64) {
-        if (reg_idx == INTREG_UREG0)
+        if (reg_idx == int_reg::Ureg0)
             ccprintf(os, "ureg0");
-        else if (reg_idx == INTREG_SPX)
+        else if (reg_idx == int_reg::Spx)
             ccprintf(os, "%s%s", (opWidth == 32) ? "w" : "", "sp");
-        else if (reg_idx == INTREG_X31)
+        else if (reg_idx == int_reg::X31)
             ccprintf(os, "%szr", (opWidth == 32) ? "w" : "x");
         else
             ccprintf(os, "%s%d", (opWidth == 32) ? "w" : "x", reg_idx);
     } else {
         switch (reg_idx) {
-          case PCReg:
+          case int_reg::Pc:
             ccprintf(os, "pc");
             break;
           case StackPointerReg:
@@ -363,7 +363,7 @@
 void
 ArmStaticInst::printCCReg(std::ostream &os, RegIndex reg_idx) const
 {
-    ccprintf(os, "cc_%s", ArmISA::ccRegName[reg_idx]);
+    ccprintf(os, "cc_%s", ArmISA::cc_reg::RegName[reg_idx]);
 }
 
 void
@@ -496,15 +496,15 @@
 
 void
 ArmStaticInst::printShiftOperand(std::ostream &os,
-                                     IntRegIndex rm,
+                                     RegIndex rm,
                                      bool immShift,
                                      uint32_t shiftAmt,
-                                     IntRegIndex rs,
+                                     RegIndex rs,
                                      ArmShiftType type) const
 {
     bool firstOp = false;
 
-    if (rm != INTREG_ZERO) {
+    if (rm != int_reg::Zero) {
         printIntReg(os, rm);
     }
 
@@ -560,7 +560,7 @@
 
 void
 ArmStaticInst::printExtendOperand(bool firstOperand, std::ostream &os,
-                                  IntRegIndex rm, ArmExtendType type,
+                                  RegIndex rm, ArmExtendType type,
                                   int64_t shiftAmt) const
 {
     if (!firstOperand)
@@ -592,21 +592,21 @@
 
 void
 ArmStaticInst::printDataInst(std::ostream &os, bool withImm,
-        bool immShift, bool s, IntRegIndex rd, IntRegIndex rn,
-        IntRegIndex rm, IntRegIndex rs, uint32_t shiftAmt,
+        bool immShift, bool s, RegIndex rd, RegIndex rn,
+        RegIndex rm, RegIndex rs, uint32_t shiftAmt,
         ArmShiftType type, uint64_t imm) const
 {
     printMnemonic(os, s ? "s" : "");
     bool firstOp = true;
 
     // Destination
-    if (rd != INTREG_ZERO) {
+    if (rd != int_reg::Zero) {
         firstOp = false;
         printIntReg(os, rd);
     }
 
     // Source 1.
-    if (rn != INTREG_ZERO) {
+    if (rn != int_reg::Zero) {
         if (!firstOp)
             os << ", ";
         firstOp = false;
@@ -678,7 +678,7 @@
         bool trap_el2 = false;
         CPTR cptr_en_check = tc->readMiscReg(MISCREG_CPTR_EL2);
         HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
-        if (HaveVirtHostExt(tc) && hcr.e2h == 0x1) {
+        if (HaveExt(tc, ArmExtension::FEAT_VHE) && hcr.e2h == 0x1) {
             switch (cptr_en_check.fpen) {
               case 0:
               case 2:
@@ -1031,7 +1031,7 @@
     if (el <= EL2 && EL2Enabled(tc)) {
         CPTR cptr_en_check = tc->readMiscReg(MISCREG_CPTR_EL2);
         HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
-        if (HaveVirtHostExt(tc) && hcr.e2h) {
+        if (HaveExt(tc, ArmExtension::FEAT_VHE) && hcr.e2h) {
             if (((cptr_en_check.zen & 0x1) == 0x0) ||
                 (cptr_en_check.zen == 0x1 && el == EL0 &&
                  hcr.tge == 0x1)) {
diff --git a/src/arch/arm/insts/static_inst.hh b/src/arch/arm/insts/static_inst.hh
index 290ff3c..e30ba28 100644
--- a/src/arch/arm/insts/static_inst.hh
+++ b/src/arch/arm/insts/static_inst.hh
@@ -184,18 +184,18 @@
     void printMemSymbol(std::ostream &os, const loader::SymbolTable *symtab,
                         const std::string &prefix, const Addr addr,
                         const std::string &suffix) const;
-    void printShiftOperand(std::ostream &os, IntRegIndex rm,
+    void printShiftOperand(std::ostream &os, RegIndex rm,
                            bool immShift, uint32_t shiftAmt,
-                           IntRegIndex rs, ArmShiftType type) const;
+                           RegIndex rs, ArmShiftType type) const;
     void printExtendOperand(bool firstOperand, std::ostream &os,
-                            IntRegIndex rm, ArmExtendType type,
+                            RegIndex rm, ArmExtendType type,
                             int64_t shiftAmt) const;
     void printPFflags(std::ostream &os, int flag) const;
 
     void printDataInst(std::ostream &os, bool withImm) const;
     void printDataInst(std::ostream &os, bool withImm, bool immShift, bool s,
-                       IntRegIndex rd, IntRegIndex rn, IntRegIndex rm,
-                       IntRegIndex rs, uint32_t shiftAmt, ArmShiftType type,
+                       RegIndex rd, RegIndex rn, RegIndex rm,
+                       RegIndex rs, uint32_t shiftAmt, ArmShiftType type,
                        uint64_t imm) const;
 
     void
diff --git a/src/arch/arm/insts/sve.hh b/src/arch/arm/insts/sve.hh
index 5a0f605..f9939e1 100644
--- a/src/arch/arm/insts/sve.hh
+++ b/src/arch/arm/insts/sve.hh
@@ -60,12 +60,12 @@
 class SveIndexIIOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
     int8_t imm1;
     int8_t imm2;
 
     SveIndexIIOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest,
+            OpClass __opClass, RegIndex _dest,
             int8_t _imm1, int8_t _imm2) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), imm1(_imm1), imm2(_imm2)
@@ -77,13 +77,13 @@
 class SveIndexIROp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
     int8_t imm1;
-    IntRegIndex op2;
+    RegIndex op2;
 
     SveIndexIROp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest,
-            int8_t _imm1, IntRegIndex _op2) :
+            OpClass __opClass, RegIndex _dest,
+            int8_t _imm1, RegIndex _op2) :
         ArmStaticInst(mnem, _machInst, __opClass),
           dest(_dest), imm1(_imm1), op2(_op2)
     {}
@@ -94,13 +94,13 @@
 class SveIndexRIOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
     int8_t imm2;
 
     SveIndexRIOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest,
-            IntRegIndex _op1, int8_t _imm2) :
+            OpClass __opClass, RegIndex _dest,
+            RegIndex _op1, int8_t _imm2) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm2(_imm2)
     {}
@@ -111,13 +111,13 @@
 class SveIndexRROp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex op2;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
 
     SveIndexRROp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest,
-            IntRegIndex _op1, IntRegIndex _op2) :
+            OpClass __opClass, RegIndex _dest,
+            RegIndex _op1, RegIndex _op2) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2)
     {}
@@ -129,13 +129,13 @@
 class SvePredCountOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
+    RegIndex dest;
+    RegIndex gp;
     bool srcIs32b;
     bool destIsVec;
 
     SvePredCountOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _gp,
+            OpClass __opClass, RegIndex _dest, RegIndex _gp,
             bool _srcIs32b = false, bool _destIsVec = false) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), gp(_gp),
@@ -149,13 +149,13 @@
 class SvePredCountPredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex gp;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex gp;
 
     SvePredCountPredOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-            IntRegIndex _gp) :
+            OpClass __opClass, RegIndex _dest, RegIndex _op1,
+            RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), gp(_gp)
     {}
@@ -167,11 +167,11 @@
 class SveWhileOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     bool srcIs32b;
 
     SveWhileOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-               IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+               RegIndex _dest, RegIndex _op1, RegIndex _op2,
                bool _srcIs32b) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), srcIs32b(_srcIs32b)
@@ -184,10 +184,10 @@
 class SveCompTermOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex op1, op2;
+    RegIndex op1, op2;
 
     SveCompTermOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                  IntRegIndex _op1, IntRegIndex _op2) :
+                  RegIndex _op1, RegIndex _op2) :
         ArmStaticInst(mnem, _machInst, __opClass),
         op1(_op1), op2(_op2)
     {}
@@ -199,10 +199,10 @@
 class SveUnaryPredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, gp;
+    RegIndex dest, op1, gp;
 
     SveUnaryPredOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _gp) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), gp(_gp)
     {}
@@ -215,10 +215,10 @@
 class SveUnaryUnpredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1;
+    RegIndex dest, op1;
 
     SveUnaryUnpredOp(const char* mnem, ExtMachInst _machInst,
-                     OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1) :
+                     OpClass __opClass, RegIndex _dest, RegIndex _op1) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1)
     {}
@@ -231,11 +231,11 @@
 class SveUnaryWideImmUnpredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
     uint64_t imm;
 
     SveUnaryWideImmUnpredOp(const char* mnem, ExtMachInst _machInst,
-                            OpClass __opClass, IntRegIndex _dest,
+                            OpClass __opClass, RegIndex _dest,
                             uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), imm(_imm)
@@ -249,15 +249,15 @@
 class SveUnaryWideImmPredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
     uint64_t imm;
-    IntRegIndex gp;
+    RegIndex gp;
 
     bool isMerging;
 
     SveUnaryWideImmPredOp(const char* mnem, ExtMachInst _machInst,
-                          OpClass __opClass, IntRegIndex _dest,
-                          uint64_t _imm, IntRegIndex _gp, bool _isMerging) :
+                          OpClass __opClass, RegIndex _dest,
+                          uint64_t _imm, RegIndex _gp, bool _isMerging) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), imm(_imm), gp(_gp), isMerging(_isMerging)
     {}
@@ -270,11 +270,11 @@
 class SveBinImmUnpredConstrOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1;
+    RegIndex dest, op1;
     uint64_t imm;
 
     SveBinImmUnpredConstrOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
+            OpClass __opClass, RegIndex _dest, RegIndex _op1,
             uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm(_imm)
@@ -288,11 +288,11 @@
 class SveBinImmPredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, gp;
+    RegIndex dest, gp;
     uint64_t imm;
 
     SveBinImmPredOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                    IntRegIndex _dest, uint64_t _imm, IntRegIndex _gp) :
+                    RegIndex _dest, uint64_t _imm, RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), gp(_gp), imm(_imm)
     {}
@@ -305,11 +305,11 @@
 class SveBinWideImmUnpredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
     uint64_t imm;
 
     SveBinWideImmUnpredOp(const char* mnem, ExtMachInst _machInst,
-                          OpClass __opClass, IntRegIndex _dest,
+                          OpClass __opClass, RegIndex _dest,
                           uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), imm(_imm)
@@ -323,11 +323,11 @@
 class SveBinDestrPredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op2, gp;
+    RegIndex dest, op2, gp;
 
     SveBinDestrPredOp(const char* mnem, ExtMachInst _machInst,
-                      OpClass __opClass, IntRegIndex _dest, IntRegIndex _op2,
-                      IntRegIndex _gp) :
+                      OpClass __opClass, RegIndex _dest, RegIndex _op2,
+                      RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op2(_op2), gp(_gp)
     {}
@@ -340,12 +340,12 @@
 class SveBinConstrPredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2, gp;
+    RegIndex dest, op1, op2, gp;
     SvePredType predType;
 
     SveBinConstrPredOp(const char* mnem, ExtMachInst _machInst,
-                       OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-                       IntRegIndex _op2, IntRegIndex _gp,
+                       OpClass __opClass, RegIndex _dest, RegIndex _op1,
+                       RegIndex _op2, RegIndex _gp,
                        SvePredType _predType) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), gp(_gp), predType(_predType)
@@ -359,10 +359,10 @@
 class SveBinUnpredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
 
     SveBinUnpredOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2)
     {}
@@ -375,12 +375,12 @@
 class SveBinIdxUnpredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     uint8_t index;
 
     SveBinIdxUnpredOp(const char* mnem, ExtMachInst _machInst,
-                      OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-                      IntRegIndex _op2, uint8_t _index) :
+                      OpClass __opClass, RegIndex _dest, RegIndex _op1,
+                      RegIndex _op2, uint8_t _index) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), index(_index)
     {}
@@ -393,12 +393,12 @@
 class SvePredLogicalOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2, gp;
+    RegIndex dest, op1, op2, gp;
     bool isSel;
 
     SvePredLogicalOp(const char* mnem, ExtMachInst _machInst,
-                     OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-                     IntRegIndex _op2, IntRegIndex _gp, bool _isSel = false) :
+                     OpClass __opClass, RegIndex _dest, RegIndex _op1,
+                     RegIndex _op2, RegIndex _gp, bool _isSel = false) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), gp(_gp), isSel(_isSel)
     {}
@@ -411,11 +411,11 @@
 class SvePredBinPermOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
 
     SvePredBinPermOp(const char* mnem, ExtMachInst _machInst,
-                     OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-                     IntRegIndex _op2) :
+                     OpClass __opClass, RegIndex _dest, RegIndex _op1,
+                     RegIndex _op2) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2)
     {}
@@ -428,11 +428,11 @@
 class SveCmpOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, gp, op1, op2;
+    RegIndex dest, gp, op1, op2;
 
     SveCmpOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-             IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-             IntRegIndex _gp) :
+             RegIndex _dest, RegIndex _op1, RegIndex _op2,
+             RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), gp(_gp), op1(_op1), op2(_op2)
     {}
@@ -445,12 +445,12 @@
 class SveCmpImmOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, gp, op1;
+    RegIndex dest, gp, op1;
     uint64_t imm;
 
     SveCmpImmOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _op1, uint64_t _imm,
-                IntRegIndex _gp) :
+                RegIndex _dest, RegIndex _op1, uint64_t _imm,
+                RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), gp(_gp), op1(_op1), imm(_imm)
     {}
@@ -463,11 +463,11 @@
 class SveTerPredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2, gp;
+    RegIndex dest, op1, op2, gp;
 
     SveTerPredOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                 IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-                 IntRegIndex _gp) :
+                 RegIndex _dest, RegIndex _op1, RegIndex _op2,
+                 RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), gp(_gp)
     {}
@@ -480,11 +480,11 @@
 class SveTerImmUnpredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op2;
+    RegIndex dest, op2;
     uint64_t imm;
 
     SveTerImmUnpredOp(const char* mnem, ExtMachInst _machInst,
-                      OpClass __opClass, IntRegIndex _dest, IntRegIndex _op2,
+                      OpClass __opClass, RegIndex _dest, RegIndex _op2,
                       uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op2(_op2), imm(_imm)
@@ -498,10 +498,10 @@
 class SveReducOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, gp;
+    RegIndex dest, op1, gp;
 
     SveReducOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-               IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _gp) :
+               RegIndex _dest, RegIndex _op1, RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), gp(_gp)
     {}
@@ -514,10 +514,10 @@
 class SveOrdReducOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, gp;
+    RegIndex dest, op1, gp;
 
     SveOrdReducOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-               IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _gp) :
+               RegIndex _dest, RegIndex _op1, RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), gp(_gp)
     {}
@@ -530,11 +530,11 @@
 class SvePtrueOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
     uint8_t imm;
 
     SvePtrueOp(const char* mnem, ExtMachInst _machInst,
-               OpClass __opClass, IntRegIndex _dest, uint8_t _imm) :
+               OpClass __opClass, RegIndex _dest, uint8_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), imm(_imm)
     {}
@@ -547,14 +547,14 @@
 class SveIntCmpOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1, op2;
-    IntRegIndex gp;
+    RegIndex dest;
+    RegIndex op1, op2;
+    RegIndex gp;
     bool op2IsWide;
 
     SveIntCmpOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                  IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-                  IntRegIndex _gp, bool _op2IsWide = false) :
+                  RegIndex _dest, RegIndex _op1, RegIndex _op2,
+                  RegIndex _gp, bool _op2IsWide = false) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), gp(_gp), op2IsWide(_op2IsWide)
     {}
@@ -566,14 +566,14 @@
 class SveIntCmpImmOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
     int64_t imm;
-    IntRegIndex gp;
+    RegIndex gp;
 
     SveIntCmpImmOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                  IntRegIndex _dest, IntRegIndex _op1, int64_t _imm,
-                  IntRegIndex _gp) :
+                  RegIndex _dest, RegIndex _op1, int64_t _imm,
+                  RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm(_imm), gp(_gp)
     {}
@@ -593,13 +593,13 @@
     };
 
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     uint8_t mult;
     SveAdrOffsetFormat offsetFormat;
 
     SveAdrOp(const char* mnem, ExtMachInst _machInst,
-             OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-             IntRegIndex _op2, uint8_t _mult,
+             OpClass __opClass, RegIndex _dest, RegIndex _op1,
+             RegIndex _op2, uint8_t _mult,
              SveAdrOffsetFormat _offsetFormat) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), mult(_mult),
@@ -613,7 +613,7 @@
 class SveElemCountOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
     uint8_t pattern;
     uint8_t imm;
     bool dstIsVec;
@@ -621,7 +621,7 @@
     uint8_t esize;
 
     SveElemCountOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                  IntRegIndex _dest, uint8_t _pattern, uint8_t _imm,
+                  RegIndex _dest, uint8_t _pattern, uint8_t _imm,
                   bool _dstIsVec, bool _dstIs32b) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), pattern(_pattern), imm(_imm), dstIsVec(_dstIsVec),
@@ -635,13 +635,13 @@
 class SvePartBrkOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
-    IntRegIndex op1;
+    RegIndex dest;
+    RegIndex gp;
+    RegIndex op1;
     bool isMerging;
 
     SvePartBrkOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                  IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _op1,
+                  RegIndex _dest, RegIndex _gp, RegIndex _op1,
                   bool _isMerging) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), gp(_gp), op1(_op1), isMerging(_isMerging)
@@ -654,14 +654,14 @@
 class SvePartBrkPropOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex op2;
-    IntRegIndex gp;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
+    RegIndex gp;
 
     SvePartBrkPropOp(const char* mnem, ExtMachInst _machInst,
-                     OpClass __opClass, IntRegIndex _dest,
-                     IntRegIndex _op1, IntRegIndex _op2, IntRegIndex _gp) :
+                     OpClass __opClass, RegIndex _dest,
+                     RegIndex _op1, RegIndex _op2, RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), gp(_gp)
     {}
@@ -673,17 +673,17 @@
 class SveSelectOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex gp;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex gp;
     bool conditional;
     bool scalar;
     bool simdFp;
     size_t scalar_width;
 
     SveSelectOp(const char* mnem, ExtMachInst _machInst,
-                      OpClass __opClass, IntRegIndex _dest,
-                      IntRegIndex _op1, IntRegIndex _gp,
+                      OpClass __opClass, RegIndex _dest,
+                      RegIndex _op1, RegIndex _gp,
                       bool _conditional, bool _scalar,
                       bool _simdFp) :
         ArmStaticInst(mnem, _machInst, __opClass),
@@ -698,13 +698,13 @@
 class SveUnaryPredPredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex gp;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex gp;
 
     SveUnaryPredPredOp(const char* mnem, ExtMachInst _machInst,
-                       OpClass __opClass, IntRegIndex _dest,
-                       IntRegIndex _op1, IntRegIndex _gp) :
+                       OpClass __opClass, RegIndex _dest,
+                       RegIndex _op1, RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), gp(_gp)
     {}
@@ -716,12 +716,12 @@
 class SveTblOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex op2;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
 
     SveTblOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-            IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2) :
+            RegIndex _dest, RegIndex _op1, RegIndex _op2) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2)
     {}
@@ -733,11 +733,11 @@
 class SveUnpackOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
 
     SveUnpackOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _op1) :
+                RegIndex _dest, RegIndex _op1) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1)
     {}
@@ -749,11 +749,11 @@
 class SvePredTestOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex op1;
-    IntRegIndex gp;
+    RegIndex op1;
+    RegIndex gp;
 
     SvePredTestOp(const char* mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _op1, IntRegIndex _gp) :
+                RegIndex _op1, RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         op1(_op1), gp(_gp)
     {}
@@ -765,10 +765,10 @@
 class SvePredUnaryWImplicitSrcOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
 
     SvePredUnaryWImplicitSrcOp(const char* mnem, ExtMachInst _machInst,
-                               OpClass __opClass, IntRegIndex _dest) :
+                               OpClass __opClass, RegIndex _dest) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest)
     {}
@@ -780,12 +780,12 @@
 class SvePredUnaryWImplicitSrcPredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
+    RegIndex dest;
+    RegIndex gp;
 
     SvePredUnaryWImplicitSrcPredOp(const char* mnem, ExtMachInst _machInst,
-                                   OpClass __opClass, IntRegIndex _dest,
-                                   IntRegIndex _gp) :
+                                   OpClass __opClass, RegIndex _dest,
+                                   RegIndex _gp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), gp(_gp)
     {}
@@ -797,10 +797,10 @@
 class SvePredUnaryWImplicitDstOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex op1;
+    RegIndex op1;
 
     SvePredUnaryWImplicitDstOp(const char* mnem, ExtMachInst _machInst,
-                               OpClass __opClass, IntRegIndex _op1) :
+                               OpClass __opClass, RegIndex _op1) :
         ArmStaticInst(mnem, _machInst, __opClass),
         op1(_op1)
     {}
@@ -824,12 +824,12 @@
 class SveBinImmUnpredDestrOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
     uint64_t imm;
 
     SveBinImmUnpredDestrOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
+            OpClass __opClass, RegIndex _dest, RegIndex _op1,
             uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm(_imm)
@@ -842,11 +842,11 @@
 class SveBinImmIdxUnpredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1;
+    RegIndex dest, op1;
     uint64_t imm;
 
     SveBinImmIdxUnpredOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
+            OpClass __opClass, RegIndex _dest, RegIndex _op1,
             uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), imm(_imm)
@@ -860,11 +860,11 @@
 class SveUnarySca2VecUnpredOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1;
+    RegIndex dest, op1;
     bool simdFp;
 
     SveUnarySca2VecUnpredOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
+            OpClass __opClass, RegIndex _dest, RegIndex _op1,
             bool _simdFp) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), simdFp(_simdFp)
@@ -878,14 +878,14 @@
 class SveDotProdIdxOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     uint64_t imm;
     uint8_t esize;
 
   public:
     SveDotProdIdxOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-            IntRegIndex _op2, uint64_t _imm) :
+            OpClass __opClass, RegIndex _dest, RegIndex _op1,
+            RegIndex _op2, uint64_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), imm(_imm)
     {}
@@ -898,13 +898,13 @@
 class SveDotProdOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     uint8_t esize;
 
   public:
     SveDotProdOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-            IntRegIndex _op2) :
+            OpClass __opClass, RegIndex _dest, RegIndex _op1,
+            RegIndex _op2) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2)
     {}
@@ -917,13 +917,13 @@
 class SveComplexOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2, gp;
+    RegIndex dest, op1, op2, gp;
     uint8_t rot;
 
   public:
     SveComplexOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-            IntRegIndex _op2, IntRegIndex _gp, uint8_t _rot) :
+            OpClass __opClass, RegIndex _dest, RegIndex _op1,
+            RegIndex _op2, RegIndex _gp, uint8_t _rot) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), gp(_gp), rot(_rot)
     {}
@@ -936,13 +936,13 @@
 class SveComplexIdxOp : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     uint8_t rot, imm;
 
   public:
     SveComplexIdxOp(const char* mnem, ExtMachInst _machInst,
-            OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-            IntRegIndex _op2, uint8_t _rot, uint8_t _imm) :
+            OpClass __opClass, RegIndex _dest, RegIndex _op1,
+            RegIndex _op2, uint8_t _rot, uint8_t _imm) :
         ArmStaticInst(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), rot(_rot), imm(_imm)
     {}
diff --git a/src/arch/arm/insts/sve_macromem.hh b/src/arch/arm/insts/sve_macromem.hh
index 84e2f94..8e94fa4 100644
--- a/src/arch/arm/insts/sve_macromem.hh
+++ b/src/arch/arm/insts/sve_macromem.hh
@@ -52,16 +52,16 @@
 class SveLdStructSS : public PredMacroOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
-    IntRegIndex base;
-    IntRegIndex offset;
+    RegIndex dest;
+    RegIndex gp;
+    RegIndex base;
+    RegIndex offset;
     uint8_t numregs;
 
   public:
     SveLdStructSS(const char* mnem, ExtMachInst machInst, OpClass __opClass,
-            IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
-            IntRegIndex _offset, uint8_t _numregs)
+            RegIndex _dest, RegIndex _gp, RegIndex _base,
+            RegIndex _offset, uint8_t _numregs)
         : PredMacroOp(mnem, machInst, __opClass),
           dest(_dest), gp(_gp), base(_base), offset(_offset), numregs(_numregs)
     {
@@ -71,12 +71,12 @@
 
         for (int i = 0; i < numregs; ++i) {
             microOps[i] = new MicroopLdMemType<Element>(
-                    mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i),
+                    mnem, machInst, static_cast<RegIndex>(INTRLVREG0 + i),
                     _gp, _base, _offset, _numregs, i);
         }
         for (int i = 0; i < numregs; ++i) {
             microOps[i + numregs] = new MicroopDeIntrlvType<Element>(
-                    mnem, machInst, static_cast<IntRegIndex>((_dest + i) % 32),
+                    mnem, machInst, static_cast<RegIndex>((_dest + i) % 32),
                     _numregs, i, this);
         }
 
@@ -124,16 +124,16 @@
 class SveStStructSS : public PredMacroOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
-    IntRegIndex base;
-    IntRegIndex offset;
+    RegIndex dest;
+    RegIndex gp;
+    RegIndex base;
+    RegIndex offset;
     uint8_t numregs;
 
   public:
     SveStStructSS(const char* mnem, ExtMachInst machInst, OpClass __opClass,
-            IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
-            IntRegIndex _offset, uint8_t _numregs)
+            RegIndex _dest, RegIndex _gp, RegIndex _base,
+            RegIndex _offset, uint8_t _numregs)
         : PredMacroOp(mnem, machInst, __opClass),
           dest(_dest), gp(_gp), base(_base), offset(_offset), numregs(_numregs)
     {
@@ -143,13 +143,13 @@
 
         for (int i = 0; i < numregs; ++i) {
             microOps[i] = new MicroopIntrlvType<Element>(
-                    mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i),
+                    mnem, machInst, static_cast<RegIndex>(INTRLVREG0 + i),
                     _dest, _numregs, i, this);
         }
 
         for (int i = 0; i < numregs; ++i) {
             microOps[i + numregs] = new MicroopStMemType<Element>(
-                    mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i),
+                    mnem, machInst, static_cast<RegIndex>(INTRLVREG0 + i),
                     _gp, _base, _offset, _numregs, i);
         }
 
@@ -198,15 +198,15 @@
 class SveLdStructSI : public PredMacroOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
-    IntRegIndex base;
+    RegIndex dest;
+    RegIndex gp;
+    RegIndex base;
     int64_t imm;
     uint8_t numregs;
 
   public:
     SveLdStructSI(const char* mnem, ExtMachInst machInst, OpClass __opClass,
-            IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
+            RegIndex _dest, RegIndex _gp, RegIndex _base,
             int64_t _imm, uint8_t _numregs)
         : PredMacroOp(mnem, machInst, __opClass),
           dest(_dest), gp(_gp), base(_base), imm(_imm), numregs(_numregs)
@@ -217,12 +217,12 @@
 
         for (int i = 0; i < numregs; ++i) {
             microOps[i] = new MicroopLdMemType<Element>(
-                    mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i),
+                    mnem, machInst, static_cast<RegIndex>(INTRLVREG0 + i),
                     _gp, _base, _imm, _numregs, i);
         }
         for (int i = 0; i < numregs; ++i) {
             microOps[i + numregs] = new MicroopDeIntrlvType<Element>(
-                    mnem, machInst, static_cast<IntRegIndex>((_dest + i) % 32),
+                    mnem, machInst, static_cast<RegIndex>((_dest + i) % 32),
                     _numregs, i, this);
         }
 
@@ -271,15 +271,15 @@
 class SveStStructSI : public PredMacroOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
-    IntRegIndex base;
+    RegIndex dest;
+    RegIndex gp;
+    RegIndex base;
     int64_t imm;
     uint8_t numregs;
 
   public:
     SveStStructSI(const char* mnem, ExtMachInst machInst, OpClass __opClass,
-            IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
+            RegIndex _dest, RegIndex _gp, RegIndex _base,
             int64_t _imm, uint8_t _numregs)
         : PredMacroOp(mnem, machInst, __opClass),
           dest(_dest), gp(_gp), base(_base), imm(_imm), numregs(_numregs)
@@ -290,13 +290,13 @@
 
         for (int i = 0; i < numregs; ++i) {
             microOps[i] = new MicroopIntrlvType<Element>(
-                    mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i),
+                    mnem, machInst, static_cast<RegIndex>(INTRLVREG0 + i),
                     _dest, _numregs, i, this);
         }
 
         for (int i = 0; i < numregs; ++i) {
             microOps[i + numregs] = new MicroopStMemType<Element>(
-                    mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i),
+                    mnem, machInst, static_cast<RegIndex>(INTRLVREG0 + i),
                     _gp, _base, _imm, _numregs, i);
         }
 
@@ -345,14 +345,14 @@
 class SveIndexedMemVI : public PredMacroOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
-    IntRegIndex base;
+    RegIndex dest;
+    RegIndex gp;
+    RegIndex base;
     uint64_t imm;
 
   public:
     SveIndexedMemVI(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-                    IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
+                    RegIndex _dest, RegIndex _gp, RegIndex _base,
                     uint64_t _imm, bool firstFault)
         : PredMacroOp(mnem, machInst, __opClass),
           dest(_dest), gp(_gp), base(_base), imm(_imm)
@@ -389,7 +389,7 @@
         for (int i = 0; i < num_elems; i++, uop++) {
             *uop = new MicroopType<RegElemType, MemElemType>(
                 mnem, machInst, __opClass, _dest, _gp,
-                isLoad ? (IntRegIndex) VECREG_UREG0 : _base, _imm, i,
+                isLoad ? (RegIndex) VECREG_UREG0 : _base, _imm, i,
                 num_elems, firstFault);
         }
 
@@ -442,10 +442,10 @@
 class SveIndexedMemSV : public PredMacroOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
-    IntRegIndex base;
-    IntRegIndex offset;
+    RegIndex dest;
+    RegIndex gp;
+    RegIndex base;
+    RegIndex offset;
 
     bool offsetIs32;
     bool offsetIsSigned;
@@ -453,8 +453,8 @@
 
   public:
     SveIndexedMemSV(const char *mnem, ExtMachInst machInst, OpClass __opClass,
-                    IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
-                    IntRegIndex _offset, bool _offsetIs32,
+                    RegIndex _dest, RegIndex _gp, RegIndex _base,
+                    RegIndex _offset, bool _offsetIs32,
                     bool _offsetIsSigned, bool _offsetIsScaled,
                     bool firstFault)
         : PredMacroOp(mnem, machInst, __opClass),
@@ -494,7 +494,7 @@
         for (int i = 0; i < num_elems; i++, uop++) {
             *uop = new MicroopType<RegElemType, MemElemType>(
                 mnem, machInst, __opClass, _dest, _gp, _base,
-                isLoad ? (IntRegIndex) VECREG_UREG0 : _offset, _offsetIs32,
+                isLoad ? (RegIndex) VECREG_UREG0 : _offset, _offsetIs32,
                 _offsetIsSigned, _offsetIsScaled, i, num_elems, firstFault);
         }
 
diff --git a/src/arch/arm/insts/sve_mem.hh b/src/arch/arm/insts/sve_mem.hh
index 167591c..6d94e0a 100644
--- a/src/arch/arm/insts/sve_mem.hh
+++ b/src/arch/arm/insts/sve_mem.hh
@@ -50,8 +50,8 @@
 class SveMemVecFillSpill : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex base;
+    RegIndex dest;
+    RegIndex base;
     uint64_t imm;
 
     /// True if the base register is SP (used for SP alignment checking).
@@ -60,8 +60,8 @@
     unsigned memAccessFlags;
 
     SveMemVecFillSpill(const char *mnem, ExtMachInst _machInst,
-                       OpClass __opClass, IntRegIndex _dest,
-                       IntRegIndex _base, uint64_t _imm)
+                       OpClass __opClass, RegIndex _dest,
+                       RegIndex _base, uint64_t _imm)
         : ArmStaticInst(mnem, _machInst, __opClass),
           dest(_dest), base(_base), imm(_imm),
           memAccessFlags(ArmISA::MMU::AllowUnaligned)
@@ -76,8 +76,8 @@
 class SveMemPredFillSpill : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex base;
+    RegIndex dest;
+    RegIndex base;
     uint64_t imm;
 
     /// True if the base register is SP (used for SP alignment checking).
@@ -86,8 +86,8 @@
     unsigned memAccessFlags;
 
     SveMemPredFillSpill(const char *mnem, ExtMachInst _machInst,
-                        OpClass __opClass, IntRegIndex _dest,
-                        IntRegIndex _base, uint64_t _imm)
+                        OpClass __opClass, RegIndex _dest,
+                        RegIndex _base, uint64_t _imm)
         : ArmStaticInst(mnem, _machInst, __opClass),
           dest(_dest), base(_base), imm(_imm),
           memAccessFlags(ArmISA::MMU::AllowUnaligned)
@@ -102,10 +102,10 @@
 class SveContigMemSS : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
-    IntRegIndex base;
-    IntRegIndex offset;
+    RegIndex dest;
+    RegIndex gp;
+    RegIndex base;
+    RegIndex offset;
 
     /// True if the base register is SP (used for SP alignment checking).
     bool baseIsSP;
@@ -113,8 +113,8 @@
     unsigned memAccessFlags;
 
     SveContigMemSS(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                   IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
-                   IntRegIndex _offset)
+                   RegIndex _dest, RegIndex _gp, RegIndex _base,
+                   RegIndex _offset)
         : ArmStaticInst(mnem, _machInst, __opClass),
           dest(_dest), gp(_gp), base(_base), offset(_offset),
           memAccessFlags(ArmISA::MMU::AllowUnaligned)
@@ -129,9 +129,9 @@
 class SveContigMemSI : public ArmStaticInst
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex gp;
-    IntRegIndex base;
+    RegIndex dest;
+    RegIndex gp;
+    RegIndex base;
     uint64_t imm;
 
     /// True if the base register is SP (used for SP alignment checking).
@@ -140,7 +140,7 @@
     unsigned memAccessFlags;
 
     SveContigMemSI(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                   IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
+                   RegIndex _dest, RegIndex _gp, RegIndex _base,
                    uint64_t _imm)
         : ArmStaticInst(mnem, _machInst, __opClass),
           dest(_dest), gp(_gp), base(_base), imm(_imm),
diff --git a/src/arch/arm/insts/tme64.cc b/src/arch/arm/insts/tme64.cc
index 12a66a8..2b82283 100644
--- a/src/arch/arm/insts/tme64.cc
+++ b/src/arch/arm/insts/tme64.cc
@@ -82,11 +82,6 @@
 {
     _numSrcRegs = 0;
     _numDestRegs = 0;
-    _numFPDestRegs = 0;
-    _numVecDestRegs = 0;
-    _numVecElemDestRegs = 0;
-    _numIntDestRegs = 0;
-    _numCCDestRegs = 0;
     flags[IsMicroop] = true;
     flags[IsReadBarrier] = true;
     flags[IsWriteBarrier] = true;
@@ -117,7 +112,7 @@
     return NoFault;
 }
 
-Tstart64::Tstart64(ExtMachInst machInst, IntRegIndex _dest)
+Tstart64::Tstart64(ExtMachInst machInst, RegIndex _dest)
     : TmeRegNone64("tstart", machInst, MemReadOp, _dest)
 {
     setRegIdxArrays(
@@ -128,13 +123,8 @@
 
     _numSrcRegs = 0;
     _numDestRegs = 0;
-    _numFPDestRegs = 0;
-    _numVecDestRegs = 0;
-    _numVecElemDestRegs = 0;
-    _numIntDestRegs = 0;
-    _numCCDestRegs = 0;
     setDestRegIdx(_numDestRegs++, RegId(IntRegClass, dest));
-    _numIntDestRegs++;
+    _numTypedDestRegs[IntRegClass]++;
     flags[IsHtmStart] = true;
     flags[IsInteger] = true;
     flags[IsLoad] = true;
@@ -151,7 +141,7 @@
     return NoFault;
 }
 
-Ttest64::Ttest64(ExtMachInst machInst, IntRegIndex _dest)
+Ttest64::Ttest64(ExtMachInst machInst, RegIndex _dest)
     : TmeRegNone64("ttest", machInst, MemReadOp, _dest)
 {
     setRegIdxArrays(
@@ -162,13 +152,8 @@
 
     _numSrcRegs = 0;
     _numDestRegs = 0;
-    _numFPDestRegs = 0;
-    _numVecDestRegs = 0;
-    _numVecElemDestRegs = 0;
-    _numIntDestRegs = 0;
-    _numCCDestRegs = 0;
     setDestRegIdx(_numDestRegs++, RegId(IntRegClass, dest));
-    _numIntDestRegs++;
+    _numTypedDestRegs[IntRegClass]++;
     flags[IsInteger] = true;
     flags[IsMicroop] = true;
 }
@@ -178,11 +163,6 @@
 {
     _numSrcRegs = 0;
     _numDestRegs = 0;
-    _numFPDestRegs = 0;
-    _numVecDestRegs = 0;
-    _numVecElemDestRegs = 0;
-    _numIntDestRegs = 0;
-    _numCCDestRegs = 0;
     flags[IsLoad] = true;
     flags[IsMicroop] = true;
     flags[IsNonSpeculative] = true;
@@ -204,11 +184,6 @@
   PredMacroOp(mnem, machInst, __opClass) {
     _numSrcRegs = 0;
     _numDestRegs = 0;
-    _numFPDestRegs = 0;
-    _numVecDestRegs = 0;
-    _numVecElemDestRegs = 0;
-    _numIntDestRegs = 0;
-    _numCCDestRegs = 0;
 
     numMicroops = 0;
     microOps = nullptr;
@@ -219,11 +194,6 @@
 {
     _numSrcRegs = 0;
     _numDestRegs = 0;
-    _numFPDestRegs = 0;
-    _numVecDestRegs = 0;
-    _numVecElemDestRegs = 0;
-    _numIntDestRegs = 0;
-    _numCCDestRegs = 0;
     flags[IsHtmStop] = true;
     flags[IsLoad] = true;
     flags[IsMicroop] = true;
diff --git a/src/arch/arm/insts/tme64.hh b/src/arch/arm/insts/tme64.hh
index c61764a..c8994f3 100644
--- a/src/arch/arm/insts/tme64.hh
+++ b/src/arch/arm/insts/tme64.hh
@@ -86,10 +86,10 @@
 class TmeRegNone64 : public ArmISA::ArmStaticInst
 {
   protected:
-    ArmISA::IntRegIndex dest;
+    RegIndex dest;
 
     TmeRegNone64(const char *mnem, ArmISA::ExtMachInst machInst,
-                 OpClass __opClass, ArmISA::IntRegIndex _dest)
+                 OpClass __opClass, RegIndex _dest)
       : ArmISA::ArmStaticInst(mnem, machInst, __opClass),
         dest(_dest)
     {}
@@ -104,7 +104,7 @@
     RegId destRegIdxArr[1];
 
   public:
-    Tstart64(ArmISA::ExtMachInst, ArmISA::IntRegIndex);
+    Tstart64(ArmISA::ExtMachInst, RegIndex);
 
     Fault execute(ExecContext *, Trace::InstRecord *) const;
     Fault initiateAcc(ExecContext *, Trace::InstRecord *) const;
@@ -117,7 +117,7 @@
     RegId destRegIdxArr[1];
 
   public:
-    Ttest64(ArmISA::ExtMachInst, ArmISA::IntRegIndex);
+    Ttest64(ArmISA::ExtMachInst, RegIndex);
 
     Fault execute(ExecContext *, Trace::InstRecord *) const;
 };
diff --git a/src/arch/arm/insts/tme64ruby.cc b/src/arch/arm/insts/tme64ruby.cc
index 12cc878..6999924 100644
--- a/src/arch/arm/insts/tme64ruby.cc
+++ b/src/arch/arm/insts/tme64ruby.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 ARM Limited
+ * Copyright (c) 2020-2021 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -77,7 +77,7 @@
             memAccessFlags = memAccessFlags | Request::NO_ACCESS;
         }
 
-        fault = xc->initiateHtmCmd(memAccessFlags);
+        fault = xc->initiateMemMgmtCmd(memAccessFlags);
     }
 
     return fault;
@@ -122,7 +122,7 @@
             tc->getIsaPtr()->globalClearExclusive();
         }
 
-        xc->setIntRegOperand(this, 0, (Dest64) & mask(intWidth));
+        xc->setRegOperand(this, 0, Dest64 & mask(intWidth));
 
 
         uint64_t final_val = Dest64;
@@ -155,7 +155,7 @@
 
     if (fault == NoFault) {
         uint64_t final_val = Dest64;
-        xc->setIntRegOperand(this, 0, (Dest64) & mask(intWidth));
+        xc->setRegOperand(this, 0, Dest64 & mask(intWidth));
         if (traceData) { traceData->setData(final_val); }
     }
 
@@ -175,7 +175,7 @@
     Request::Flags memAccessFlags =
         Request::STRICT_ORDER|Request::PHYSICAL|Request::HTM_CANCEL;
 
-    fault = xc->initiateHtmCmd(memAccessFlags);
+    fault = xc->initiateMemMgmtCmd(memAccessFlags);
 
     return fault;
 }
@@ -231,7 +231,7 @@
         memAccessFlags = memAccessFlags | Request::NO_ACCESS;
     }
 
-    fault = xc->initiateHtmCmd(memAccessFlags);
+    fault = xc->initiateMemMgmtCmd(memAccessFlags);
 
     return fault;
 }
diff --git a/src/arch/arm/insts/vfp.cc b/src/arch/arm/insts/vfp.cc
index 4246af6..a76fd1d 100644
--- a/src/arch/arm/insts/vfp.cc
+++ b/src/arch/arm/insts/vfp.cc
@@ -1163,21 +1163,21 @@
 double FpOp::unaryOp(FPSCR &fpscr, double op1, double (*func)(double),
                      bool flush, uint32_t rMode) const;
 
-IntRegIndex
-VfpMacroOp::addStride(IntRegIndex idx, unsigned stride)
+RegIndex
+VfpMacroOp::addStride(RegIndex idx, unsigned stride)
 {
     if (wide) {
         stride *= 2;
     }
     unsigned offset = idx % 8;
-    idx = (IntRegIndex)(idx - offset);
+    idx = (RegIndex)(idx - offset);
     offset += stride;
-    idx = (IntRegIndex)(idx + (offset % 8));
+    idx = (RegIndex)(idx + (offset % 8));
     return idx;
 }
 
 void
-VfpMacroOp::nextIdxs(IntRegIndex &dest, IntRegIndex &op1, IntRegIndex &op2)
+VfpMacroOp::nextIdxs(RegIndex &dest, RegIndex &op1, RegIndex &op2)
 {
     unsigned stride = (machInst.fpscrStride == 0) ? 1 : 2;
     assert(!inScalarBank(dest));
@@ -1189,7 +1189,7 @@
 }
 
 void
-VfpMacroOp::nextIdxs(IntRegIndex &dest, IntRegIndex &op1)
+VfpMacroOp::nextIdxs(RegIndex &dest, RegIndex &op1)
 {
     unsigned stride = (machInst.fpscrStride == 0) ? 1 : 2;
     assert(!inScalarBank(dest));
@@ -1200,7 +1200,7 @@
 }
 
 void
-VfpMacroOp::nextIdxs(IntRegIndex &dest)
+VfpMacroOp::nextIdxs(RegIndex &dest)
 {
     unsigned stride = (machInst.fpscrStride == 0) ? 1 : 2;
     assert(!inScalarBank(dest));
diff --git a/src/arch/arm/insts/vfp.hh b/src/arch/arm/insts/vfp.hh
index e2409ac..96db063 100644
--- a/src/arch/arm/insts/vfp.hh
+++ b/src/arch/arm/insts/vfp.hh
@@ -458,7 +458,7 @@
 {
   public:
     static bool
-    inScalarBank(IntRegIndex idx)
+    inScalarBank(RegIndex idx)
     {
         return (idx % 32) < 8;
     }
@@ -471,10 +471,10 @@
         PredMacroOp(mnem, _machInst, __opClass), wide(_wide)
     {}
 
-    IntRegIndex addStride(IntRegIndex idx, unsigned stride);
-    void nextIdxs(IntRegIndex &dest, IntRegIndex &op1, IntRegIndex &op2);
-    void nextIdxs(IntRegIndex &dest, IntRegIndex &op1);
-    void nextIdxs(IntRegIndex &dest);
+    RegIndex addStride(RegIndex idx, unsigned stride);
+    void nextIdxs(RegIndex &dest, RegIndex &op1, RegIndex &op2);
+    void nextIdxs(RegIndex &dest, RegIndex &op1);
+    void nextIdxs(RegIndex &dest);
 };
 
 template <typename T>
@@ -901,12 +901,12 @@
 class FpCondCompRegOp : public FpOp
 {
   protected:
-    IntRegIndex op1, op2;
+    RegIndex op1, op2;
     ConditionCode condCode;
     uint8_t defCc;
 
     FpCondCompRegOp(const char *mnem, ExtMachInst _machInst,
-                       OpClass __opClass, IntRegIndex _op1, IntRegIndex _op2,
+                       OpClass __opClass, RegIndex _op1, RegIndex _op2,
                        ConditionCode _condCode, uint8_t _defCc) :
         FpOp(mnem, _machInst, __opClass),
         op1(_op1), op2(_op2), condCode(_condCode), defCc(_defCc)
@@ -919,11 +919,11 @@
 class FpCondSelOp : public FpOp
 {
   protected:
-    IntRegIndex dest, op1, op2;
+    RegIndex dest, op1, op2;
     ConditionCode condCode;
 
     FpCondSelOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                RegIndex _dest, RegIndex _op1, RegIndex _op2,
                 ConditionCode _condCode) :
         FpOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), condCode(_condCode)
@@ -936,11 +936,11 @@
 class FpRegRegOp : public FpOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
 
     FpRegRegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-               IntRegIndex _dest, IntRegIndex _op1,
+               RegIndex _dest, RegIndex _op1,
                VfpMicroMode mode = VfpNotAMicroop) :
         FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1)
     {
@@ -954,11 +954,11 @@
 class FpRegImmOp : public FpOp
 {
   protected:
-    IntRegIndex dest;
+    RegIndex dest;
     uint64_t imm;
 
     FpRegImmOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-               IntRegIndex _dest, uint64_t _imm,
+               RegIndex _dest, uint64_t _imm,
                VfpMicroMode mode = VfpNotAMicroop) :
         FpOp(mnem, _machInst, __opClass), dest(_dest), imm(_imm)
     {
@@ -972,12 +972,12 @@
 class FpRegRegImmOp : public FpOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
+    RegIndex dest;
+    RegIndex op1;
     uint64_t imm;
 
     FpRegRegImmOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                  IntRegIndex _dest, IntRegIndex _op1,
+                  RegIndex _dest, RegIndex _op1,
                   uint64_t _imm, VfpMicroMode mode = VfpNotAMicroop) :
         FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1), imm(_imm)
     {
@@ -991,12 +991,12 @@
 class FpRegRegRegOp : public FpOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex op2;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
 
     FpRegRegRegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                  IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                  RegIndex _dest, RegIndex _op1, RegIndex _op2,
                   VfpMicroMode mode = VfpNotAMicroop) :
         FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1), op2(_op2)
     {
@@ -1010,14 +1010,14 @@
 class FpRegRegRegCondOp : public FpOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex op2;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
     ConditionCode cond;
 
     FpRegRegRegCondOp(const char *mnem, ExtMachInst _machInst,
-                      OpClass __opClass, IntRegIndex _dest, IntRegIndex _op1,
-                      IntRegIndex _op2, ConditionCode _cond,
+                      OpClass __opClass, RegIndex _dest, RegIndex _op1,
+                      RegIndex _op2, ConditionCode _cond,
                       VfpMicroMode mode = VfpNotAMicroop) :
         FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1), op2(_op2),
         cond(_cond)
@@ -1032,14 +1032,14 @@
 class FpRegRegRegRegOp : public FpOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex op2;
-    IntRegIndex op3;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
+    RegIndex op3;
 
     FpRegRegRegRegOp(const char *mnem, ExtMachInst _machInst, OpClass __opClass,
-                     IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-                     IntRegIndex _op3, VfpMicroMode mode = VfpNotAMicroop) :
+                     RegIndex _dest, RegIndex _op1, RegIndex _op2,
+                     RegIndex _op3, VfpMicroMode mode = VfpNotAMicroop) :
         FpOp(mnem, _machInst, __opClass), dest(_dest), op1(_op1), op2(_op2),
         op3(_op3)
     {
@@ -1053,14 +1053,14 @@
 class FpRegRegRegImmOp : public FpOp
 {
   protected:
-    IntRegIndex dest;
-    IntRegIndex op1;
-    IntRegIndex op2;
+    RegIndex dest;
+    RegIndex op1;
+    RegIndex op2;
     uint64_t imm;
 
     FpRegRegRegImmOp(const char *mnem, ExtMachInst _machInst,
-                     OpClass __opClass, IntRegIndex _dest,
-                     IntRegIndex _op1, IntRegIndex _op2,
+                     OpClass __opClass, RegIndex _dest,
+                     RegIndex _op1, RegIndex _op2,
                      uint64_t _imm, VfpMicroMode mode = VfpNotAMicroop) :
         FpOp(mnem, _machInst, __opClass),
         dest(_dest), op1(_op1), op2(_op2), imm(_imm)
diff --git a/src/arch/arm/interrupts.hh b/src/arch/arm/interrupts.hh
index 2f99d6e..a510d29 100644
--- a/src/arch/arm/interrupts.hh
+++ b/src/arch/arm/interrupts.hh
@@ -47,6 +47,7 @@
 #include "arch/generic/interrupts.hh"
 #include "cpu/thread_context.hh"
 #include "debug/Interrupt.hh"
+#include "enums/ArmExtension.hh"
 #include "params/ArmInterrupts.hh"
 
 namespace gem5
@@ -139,7 +140,7 @@
 
         CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
 
-        bool no_vhe = !HaveVirtHostExt(tc);
+        bool no_vhe = !HaveExt(tc, ArmExtension::FEAT_VHE);
         bool amo, fmo, imo;
         if (hcr.tge == 1){
             amo =  (no_vhe || hcr.e2h == 0);
@@ -238,7 +239,7 @@
         HCR  hcr  = tc->readMiscReg(MISCREG_HCR);
         CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
 
-        bool no_vhe = !HaveVirtHostExt(tc);
+        bool no_vhe = !HaveExt(tc, ArmExtension::FEAT_VHE);
         bool amo, fmo, imo;
         if (hcr.tge == 1){
             amo =  (no_vhe || hcr.e2h == 0);
diff --git a/src/arch/arm/isa.cc b/src/arch/arm/isa.cc
index 02af0bc..fda47b7 100644
--- a/src/arch/arm/isa.cc
+++ b/src/arch/arm/isa.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2021 ARM Limited
+ * Copyright (c) 2010-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -46,7 +46,6 @@
 #include "arch/arm/regs/misc.hh"
 #include "arch/arm/self_debug.hh"
 #include "arch/arm/system.hh"
-#include "arch/arm/tlbi_op.hh"
 #include "arch/arm/utility.hh"
 #include "arch/generic/decoder.hh"
 #include "base/cprintf.hh"
@@ -54,8 +53,13 @@
 #include "cpu/checker/cpu.hh"
 #include "cpu/reg_class.hh"
 #include "debug/Arm.hh"
+#include "debug/CCRegs.hh"
+#include "debug/FloatRegs.hh"
+#include "debug/IntRegs.hh"
 #include "debug/LLSC.hh"
 #include "debug/MiscRegs.hh"
+#include "debug/VecPredRegs.hh"
+#include "debug/VecRegs.hh"
 #include "dev/arm/generic_timer.hh"
 #include "dev/arm/gic_v3.hh"
 #include "dev/arm/gic_v3_cpu_interface.hh"
@@ -80,17 +84,24 @@
     }
 } miscRegClassOps;
 
+VecElemRegClassOps<ArmISA::VecElem> vecRegElemClassOps(NumVecElemPerVecReg);
+TypedRegClassOps<ArmISA::VecRegContainer> vecRegClassOps;
+TypedRegClassOps<ArmISA::VecPredRegContainer> vecPredRegClassOps;
+
 ISA::ISA(const Params &p) : BaseISA(p), system(NULL),
     _decoderFlavor(p.decoderFlavor), pmu(p.pmu), impdefAsNop(p.impdef_nop),
     afterStartup(false)
 {
-    _regClasses.emplace_back(NUM_INTREGS, INTREG_ZERO);
-    _regClasses.emplace_back(0);
-    _regClasses.emplace_back(NumVecRegs);
-    _regClasses.emplace_back(NumVecRegs * TheISA::NumVecElemPerVecReg);
-    _regClasses.emplace_back(NumVecPredRegs);
-    _regClasses.emplace_back(NUM_CCREGS);
-    _regClasses.emplace_back(NUM_MISCREGS, miscRegClassOps);
+    _regClasses.emplace_back(int_reg::NumRegs, debug::IntRegs);
+    _regClasses.emplace_back(0, debug::FloatRegs);
+    _regClasses.emplace_back(NumVecRegs, vecRegClassOps, debug::VecRegs,
+            sizeof(VecRegContainer));
+    _regClasses.emplace_back(NumVecRegs * NumVecElemPerVecReg,
+            vecRegElemClassOps, debug::VecRegs);
+    _regClasses.emplace_back(NumVecPredRegs, vecPredRegClassOps,
+            debug::VecPredRegs, sizeof(VecPredRegContainer));
+    _regClasses.emplace_back(cc_reg::NumRegs, debug::CCRegs);
+    _regClasses.emplace_back(NUM_MISCREGS, miscRegClassOps, debug::MiscRegs);
 
     miscRegs[MISCREG_SCTLR_RST] = 0;
 
@@ -552,22 +563,29 @@
 void
 ISA::copyRegsFrom(ThreadContext *src)
 {
-    for (int i = 0; i < NUM_INTREGS; i++)
-        tc->setIntRegFlat(i, src->readIntRegFlat(i));
+    for (int i = 0; i < int_reg::NumRegs; i++) {
+        RegId reg(IntRegClass, i);
+        tc->setRegFlat(reg, src->getRegFlat(reg));
+    }
 
-    for (int i = 0; i < NUM_CCREGS; i++)
-        tc->setCCReg(i, src->readCCReg(i));
+    for (int i = 0; i < cc_reg::NumRegs; i++) {
+        RegId reg(CCRegClass, i);
+        tc->setReg(reg, src->getReg(reg));
+    }
 
     for (int i = 0; i < NUM_MISCREGS; i++)
         tc->setMiscRegNoEffect(i, src->readMiscRegNoEffect(i));
 
-    for (int i = 0; i < NumVecRegs; i++)
-        tc->setVecRegFlat(i, src->readVecRegFlat(i));
-
+    ArmISA::VecRegContainer vc;
     for (int i = 0; i < NumVecRegs; i++) {
-        for (int e = 0; e < NumVecElemPerVecReg; e++) {
-            tc->setVecElemFlat(i, e, src->readVecElemFlat(i, e));
-        }
+        RegId reg(VecRegClass, i);
+        src->getRegFlat(reg, &vc);
+        tc->setRegFlat(reg, &vc);
+    }
+
+    for (int i = 0; i < NumVecRegs * NumVecElemPerVecReg; i++) {
+        RegId reg(VecElemClass, i);
+        tc->setRegFlat(reg, src->getRegFlat(reg));
     }
 
     // setMiscReg "with effect" will set the misc register mapping correctly.
@@ -581,6 +599,156 @@
     static_cast<MMU *>(tc->getMMUPtr())->invalidateMiscReg();
 }
 
+/**
+ * Returns the enconcing equivalent when VHE is implemented and
+ * HCR_EL2.E2H is enabled and executing at EL2
+ */
+int
+ISA::redirectRegVHE(int misc_reg)
+{
+    const HCR hcr = readMiscRegNoEffect(MISCREG_HCR_EL2);
+    if (hcr.e2h == 0x0)
+        return misc_reg;
+    SCR scr = readMiscRegNoEffect(MISCREG_SCR_EL3);
+    bool sec_el2 = scr.eel2 && release->has(ArmExtension::FEAT_SEL2);
+    switch(misc_reg) {
+      case MISCREG_SPSR_EL1:
+        return currEL() == EL2 ? MISCREG_SPSR_EL2 : misc_reg;
+      case MISCREG_ELR_EL1:
+        return currEL() == EL2 ? MISCREG_ELR_EL2 : misc_reg;
+      case MISCREG_SCTLR_EL1:
+        return currEL() == EL2 ? MISCREG_SCTLR_EL2 : misc_reg;
+      case MISCREG_CPACR_EL1:
+        return currEL() == EL2 ? MISCREG_CPTR_EL2 : misc_reg;
+//    case MISCREG_TRFCR_EL1:
+//      return currEL() == EL2 ? MISCREG_TRFCR_EL2 : misc_reg;
+      case MISCREG_TTBR0_EL1:
+        return currEL() == EL2 ? MISCREG_TTBR0_EL2 : misc_reg;
+      case MISCREG_TTBR1_EL1:
+        return currEL() == EL2 ? MISCREG_TTBR1_EL2 : misc_reg;
+      case MISCREG_TCR_EL1:
+        return currEL() == EL2 ? MISCREG_TCR_EL2 : misc_reg;
+      case MISCREG_AFSR0_EL1:
+        return currEL() == EL2 ? MISCREG_AFSR0_EL2 : misc_reg;
+      case MISCREG_AFSR1_EL1:
+        return currEL() == EL2 ? MISCREG_AFSR1_EL2 : misc_reg;
+      case MISCREG_ESR_EL1:
+        return currEL() == EL2 ? MISCREG_ESR_EL2 : misc_reg;
+      case MISCREG_FAR_EL1:
+        return currEL() == EL2 ? MISCREG_FAR_EL2 : misc_reg;
+      case MISCREG_MAIR_EL1:
+        return currEL() == EL2 ? MISCREG_MAIR_EL2 : misc_reg;
+      case MISCREG_AMAIR_EL1:
+        return currEL() == EL2 ? MISCREG_AMAIR_EL2 : misc_reg;
+      case MISCREG_VBAR_EL1:
+        return currEL() == EL2 ? MISCREG_VBAR_EL2 : misc_reg;
+      case MISCREG_CONTEXTIDR_EL1:
+        return currEL() == EL2 ? MISCREG_CONTEXTIDR_EL2 : misc_reg;
+      case MISCREG_CNTKCTL_EL1:
+        return currEL() == EL2 ? MISCREG_CNTHCTL_EL2 : misc_reg;
+      case MISCREG_CNTP_TVAL:
+      case MISCREG_CNTP_TVAL_EL0:
+        if (ELIsInHost(tc, currEL())) {
+            return sec_el2 && !scr.ns ? MISCREG_CNTHPS_TVAL_EL2:
+                                        MISCREG_CNTHP_TVAL_EL2;
+        } else {
+            return misc_reg;
+        }
+      case MISCREG_CNTP_CTL:
+      case MISCREG_CNTP_CTL_EL0:
+        if (ELIsInHost(tc, currEL())) {
+            return sec_el2 && !scr.ns ? MISCREG_CNTHPS_CTL_EL2:
+                                        MISCREG_CNTHP_CTL_EL2;
+        } else {
+            return misc_reg;
+        }
+      case MISCREG_CNTP_CVAL:
+      case MISCREG_CNTP_CVAL_EL0:
+        if (ELIsInHost(tc, currEL())) {
+            return sec_el2 && !scr.ns ? MISCREG_CNTHPS_CVAL_EL2:
+                                        MISCREG_CNTHP_CVAL_EL2;
+        } else {
+            return misc_reg;
+        }
+      case MISCREG_CNTV_TVAL:
+      case MISCREG_CNTV_TVAL_EL0:
+        if (ELIsInHost(tc, currEL())) {
+            return sec_el2 && !scr.ns ? MISCREG_CNTHVS_TVAL_EL2:
+                                        MISCREG_CNTHV_TVAL_EL2;
+        } else {
+            return misc_reg;
+        }
+      case MISCREG_CNTV_CTL:
+      case MISCREG_CNTV_CTL_EL0:
+        if (ELIsInHost(tc, currEL())) {
+            return sec_el2 && !scr.ns ? MISCREG_CNTHVS_CTL_EL2:
+                                        MISCREG_CNTHV_CTL_EL2;
+        } else {
+            return misc_reg;
+        }
+      case MISCREG_CNTV_CVAL:
+      case MISCREG_CNTV_CVAL_EL0:
+        if (ELIsInHost(tc, currEL())) {
+            return sec_el2 && !scr.ns ? MISCREG_CNTHVS_CVAL_EL2:
+                                        MISCREG_CNTHV_CVAL_EL2;
+        } else {
+            return misc_reg;
+        }
+      case MISCREG_CNTVCT:
+      case MISCREG_CNTVCT_EL0:
+        return ELIsInHost(tc, currEL()) ? MISCREG_CNTPCT_EL0 : misc_reg;
+      case MISCREG_SCTLR_EL12:
+        return MISCREG_SCTLR_EL1;
+      case MISCREG_CPACR_EL12:
+        return MISCREG_CPACR_EL1;
+      case MISCREG_ZCR_EL12:
+        return MISCREG_ZCR_EL1;
+      case MISCREG_TTBR0_EL12:
+        return MISCREG_TTBR0_EL1;
+      case MISCREG_TTBR1_EL12:
+        return MISCREG_TTBR1_EL1;
+      case MISCREG_TCR_EL12:
+        return MISCREG_TCR_EL1;
+      case MISCREG_SPSR_EL12:
+        return MISCREG_SPSR_EL1;
+      case MISCREG_ELR_EL12:
+        return MISCREG_ELR_EL1;
+      case MISCREG_AFSR0_EL12:
+        return MISCREG_AFSR0_EL1;
+      case MISCREG_AFSR1_EL12:
+        return MISCREG_AFSR1_EL1;
+      case MISCREG_ESR_EL12:
+        return MISCREG_ESR_EL1;
+      case MISCREG_FAR_EL12:
+        return MISCREG_FAR_EL1;
+      case MISCREG_MAIR_EL12:
+        return MISCREG_MAIR_EL1;
+      case MISCREG_AMAIR_EL12:
+        return MISCREG_AMAIR_EL1;
+      case MISCREG_VBAR_EL12:
+        return MISCREG_VBAR_EL1;
+      case MISCREG_CONTEXTIDR_EL12:
+        return MISCREG_CONTEXTIDR_EL1;
+      case MISCREG_CNTKCTL_EL12:
+        return MISCREG_CNTKCTL_EL1;
+      // _EL02 registers
+      case MISCREG_CNTP_TVAL_EL02:
+        return MISCREG_CNTP_TVAL_EL0;
+      case MISCREG_CNTP_CTL_EL02:
+        return MISCREG_CNTP_CTL_EL0;
+      case MISCREG_CNTP_CVAL_EL02:
+        return MISCREG_CNTP_CVAL_EL0;
+      case MISCREG_CNTV_TVAL_EL02:
+        return MISCREG_CNTV_TVAL_EL0;
+      case MISCREG_CNTV_CTL_EL02:
+        return MISCREG_CNTV_CTL_EL0;
+      case MISCREG_CNTV_CVAL_EL02:
+        return MISCREG_CNTV_CVAL_EL0;
+      default:
+        return misc_reg;
+    }
+}
+
 RegVal
 ISA::readMiscRegNoEffect(int misc_reg) const
 {
@@ -776,9 +944,9 @@
       case MISCREG_NZCV:
         {
             CPSR cpsr = 0;
-            cpsr.nz   = tc->readCCReg(CCREG_NZ);
-            cpsr.c    = tc->readCCReg(CCREG_C);
-            cpsr.v    = tc->readCCReg(CCREG_V);
+            cpsr.nz   = tc->getReg(cc_reg::Nz);
+            cpsr.c    = tc->getReg(cc_reg::C);
+            cpsr.v    = tc->getReg(cc_reg::V);
             return cpsr;
         }
       case MISCREG_DAIF:
@@ -789,15 +957,15 @@
         }
       case MISCREG_SP_EL0:
         {
-            return tc->readIntReg(INTREG_SP0);
+            return tc->getReg(int_reg::Sp0);
         }
       case MISCREG_SP_EL1:
         {
-            return tc->readIntReg(INTREG_SP1);
+            return tc->getReg(int_reg::Sp1);
         }
       case MISCREG_SP_EL2:
         {
-            return tc->readIntReg(INTREG_SP2);
+            return tc->getReg(int_reg::Sp2);
         }
       case MISCREG_SPSEL:
         {
@@ -1551,666 +1719,6 @@
             return;
 
           // TLB Invalidate All
-          case MISCREG_TLBIALL: // TLBI all entries, EL0&1,
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIALL tlbiOp(EL1, secure);
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate All, Inner Shareable
-          case MISCREG_TLBIALLIS:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIALL tlbiOp(EL1, secure);
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // Instruction TLB Invalidate All
-          case MISCREG_ITLBIALL:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                ITLBIALL tlbiOp(EL1, secure);
-                tlbiOp(tc);
-                return;
-            }
-          // Data TLB Invalidate All
-          case MISCREG_DTLBIALL:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                DTLBIALL tlbiOp(EL1, secure);
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate by VA
-          // mcr tlbimval(is) is invalidating all matching entries
-          // regardless of the level of lookup, since in gem5 we cache
-          // in the tlb the last level of lookup only.
-          case MISCREG_TLBIMVA:
-          case MISCREG_TLBIMVAL:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVA tlbiOp(EL1,
-                               secure,
-                               mbits(newVal, 31, 12),
-                               bits(newVal, 7,0));
-
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate by VA, Inner Shareable
-          case MISCREG_TLBIMVAIS:
-          case MISCREG_TLBIMVALIS:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVA tlbiOp(EL1,
-                               secure,
-                               mbits(newVal, 31, 12),
-                               bits(newVal, 7,0));
-
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // TLB Invalidate by ASID match
-          case MISCREG_TLBIASID:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIASID tlbiOp(EL1,
-                                secure,
-                                bits(newVal, 7,0));
-
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate by ASID match, Inner Shareable
-          case MISCREG_TLBIASIDIS:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIASID tlbiOp(EL1,
-                                secure,
-                                bits(newVal, 7,0));
-
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // mcr tlbimvaal(is) is invalidating all matching entries
-          // regardless of the level of lookup, since in gem5 we cache
-          // in the tlb the last level of lookup only.
-          // TLB Invalidate by VA, All ASID
-          case MISCREG_TLBIMVAA:
-          case MISCREG_TLBIMVAAL:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVAA tlbiOp(EL1, secure,
-                                mbits(newVal, 31,12));
-
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate by VA, All ASID, Inner Shareable
-          case MISCREG_TLBIMVAAIS:
-          case MISCREG_TLBIMVAALIS:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVAA tlbiOp(EL1, secure,
-                                mbits(newVal, 31,12));
-
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // mcr tlbimvalh(is) is invalidating all matching entries
-          // regardless of the level of lookup, since in gem5 we cache
-          // in the tlb the last level of lookup only.
-          // TLB Invalidate by VA, Hyp mode
-          case MISCREG_TLBIMVAH:
-          case MISCREG_TLBIMVALH:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVAA tlbiOp(EL2, secure,
-                                mbits(newVal, 31,12));
-
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate by VA, Hyp mode, Inner Shareable
-          case MISCREG_TLBIMVAHIS:
-          case MISCREG_TLBIMVALHIS:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVAA tlbiOp(EL2, secure,
-                                mbits(newVal, 31,12));
-
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // mcr tlbiipas2l(is) is invalidating all matching entries
-          // regardless of the level of lookup, since in gem5 we cache
-          // in the tlb the last level of lookup only.
-          // TLB Invalidate by Intermediate Physical Address, Stage 2
-          case MISCREG_TLBIIPAS2:
-          case MISCREG_TLBIIPAS2L:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIIPA tlbiOp(EL1,
-                               secure,
-                               static_cast<Addr>(bits(newVal, 35, 0)) << 12);
-
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate by Intermediate Physical Address, Stage 2,
-          // Inner Shareable
-          case MISCREG_TLBIIPAS2IS:
-          case MISCREG_TLBIIPAS2LIS:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIIPA tlbiOp(EL1,
-                               secure,
-                               static_cast<Addr>(bits(newVal, 35, 0)) << 12);
-
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // Instruction TLB Invalidate by VA
-          case MISCREG_ITLBIMVA:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                ITLBIMVA tlbiOp(EL1,
-                                secure,
-                                mbits(newVal, 31, 12),
-                                bits(newVal, 7,0));
-
-                tlbiOp(tc);
-                return;
-            }
-          // Data TLB Invalidate by VA
-          case MISCREG_DTLBIMVA:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                DTLBIMVA tlbiOp(EL1,
-                                secure,
-                                mbits(newVal, 31, 12),
-                                bits(newVal, 7,0));
-
-                tlbiOp(tc);
-                return;
-            }
-          // Instruction TLB Invalidate by ASID match
-          case MISCREG_ITLBIASID:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                ITLBIASID tlbiOp(EL1,
-                                 secure,
-                                 bits(newVal, 7,0));
-
-                tlbiOp(tc);
-                return;
-            }
-          // Data TLB Invalidate by ASID match
-          case MISCREG_DTLBIASID:
-            {
-                assert32();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                DTLBIASID tlbiOp(EL1,
-                                 secure,
-                                 bits(newVal, 7,0));
-
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate All, Non-Secure Non-Hyp
-          case MISCREG_TLBIALLNSNH:
-            {
-                assert32();
-
-                TLBIALLN tlbiOp(EL1);
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate All, Non-Secure Non-Hyp, Inner Shareable
-          case MISCREG_TLBIALLNSNHIS:
-            {
-                assert32();
-
-                TLBIALLN tlbiOp(EL1);
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // TLB Invalidate All, Hyp mode
-          case MISCREG_TLBIALLH:
-            {
-                assert32();
-
-                TLBIALLN tlbiOp(EL2);
-                tlbiOp(tc);
-                return;
-            }
-          // TLB Invalidate All, Hyp mode, Inner Shareable
-          case MISCREG_TLBIALLHIS:
-            {
-                assert32();
-
-                TLBIALLN tlbiOp(EL2);
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate All, EL3
-          case MISCREG_TLBI_ALLE3:
-            {
-                assert64();
-
-                TLBIALLEL tlbiOp(EL3, true);
-                tlbiOp(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate All, EL3, Inner Shareable
-          case MISCREG_TLBI_ALLE3IS:
-            {
-                assert64();
-
-                TLBIALLEL tlbiOp(EL3, true);
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate All, EL2
-          case MISCREG_TLBI_ALLE2:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIALLEL tlbiOp(EL2, secure);
-                tlbiOp(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate All, EL2, Inner Shareable
-          case MISCREG_TLBI_ALLE2IS:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIALLEL tlbiOp(EL2, secure);
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate All, EL1
-          case MISCREG_TLBI_ALLE1:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIALLEL tlbiOp(EL1, secure);
-                tlbiOp(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate All, EL1, Inner Shareable
-          case MISCREG_TLBI_ALLE1IS:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIALLEL tlbiOp(EL1, secure);
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          case MISCREG_TLBI_VMALLS12E1:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIVMALL tlbiOp(EL1, secure, true);
-                tlbiOp(tc);
-                return;
-            }
-          case MISCREG_TLBI_VMALLE1:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                ExceptionLevel target_el = EL1;
-                if (EL2Enabled(tc)) {
-                    HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-                    if (hcr.tge && hcr.e2h) {
-                        target_el = EL2;
-                    }
-                }
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIVMALL tlbiOp(target_el, secure, false);
-                tlbiOp(tc);
-                return;
-            }
-          case MISCREG_TLBI_VMALLS12E1IS:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIVMALL tlbiOp(EL1, secure, true);
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          case MISCREG_TLBI_VMALLE1IS:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                ExceptionLevel target_el = EL1;
-                if (EL2Enabled(tc)) {
-                    HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-                    if (hcr.tge && hcr.e2h) {
-                        target_el = EL2;
-                    }
-                }
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIVMALL tlbiOp(target_el, secure, false);
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // VAEx(IS) and VALEx(IS) are the same because TLBs
-          // only store entries
-          // from the last level of translation table walks
-          // AArch64 TLB Invalidate by VA, EL3
-          case MISCREG_TLBI_VAE3_Xt:
-          case MISCREG_TLBI_VALE3_Xt:
-            {
-                assert64();
-
-                TLBIMVAA tlbiOp(EL3, true,
-                                static_cast<Addr>(bits(newVal, 43, 0)) << 12);
-                tlbiOp(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate by VA, EL3, Inner Shareable
-          case MISCREG_TLBI_VAE3IS_Xt:
-          case MISCREG_TLBI_VALE3IS_Xt:
-            {
-                assert64();
-
-                TLBIMVAA tlbiOp(EL3, true,
-                                static_cast<Addr>(bits(newVal, 43, 0)) << 12);
-
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate by VA, EL2
-          case MISCREG_TLBI_VAE2_Xt:
-          case MISCREG_TLBI_VALE2_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-                HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-
-                if (hcr.e2h) {
-                    // The asid will only be used when e2h == 1
-                    auto asid = haveLargeAsid64 ? bits(newVal, 63, 48) :
-                                                  bits(newVal, 55, 48);
-
-                    TLBIMVA tlbiOp(EL2, secure,
-                                   static_cast<Addr>(bits(newVal, 43, 0)) << 12,
-                                   asid);
-                    tlbiOp(tc);
-                } else {
-                    TLBIMVAA tlbiOp(EL2, secure,
-                                    static_cast<Addr>(bits(newVal, 43, 0)) << 12);
-                    tlbiOp(tc);
-                }
-                return;
-            }
-          // AArch64 TLB Invalidate by VA, EL2, Inner Shareable
-          case MISCREG_TLBI_VAE2IS_Xt:
-          case MISCREG_TLBI_VALE2IS_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-                HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-
-                if (hcr.e2h) {
-                    // The asid will only be used when e2h == 1
-                    auto asid = haveLargeAsid64 ? bits(newVal, 63, 48) :
-                                                  bits(newVal, 55, 48);
-
-                    TLBIMVA tlbiOp(EL2, secure,
-                                   static_cast<Addr>(bits(newVal, 43, 0)) << 12,
-                                   asid);
-                    tlbiOp.broadcast(tc);
-                } else {
-                    TLBIMVAA tlbiOp(EL2, secure,
-                                    static_cast<Addr>(bits(newVal, 43, 0)) << 12);
-                    tlbiOp.broadcast(tc);
-                }
-                return;
-            }
-          // AArch64 TLB Invalidate by VA, EL1
-          case MISCREG_TLBI_VAE1_Xt:
-          case MISCREG_TLBI_VALE1_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-                auto asid = haveLargeAsid64 ? bits(newVal, 63, 48) :
-                                              bits(newVal, 55, 48);
-
-                ExceptionLevel target_el = EL1;
-                if (EL2Enabled(tc)) {
-                    HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-                    if (hcr.tge && hcr.e2h) {
-                        target_el = EL2;
-                    }
-                }
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVA tlbiOp(target_el, secure,
-                               static_cast<Addr>(bits(newVal, 43, 0)) << 12,
-                               asid);
-
-                tlbiOp(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate by VA, EL1, Inner Shareable
-          case MISCREG_TLBI_VAE1IS_Xt:
-          case MISCREG_TLBI_VALE1IS_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-                auto asid = haveLargeAsid64 ? bits(newVal, 63, 48) :
-                                              bits(newVal, 55, 48);
-
-                ExceptionLevel target_el = EL1;
-                if (EL2Enabled(tc)) {
-                    HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-                    if (hcr.tge && hcr.e2h) {
-                        target_el = EL2;
-                    }
-                }
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVA tlbiOp(target_el, secure,
-                                static_cast<Addr>(bits(newVal, 43, 0)) << 12,
-                                asid);
-
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate by ASID, EL1
-          case MISCREG_TLBI_ASIDE1_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-                auto asid = haveLargeAsid64 ? bits(newVal, 63, 48) :
-                                              bits(newVal, 55, 48);
-
-                ExceptionLevel target_el = EL1;
-                if (EL2Enabled(tc)) {
-                    HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-                    if (hcr.tge && hcr.e2h) {
-                        target_el = EL2;
-                    }
-                }
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIASID tlbiOp(target_el, secure, asid);
-                tlbiOp(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate by ASID, EL1, Inner Shareable
-          case MISCREG_TLBI_ASIDE1IS_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-                auto asid = haveLargeAsid64 ? bits(newVal, 63, 48) :
-                                              bits(newVal, 55, 48);
-
-                ExceptionLevel target_el = EL1;
-                if (EL2Enabled(tc)) {
-                    HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-                    if (hcr.tge && hcr.e2h) {
-                        target_el = EL2;
-                    }
-                }
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIASID tlbiOp(target_el, secure, asid);
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // VAAE1(IS) and VAALE1(IS) are the same because TLBs only store
-          // entries from the last level of translation table walks
-          // AArch64 TLB Invalidate by VA, All ASID, EL1
-          case MISCREG_TLBI_VAAE1_Xt:
-          case MISCREG_TLBI_VAALE1_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                ExceptionLevel target_el = EL1;
-                if (EL2Enabled(tc)) {
-                    HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-                    if (hcr.tge && hcr.e2h) {
-                        target_el = EL2;
-                    }
-                }
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVAA tlbiOp(target_el, secure,
-                    static_cast<Addr>(bits(newVal, 43, 0)) << 12);
-
-                tlbiOp(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate by VA, All ASID, EL1, Inner Shareable
-          case MISCREG_TLBI_VAAE1IS_Xt:
-          case MISCREG_TLBI_VAALE1IS_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                ExceptionLevel target_el = EL1;
-                if (EL2Enabled(tc)) {
-                    HCR hcr = readMiscReg(MISCREG_HCR_EL2);
-                    if (hcr.tge && hcr.e2h) {
-                        target_el = EL2;
-                    }
-                }
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIMVAA tlbiOp(target_el, secure,
-                    static_cast<Addr>(bits(newVal, 43, 0)) << 12);
-
-                tlbiOp.broadcast(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate by Intermediate Physical Address,
-          // Stage 2, EL1
-          case MISCREG_TLBI_IPAS2E1_Xt:
-          case MISCREG_TLBI_IPAS2LE1_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIIPA tlbiOp(EL1, secure,
-                               static_cast<Addr>(bits(newVal, 35, 0)) << 12);
-
-                tlbiOp(tc);
-                return;
-            }
-          // AArch64 TLB Invalidate by Intermediate Physical Address,
-          // Stage 2, EL1, Inner Shareable
-          case MISCREG_TLBI_IPAS2E1IS_Xt:
-          case MISCREG_TLBI_IPAS2LE1IS_Xt:
-            {
-                assert64();
-                scr = readMiscReg(MISCREG_SCR);
-
-                bool secure = release->has(ArmExtension::SECURITY) && !scr.ns;
-                TLBIIPA tlbiOp(EL1, secure,
-                               static_cast<Addr>(bits(newVal, 35, 0)) << 12);
-
-                tlbiOp.broadcast(tc);
-                return;
-            }
           case MISCREG_ACTLR:
             warn("Not doing anything for write of miscreg ACTLR\n");
             break;
@@ -2377,9 +1885,9 @@
             {
                 CPSR cpsr = val;
 
-                tc->setCCReg(CCREG_NZ, cpsr.nz);
-                tc->setCCReg(CCREG_C,  cpsr.c);
-                tc->setCCReg(CCREG_V,  cpsr.v);
+                tc->setReg(cc_reg::Nz, cpsr.nz);
+                tc->setReg(cc_reg::C,  cpsr.c);
+                tc->setReg(cc_reg::V,  cpsr.v);
             }
             break;
           case MISCREG_DAIF:
@@ -2391,13 +1899,13 @@
             }
             break;
           case MISCREG_SP_EL0:
-            tc->setIntReg(INTREG_SP0, newVal);
+            tc->setReg(int_reg::Sp0, newVal);
             break;
           case MISCREG_SP_EL1:
-            tc->setIntReg(INTREG_SP1, newVal);
+            tc->setReg(int_reg::Sp1, newVal);
             break;
           case MISCREG_SP_EL2:
-            tc->setIntReg(INTREG_SP2, newVal);
+            tc->setReg(int_reg::Sp2, newVal);
             break;
           case MISCREG_SPSEL:
             {
diff --git a/src/arch/arm/isa.hh b/src/arch/arm/isa.hh
index 2afcc51..599411f 100644
--- a/src/arch/arm/isa.hh
+++ b/src/arch/arm/isa.hh
@@ -567,39 +567,39 @@
         void initializeMiscRegMetadata();
 
         RegVal miscRegs[NUM_MISCREGS];
-        const IntRegIndex *intRegMap;
+        const RegId *intRegMap;
 
         void
         updateRegMap(CPSR cpsr)
         {
             if (cpsr.width == 0) {
-                intRegMap = IntReg64Map;
+                intRegMap = int_reg::Reg64Map;
             } else {
                 switch (cpsr.mode) {
                   case MODE_USER:
                   case MODE_SYSTEM:
-                    intRegMap = IntRegUsrMap;
+                    intRegMap = int_reg::RegUsrMap;
                     break;
                   case MODE_FIQ:
-                    intRegMap = IntRegFiqMap;
+                    intRegMap = int_reg::RegFiqMap;
                     break;
                   case MODE_IRQ:
-                    intRegMap = IntRegIrqMap;
+                    intRegMap = int_reg::RegIrqMap;
                     break;
                   case MODE_SVC:
-                    intRegMap = IntRegSvcMap;
+                    intRegMap = int_reg::RegSvcMap;
                     break;
                   case MODE_MON:
-                    intRegMap = IntRegMonMap;
+                    intRegMap = int_reg::RegMonMap;
                     break;
                   case MODE_ABORT:
-                    intRegMap = IntRegAbtMap;
+                    intRegMap = int_reg::RegAbtMap;
                     break;
                   case MODE_HYP:
-                    intRegMap = IntRegHypMap;
+                    intRegMap = int_reg::RegHypMap;
                     break;
                   case MODE_UNDEFINED:
-                    intRegMap = IntRegUndMap;
+                    intRegMap = int_reg::RegUndMap;
                     break;
                   default:
                     panic("Unrecognized mode setting in CPSR.\n");
@@ -610,10 +610,6 @@
         BaseISADevice &getGenericTimer();
         BaseISADevice &getGICv3CPUInterface();
 
-      private:
-        void assert32() { assert(((CPSR)readMiscReg(MISCREG_CPSR)).width); }
-        void assert64() { assert(!((CPSR)readMiscReg(MISCREG_CPSR)).width); }
-
       public:
         void clear();
 
@@ -642,6 +638,8 @@
             return arm_isa->getSelfDebug();
         }
 
+        const ArmRelease* getRelease() const { return release; }
+
         RegVal readMiscRegNoEffect(int misc_reg) const;
         RegVal readMiscReg(int misc_reg);
         void setMiscRegNoEffect(int misc_reg, RegVal val);
@@ -658,8 +656,7 @@
               case VecRegClass:
                 return RegId(VecRegClass, flattenVecIndex(regId.index()));
               case VecElemClass:
-                return RegId(VecElemClass, flattenVecElemIndex(regId.index()),
-                             regId.elemIndex());
+                return RegId(VecElemClass, flattenVecElemIndex(regId.index()));
               case VecPredRegClass:
                 return RegId(VecPredRegClass,
                              flattenVecPredIndex(regId.index()));
@@ -667,33 +664,35 @@
                 return RegId(CCRegClass, flattenCCIndex(regId.index()));
               case MiscRegClass:
                 return RegId(MiscRegClass, flattenMiscIndex(regId.index()));
+              case InvalidRegClass:
+                return RegId();
             }
-            return RegId();
+            panic("Unrecognized register class %d.", regId.classValue());
         }
 
         int
         flattenIntIndex(int reg) const
         {
             assert(reg >= 0);
-            if (reg < NUM_ARCH_INTREGS) {
+            if (reg < int_reg::NumArchRegs) {
                 return intRegMap[reg];
-            } else if (reg < NUM_INTREGS) {
+            } else if (reg < int_reg::NumRegs) {
                 return reg;
-            } else if (reg == INTREG_SPX) {
+            } else if (reg == int_reg::Spx) {
                 CPSR cpsr = miscRegs[MISCREG_CPSR];
                 ExceptionLevel el = opModeToEL(
                     (OperatingMode) (uint8_t) cpsr.mode);
                 if (!cpsr.sp && el != EL0)
-                    return INTREG_SP0;
+                    return int_reg::Sp0;
                 switch (el) {
                   case EL3:
-                    return INTREG_SP3;
+                    return int_reg::Sp3;
                   case EL2:
-                    return INTREG_SP2;
+                    return int_reg::Sp2;
                   case EL1:
-                    return INTREG_SP1;
+                    return int_reg::Sp1;
                   case EL0:
-                    return INTREG_SP0;
+                    return int_reg::Sp0;
                   default:
                     panic("Invalid exception level");
                     return 0;  // Never happens.
@@ -858,73 +857,7 @@
          * Returns the enconcing equivalent when VHE is implemented and
          * HCR_EL2.E2H is enabled and executing at EL2
          */
-        int
-        redirectRegVHE(int misc_reg)
-        {
-            const HCR hcr = readMiscRegNoEffect(MISCREG_HCR_EL2);
-            if (hcr.e2h == 0x0 || currEL() != EL2)
-                return misc_reg;
-            SCR scr = readMiscRegNoEffect(MISCREG_SCR_EL3);
-            bool sec_el2 = scr.eel2 && release->has(ArmExtension::FEAT_SEL2);
-            switch(misc_reg) {
-              case MISCREG_SPSR_EL1:
-                  return MISCREG_SPSR_EL2;
-              case MISCREG_ELR_EL1:
-                  return MISCREG_ELR_EL2;
-              case MISCREG_SCTLR_EL1:
-                  return MISCREG_SCTLR_EL2;
-              case MISCREG_CPACR_EL1:
-                  return MISCREG_CPTR_EL2;
-        //      case :
-        //          return MISCREG_TRFCR_EL2;
-              case MISCREG_TTBR0_EL1:
-                  return MISCREG_TTBR0_EL2;
-              case MISCREG_TTBR1_EL1:
-                  return MISCREG_TTBR1_EL2;
-              case MISCREG_TCR_EL1:
-                  return MISCREG_TCR_EL2;
-              case MISCREG_AFSR0_EL1:
-                  return MISCREG_AFSR0_EL2;
-              case MISCREG_AFSR1_EL1:
-                  return MISCREG_AFSR1_EL2;
-              case MISCREG_ESR_EL1:
-                  return MISCREG_ESR_EL2;
-              case MISCREG_FAR_EL1:
-                  return MISCREG_FAR_EL2;
-              case MISCREG_MAIR_EL1:
-                  return MISCREG_MAIR_EL2;
-              case MISCREG_AMAIR_EL1:
-                  return MISCREG_AMAIR_EL2;
-              case MISCREG_VBAR_EL1:
-                  return MISCREG_VBAR_EL2;
-              case MISCREG_CONTEXTIDR_EL1:
-                  return MISCREG_CONTEXTIDR_EL2;
-              case MISCREG_CNTKCTL_EL1:
-                  return MISCREG_CNTHCTL_EL2;
-              case MISCREG_CNTP_TVAL_EL0:
-                  return sec_el2? MISCREG_CNTHPS_TVAL_EL2:
-                                 MISCREG_CNTHP_TVAL_EL2;
-              case MISCREG_CNTP_CTL_EL0:
-                  return sec_el2? MISCREG_CNTHPS_CTL_EL2:
-                                 MISCREG_CNTHP_CTL_EL2;
-              case MISCREG_CNTP_CVAL_EL0:
-                  return sec_el2? MISCREG_CNTHPS_CVAL_EL2:
-                                 MISCREG_CNTHP_CVAL_EL2;
-              case MISCREG_CNTV_TVAL_EL0:
-                  return sec_el2? MISCREG_CNTHVS_TVAL_EL2:
-                                 MISCREG_CNTHV_TVAL_EL2;
-              case MISCREG_CNTV_CTL_EL0:
-                  return sec_el2? MISCREG_CNTHVS_CTL_EL2:
-                                 MISCREG_CNTHV_CTL_EL2;
-              case MISCREG_CNTV_CVAL_EL0:
-                  return sec_el2? MISCREG_CNTHVS_CVAL_EL2:
-                                 MISCREG_CNTHV_CVAL_EL2;
-              default:
-                  return misc_reg;
-            }
-            /*should not be accessible */
-            return misc_reg;
-        }
+        int redirectRegVHE(int misc_reg);
 
         int
         snsBankedIndex64(MiscRegIndex reg, bool ns) const
diff --git a/src/arch/arm/isa/formats/aarch64.isa b/src/arch/arm/isa/formats/aarch64.isa
index 0c67645..a55d0dd 100644
--- a/src/arch/arm/isa/formats/aarch64.isa
+++ b/src/arch/arm/isa/formats/aarch64.isa
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2020 ARM Limited
+// Copyright (c) 2011-2022 Arm Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -37,7 +37,7 @@
 namespace Aarch64
 {
     StaticInstPtr decodeDataProcImm(ExtMachInst machInst);
-    StaticInstPtr decodeBranchExcSys(ExtMachInst machInst);
+    StaticInstPtr decodeBranchExcSys(const Decoder &dec, ExtMachInst machInst);
     StaticInstPtr decodeLoadsStores(ExtMachInst machInst);
     StaticInstPtr decodeDataProcReg(ExtMachInst machInst);
 
@@ -62,11 +62,11 @@
     StaticInstPtr
     decodeDataProcImm(ExtMachInst machInst)
     {
-        IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-        IntRegIndex rdsp = makeSP(rd);
-        IntRegIndex rdzr = makeZero(rd);
-        IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 9, 5);
-        IntRegIndex rnsp = makeSP(rn);
+        RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 4, 0);
+        RegIndex rdsp = makeSP(rd);
+        RegIndex rdzr = makeZero(rd);
+        RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 9, 5);
+        RegIndex rnsp = makeSP(rn);
 
         uint8_t opc = bits(machInst, 30, 29);
         bool sf = bits(machInst, 31);
@@ -81,9 +81,10 @@
             uint64_t immhi = bits(machInst, 23, 5);
             uint64_t imm = (immlo << 0) | (immhi << 2);
             if (bits(machInst, 31) == 0)
-                return new AdrXImm(machInst, rdzr, INTREG_ZERO, sext<21>(imm));
+                return new AdrXImm(machInst, rdzr, int_reg::Zero,
+                                   sext<21>(imm));
             else
-                return new AdrpXImm(machInst, rdzr, INTREG_ZERO,
+                return new AdrpXImm(machInst, rdzr, int_reg::Zero,
                                     sext<33>(imm << 12));
           }
           case 0x2:
@@ -157,8 +158,8 @@
           }
           case 0x5:
           {
-            IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-            IntRegIndex rdzr = makeZero(rd);
+            RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 4, 0);
+            RegIndex rdzr = makeZero(rd);
             uint32_t imm16 = bits(machInst, 20, 5);
             uint32_t hw = bits(machInst, 22, 21);
             switch (opc) {
@@ -191,7 +192,7 @@
             }
           case 0x7:
           {
-            IntRegIndex rm = (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+            RegIndex rm = (RegIndex)(uint8_t)bits(machInst, 20, 16);
             if (opc || bits(machInst, 21))
                 return new Unknown64(machInst);
             else
@@ -207,7 +208,7 @@
 namespace Aarch64
 {
     StaticInstPtr
-    decodeBranchExcSys(ExtMachInst machInst)
+    decodeBranchExcSys(const Decoder &dec, ExtMachInst machInst)
     {
         switch (bits(machInst, 30, 29)) {
           case 0x0:
@@ -220,7 +221,7 @@
           }
           case 0x1:
           {
-            IntRegIndex rt = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
+            RegIndex rt = (RegIndex)(uint8_t)bits(machInst, 4, 0);
             if (bits(machInst, 25) == 0) {
                 int64_t imm = sext<19>(bits(machInst, 23, 5)) << 2;
                 if (bits(machInst, 24) == 0)
@@ -287,7 +288,7 @@
                 uint8_t crn = bits(machInst, 15, 12);
                 uint8_t crm = bits(machInst, 11, 8);
                 uint8_t op2 = bits(machInst, 7, 5);
-                IntRegIndex rt = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
+                RegIndex rt = (RegIndex)(uint8_t)bits(machInst, 4, 0);
                 switch (op0) {
                   case 0x0:
                     // early out for TME
@@ -323,23 +324,23 @@
                               case 0x5:
                                 return new SevlInst(machInst);
                               case 0x7:
-                                return new Xpaclri(machInst, INTREG_X30);
+                                return new Xpaclri(machInst, int_reg::X30);
                             }
                             break;
                           case 0x1:
                             switch (op2) {
                               case 0x0:
-                                return new Pacia1716(machInst, INTREG_X17,
-                                                               INTREG_X16);
+                                return new Pacia1716(machInst, int_reg::X17,
+                                                               int_reg::X16);
                               case 0x2:
-                                return new Pacib1716(machInst, INTREG_X17,
-                                                               INTREG_X16);
+                                return new Pacib1716(machInst, int_reg::X17,
+                                                               int_reg::X16);
                               case 0x4:
-                                return new Autia1716(machInst, INTREG_X17,
-                                                               INTREG_X16);
+                                return new Autia1716(machInst, int_reg::X17,
+                                                               int_reg::X16);
                               case 0x6:
-                                return new Autib1716(machInst, INTREG_X17,
-                                                               INTREG_X16);
+                                return new Autib1716(machInst, int_reg::X17,
+                                                               int_reg::X16);
                             }
                             break;
                           case 0x2:
@@ -362,28 +363,28 @@
                             switch (op2) {
                               case 0x0:
                                 return new Paciaz(machInst,
-                                         INTREG_X30, INTREG_ZERO);
+                                         int_reg::X30, int_reg::Zero);
                               case 0x1:
                                 return new Paciasp(machInst,
-                                         INTREG_X30, INTREG_SPX);
+                                         int_reg::X30, int_reg::Spx);
                               case 0x2:
                                 return new Pacibz(machInst,
-                                         INTREG_X30, INTREG_ZERO);
+                                         int_reg::X30, int_reg::Zero);
                               case 0x3:
                                 return new Pacibsp(machInst,
-                                         INTREG_X30, INTREG_SPX);
+                                         int_reg::X30, int_reg::Spx);
                               case 0x4:
                                 return new Autiaz(machInst,
-                                         INTREG_X30, INTREG_ZERO);
+                                         int_reg::X30, int_reg::Zero);
                               case 0x5:
                                 return new Autiasp(machInst,
-                                         INTREG_X30, INTREG_SPX);
+                                         int_reg::X30, int_reg::Spx);
                               case 0x6:
                                 return new Autibz(machInst,
-                                         INTREG_X30, INTREG_ZERO);
+                                         int_reg::X30, int_reg::Zero);
                               case 0x7:
                                 return new Autibsp(machInst,
-                                         INTREG_X30, INTREG_SPX);
+                                         int_reg::X30, int_reg::Spx);
                             }
                             break;
                           case 0x4:
@@ -401,7 +402,17 @@
                           case 0x2:
                             return new Clrex64(machInst);
                           case 0x4:
-                            return new Dsb64(machInst);
+                            switch (bits(crm, 3, 2)) {
+                              case 0x1: // Non-Shareable
+                                return new Dsb64Local(machInst);
+                              case 0x0: // OuterShareable
+                              case 0x2: // InnerShareable
+                              case 0x3: // FullSystem
+                                return new Dsb64Shareable(
+                                    machInst, dec.dvmEnabled);
+                              default:
+                                GEM5_UNREACHABLE;
+                            }
                           case 0x5:
                             return new Dmb64(machInst);
                           case 0x6:
@@ -483,13 +494,15 @@
                     } else if (miscRegInfo[miscReg][MISCREG_IMPLEMENTED]) {
                         if (miscReg == MISCREG_NZCV) {
                             if (read)
-                                return new MrsNZCV64(machInst, rt, (IntRegIndex) miscReg);
+                                return new MrsNZCV64(machInst, rt, miscReg);
                             else
-                                return new MsrNZCV64(machInst, (IntRegIndex) miscReg, rt);
+                                return new MsrNZCV64(machInst, miscReg, rt);
                         }
-                        uint32_t iss = msrMrs64IssBuild(read, op0, op1, crn, crm, op2, rt);
+                        uint32_t iss = msrMrs64IssBuild(read, op0, op1, crn,
+                                                        crm, op2, rt);
                         if (read) {
-                            StaticInstPtr si = new Mrs64(machInst, rt, miscReg, iss);
+                            StaticInstPtr si = new Mrs64(machInst, rt, miscReg,
+                                                         iss);
                             if (miscRegInfo[miscReg][MISCREG_UNVERIFIABLE])
                                 si->setFlag(StaticInst::IsUnverifiable);
                             return si;
@@ -505,6 +518,44 @@
                                 return new Dccivac(machInst, rt, miscReg, iss);
                               case MISCREG_DC_IVAC_Xt:
                                 return new Dcivac(machInst, rt, miscReg, iss);
+                              // 64-bit TLBIs split into "Local"
+                              // and "Shareable"
+                              case MISCREG_TLBI_ALLE3:
+                              case MISCREG_TLBI_ALLE2:
+                              case MISCREG_TLBI_ALLE1:
+                              case MISCREG_TLBI_VMALLS12E1:
+                              case MISCREG_TLBI_VMALLE1:
+                              case MISCREG_TLBI_VAE3_Xt:
+                              case MISCREG_TLBI_VALE3_Xt:
+                              case MISCREG_TLBI_VAE2_Xt:
+                              case MISCREG_TLBI_VALE2_Xt:
+                              case MISCREG_TLBI_VAE1_Xt:
+                              case MISCREG_TLBI_VALE1_Xt:
+                              case MISCREG_TLBI_ASIDE1_Xt:
+                              case MISCREG_TLBI_VAAE1_Xt:
+                              case MISCREG_TLBI_VAALE1_Xt:
+                              case MISCREG_TLBI_IPAS2E1_Xt:
+                              case MISCREG_TLBI_IPAS2LE1_Xt:
+                                return new Tlbi64LocalHub(
+                                  machInst, miscReg, rt, iss);
+                              case MISCREG_TLBI_ALLE3IS:
+                              case MISCREG_TLBI_ALLE2IS:
+                              case MISCREG_TLBI_ALLE1IS:
+                              case MISCREG_TLBI_VMALLS12E1IS:
+                              case MISCREG_TLBI_VMALLE1IS:
+                              case MISCREG_TLBI_VAE3IS_Xt:
+                              case MISCREG_TLBI_VALE3IS_Xt:
+                              case MISCREG_TLBI_VAE2IS_Xt:
+                              case MISCREG_TLBI_VALE2IS_Xt:
+                              case MISCREG_TLBI_VAE1IS_Xt:
+                              case MISCREG_TLBI_VALE1IS_Xt:
+                              case MISCREG_TLBI_ASIDE1IS_Xt:
+                              case MISCREG_TLBI_VAAE1IS_Xt:
+                              case MISCREG_TLBI_VAALE1IS_Xt:
+                              case MISCREG_TLBI_IPAS2E1IS_Xt:
+                              case MISCREG_TLBI_IPAS2LE1IS_Xt:
+                                return new Tlbi64ShareableHub(
+                                  machInst, miscReg, rt, iss, dec.dvmEnabled);
                               default:
                                 return new Msr64(machInst, miscReg, rt, iss);
                             }
@@ -530,8 +581,8 @@
                 uint8_t opc = bits(machInst, 24, 21);
                 uint8_t op2 = bits(machInst, 20, 16);
                 uint8_t op3 = bits(machInst, 15, 12);
-                IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-                IntRegIndex rm = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
+                RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+                RegIndex rm = (RegIndex)(uint8_t)bits(machInst, 4, 0);
                 uint8_t op4 = bits(machInst, 4, 0);
                 if (op2 != 0x1f || op3 != 0x0)
                     return new Unknown64(machInst);
@@ -617,10 +668,10 @@
         uint8_t opc  = bits(machInst, 14, 12);
         uint8_t o3  = bits(machInst, 15);
         uint8_t size_ar = bits(machInst, 23, 22)<<0 | bits(machInst, 31, 30)<<2;
-        IntRegIndex rt = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-        IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-        IntRegIndex rnsp = makeSP(rn);
-        IntRegIndex rs = (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+        RegIndex rt = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+        RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+        RegIndex rnsp = makeSP(rn);
+        RegIndex rs = (RegIndex)(uint8_t)bits(machInst, 20, 16);
         uint8_t  A_rt = bits(machInst, 4, 0)<<0 | bits(machInst, 23)<<5;
 
         switch(opc) {
@@ -1174,11 +1225,11 @@
             if (bits(machInst, 26) == 0) {
                 if (bits(machInst, 24) != 0)
                     return new Unknown64(machInst);
-                IntRegIndex rt = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-                IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-                IntRegIndex rnsp = makeSP(rn);
-                IntRegIndex rt2 = (IntRegIndex)(uint8_t)bits(machInst, 14, 10);
-                IntRegIndex rs = (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+                RegIndex rt = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+                RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+                RegIndex rnsp = makeSP(rn);
+                RegIndex rt2 = (RegIndex)(uint8_t)bits(machInst, 14, 10);
+                RegIndex rs = (RegIndex)(uint8_t)bits(machInst, 20, 16);
                 uint8_t opc = (bits(machInst, 15) << 0) |
                               (bits(machInst, 23, 21) << 1);
                 uint8_t size = bits(machInst, 31, 30);
@@ -1382,7 +1433,7 @@
             uint8_t switchVal = (bits(machInst, 26) << 0) |
                                 (bits(machInst, 31, 30) << 1);
             int64_t imm = sext<19>(bits(machInst, 23, 5)) << 2;
-            IntRegIndex rt = (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
+            RegIndex rt = (RegIndex)(uint32_t)bits(machInst, 4, 0);
             switch (switchVal) {
               case 0x0:
                 return new LDRWL64_LIT(machInst, rt, imm);
@@ -1420,9 +1471,9 @@
             uint8_t type = bits(machInst, 24, 23);
             int64_t imm = sext<7>(bits(machInst, 21, 15)) * size;
 
-            IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-            IntRegIndex rt = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-            IntRegIndex rt2 = (IntRegIndex)(uint8_t)bits(machInst, 14, 10);
+            RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+            RegIndex rt = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+            RegIndex rt2 = (RegIndex)(uint8_t)bits(machInst, 14, 10);
 
             bool noAlloc = (type == 0);
             bool signExt = !noAlloc && !fp && opc == 1;
@@ -1467,9 +1518,9 @@
                                 (bits(machInst, 31, 30) << 3);
             if (bits(machInst, 24) == 1) {
                 uint64_t imm12 = bits(machInst, 21, 10);
-                IntRegIndex rt = (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-                IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 9, 5);
-                IntRegIndex rnsp = makeSP(rn);
+                RegIndex rt = (RegIndex)(uint32_t)bits(machInst, 4, 0);
+                RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 9, 5);
+                RegIndex rnsp = makeSP(rn);
                 switch (switchVal) {
                   case 0x00:
                     return new STRB64_IMM(machInst, rt, rnsp, imm12);
@@ -1531,11 +1582,11 @@
                     {
                         if ((switchVal & 0x7) == 0x2 &&
                                 bits(machInst, 20, 12) == 0x1fc) {
-                            IntRegIndex rt = (IntRegIndex)(uint32_t)
+                            RegIndex rt = (RegIndex)(uint32_t)
                                 bits(machInst, 4, 0);
-                            IntRegIndex rn = (IntRegIndex)(uint32_t)
+                            RegIndex rn = (RegIndex)(uint32_t)
                                 bits(machInst, 9, 5);
-                            IntRegIndex rnsp = makeSP(rn);
+                            RegIndex rnsp = makeSP(rn);
                             uint8_t size = bits(machInst, 31, 30);
                             switch (size) {
                               case 0x0:
@@ -1556,9 +1607,9 @@
                   case 0x1:
                   case 0x3:
                     {
-                        IntRegIndex rt = (IntRegIndex)(uint32_t)
+                        RegIndex rt = (RegIndex)(uint32_t)
                                            bits(machInst, 4, 0);
-                        IntRegIndex rn = (IntRegIndex)(uint32_t)
+                        RegIndex rn = (RegIndex)(uint32_t)
                                            bits(machInst, 9, 5);
                         uint8_t  s = bits(machInst, 22);
                         uint64_t imm9 = bits(machInst, 20, 12);
@@ -1586,12 +1637,12 @@
                     {
                         if (!bits(machInst, 14))
                             return new Unknown64(machInst);
-                        IntRegIndex rt = (IntRegIndex)(uint32_t)
+                        RegIndex rt = (RegIndex)(uint32_t)
                             bits(machInst, 4, 0);
-                        IntRegIndex rn = (IntRegIndex)(uint32_t)
+                        RegIndex rn = (RegIndex)(uint32_t)
                             bits(machInst, 9, 5);
-                        IntRegIndex rnsp = makeSP(rn);
-                        IntRegIndex rm = (IntRegIndex)(uint32_t)
+                        RegIndex rnsp = makeSP(rn);
+                        RegIndex rm = (RegIndex)(uint32_t)
                             bits(machInst, 20, 16);
                         ArmExtendType type =
                             (ArmExtendType)(uint32_t)bits(machInst, 15, 13);
@@ -1682,11 +1733,11 @@
                 switch (bits(machInst, 11, 10)) {
                   case 0x0:
                   {
-                    IntRegIndex rt =
-                        (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-                    IntRegIndex rn =
-                        (IntRegIndex)(uint32_t)bits(machInst, 9, 5);
-                    IntRegIndex rnsp = makeSP(rn);
+                    RegIndex rt =
+                        (RegIndex)(uint32_t)bits(machInst, 4, 0);
+                    RegIndex rn =
+                        (RegIndex)(uint32_t)bits(machInst, 9, 5);
+                    RegIndex rnsp = makeSP(rn);
                     uint64_t imm = sext<9>(bits(machInst, 20, 12));
                     switch (switchVal) {
                       case 0x00:
@@ -1746,11 +1797,11 @@
                   // bit 29:27=111, 25:24=00, 21=0, 11:10=01
                   case 0x1:
                   {
-                    IntRegIndex rt =
-                        (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-                    IntRegIndex rn =
-                        (IntRegIndex)(uint32_t)bits(machInst, 9, 5);
-                    IntRegIndex rnsp = makeSP(rn);
+                    RegIndex rt =
+                        (RegIndex)(uint32_t)bits(machInst, 4, 0);
+                    RegIndex rn =
+                        (RegIndex)(uint32_t)bits(machInst, 9, 5);
+                    RegIndex rnsp = makeSP(rn);
                     uint64_t imm = sext<9>(bits(machInst, 20, 12));
                     switch (switchVal) {
                       case 0x00:
@@ -1807,11 +1858,11 @@
                   }
                   case 0x2:
                   {
-                    IntRegIndex rt =
-                        (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-                    IntRegIndex rn =
-                        (IntRegIndex)(uint32_t)bits(machInst, 9, 5);
-                    IntRegIndex rnsp = makeSP(rn);
+                    RegIndex rt =
+                        (RegIndex)(uint32_t)bits(machInst, 4, 0);
+                    RegIndex rn =
+                        (RegIndex)(uint32_t)bits(machInst, 9, 5);
+                    RegIndex rnsp = makeSP(rn);
                     uint64_t imm = sext<9>(bits(machInst, 20, 12));
                     switch (switchVal) {
                       case 0x00:
@@ -1846,11 +1897,11 @@
                   }
                   case 0x3:
                   {
-                    IntRegIndex rt =
-                        (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-                    IntRegIndex rn =
-                        (IntRegIndex)(uint32_t)bits(machInst, 9, 5);
-                    IntRegIndex rnsp = makeSP(rn);
+                    RegIndex rt =
+                        (RegIndex)(uint32_t)bits(machInst, 4, 0);
+                    RegIndex rn =
+                        (RegIndex)(uint32_t)bits(machInst, 9, 5);
+                    RegIndex rnsp = makeSP(rn);
                     uint64_t imm = sext<9>(bits(machInst, 20, 12));
                     switch (switchVal) {
                       case 0x00:
@@ -1936,10 +1987,10 @@
             bool sf = bits(machInst, 31);
             if (!sf && (imm6 & 0x20))
                 return new Unknown64(machInst);
-            IntRegIndex rd = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-            IntRegIndex rdzr = makeZero(rd);
-            IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-            IntRegIndex rm = (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+            RegIndex rd = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+            RegIndex rdzr = makeZero(rd);
+            RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+            RegIndex rm = (RegIndex)(uint8_t)bits(machInst, 20, 16);
 
             switch (switchVal) {
               case 0x0:
@@ -1973,10 +2024,10 @@
                 uint8_t imm6 = bits(machInst, 15, 10);
                 if (!bits(machInst, 31) && bits(imm6, 5))
                     return new Unknown64(machInst);
-                IntRegIndex rd = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-                IntRegIndex rdzr = makeZero(rd);
-                IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-                IntRegIndex rm = (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+                RegIndex rd = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+                RegIndex rdzr = makeZero(rd);
+                RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+                RegIndex rm = (RegIndex)(uint8_t)bits(machInst, 20, 16);
                 switch (switchVal) {
                   case 0x0:
                     return new AddXSReg(machInst, rdzr, rn, rm, imm6, type);
@@ -1995,12 +2046,12 @@
                 ArmExtendType type =
                     (ArmExtendType)(uint8_t)bits(machInst, 15, 13);
                 uint8_t imm3 = bits(machInst, 12, 10);
-                IntRegIndex rd = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-                IntRegIndex rdsp = makeSP(rd);
-                IntRegIndex rdzr = makeZero(rd);
-                IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-                IntRegIndex rnsp = makeSP(rn);
-                IntRegIndex rm = (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+                RegIndex rd = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+                RegIndex rdsp = makeSP(rd);
+                RegIndex rdzr = makeZero(rd);
+                RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+                RegIndex rnsp = makeSP(rn);
+                RegIndex rm = (RegIndex)(uint8_t)bits(machInst, 20, 16);
 
                 switch (switchVal) {
                   case 0x0:
@@ -2020,10 +2071,10 @@
           {
             if (bits(machInst, 21) == 1)
                 return new Unknown64(machInst);
-            IntRegIndex rd = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-            IntRegIndex rdzr = makeZero(rd);
-            IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-            IntRegIndex rm = (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+            RegIndex rd = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+            RegIndex rdzr = makeZero(rd);
+            RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+            RegIndex rm = (RegIndex)(uint8_t)bits(machInst, 20, 16);
             switch (bits(machInst, 23, 22)) {
               case 0x0:
               {
@@ -2053,10 +2104,10 @@
                 ConditionCode cond =
                     (ConditionCode)(uint8_t)bits(machInst, 15, 12);
                 uint8_t flags = bits(machInst, 3, 0);
-                IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
+                RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
                 if (bits(machInst, 11) == 0) {
-                    IntRegIndex rm =
-                        (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+                    RegIndex rm =
+                        (RegIndex)(uint8_t)bits(machInst, 20, 16);
                     if (bits(machInst, 30) == 0) {
                         return new CcmnReg64(machInst, rn, rm, cond, flags);
                     } else {
@@ -2079,10 +2130,10 @@
                 }
                 uint8_t switchVal = (bits(machInst, 10) << 0) |
                                     (bits(machInst, 30) << 1);
-                IntRegIndex rd = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-                IntRegIndex rdzr = makeZero(rd);
-                IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-                IntRegIndex rm = (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+                RegIndex rd = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+                RegIndex rdzr = makeZero(rd);
+                RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+                RegIndex rm = (RegIndex)(uint8_t)bits(machInst, 20, 16);
                 ConditionCode cond =
                     (ConditionCode)(uint8_t)bits(machInst, 15, 12);
                 switch (switchVal) {
@@ -2163,49 +2214,49 @@
                             case 0x8:
                                 if (rn == 0x1f)
                                     return new Paciza(machInst, rd,
-                                                      INTREG_ZERO);
+                                                      int_reg::Zero);
                                 else
                                     return new Unknown64(machInst);
                             case 0x9:
                                 if (rn == 0x1f)
                                     return new Pacizb(machInst, rd,
-                                                      INTREG_ZERO);
+                                                      int_reg::Zero);
                                 else
                                     return new Unknown64(machInst);
                             case 0xa:
                                 if (rn == 0x1f)
                                     return new Pacdza(machInst, rd,
-                                                      INTREG_ZERO);
+                                                      int_reg::Zero);
                                 else
                                     return new Unknown64(machInst);
                             case 0xb:
                                 if (rn == 0x1f)
                                     return new Pacdzb(machInst, rd,
-                                                      INTREG_ZERO);
+                                                      int_reg::Zero);
                                 else
                                     return new Unknown64(machInst);
                             case 0xc:
                                 if (rn == 0x1f)
                                     return new Autiza(machInst, rd,
-                                                      INTREG_ZERO);
+                                                      int_reg::Zero);
                                 else
                                     return new Unknown64(machInst);
                             case 0xd:
                                 if (rn == 0x1f)
                                     return new Autizb(machInst, rd,
-                                                      INTREG_ZERO);
+                                                      int_reg::Zero);
                                 else
                                     return new Unknown64(machInst);
                             case 0xe:
                                 if (rn == 0x1f)
                                     return new Autdza(machInst, rd,
-                                                      INTREG_ZERO);
+                                                      int_reg::Zero);
                                 else
                                     return new Unknown64(machInst);
                             case 0xf:
                                 if (rn == 0x1f)
                                     return new Autdzb(machInst, rd,
-                                                      INTREG_ZERO);
+                                                      int_reg::Zero);
                                 else
                                     return new Unknown64(machInst);
                             default:
@@ -2259,11 +2310,11 @@
             if (bits(machInst, 30, 29) != 0x0 ||
                     (bits(machInst, 23, 21) != 0 && bits(machInst, 31) == 0))
                 return new Unknown64(machInst);
-            IntRegIndex rd = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-            IntRegIndex rdzr = makeZero(rd);
-            IntRegIndex rn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
-            IntRegIndex ra = (IntRegIndex)(uint8_t)bits(machInst, 14, 10);
-            IntRegIndex rm = (IntRegIndex)(uint8_t)bits(machInst, 20, 16);
+            RegIndex rd = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+            RegIndex rdzr = makeZero(rd);
+            RegIndex rn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
+            RegIndex ra = (RegIndex)(uint8_t)bits(machInst, 14, 10);
+            RegIndex rm = (RegIndex)(uint8_t)bits(machInst, 20, 16);
             switch (bits(machInst, 23, 21)) {
               case 0x0:
                 if (bits(machInst, 15) == 0)
@@ -2364,10 +2415,10 @@
         if (bits(machInst, 24) == 1) {
             if (bits(machInst, 31) || bits(machInst, 29))
                 return new Unknown64(machInst);
-            IntRegIndex rd    = (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-            IntRegIndex rn    = (IntRegIndex)(uint32_t)bits(machInst, 9, 5);
-            IntRegIndex rm    = (IntRegIndex)(uint32_t)bits(machInst, 20, 16);
-            IntRegIndex ra    = (IntRegIndex)(uint32_t)bits(machInst, 14, 10);
+            RegIndex rd    = (RegIndex)(uint32_t)bits(machInst, 4, 0);
+            RegIndex rn    = (RegIndex)(uint32_t)bits(machInst, 9, 5);
+            RegIndex rm    = (RegIndex)(uint32_t)bits(machInst, 20, 16);
+            RegIndex ra    = (RegIndex)(uint32_t)bits(machInst, 14, 10);
             uint8_t switchVal = (bits(machInst, 23, 21) << 1) |
                                 (bits(machInst, 15)     << 0);
             switch (switchVal) {
@@ -2397,8 +2448,8 @@
             uint8_t switchVal = bits(machInst, 20, 16);
             uint8_t type      = bits(machInst, 23, 22);
             uint8_t scale     = bits(machInst, 15, 10);
-            IntRegIndex rd    = (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-            IntRegIndex rn    = (IntRegIndex)(uint32_t)bits(machInst, 9, 5);
+            RegIndex rd    = (RegIndex)(uint32_t)bits(machInst, 4, 0);
+            RegIndex rn    = (RegIndex)(uint32_t)bits(machInst, 9, 5);
             if (bits(machInst, 18, 17) == 3 && scale != 0)
                 return new Unknown64(machInst);
             // 30:24=0011110, 21=0
@@ -2482,8 +2533,8 @@
             // 30=0, 28:24=11110, 21=1
             uint8_t type   = bits(machInst, 23, 22);
             uint8_t imm8   = bits(machInst, 20, 13);
-            IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 4, 0);
-            IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 9, 5);
+            RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 4, 0);
+            RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 9, 5);
             switch (bits(machInst, 11, 10)) {
               case 0x0:
                 if (bits(machInst, 12) == 1) {
@@ -2518,7 +2569,7 @@
                     }
                     uint8_t switchVal = (bits(machInst, 4, 3) << 0) |
                                         (bits(machInst, 22) << 2);
-                    IntRegIndex rm = (IntRegIndex)(uint32_t)
+                    RegIndex rm = (RegIndex)(uint32_t)
                                         bits(machInst, 20, 16);
                     // 28:23=000111100, 21=1, 15:10=001000, 2:0=000
                     switch (switchVal) {
@@ -2847,9 +2898,9 @@
                     bits(machInst, 23)) {
                     return new Unknown64(machInst);
                 }
-                IntRegIndex rm = (IntRegIndex)(uint32_t) bits(machInst, 20, 16);
-                IntRegIndex rn = (IntRegIndex)(uint32_t) bits(machInst, 9, 5);
-                uint8_t    imm = (IntRegIndex)(uint32_t) bits(machInst, 3, 0);
+                RegIndex rm = (RegIndex)(uint32_t) bits(machInst, 20, 16);
+                RegIndex rn = (RegIndex)(uint32_t) bits(machInst, 9, 5);
+                uint8_t    imm = (RegIndex)(uint32_t) bits(machInst, 3, 0);
                 ConditionCode cond =
                     (ConditionCode)(uint8_t)(bits(machInst, 15, 12));
                 uint8_t switchVal = (bits(machInst, 4) << 0) |
@@ -2881,9 +2932,9 @@
                         bits(machInst, 23)) {
                     return new Unknown64(machInst);
                 }
-                IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst,  4,  0);
-                IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst,  9,  5);
-                IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 20, 16);
+                RegIndex rd = (RegIndex)(uint32_t)bits(machInst,  4,  0);
+                RegIndex rn = (RegIndex)(uint32_t)bits(machInst,  9,  5);
+                RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 20, 16);
                 uint8_t switchVal = (bits(machInst, 15, 12) << 0) |
                                     (bits(machInst, 22) << 4);
                 switch (switchVal) {
@@ -2932,9 +2983,9 @@
                 if (bits(machInst, 31) || bits(machInst, 29))
                     return new Unknown64(machInst);
                 uint8_t type = bits(machInst, 23, 22);
-                IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst,  4,  0);
-                IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst,  9,  5);
-                IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 20, 16);
+                RegIndex rd = (RegIndex)(uint32_t)bits(machInst,  4,  0);
+                RegIndex rn = (RegIndex)(uint32_t)bits(machInst,  9,  5);
+                RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 20, 16);
                 ConditionCode cond =
                     (ConditionCode)(uint8_t)(bits(machInst, 15, 12));
                 if (type == 0) // FCSEL Sd = if cond then Sn else Sm
@@ -3059,7 +3110,7 @@
                 return decodeDataProcImm(machInst);
             else
                 // bit 28:26=101
-                return decodeBranchExcSys(machInst);
+                return decodeBranchExcSys(*this, machInst);
         } else if (bits(machInst, 25) == 0) {
             // bit 27=1, 25=0
             return decodeLoadsStores(machInst);
diff --git a/src/arch/arm/isa/formats/branch.isa b/src/arch/arm/isa/formats/branch.isa
index 7c726ef..ff6bfda 100644
--- a/src/arch/arm/isa/formats/branch.isa
+++ b/src/arch/arm/isa/formats/branch.isa
@@ -72,8 +72,8 @@
 def format ArmBxClz() {{
     decode_block = '''
     {
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 15, 12);
         if (OPCODE == 0x9) {
             return new BxReg(machInst, rm,
                     (ConditionCode)(uint32_t)machInst.condCode);
@@ -88,7 +88,7 @@
 
 def format ArmBlxReg() {{
     decode_block = '''
-        return new BlxReg(machInst, (IntRegIndex)(uint32_t)bits(machInst, 3, 0),
+        return new BlxReg(machInst, (RegIndex)(uint32_t)bits(machInst, 3, 0),
                           (ConditionCode)(uint32_t)machInst.condCode);
     '''
 }};
@@ -144,8 +144,8 @@
                   case 0x38:
                   case 0x39:
                     {
-                        const IntRegIndex rn =
-                            (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
+                        const RegIndex rn =
+                            (RegIndex)(uint32_t)bits(machInst, 19, 16);
                         const uint8_t byteMask = bits(machInst, 11, 8);
                         const bool    r        = bits(machInst, 20);
                         if (bits(machInst, 5)) {
@@ -232,7 +232,7 @@
                   case 0x3c:
                     {
                         return new BxjReg(machInst,
-                                 (IntRegIndex)(uint32_t)bits(machInst, 19, 16),
+                                 (RegIndex)(uint32_t)bits(machInst, 19, 16),
                                  COND_UC);
                     }
                   case 0x3d:
@@ -241,16 +241,16 @@
                         if (imm32 == 0) {
                             return new Eret(machInst);
                         } else {
-                            return new SubsImmPclr(machInst, INTREG_PC,
-                                                   INTREG_LR, imm32, false);
+                            return new SubsImmPclr(machInst, int_reg::Pc,
+                                                   int_reg::Lr, imm32, false);
                         }
                     }
                   case 0x3e:
                   case 0x3f:
                     {
 
-                        const IntRegIndex rd =
-                            (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
+                        const RegIndex rd =
+                            (RegIndex)(uint32_t)bits(machInst, 11, 8);
                         const bool    r        = bits(machInst, 20);
                         if (bits(machInst, 5)) {
                             const uint8_t sysM = (bits(machInst, 4) << 4) |
diff --git a/src/arch/arm/isa/formats/crypto64.isa b/src/arch/arm/isa/formats/crypto64.isa
index 133b9c6..702d2ef 100644
--- a/src/arch/arm/isa/formats/crypto64.isa
+++ b/src/arch/arm/isa/formats/crypto64.isa
@@ -55,8 +55,8 @@
         const auto opcode = bits(machInst, 16, 12);
         const auto size = bits(machInst, 23, 22);
 
-        IntRegIndex rd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex rd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         if (size) {
             // UNALLOCATED
@@ -78,8 +78,8 @@
         const auto opcode = bits(machInst, 16, 12);
         const auto size = bits(machInst, 23, 22);
 
-        IntRegIndex rd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex rd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         if (size) {
             // UNALLOCATED
@@ -100,9 +100,9 @@
         const auto opcode = bits(machInst, 14, 12);
         const auto size = bits(machInst, 23, 22);
 
-        IntRegIndex rd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex rd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         if (size) {
             // UNALLOCATED
diff --git a/src/arch/arm/isa/formats/data.isa b/src/arch/arm/isa/formats/data.isa
index 33d8d0f..89d8cb8 100644
--- a/src/arch/arm/isa/formats/data.isa
+++ b/src/arch/arm/isa/formats/data.isa
@@ -38,13 +38,13 @@
     {
         const uint32_t op1 = bits(machInst, 22, 20);
         const uint32_t op2 = bits(machInst, 7, 5);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
-        const IntRegIndex ra = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex ra = (RegIndex)(uint32_t)bits(machInst, 15, 12);
         if (op1 == 0 && op2 == 0) {
-            const IntRegIndex rd =
-                (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-            const IntRegIndex rm =
-                (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
+            const RegIndex rd =
+                (RegIndex)(uint32_t)bits(machInst, 19, 16);
+            const RegIndex rm =
+                (RegIndex)(uint32_t)bits(machInst, 11, 8);
             if (ra == 0xf) {
                 return new Usad8(machInst, rd, rn, rm);
             } else {
@@ -74,7 +74,7 @@
 
 def format ArmDataProcReg() {{
     pclr = '''
-                    if (%(dest)s == INTREG_PC) {
+                    if (%(dest)s == int_reg::Pc) {
                         return new %(className)ssRegPclr(machInst, %(dest)s,
                                                          %(op1)s, rm, imm5,
                                                          type);
@@ -109,11 +109,11 @@
         if useDest:
             dest = "rd"
         else:
-            dest = "INTREG_ZERO"
+            dest = "int_reg::Zero"
         if useOp1:
             op1 = "rn"
         else:
-            op1 = "INTREG_ZERO"
+            op1 = "int_reg::Zero"
         global instDecode, pclrCode
         substDict = { "className": mnem.capitalize(),
                       "opcode": opcode,
@@ -131,10 +131,10 @@
         const bool setCc = (bits(machInst, 20) == 1);
         const uint32_t imm5 = bits(machInst, 11, 7);
         const ArmShiftType type = (ArmShiftType)(uint32_t)bits(machInst, 6, 5);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)RD;
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)RN;
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)RM;
-        const IntRegIndex rs = (IntRegIndex)(uint32_t)RS;
+        const RegIndex rd = (RegIndex)(uint32_t)RD;
+        const RegIndex rn = (RegIndex)(uint32_t)RN;
+        const RegIndex rm = (RegIndex)(uint32_t)RM;
+        const RegIndex rs = (RegIndex)(uint32_t)RS;
         switch (OPCODE) {
     '''
     decode_block += instCode(0x0, "and")
@@ -168,20 +168,20 @@
         const uint32_t a = bits(machInst, 19, 16);
         const uint32_t op2 = bits(machInst, 7, 5);
         if (bits(op2, 0) == 0) {
-            const IntRegIndex rn =
-                (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
-            const IntRegIndex rd =
-                (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+            const RegIndex rn =
+                (RegIndex)(uint32_t)bits(machInst, 3, 0);
+            const RegIndex rd =
+                (RegIndex)(uint32_t)bits(machInst, 15, 12);
             const uint32_t satImm = bits(machInst, 20, 16);
             const uint32_t imm = bits(machInst, 11, 7);
             const ArmShiftType type =
                 (ArmShiftType)(uint32_t)bits(machInst, 6, 5);
             if (op1 == 0) {
                 if (type) {
-                    return new PkhtbReg(machInst, rd, (IntRegIndex)a,
+                    return new PkhtbReg(machInst, rd, (RegIndex)a,
                                         rn, imm, type);
                 } else {
-                    return new PkhbtReg(machInst, rd, (IntRegIndex)a,
+                    return new PkhbtReg(machInst, rd, (RegIndex)a,
                                         rn, imm, type);
                 }
             } else if (bits(op1, 2, 1) == 1) {
@@ -194,12 +194,12 @@
         switch (op1) {
           case 0x0:
             {
-                const IntRegIndex rn =
-                    (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rn =
+                    (RegIndex)(uint32_t)bits(machInst, 19, 16);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 if (op2 == 0x3) {
                     const uint32_t rotation =
                         (uint32_t)bits(machInst, 11, 10) << 3;
@@ -215,19 +215,19 @@
             break;
           case 0x2:
             if (op2 == 0x1) {
-                const IntRegIndex rn =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rn =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
                 const uint32_t satImm = bits(machInst, 20, 16);
                 return new Ssat16(machInst, rd, satImm + 1, rn);
             } else if (op2 == 0x3) {
-                const IntRegIndex rn =
-                    (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rn =
+                    (RegIndex)(uint32_t)bits(machInst, 19, 16);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 const uint32_t rotation =
                     (uint32_t)bits(machInst, 11, 10) << 3;
                 if (a == 0xf) {
@@ -239,16 +239,16 @@
             break;
           case 0x3:
             if (op2 == 0x1) {
-                IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-                IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+                RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 return new Rev(machInst, rd, rm);
             } else if (op2 == 0x3) {
-                const IntRegIndex rn =
-                    (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rn =
+                    (RegIndex)(uint32_t)bits(machInst, 19, 16);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 const uint32_t rotation =
                     (uint32_t)bits(machInst, 11, 10) << 3;
                 if (a == 0xf) {
@@ -257,19 +257,19 @@
                     return new Sxtah(machInst, rd, rn, rm, rotation);
                 }
             } else if (op2 == 0x5) {
-                IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-                IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+                RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 return new Rev16(machInst, rd, rm);
             }
             break;
           case 0x4:
             if (op2 == 0x3) {
-                const IntRegIndex rn =
-                    (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rn =
+                    (RegIndex)(uint32_t)bits(machInst, 19, 16);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 const uint32_t rotation =
                     (uint32_t)bits(machInst, 11, 10) << 3;
                 if (a == 0xf) {
@@ -281,19 +281,19 @@
             break;
           case 0x6:
             if (op2 == 0x1) {
-                const IntRegIndex rn =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rn =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
                 const uint32_t satImm = bits(machInst, 20, 16);
                 return new Usat16(machInst, rd, satImm, rn);
             } else if (op2 == 0x3) {
-                const IntRegIndex rn =
-                    (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rn =
+                    (RegIndex)(uint32_t)bits(machInst, 19, 16);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 const uint32_t rotation =
                     (uint32_t)bits(machInst, 11, 10) << 3;
                 if (a == 0xf) {
@@ -305,12 +305,12 @@
             break;
           case 0x7:
             {
-                const IntRegIndex rn =
-                    (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rn =
+                    (RegIndex)(uint32_t)bits(machInst, 19, 16);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 if (op2 == 0x1) {
                     return new Rbit(machInst, rd, rm);
                 } else if (op2 == 0x3) {
@@ -337,9 +337,9 @@
     {
         const uint32_t op1 = bits(machInst, 21, 20);
         const uint32_t op2 = bits(machInst, 7, 5);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         if (bits(machInst, 22) == 0) {
             switch (op1) {
               case 0x1:
@@ -450,13 +450,13 @@
 
 def format ArmDataProcImm() {{
     pclr = '''
-                if (%(dest)s == INTREG_PC) {
+                if (%(dest)s == int_reg::Pc) {
                     return new %(className)ssImmPclr(machInst, %(dest)s,
                                                      %(op1)s, imm, false);
                 } else
     '''
     adr = '''
-                if (%(op1)s == INTREG_PC) {
+                if (%(op1)s == int_reg::Pc) {
                     return new AdrImm(machInst, %(dest)s, %(add)s,
                                       imm, false);
                 } else
@@ -482,11 +482,11 @@
         if useDest:
             dest = "rd"
         else:
-            dest = "INTREG_ZERO"
+            dest = "int_reg::Zero"
         if useOp1:
             op1 = "rn"
         else:
-            op1 = "INTREG_ZERO"
+            op1 = "int_reg::Zero"
         substDict = { "className": mnem.capitalize(),
                       "opcode": opcode,
                       "dest": dest,
@@ -516,15 +516,15 @@
         const uint32_t rotation = (bits(machInst, 11, 8) << 1);
         const bool rotC = (rotation != 0);
         const uint32_t imm = rotate_imm(unrotated, rotation);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)RD;
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)RN;
+        const RegIndex rd = (RegIndex)(uint32_t)RD;
+        const RegIndex rn = (RegIndex)(uint32_t)RN;
         switch (OPCODE) {
     '''
     decode_block += instCode(0x0, "and")
     decode_block += instCode(0x1, "eor")
-    decode_block += adrCode(0x2, "sub", add="(IntRegIndex)0")
+    decode_block += adrCode(0x2, "sub", add="(RegIndex)0")
     decode_block += instCode(0x3, "rsb")
-    decode_block += adrCode(0x4, "add", add="(IntRegIndex)1")
+    decode_block += adrCode(0x4, "add", add="(RegIndex)1")
     decode_block += instCode(0x5, "adc")
     decode_block += instCode(0x6, "sbc")
     decode_block += instCode(0x7, "rsc")
@@ -547,9 +547,9 @@
 def format ArmSatAddSub() {{
     decode_block = '''
     {
-        IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         switch (OPCODE) {
           case 0x8:
             return new QaddRegCc(machInst, rd, rm, rn, 0, LSL);
@@ -570,50 +570,50 @@
     decode_block = '''
     {
         const uint32_t op1 = bits(machInst, 23, 20);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
         const uint32_t op2 = bits(machInst, 7, 4);
         if (bits(machInst, 15, 12) != 0xf) {
             return new Unknown(machInst);
         }
         if (bits(op1, 3) != 1) {
             if (op2 == 0) {
-                IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-                IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 11, 8);
+                RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 switch (bits(op1, 2, 0)) {
                   case 0x0:
                     return new MovRegReg(machInst, rd,
-                            INTREG_ZERO, rn, rm, LSL);
+                            int_reg::Zero, rn, rm, LSL);
                   case 0x1:
                     return new MovRegRegCc(machInst, rd,
-                            INTREG_ZERO, rn, rm, LSL);
+                            int_reg::Zero, rn, rm, LSL);
                   case 0x2:
                     return new MovRegReg(machInst, rd,
-                            INTREG_ZERO, rn, rm, LSR);
+                            int_reg::Zero, rn, rm, LSR);
                   case 0x3:
                     return new MovRegRegCc(machInst, rd,
-                            INTREG_ZERO, rn, rm, LSR);
+                            int_reg::Zero, rn, rm, LSR);
                   case 0x4:
                     return new MovRegReg(machInst, rd,
-                            INTREG_ZERO, rn, rm, ASR);
+                            int_reg::Zero, rn, rm, ASR);
                   case 0x5:
                     return new MovRegRegCc(machInst, rd,
-                            INTREG_ZERO, rn, rm, ASR);
+                            int_reg::Zero, rn, rm, ASR);
                   case 0x6:
                     return new MovRegReg(machInst, rd,
-                            INTREG_ZERO, rn, rm, ROR);
+                            int_reg::Zero, rn, rm, ROR);
                   case 0x7:
                     return new MovRegRegCc(machInst, rd,
-                            INTREG_ZERO, rn, rm, ROR);
+                            int_reg::Zero, rn, rm, ROR);
                   default:
                     GEM5_UNREACHABLE;
                 }
             } else if (bits(op2, 3) == 0) {
                 return new Unknown(machInst);
             } else {
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 11, 8);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 const uint32_t rotation =
                     (uint32_t)bits(machInst, 5, 4) << 3;
                 switch (bits(op1, 2, 0)) {
@@ -659,10 +659,10 @@
             }
         } else {
             if (bits(op2, 3) == 0) {
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 11, 8);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 if (bits(op2, 2) == 0x0) {
                     const uint32_t op1 = bits(machInst, 22, 20);
                     const uint32_t op2 = bits(machInst, 5, 4);
@@ -785,10 +785,10 @@
             } else if (bits(op1, 3, 2) == 0x2 && bits(op2, 3, 2) == 0x2) {
                 const uint32_t op1 = bits(machInst, 22, 20);
                 const uint32_t op2 = bits(machInst, 5, 4);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 11, 8);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 switch (op1) {
                   case 0x0:
                     switch (op2) {
@@ -832,10 +832,10 @@
             } else if (bits(op1, 3, 2) == 0x3 && bits(op2, 3, 2) == 0x2) {
                 const uint32_t op1 = bits(machInst, 22, 20);
                 const uint32_t op2 = bits(machInst, 5, 4);
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 11, 8);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 3, 0);
                 switch (op1) {
                   case 0x4:
                     switch (op2) {
@@ -871,28 +871,31 @@
         const uint32_t imm5 = bits(machInst, 10, 6);
         const uint32_t imm3 = bits(machInst, 8, 6);
         const uint32_t imm8 = bits(machInst, 7, 0);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 2, 0);
-        const IntRegIndex rd8 = (IntRegIndex)(uint32_t)bits(machInst, 10, 8);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 5, 3);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 8, 6);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 2, 0);
+        const RegIndex rd8 = (RegIndex)(uint32_t)bits(machInst, 10, 8);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 5, 3);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 8, 6);
         switch (bits(machInst, 13, 11)) {
           case 0x0: // lsl
             if (machInst.itstateMask) {
-                return new MovReg(machInst, rd, INTREG_ZERO, rn, imm5, LSL);
+                return new MovReg(machInst, rd, int_reg::Zero, rn, imm5, LSL);
             } else {
-                return new MovRegCc(machInst, rd, INTREG_ZERO, rn, imm5, LSL);
+                return new MovRegCc(machInst, rd, int_reg::Zero, rn, imm5,
+                                    LSL);
             }
           case 0x1: // lsr
             if (machInst.itstateMask) {
-                return new MovReg(machInst, rd, INTREG_ZERO, rn, imm5, LSR);
+                return new MovReg(machInst, rd, int_reg::Zero, rn, imm5, LSR);
             } else {
-                return new MovRegCc(machInst, rd, INTREG_ZERO, rn, imm5, LSR);
+                return new MovRegCc(machInst, rd, int_reg::Zero, rn, imm5,
+                                    LSR);
             }
           case 0x2: // asr
             if (machInst.itstateMask) {
-                return new MovReg(machInst, rd, INTREG_ZERO, rn, imm5, ASR);
+                return new MovReg(machInst, rd, int_reg::Zero, rn, imm5, ASR);
             } else {
-                return new MovRegCc(machInst, rd, INTREG_ZERO, rn, imm5, ASR);
+                return new MovRegCc(machInst, rd, int_reg::Zero, rn, imm5,
+                                    ASR);
             }
           case 0x3:
             switch (bits(machInst, 10, 9)) {
@@ -925,12 +928,12 @@
             }
           case 0x4:
             if (machInst.itstateMask) {
-                return new MovImm(machInst, rd8, INTREG_ZERO, imm8, false);
+                return new MovImm(machInst, rd8, int_reg::Zero, imm8, false);
             } else {
-                return new MovImmCc(machInst, rd8, INTREG_ZERO, imm8, false);
+                return new MovImmCc(machInst, rd8, int_reg::Zero, imm8, false);
             }
           case 0x5:
-            return new CmpImmCc(machInst, INTREG_ZERO, rd8, imm8, true);
+            return new CmpImmCc(machInst, int_reg::Zero, rd8, imm8, true);
           case 0x6:
             if (machInst.itstateMask) {
                 return new AddImm(machInst, rd8, rd8, imm8, true);
@@ -953,8 +956,8 @@
 def format Thumb16DataProcessing() {{
     decode_block = '''
     {
-        const IntRegIndex rdn = (IntRegIndex)(uint32_t)bits(machInst, 2, 0);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 5, 3);
+        const RegIndex rdn = (RegIndex)(uint32_t)bits(machInst, 2, 0);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 5, 3);
         switch (bits(machInst, 9, 6)) {
           case 0x0:
             if (machInst.itstateMask) {
@@ -971,26 +974,26 @@
           case 0x2: //lsl
             if (machInst.itstateMask) {
                 return new MovRegReg(machInst, rdn,
-                        INTREG_ZERO, rdn, rm, LSL);
+                        int_reg::Zero, rdn, rm, LSL);
             } else {
                 return new MovRegRegCc(machInst, rdn,
-                        INTREG_ZERO, rdn, rm, LSL);
+                        int_reg::Zero, rdn, rm, LSL);
             }
           case 0x3: //lsr
             if (machInst.itstateMask) {
                 return new MovRegReg(machInst, rdn,
-                        INTREG_ZERO, rdn, rm, LSR);
+                        int_reg::Zero, rdn, rm, LSR);
             } else {
                 return new MovRegRegCc(machInst, rdn,
-                        INTREG_ZERO, rdn, rm, LSR);
+                        int_reg::Zero, rdn, rm, LSR);
             }
           case 0x4: //asr
             if (machInst.itstateMask) {
                 return new MovRegReg(machInst, rdn,
-                        INTREG_ZERO, rdn, rm, ASR);
+                        int_reg::Zero, rdn, rm, ASR);
             } else {
                 return new MovRegRegCc(machInst, rdn,
-                        INTREG_ZERO, rdn, rm, ASR);
+                        int_reg::Zero, rdn, rm, ASR);
             }
           case 0x5:
             if (machInst.itstateMask) {
@@ -1007,13 +1010,13 @@
           case 0x7: // ror
             if (machInst.itstateMask) {
                 return new MovRegReg(machInst, rdn,
-                        INTREG_ZERO, rdn, rm, ROR);
+                        int_reg::Zero, rdn, rm, ROR);
             } else {
                 return new MovRegRegCc(machInst, rdn,
-                        INTREG_ZERO, rdn, rm, ROR);
+                        int_reg::Zero, rdn, rm, ROR);
             }
           case 0x8:
-            return new TstRegCc(machInst, INTREG_ZERO, rdn, rm, 0, LSL);
+            return new TstRegCc(machInst, int_reg::Zero, rdn, rm, 0, LSL);
           case 0x9:
             if (machInst.itstateMask) {
                 return new RsbImm(machInst, rdn, rm, 0, true);
@@ -1021,9 +1024,9 @@
                 return new RsbImmCc(machInst, rdn, rm, 0, true);
             }
           case 0xa:
-            return new CmpRegCc(machInst, INTREG_ZERO, rdn, rm, 0, LSL);
+            return new CmpRegCc(machInst, int_reg::Zero, rdn, rm, 0, LSL);
           case 0xb:
-            return new CmnRegCc(machInst, INTREG_ZERO, rdn, rm, 0, LSL);
+            return new CmnRegCc(machInst, int_reg::Zero, rdn, rm, 0, LSL);
           case 0xc:
             if (machInst.itstateMask) {
                 return new OrrReg(machInst, rdn, rdn, rm, 0, LSL);
@@ -1044,9 +1047,9 @@
             }
           case 0xf:
             if (machInst.itstateMask) {
-                return new MvnReg(machInst, rdn, INTREG_ZERO, rm, 0, LSL);
+                return new MvnReg(machInst, rdn, int_reg::Zero, rm, 0, LSL);
             } else {
-                return new MvnRegCc(machInst, rdn, INTREG_ZERO, rm, 0, LSL);
+                return new MvnRegCc(machInst, rdn, int_reg::Zero, rm, 0, LSL);
             }
           default:
             GEM5_UNREACHABLE;
@@ -1058,25 +1061,25 @@
 def format Thumb16SpecDataAndBx() {{
     decode_block = '''
     {
-        const IntRegIndex rdn =
-            (IntRegIndex)(uint32_t)(bits(machInst, 2, 0) |
+        const RegIndex rdn =
+            (RegIndex)(uint32_t)(bits(machInst, 2, 0) |
                                     (bits(machInst, 7) << 3));
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 6, 3);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 6, 3);
         switch (bits(machInst, 9, 8)) {
           case 0x0:
             return new AddReg(machInst, rdn, rdn, rm, 0, LSL);
           case 0x1:
-            return new CmpRegCc(machInst, INTREG_ZERO, rdn, rm, 0, LSL);
+            return new CmpRegCc(machInst, int_reg::Zero, rdn, rm, 0, LSL);
           case 0x2:
-            return new MovReg(machInst, rdn, INTREG_ZERO, rm, 0, LSL);
+            return new MovReg(machInst, rdn, int_reg::Zero, rm, 0, LSL);
           case 0x3:
             if (bits(machInst, 7) == 0) {
                 return new BxReg(machInst,
-                                 (IntRegIndex)(uint32_t)bits(machInst, 6, 3),
+                                 (RegIndex)(uint32_t)bits(machInst, 6, 3),
                                  COND_UC);
             } else {
                 return new BlxReg(machInst,
-                                  (IntRegIndex)(uint32_t)bits(machInst, 6, 3),
+                                  (RegIndex)(uint32_t)bits(machInst, 6, 3),
                                   COND_UC);
             }
           default:
@@ -1089,9 +1092,9 @@
 def format Thumb16Adr() {{
     decode_block = '''
     {
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 10, 8);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 10, 8);
         const uint32_t imm8 = bits(machInst, 7, 0) << 2;
-        return new AdrImm(machInst, rd, (IntRegIndex)1, imm8, false);
+        return new AdrImm(machInst, rd, (RegIndex)1, imm8, false);
     }
     '''
 }};
@@ -1099,9 +1102,9 @@
 def format Thumb16AddSp() {{
     decode_block = '''
     {
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 10, 8);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 10, 8);
         const uint32_t imm8 = bits(machInst, 7, 0) << 2;
-        return new AddImm(machInst, rd, INTREG_SP, imm8, true);
+        return new AddImm(machInst, rd, int_reg::Sp, imm8, true);
     }
     '''
 }};
@@ -1115,8 +1118,7 @@
         const uint8_t byteMask = bits(machInst, 19, 16);
         switch (OPCODE) {
           case 0x8:
-            return new MovImm(machInst, (IntRegIndex)(uint32_t)RD,
-                    (IntRegIndex)INTREG_ZERO,
+            return new MovImm(machInst, (RegIndex)RD, int_reg::Zero,
                     bits(machInst, 11, 0) | (bits(machInst, 19, 16) << 12),
                     false);
           case 0x9:
@@ -1158,8 +1160,8 @@
             {
                 const uint32_t timm = (bits(machInst, 19, 16) << 12) |
                                        bits(machInst, 11, 0);
-                return new MovtImm(machInst, (IntRegIndex)(uint32_t)RD,
-                                   (IntRegIndex)(uint32_t)RD, timm, true);
+                return new MovtImm(machInst, (RegIndex)(uint32_t)RD,
+                                   (RegIndex)(uint32_t)RD, timm, true);
             }
           case 0xb:
             return new MsrSpsrImm(machInst, imm, byteMask);
@@ -1176,18 +1178,18 @@
         switch (bits(machInst, 11, 8)) {
           case 0x0:
             if (bits(machInst, 7)) {
-                return new SubImm(machInst, INTREG_SP, INTREG_SP,
+                return new SubImm(machInst, int_reg::Sp, int_reg::Sp,
                                    bits(machInst, 6, 0) << 2, true);
             } else {
-                return new AddImm(machInst, INTREG_SP, INTREG_SP,
+                return new AddImm(machInst, int_reg::Sp, int_reg::Sp,
                                    bits(machInst, 6, 0) << 2, true);
             }
           case 0x2:
             {
-                const IntRegIndex rd =
-                    (IntRegIndex)(uint32_t)bits(machInst, 2, 0);
-                const IntRegIndex rm =
-                    (IntRegIndex)(uint32_t)bits(machInst, 5, 3);
+                const RegIndex rd =
+                    (RegIndex)(uint32_t)bits(machInst, 2, 0);
+                const RegIndex rm =
+                    (RegIndex)(uint32_t)bits(machInst, 5, 3);
                 switch (bits(machInst, 7, 6)) {
                   case 0x0:
                     return new Sxth(machInst, rd, 0, rm);
@@ -1206,13 +1208,13 @@
             return new Cbz(machInst,
                            (bits(machInst, 9) << 6) |
                            (bits(machInst, 7, 3) << 1),
-                           (IntRegIndex)(uint32_t)bits(machInst, 2, 0));
+                           (RegIndex)(uint32_t)bits(machInst, 2, 0));
           case 0x4:
           case 0x5:
             {
                 const uint32_t m = bits(machInst, 8);
                 const uint32_t regList = bits(machInst, 7, 0) | (m << 14);
-                return new LdmStm(machInst, INTREG_SP, false, false, false,
+                return new LdmStm(machInst, int_reg::Sp, false, false, false,
                                   true, false, regList);
             }
           case 0x6:
@@ -1234,10 +1236,10 @@
                 if (op1 == 0x2) {
                     return new Hlt(machInst, bits(machInst, 5, 0));
                 } else {
-                    IntRegIndex rd =
-                        (IntRegIndex)(uint32_t)bits(machInst, 2, 0);
-                    IntRegIndex rm =
-                        (IntRegIndex)(uint32_t)bits(machInst, 5, 3);
+                    RegIndex rd =
+                        (RegIndex)(uint32_t)bits(machInst, 2, 0);
+                    RegIndex rm =
+                        (RegIndex)(uint32_t)bits(machInst, 5, 3);
 
                     switch (op1) {
                       case 0x0:
@@ -1257,13 +1259,13 @@
             return new Cbnz(machInst,
                             (bits(machInst, 9) << 6) |
                             (bits(machInst, 7, 3) << 1),
-                            (IntRegIndex)(uint32_t)bits(machInst, 2, 0));
+                            (RegIndex)(uint32_t)bits(machInst, 2, 0));
           case 0xc:
           case 0xd:
             {
                 const uint32_t p = bits(machInst, 8);
                 const uint32_t regList = bits(machInst, 7, 0) | (p << 15);
-                return new LdmStm(machInst, INTREG_SP, true, true, false,
+                return new LdmStm(machInst, int_reg::Sp, true, true, false,
                                   true, true, regList);
             }
           case 0xe:
@@ -1312,8 +1314,8 @@
     {
         const uint32_t op = bits(machInst, 24, 21);
         const bool s = (bits(machInst, 20) == 1);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 11, 8);
         const uint32_t ctrlImm = bits(machInst.instBits, 26) << 3 |
                                  bits(machInst, 14, 12);
         const bool rotC = ctrlImm > 3;
@@ -1321,7 +1323,7 @@
         const uint32_t imm = modified_imm(ctrlImm, dataImm);
         switch (op) {
           case 0x0:
-            if (rd == INTREG_PC) {
+            if (rd == int_reg::Pc) {
                 %(tst)s
             } else {
                 %(and)s
@@ -1329,25 +1331,25 @@
           case 0x1:
             %(bic)s
           case 0x2:
-            if (rn == INTREG_PC) {
+            if (rn == int_reg::Pc) {
                 %(mov)s
             } else {
                 %(orr)s
             }
           case 0x3:
-            if (rn == INTREG_PC) {
+            if (rn == int_reg::Pc) {
                 %(mvn)s
             } else {
                 %(orn)s
             }
           case 0x4:
-            if (rd == INTREG_PC) {
+            if (rd == int_reg::Pc) {
                 %(teq)s
             } else {
                 %(eor)s
             }
           case 0x8:
-            if (rd == INTREG_PC) {
+            if (rd == int_reg::Pc) {
                 %(cmn)s
             } else {
                 %(add)s
@@ -1357,7 +1359,7 @@
           case 0xb:
             %(sbc)s
           case 0xd:
-            if (rd == INTREG_PC) {
+            if (rd == int_reg::Pc) {
                 %(cmp)s
             } else {
                 %(sub)s
@@ -1369,20 +1371,20 @@
         }
     }
     ''' % {
-        "tst" : decInst("Tst", "INTREG_ZERO"),
+        "tst" : decInst("Tst", "int_reg::Zero"),
         "and" : decInst("And"),
         "bic" : decInst("Bic"),
-        "mov" : decInst("Mov", op1="INTREG_ZERO"),
+        "mov" : decInst("Mov", op1="int_reg::Zero"),
         "orr" : decInst("Orr"),
-        "mvn" : decInst("Mvn", op1="INTREG_ZERO"),
+        "mvn" : decInst("Mvn", op1="int_reg::Zero"),
         "orn" : decInst("Orn"),
-        "teq" : decInst("Teq", dest="INTREG_ZERO"),
+        "teq" : decInst("Teq", dest="int_reg::Zero"),
         "eor" : decInst("Eor"),
-        "cmn" : decInst("Cmn", dest="INTREG_ZERO"),
+        "cmn" : decInst("Cmn", dest="int_reg::Zero"),
         "add" : decInst("Add"),
         "adc" : decInst("Adc"),
         "sbc" : decInst("Sbc"),
-        "cmp" : decInst("Cmp", dest="INTREG_ZERO"),
+        "cmp" : decInst("Cmp", dest="int_reg::Zero"),
         "sub" : decInst("Sub"),
         "rsb" : decInst("Rsb")
     }
@@ -1392,8 +1394,8 @@
     decode_block = '''
     {
         const uint32_t op = bits(machInst, 24, 20);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 11, 8);
         switch (op) {
           case 0x0:
             {
@@ -1401,7 +1403,7 @@
                                      (bits(machInst, 14, 12) << 8) |
                                      (bits(machInst, 26) << 11);
                 if (rn == 0xf) {
-                    return new AdrImm(machInst, rd, (IntRegIndex)1,
+                    return new AdrImm(machInst, rd, (RegIndex)1,
                                       imm, false);
                 } else {
                     return new AddImm(machInst, rd, rn, imm, true);
@@ -1413,7 +1415,7 @@
                                      (bits(machInst, 14, 12) << 8) |
                                      (bits(machInst, 26) << 11) |
                                      (bits(machInst, 19, 16) << 12);
-                return new MovImm(machInst, rd, INTREG_ZERO, imm, true);
+                return new MovImm(machInst, rd, int_reg::Zero, imm, true);
             }
           case 0xa:
             {
@@ -1421,7 +1423,7 @@
                                      (bits(machInst, 14, 12) << 8) |
                                      (bits(machInst, 26) << 11);
                 if (rn == 0xf) {
-                    return new AdrImm(machInst, rd, (IntRegIndex)0,
+                    return new AdrImm(machInst, rd, (RegIndex)0,
                                       imm, false);
                 } else {
                     return new SubImm(machInst, rd, rn, imm, true);
@@ -1514,15 +1516,15 @@
     {
         const uint32_t op = bits(machInst, 24, 21);
         const bool s = (bits(machInst, 20) == 1);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 11, 8);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         const uint32_t amt = (bits(machInst, 14, 12) << 2) |
                               bits(machInst, 7, 6);
         const ArmShiftType type = (ArmShiftType)(uint32_t)bits(machInst, 5, 4);
         switch (op) {
           case 0x0:
-            if (rd == INTREG_PC) {
+            if (rd == int_reg::Pc) {
                 %(tst)s
             } else {
                 %(and)s
@@ -1530,19 +1532,19 @@
           case 0x1:
             %(bic)s
           case 0x2:
-            if (rn == INTREG_PC) {
+            if (rn == int_reg::Pc) {
                 %(mov)s
             } else {
                 %(orr)s
             }
           case 0x3:
-            if (rn == INTREG_PC) {
+            if (rn == int_reg::Pc) {
                 %(mvn)s
             } else {
                 %(orn)s
             }
           case 0x4:
-            if (rd == INTREG_PC) {
+            if (rd == int_reg::Pc) {
                 %(teq)s
             } else {
                 %(eor)s
@@ -1554,7 +1556,7 @@
                 return new PkhbtReg(machInst, rd, rn, rm, amt, type);
             }
           case 0x8:
-            if (rd == INTREG_PC) {
+            if (rd == int_reg::Pc) {
                 %(cmn)s
             } else {
                 %(add)s
@@ -1564,7 +1566,7 @@
           case 0xb:
             %(sbc)s
           case 0xd:
-            if (rd == INTREG_PC) {
+            if (rd == int_reg::Pc) {
                 %(cmp)s
             } else {
                 %(sub)s
@@ -1576,20 +1578,20 @@
         }
     }
     ''' % {
-        "tst" : decInst("Tst", "INTREG_ZERO"),
+        "tst" : decInst("Tst", "int_reg::Zero"),
         "and" : decInst("And"),
         "bic" : decInst("Bic"),
-        "mov" : decInst("Mov", op1="INTREG_ZERO"),
+        "mov" : decInst("Mov", op1="int_reg::Zero"),
         "orr" : decInst("Orr"),
-        "mvn" : decInst("Mvn", op1="INTREG_ZERO"),
+        "mvn" : decInst("Mvn", op1="int_reg::Zero"),
         "orn" : decInst("Orn"),
-        "teq" : decInst("Teq", "INTREG_ZERO"),
+        "teq" : decInst("Teq", "int_reg::Zero"),
         "eor" : decInst("Eor"),
-        "cmn" : decInst("Cmn", "INTREG_ZERO"),
+        "cmn" : decInst("Cmn", "int_reg::Zero"),
         "add" : decInst("Add"),
         "adc" : decInst("Adc"),
         "sbc" : decInst("Sbc"),
-        "cmp" : decInst("Cmp", "INTREG_ZERO"),
+        "cmp" : decInst("Cmp", "int_reg::Zero"),
         "sub" : decInst("Sub"),
         "rsb" : decInst("Rsb")
     }
diff --git a/src/arch/arm/isa/formats/fp.isa b/src/arch/arm/isa/formats/fp.isa
index ce492c9..eac2ba0 100644
--- a/src/arch/arm/isa/formats/fp.isa
+++ b/src/arch/arm/isa/formats/fp.isa
@@ -343,11 +343,11 @@
         uint8_t op_code = (bits(machInst, 25) << 1)
                           | bits(machInst, 21);
 
-        IntRegIndex vd = (IntRegIndex)(2 * (bits(machInst, 15, 12) |
+        RegIndex vd = (RegIndex)(2 * (bits(machInst, 15, 12) |
                                (bits(machInst, 22) << 4)));
-        IntRegIndex vn = (IntRegIndex)(2 * (bits(machInst, 19, 16) |
+        RegIndex vn = (RegIndex)(2 * (bits(machInst, 19, 16) |
                                (bits(machInst, 7) << 4)));
-        IntRegIndex vm = (IntRegIndex)(2 * (bits(machInst, 3, 0) |
+        RegIndex vm = (RegIndex)(2 * (bits(machInst, 3, 0) |
                                (bits(machInst, 5) << 4)));
         bool q = bits (machInst, 6);
         switch (op_code) {
@@ -397,7 +397,7 @@
                    return new VcmlaElemD<uint32_t>(machInst, vd, vn, vm,
                                                    index_fp);
             } else {
-               vm = (IntRegIndex)(uint8_t)(2* bits(machInst, 3, 0));
+               vm = (RegIndex)(uint8_t)(2* bits(machInst, 3, 0));
                uint8_t index_fp = bits(machInst, 5);
                if (q)
                    return new VcmlaElemQ<uint16_t>(machInst, vd, vn, vm,
@@ -423,14 +423,14 @@
         const uint32_t opc = bits(machInst, 11, 8);
         const bool o1 = bits(machInst, 4);
         const uint32_t size = bits(machInst, 21, 20);
-        const IntRegIndex vd =
-            (IntRegIndex)(2 * (bits(machInst, 15, 12) |
+        const RegIndex vd =
+            (RegIndex)(2 * (bits(machInst, 15, 12) |
                                (bits(machInst, 22) << 4)));
-        const IntRegIndex vn =
-            (IntRegIndex)(2 * (bits(machInst, 19, 16) |
+        const RegIndex vn =
+            (RegIndex)(2 * (bits(machInst, 19, 16) |
                                (bits(machInst, 7) << 4)));
-        const IntRegIndex vm =
-            (IntRegIndex)(2 * (bits(machInst, 3, 0) |
+        const RegIndex vm =
+            (RegIndex)(2 * (bits(machInst, 3, 0) |
                                (bits(machInst, 5) << 4)));
         const bool q = bits(machInst, 6);
         if (q && ((vd & 0x1) || (vn & 0x1) || (vm & 0x1)))
@@ -905,8 +905,8 @@
     static StaticInstPtr
     decodeNeonOneRegModImm(ExtMachInst machInst)
     {
-        const IntRegIndex vd =
-            (IntRegIndex)(2 * (bits(machInst, 15, 12) |
+        const RegIndex vd =
+            (RegIndex)(2 * (bits(machInst, 15, 12) |
                                (bits(machInst, 22) << 4)));
         const bool q = bits(machInst, 6);
         const bool op = bits(machInst, 5);
@@ -1015,11 +1015,11 @@
         const bool u = THUMB ? bits(machInst, 28) : bits(machInst, 24);
         const bool q = bits(machInst, 6);
         const bool l = bits(machInst, 7);
-        const IntRegIndex vd =
-            (IntRegIndex)(2 * (bits(machInst, 15, 12) |
+        const RegIndex vd =
+            (RegIndex)(2 * (bits(machInst, 15, 12) |
                                (bits(machInst, 22) << 4)));
-        const IntRegIndex vm =
-            (IntRegIndex)(2 * (bits(machInst, 3, 0) |
+        const RegIndex vm =
+            (RegIndex)(2 * (bits(machInst, 3, 0) |
                                (bits(machInst, 5) << 4)));
         unsigned imm6 = bits(machInst, 21, 16);
         unsigned imm = ((l ? 1 : 0) << 6) | imm6;
@@ -1168,14 +1168,14 @@
     {
         const bool u = THUMB ? bits(machInst, 28) : bits(machInst, 24);
         const uint32_t opc = bits(machInst, 11, 8);
-        const IntRegIndex vd =
-            (IntRegIndex)(2 * (bits(machInst, 15, 12) |
+        const RegIndex vd =
+            (RegIndex)(2 * (bits(machInst, 15, 12) |
                                (bits(machInst, 22) << 4)));
-        const IntRegIndex vn =
-            (IntRegIndex)(2 * (bits(machInst, 19, 16) |
+        const RegIndex vn =
+            (RegIndex)(2 * (bits(machInst, 19, 16) |
                                (bits(machInst, 7) << 4)));
-        const IntRegIndex vm =
-            (IntRegIndex)(2 * (bits(machInst, 3, 0) |
+        const RegIndex vm =
+            (RegIndex)(2 * (bits(machInst, 3, 0) |
                                (bits(machInst, 5) << 4)));
         const unsigned size = bits(machInst, 21, 20);
         switch (opc) {
@@ -1261,15 +1261,15 @@
         const bool u = THUMB ? bits(machInst, 28) : bits(machInst, 24);
         const uint32_t opc = bits(machInst, 11, 8);
         const unsigned size = bits(machInst, 21, 20);
-        const IntRegIndex vd =
-            (IntRegIndex)(2 * (bits(machInst, 15, 12) |
+        const RegIndex vd =
+            (RegIndex)(2 * (bits(machInst, 15, 12) |
                                (bits(machInst, 22) << 4)));
-        const IntRegIndex vn =
-            (IntRegIndex)(2 * (bits(machInst, 19, 16) |
+        const RegIndex vn =
+            (RegIndex)(2 * (bits(machInst, 19, 16) |
                                (bits(machInst, 7) << 4)));
-        const IntRegIndex vm = (size == 2) ?
-            (IntRegIndex)(2 * bits(machInst, 3, 0)) :
-            (IntRegIndex)(2 * bits(machInst, 2, 0));
+        const RegIndex vm = (size == 2) ?
+            (RegIndex)(2 * bits(machInst, 3, 0)) :
+            (RegIndex)(2 * bits(machInst, 2, 0));
         const unsigned index = (size == 2) ? (unsigned)bits(machInst, 5) :
             (bits(machInst, 3) | (bits(machInst, 5) << 1));
         switch (opc) {
@@ -1568,11 +1568,11 @@
         const uint32_t opc1 = bits(machInst, 17, 16);
         const uint32_t b = bits(machInst, 10, 6);
         const bool q = bits(machInst, 6);
-        const IntRegIndex vd =
-            (IntRegIndex)(2 * (bits(machInst, 15, 12) |
+        const RegIndex vd =
+            (RegIndex)(2 * (bits(machInst, 15, 12) |
                                (bits(machInst, 22) << 4)));
-        const IntRegIndex vm =
-            (IntRegIndex)(2 * (bits(machInst, 3, 0) |
+        const RegIndex vm =
+            (RegIndex)(2 * (bits(machInst, 3, 0) |
                                (bits(machInst, 5) << 4)));
         const unsigned size = bits(machInst, 19, 18);
         switch (opc1) {
@@ -1922,14 +1922,14 @@
                 return decodeNeonTwoRegScalar(machInst);
             }
         } else if ((a & 0x16) == 0x16) {
-            const IntRegIndex vd =
-                (IntRegIndex)(2 * (bits(machInst, 15, 12) |
+            const RegIndex vd =
+                (RegIndex)(2 * (bits(machInst, 15, 12) |
                                    (bits(machInst, 22) << 4)));
-            const IntRegIndex vn =
-                (IntRegIndex)(2 * (bits(machInst, 19, 16) |
+            const RegIndex vn =
+                (RegIndex)(2 * (bits(machInst, 19, 16) |
                                    (bits(machInst, 7) << 4)));
-            const IntRegIndex vm =
-                (IntRegIndex)(2 * (bits(machInst, 3, 0) |
+            const RegIndex vm =
+                (RegIndex)(2 * (bits(machInst, 3, 0) |
                                    (bits(machInst, 5) << 4)));
             if (!u) {
                 if (bits(c, 0) == 0) {
@@ -2013,7 +2013,7 @@
 let {{
     header_output = '''
     bool
-    wrongVLdmStmRegs(IntRegIndex start_reg, uint8_t count, bool single);
+    wrongVLdmStmRegs(RegIndex start_reg, uint8_t count, bool single);
 
     StaticInstPtr
     decodeExtensionRegLoadStore(ExtMachInst machInst);
@@ -2041,7 +2041,7 @@
         const uint32_t opcode = bits(machInst, 24, 20);
         const uint32_t offset = bits(machInst, 7, 0);
         const bool single = (bits(machInst, 8) == 0);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
         RegIndex vd = decodeFpVd(machInst, single ? 0x2 : 0x3, false);
 
         switch (bits(opcode, 4, 3)) {
@@ -2052,10 +2052,10 @@
                 if ((bits(machInst, 7, 4) & 0xd) != 1) {
                     break;
                 }
-                const IntRegIndex rt =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-                const IntRegIndex rt2 =
-                    (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
+                const RegIndex rt =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rt2 =
+                    (RegIndex)(uint32_t)bits(machInst, 19, 16);
                 const bool op = bits(machInst, 20);
                 uint32_t vm;
                 if (single) {
@@ -2066,9 +2066,9 @@
                 }
                 if (op) {
                     return new Vmov2Core2Reg(machInst, rt, rt2,
-                                             (IntRegIndex)vm);
+                                             (RegIndex)vm);
                 } else {
-                    return new Vmov2Reg2Core(machInst, (IntRegIndex)vm,
+                    return new Vmov2Reg2Core(machInst, (RegIndex)vm,
                                              rt, rt2);
                 }
             }
@@ -2176,40 +2176,40 @@
     StaticInstPtr
     decodeShortFpTransfer(ExtMachInst machInst);
 
-    IntRegIndex decodeFpVd(ExtMachInst machInst, uint32_t size, bool isInt);
-    IntRegIndex decodeFpVm(ExtMachInst machInst, uint32_t size, bool isInt);
-    IntRegIndex decodeFpVn(ExtMachInst machInst, uint32_t size);
+    RegIndex decodeFpVd(ExtMachInst machInst, uint32_t size, bool isInt);
+    RegIndex decodeFpVm(ExtMachInst machInst, uint32_t size, bool isInt);
+    RegIndex decodeFpVn(ExtMachInst machInst, uint32_t size);
     '''
     decoder_output = '''
-    IntRegIndex decodeFpVd(ExtMachInst machInst, uint32_t size, bool isInt)
+    RegIndex decodeFpVd(ExtMachInst machInst, uint32_t size, bool isInt)
     {
         if (!isInt and size == 3) {
-            return (IntRegIndex)((bits(machInst, 22) << 5) |
+            return (RegIndex)((bits(machInst, 22) << 5) |
                                (bits(machInst, 15, 12) << 1));
         } else {
-            return (IntRegIndex)(bits(machInst, 22) |
+            return (RegIndex)(bits(machInst, 22) |
                               (bits(machInst, 15, 12) << 1));
         }
     }
 
-    IntRegIndex decodeFpVm(ExtMachInst machInst, uint32_t size, bool isInt)
+    RegIndex decodeFpVm(ExtMachInst machInst, uint32_t size, bool isInt)
     {
         if (!isInt and size == 3) {
-            return (IntRegIndex)((bits(machInst, 5) << 5) |
+            return (RegIndex)((bits(machInst, 5) << 5) |
                                (bits(machInst, 3, 0) << 1));
         } else {
-            return (IntRegIndex)(bits(machInst, 5) |
+            return (RegIndex)(bits(machInst, 5) |
                               (bits(machInst, 3, 0) << 1));
         }
     }
 
-    IntRegIndex decodeFpVn(ExtMachInst machInst, uint32_t size)
+    RegIndex decodeFpVn(ExtMachInst machInst, uint32_t size)
     {
         if (size == 3) {
-            return (IntRegIndex)((bits(machInst, 7) << 5) |
+            return (RegIndex)((bits(machInst, 7) << 5) |
                             (bits(machInst, 19, 16) << 1));
         } else {
-            return (IntRegIndex)(bits(machInst, 7) |
+            return (RegIndex)(bits(machInst, 7) |
                             (bits(machInst, 19, 16) << 1));
         }
     }
@@ -2222,10 +2222,10 @@
         const uint32_t op3 = bits(machInst, 6);
         const uint32_t rm = bits(machInst, 17, 16);
         const uint32_t size = bits(machInst, 9, 8);
-        IntRegIndex vd = decodeFpVd(machInst, size, false);
-        IntRegIndex vm = decodeFpVm(machInst, size, false);
-        IntRegIndex vdInt = decodeFpVd(machInst, size, true);
-        IntRegIndex vn = decodeFpVn(machInst, size);
+        RegIndex vd = decodeFpVd(machInst, size, false);
+        RegIndex vm = decodeFpVm(machInst, size, false);
+        RegIndex vdInt = decodeFpVd(machInst, size, true);
+        RegIndex vn = decodeFpVn(machInst, size);
         if (bits(machInst, 31, 24) == 0xFE && !bits(machInst, 4)) {
             if (bits(op0, 3) == 0 && op2 != 0 && !op3){
                 ConditionCode cond;
@@ -2407,16 +2407,16 @@
             if (a == 0) {
                 const uint32_t vn = (bits(machInst, 19, 16) << 1) |
                                     bits(machInst, 7);
-                const IntRegIndex rt =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rt =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
                 if (bits(machInst, 20) == 1) {
-                    return new VmovRegCoreW(machInst, rt, (IntRegIndex)vn);
+                    return new VmovRegCoreW(machInst, rt, (RegIndex)vn);
                 } else {
-                    return new VmovCoreRegW(machInst, (IntRegIndex)vn, rt);
+                    return new VmovCoreRegW(machInst, (RegIndex)vn, rt);
                 }
             } else if (a == 0x7) {
-                const IntRegIndex rt =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rt =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
                 uint32_t reg = bits(machInst, 19, 16);
                 uint32_t specReg;
                 switch (reg) {
@@ -2439,11 +2439,11 @@
                     return new Unknown(machInst);
                 }
                 if (specReg == MISCREG_FPSCR) {
-                    return new VmsrFpscr(machInst, (IntRegIndex)specReg, rt);
+                    return new VmsrFpscr(machInst, (RegIndex)specReg, rt);
                 } else {
                     uint32_t iss = mcrMrcIssBuild(0, bits(machInst, 3, 0), rt,
                         reg, a, bits(machInst, 7, 5));
-                    return new Vmsr(machInst, (IntRegIndex)specReg, rt, iss);
+                    return new Vmsr(machInst, (RegIndex)specReg, rt, iss);
                 }
             }
         } else if (l == 0 && c == 1) {
@@ -2452,25 +2452,25 @@
                               (bits(machInst, 19, 16) << 1);
                 // Handle accessing each single precision half of the vector.
                 vd += bits(machInst, 21);
-                const IntRegIndex rt =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rt =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
                 if (bits(machInst, 22) == 1) {
-                    return new VmovCoreRegB(machInst, (IntRegIndex)vd,
+                    return new VmovCoreRegB(machInst, (RegIndex)vd,
                                             rt, bits(machInst, 6, 5));
                 } else if (bits(machInst, 5) == 1) {
-                    return new VmovCoreRegH(machInst, (IntRegIndex)vd,
+                    return new VmovCoreRegH(machInst, (RegIndex)vd,
                                             rt, bits(machInst, 6));
                 } else if (bits(machInst, 6) == 0) {
-                    return new VmovCoreRegW(machInst, (IntRegIndex)vd, rt);
+                    return new VmovCoreRegW(machInst, (RegIndex)vd, rt);
                 } else {
                     return new Unknown(machInst);
                 }
             } else if (bits(q, 1) == 0) {
                 bool q = bits(machInst, 21);
                 unsigned be = (bits(machInst, 22) << 1) | (bits(machInst, 5));
-                IntRegIndex vd = (IntRegIndex)(2 * (uint32_t)
+                RegIndex vd = (RegIndex)(2 * (uint32_t)
                     (bits(machInst, 19, 16) | (bits(machInst, 7) << 4)));
-                IntRegIndex rt = (IntRegIndex)(uint32_t)
+                RegIndex rt = (RegIndex)(uint32_t)
                     bits(machInst, 15, 12);
                 if (q) {
                     switch (be) {
@@ -2500,16 +2500,16 @@
             if (a == 0) {
                 const uint32_t vn = (bits(machInst, 19, 16) << 1) |
                                     bits(machInst, 7);
-                const IntRegIndex rt =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rt =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
                 if (bits(machInst, 20) == 1) {
-                    return new VmovRegCoreW(machInst, rt, (IntRegIndex)vn);
+                    return new VmovRegCoreW(machInst, rt, (RegIndex)vn);
                 } else {
-                    return new VmovCoreRegW(machInst, (IntRegIndex)vn, rt);
+                    return new VmovCoreRegW(machInst, (RegIndex)vn, rt);
                 }
             } else if (a == 7) {
-                const IntRegIndex rt =
-                    (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+                const RegIndex rt =
+                    (RegIndex)(uint32_t)bits(machInst, 15, 12);
                 uint32_t reg = bits(machInst, 19, 16);
                 uint32_t specReg;
                 switch (reg) {
@@ -2538,11 +2538,11 @@
                         return new Unknown(machInst);
                     }
                 } else if (specReg == MISCREG_FPSCR) {
-                    return new VmrsFpscr(machInst, rt, (IntRegIndex)specReg);
+                    return new VmrsFpscr(machInst, rt, (RegIndex)specReg);
                 } else {
                     uint32_t iss = mcrMrcIssBuild(l, bits(machInst, 3, 0), rt,
                         reg, a, bits(machInst, 7, 5));
-                    return new Vmrs(machInst, rt, (IntRegIndex)specReg, iss);
+                    return new Vmrs(machInst, rt, (RegIndex)specReg, iss);
                 }
             }
         } else {
@@ -2551,29 +2551,29 @@
             // Handle indexing into each single precision half of the vector.
             vd += bits(machInst, 21);
             uint32_t index;
-            const IntRegIndex rt =
-                (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+            const RegIndex rt =
+                (RegIndex)(uint32_t)bits(machInst, 15, 12);
             const bool u = (bits(machInst, 23) == 1);
             if (bits(machInst, 22) == 1) {
                 index = bits(machInst, 6, 5);
                 if (u) {
                     return new VmovRegCoreUB(machInst, rt,
-                                             (IntRegIndex)vd, index);
+                                             (RegIndex)vd, index);
                 } else {
                     return new VmovRegCoreSB(machInst, rt,
-                                             (IntRegIndex)vd, index);
+                                             (RegIndex)vd, index);
                 }
             } else if (bits(machInst, 5) == 1) {
                 index = bits(machInst, 6);
                 if (u) {
                     return new VmovRegCoreUH(machInst, rt,
-                                             (IntRegIndex)vd, index);
+                                             (RegIndex)vd, index);
                 } else {
                     return new VmovRegCoreSH(machInst, rt,
-                                             (IntRegIndex)vd, index);
+                                             (RegIndex)vd, index);
                 }
             } else if (bits(machInst, 6) == 0 && !u) {
-                return new VmovRegCoreW(machInst, rt, (IntRegIndex)vd);
+                return new VmovRegCoreW(machInst, rt, (RegIndex)vd);
             } else {
                 return new Unknown(machInst);
             }
@@ -2605,22 +2605,22 @@
         const bool single = (bits(machInst, 8) == 0);
         // Used to select between vcmp and vcmpe.
         const bool e = (bits(machInst, 7) == 1);
-        IntRegIndex vd;
-        IntRegIndex vm;
-        IntRegIndex vn;
+        RegIndex vd;
+        RegIndex vm;
+        RegIndex vn;
         if (single) {
-            vd = (IntRegIndex)(bits(machInst, 22) |
+            vd = (RegIndex)(bits(machInst, 22) |
                     (bits(machInst, 15, 12) << 1));
-            vm = (IntRegIndex)(bits(machInst, 5) |
+            vm = (RegIndex)(bits(machInst, 5) |
                     (bits(machInst, 3, 0) << 1));
-            vn = (IntRegIndex)(bits(machInst, 7) |
+            vn = (RegIndex)(bits(machInst, 7) |
                     (bits(machInst, 19, 16) << 1));
         } else {
-            vd = (IntRegIndex)((bits(machInst, 22) << 5) |
+            vd = (RegIndex)((bits(machInst, 22) << 5) |
                     (bits(machInst, 15, 12) << 1));
-            vm = (IntRegIndex)((bits(machInst, 5) << 5) |
+            vm = (RegIndex)((bits(machInst, 5) << 5) |
                     (bits(machInst, 3, 0) << 1));
-            vn = (IntRegIndex)((bits(machInst, 7) << 5) |
+            vn = (RegIndex)((bits(machInst, 7) << 5) |
                     (bits(machInst, 19, 16) << 1));
         }
         switch (opc1 & 0xb /* 1011 */) {
@@ -2863,11 +2863,11 @@
               case 0x7:
                 if (opc3 == 0x3) {
                     if (single) {
-                        vd = (IntRegIndex)((bits(machInst, 22) << 5) |
+                        vd = (RegIndex)((bits(machInst, 22) << 5) |
                                 (bits(machInst, 15, 12) << 1));
                         return new VcvtFpSFpD(machInst, vd, vm);
                     } else {
-                        vd = (IntRegIndex)(bits(machInst, 22) |
+                        vd = (RegIndex)(bits(machInst, 22) |
                                 (bits(machInst, 15, 12) << 1));
                         return new VcvtFpDFpS(machInst, vd, vm);
                     }
@@ -2878,7 +2878,7 @@
                     if (single) {
                         return new VcvtUIntFpS(machInst, vd, vm);
                     } else {
-                        vm = (IntRegIndex)(bits(machInst, 5) |
+                        vm = (RegIndex)(bits(machInst, 5) |
                                 (bits(machInst, 3, 0) << 1));
                         return new VcvtUIntFpD(machInst, vd, vm);
                     }
@@ -2886,7 +2886,7 @@
                     if (single) {
                         return new VcvtSIntFpS(machInst, vd, vm);
                     } else {
-                        vm = (IntRegIndex)(bits(machInst, 5) |
+                        vm = (RegIndex)(bits(machInst, 5) |
                                 (bits(machInst, 3, 0) << 1));
                         return new VcvtSIntFpD(machInst, vd, vm);
                     }
@@ -2894,7 +2894,7 @@
               case 0x9:
                 if (bits(machInst, 31, 28) != 0xF
                     && bits(machInst, 27, 23) == 0x1D) {
-                    vd = (IntRegIndex)(bits(machInst, 22) |
+                    vd = (RegIndex)(bits(machInst, 22) |
                          (bits(machInst, 15, 12) << 1));
                     return new VjcvtSFixedFpD(machInst, vd, vm);
                 }
@@ -2946,7 +2946,7 @@
                     if (single) {
                         return new VcvtFpUIntSR(machInst, vd, vm);
                     } else {
-                        vd = (IntRegIndex)(bits(machInst, 22) |
+                        vd = (RegIndex)(bits(machInst, 22) |
                                 (bits(machInst, 15, 12) << 1));
                         return new VcvtFpUIntDR(machInst, vd, vm);
                     }
@@ -2954,7 +2954,7 @@
                     if (single) {
                         return new VcvtFpUIntS(machInst, vd, vm);
                     } else {
-                        vd = (IntRegIndex)(bits(machInst, 22) |
+                        vd = (RegIndex)(bits(machInst, 22) |
                                 (bits(machInst, 15, 12) << 1));
                         return new VcvtFpUIntD(machInst, vd, vm);
                     }
@@ -2964,7 +2964,7 @@
                     if (single) {
                         return new VcvtFpSIntSR(machInst, vd, vm);
                     } else {
-                        vd = (IntRegIndex)(bits(machInst, 22) |
+                        vd = (RegIndex)(bits(machInst, 22) |
                                 (bits(machInst, 15, 12) << 1));
                         return new VcvtFpSIntDR(machInst, vd, vm);
                     }
@@ -2972,7 +2972,7 @@
                     if (single) {
                         return new VcvtFpSIntS(machInst, vd, vm);
                     } else {
-                        vd = (IntRegIndex)(bits(machInst, 22) |
+                        vd = (RegIndex)(bits(machInst, 22) |
                                 (bits(machInst, 15, 12) << 1));
                         return new VcvtFpSIntD(machInst, vd, vm);
                     }
diff --git a/src/arch/arm/isa/formats/macromem.isa b/src/arch/arm/isa/formats/macromem.isa
index 8eb77e9..350908a 100644
--- a/src/arch/arm/isa/formats/macromem.isa
+++ b/src/arch/arm/isa/formats/macromem.isa
@@ -37,7 +37,7 @@
 
 def format ArmMacroMem() {{
     decode_block = '''
-    return new LdmStm(machInst, (IntRegIndex)(uint32_t)RN, !PREPOST, UP,
+    return new LdmStm(machInst, (RegIndex)(uint32_t)RN, !PREPOST, UP,
                       PSRUSER, WRITEBACK, LOADOP, machInst.regList);
     '''
 }};
@@ -45,7 +45,7 @@
 def format Thumb16MacroMem() {{
     decode_block = '''
     {
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 10, 8);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 10, 8);
         const bool load = (bits(machInst, 11) == 1);
         const uint32_t regList = bits(machInst, 7, 0);
         const bool writeback = (!load || bits(regList, rn) == 0);
diff --git a/src/arch/arm/isa/formats/mem.isa b/src/arch/arm/isa/formats/mem.isa
index 7976902..aecd378 100644
--- a/src/arch/arm/isa/formats/mem.isa
+++ b/src/arch/arm/isa/formats/mem.isa
@@ -220,9 +220,9 @@
 def format ArmSyncMem() {{
     decode_block = '''
     {
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rt = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        const IntRegIndex rt2 = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rt = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rt2 = (RegIndex)(uint32_t)bits(machInst, 3, 0);
 
         const auto type_L = bits(machInst, 22, 20);
         const auto ex_ord = bits(machInst, 9, 8);
@@ -312,8 +312,8 @@
         const bool add = (bits(machInst, 24, 23) == 0x3);
         if (bits(machInst, 20) == 1) {
             // post == add
-            const IntRegIndex rn =
-                (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
+            const RegIndex rn =
+                (RegIndex)(uint32_t)bits(machInst, 19, 16);
             if (!add && !wb) {
                 return new %(rfe)s(machInst, rn, RfeOp::DecrementBefore, wb);
             } else if (add && !wb) {
@@ -365,10 +365,10 @@
         const uint32_t op1 = bits(machInst, 24, 23);
         const uint32_t op2 = bits(machInst, 21, 20);
         const uint32_t op3 = bits(machInst, 7, 4);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rt = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        const IntRegIndex rt2 = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rt = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rt2 = (RegIndex)(uint32_t)bits(machInst, 11, 8);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         const uint32_t imm8 = bits(machInst, 7, 0);
         if (bits(op1, 1) == 0 && bits(op2, 1) == 0) {
             if (op1 == 0) {
@@ -525,10 +525,10 @@
             uint32_t op2 = bits(machInst, 11, 6);
             if (HTRN == 0xF) {
                 if (UP) {
-                    return new %(literal_u)s(machInst, RT, INTREG_PC,
+                    return new %(literal_u)s(machInst, RT, int_reg::Pc,
                                              true, IMMED_11_0);
                 } else {
-                    return new %(literal)s(machInst, RT, INTREG_PC,
+                    return new %(literal)s(machInst, RT, int_reg::Pc,
                                            false, IMMED_11_0);
                 }
             } else if (op1 == 0x1) {
@@ -683,9 +683,9 @@
     {
         const uint32_t op1 = bits(machInst, 24, 23);
         const uint32_t op2 = bits(machInst, 11, 6);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rt = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rt = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         const uint32_t imm12 = bits(machInst, 11, 0);
         const uint32_t imm8 = bits(machInst, 7, 0);
         bool pldw = bits(machInst, 21);
@@ -695,36 +695,36 @@
                 const bool add = bits(machInst, 23);
                 if (bits(op1, 1) == 1) {
                     if (add) {
-                        return new %(pli_iulit)s(machInst, INTREG_ZERO,
-                                                 INTREG_PC, true, imm12);
+                        return new %(pli_iulit)s(machInst, int_reg::Zero,
+                                                 int_reg::Pc, true, imm12);
                     } else {
-                        return new %(pli_ilit)s(machInst, INTREG_ZERO,
-                                                INTREG_PC, false, imm12);
+                        return new %(pli_ilit)s(machInst, int_reg::Zero,
+                                                int_reg::Pc, false, imm12);
                     }
                 } else {
                     if (add) {
-                        return new %(pld_iulit)s(machInst, INTREG_ZERO,
-                                                 INTREG_PC, true, imm12);
+                        return new %(pld_iulit)s(machInst, int_reg::Zero,
+                                                 int_reg::Pc, true, imm12);
                     } else {
-                        return new %(pld_ilit)s(machInst, INTREG_ZERO,
-                                                INTREG_PC, false, imm12);
+                        return new %(pld_ilit)s(machInst, int_reg::Zero,
+                                                int_reg::Pc, false, imm12);
                     }
                 }
             } else {
                 if (bits(op1, 1) == 1) {
                     if (bits(machInst, 23)) {
-                        return new %(ldrsb_lit_u)s(machInst, rt, INTREG_PC,
+                        return new %(ldrsb_lit_u)s(machInst, rt, int_reg::Pc,
                                                    true, imm12);
                     } else {
-                        return new %(ldrsb_lit)s(machInst, rt, INTREG_PC,
+                        return new %(ldrsb_lit)s(machInst, rt, int_reg::Pc,
                                                  false, imm12);
                     }
                 } else {
                     if (bits(machInst, 23)) {
-                        return new %(ldrb_lit_u)s(machInst, rt, INTREG_PC,
+                        return new %(ldrb_lit_u)s(machInst, rt, int_reg::Pc,
                                                   true, imm12);
                     } else {
-                        return new %(ldrb_lit)s(machInst, rt, INTREG_PC,
+                        return new %(ldrb_lit)s(machInst, rt, int_reg::Pc,
                                                 false, imm12);
                     }
                 }
@@ -734,42 +734,42 @@
               case 0x0:
                 if (op2 == 0x0) {
                     if (pldw) {
-                        return new %(pldw_radd)s(machInst, INTREG_ZERO,
+                        return new %(pldw_radd)s(machInst, int_reg::Zero,
                                                  rn, true, imm2, LSL, rm);
                     } else {
-                        return new %(pld_radd)s(machInst, INTREG_ZERO,
+                        return new %(pld_radd)s(machInst, int_reg::Zero,
                                                 rn, true, imm2, LSL, rm);
                     }
                 } else if (bits(op2, 5, 2) == 0xc) {
                     if (pldw) {
-                        return new %(pldw_isub)s(machInst, INTREG_ZERO,
+                        return new %(pldw_isub)s(machInst, int_reg::Zero,
                                                  rn, false, imm8);
                     } else {
-                        return new %(pld_isub)s(machInst, INTREG_ZERO,
+                        return new %(pld_isub)s(machInst, int_reg::Zero,
                                                 rn, false, imm8);
                     }
                 }
                 break;
               case 0x1:
                 if (pldw) {
-                    return new %(pldw_iadd)s(machInst, INTREG_ZERO,
+                    return new %(pldw_iadd)s(machInst, int_reg::Zero,
                                              rn, true, imm12);
                 } else {
-                    return new %(pld_iadd)s(machInst, INTREG_ZERO,
+                    return new %(pld_iadd)s(machInst, int_reg::Zero,
                                             rn, true, imm12);
                 }
               case 0x2:
                 if (op2 == 0x0) {
-                    return new %(pli_radd)s(machInst, INTREG_ZERO, rn,
+                    return new %(pli_radd)s(machInst, int_reg::Zero, rn,
                                             true, imm2, LSL, rm);
                 } else if (bits(op2, 5, 2) == 0xc) {
-                    return new %(pli_ilit)s(machInst, INTREG_ZERO,
-                                            INTREG_PC, false, imm8);
+                    return new %(pli_ilit)s(machInst, int_reg::Zero,
+                                            int_reg::Pc, false, imm8);
                 }
                 break;
               case 0x3:
-                return new %(pli_iulit)s(machInst, INTREG_ZERO,
-                                        INTREG_PC, true, imm12);
+                return new %(pli_iulit)s(machInst, int_reg::Zero,
+                                        int_reg::Pc, true, imm12);
             }
             return new Unknown(machInst);
         } else {
@@ -880,9 +880,9 @@
     {
         const uint32_t op1 = bits(machInst, 24, 23);
         const uint32_t op2 = bits(machInst, 11, 6);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rt = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rt = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         const uint32_t imm12 = bits(machInst, 11, 0);
         const uint32_t imm8 = bits(machInst, 7, 0);
         bool pldw = bits(machInst, 21);
@@ -898,18 +898,18 @@
             } else {
                 if (bits(op1, 1) == 1) {
                     if (bits(machInst, 23)) {
-                        return new %(ldrsh_lit_u)s(machInst, rt, INTREG_PC,
+                        return new %(ldrsh_lit_u)s(machInst, rt, int_reg::Pc,
                                                    true, imm12);
                     } else {
-                        return new %(ldrsh_lit)s(machInst, rt, INTREG_PC,
+                        return new %(ldrsh_lit)s(machInst, rt, int_reg::Pc,
                                                  false, imm12);
                     }
                 } else {
                     if (bits(machInst, 23)) {
-                        return new %(ldrh_lit_u)s(machInst, rt, INTREG_PC,
+                        return new %(ldrh_lit_u)s(machInst, rt, int_reg::Pc,
                                                   true, imm12);
                     } else {
-                        return new %(ldrh_lit)s(machInst, rt, INTREG_PC,
+                        return new %(ldrh_lit)s(machInst, rt, int_reg::Pc,
                                                 false, imm12);
                     }
                 }
@@ -919,28 +919,28 @@
               case 0x0:
                 if (op2 == 0x0) {
                     if (pldw) {
-                        return new %(pldw_radd)s(machInst, INTREG_ZERO,
+                        return new %(pldw_radd)s(machInst, int_reg::Zero,
                                                  rn, true, imm2, LSL, rm);
                     } else {
-                        return new %(pld_radd)s(machInst, INTREG_ZERO,
+                        return new %(pld_radd)s(machInst, int_reg::Zero,
                                                 rn, true, imm2, LSL, rm);
                     }
                 } else if (bits(op2, 5, 2) == 0xc) {
                     if (pldw) {
-                        return new %(pldw_isub)s(machInst, INTREG_ZERO,
+                        return new %(pldw_isub)s(machInst, int_reg::Zero,
                                                  rn, false, imm8);
                     } else {
-                        return new %(pld_isub)s(machInst, INTREG_ZERO,
+                        return new %(pld_isub)s(machInst, int_reg::Zero,
                                                 rn, false, imm8);
                     }
                 }
                 break;
               case 0x1:
                 if (pldw) {
-                    return new %(pldw_iadd)s(machInst, INTREG_ZERO,
+                    return new %(pldw_iadd)s(machInst, int_reg::Zero,
                                              rn, true, imm12);
                 } else {
-                    return new %(pld_iadd)s(machInst, INTREG_ZERO,
+                    return new %(pld_iadd)s(machInst, int_reg::Zero,
                                             rn, true, imm12);
                 }
               case 0x2:
@@ -1123,9 +1123,11 @@
             }
           case 0x9:
             if (load) {
-                return new %(ldr)s(machInst, hrt, INTREG_SP, true, imm8 << 2);
+                return new %(ldr)s(machInst, hrt, int_reg::Sp, true,
+                                   imm8 << 2);
             } else {
-                return new %(str)s(machInst, hrt, INTREG_SP, true, imm8 << 2);
+                return new %(str)s(machInst, hrt, int_reg::Sp, true,
+                                   imm8 << 2);
             }
           default:
             return new Unknown(machInst);
@@ -1148,7 +1150,7 @@
     {
         const uint32_t rt = bits(machInst, 10, 8);
         const uint32_t imm8 = bits(machInst, 7, 0);
-        return new %s(machInst, rt, INTREG_PC, true, imm8 << 2);
+        return new %s(machInst, rt, int_reg::Pc, true, imm8 << 2);
     }
     ''' % loadImmClassName(False, True, False)
 }};
diff --git a/src/arch/arm/isa/formats/misc.isa b/src/arch/arm/isa/formats/misc.isa
index 32fccc8..ad3c6e9 100644
--- a/src/arch/arm/isa/formats/misc.isa
+++ b/src/arch/arm/isa/formats/misc.isa
@@ -1,6 +1,6 @@
 // -*- mode:c++ -*-
 
-// Copyright (c) 2010-2013,2016-2018 ARM Limited
+// Copyright (c) 2010-2013,2016-2018, 2021 Arm Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -38,9 +38,9 @@
 def format Crc32() {{
     decode_block = '''
     {
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 15, 12);
 
         uint8_t c_poly = bits(machInst, 9);
         uint8_t sz = bits(machInst, 22, 21);
@@ -94,8 +94,8 @@
     {
         const uint8_t byteMask = bits(machInst, 19, 16);
         const uint8_t sysM     = byteMask | (bits(machInst, 8) << 4);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 15, 12);
         const uint32_t opcode = bits(machInst, 24, 21);
         const bool useImm = bits(machInst, 25);
         const bool r      = bits(machInst, 22);
@@ -159,14 +159,14 @@
         const uint32_t opc2 = bits(machInst, 7, 5);
         const uint32_t crm = bits(machInst, 3, 0);
         const MiscRegIndex miscReg = decodeCP14Reg(crn, opc1, crm, opc2);
-        const IntRegIndex rt = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rt = (RegIndex)(uint32_t)bits(machInst, 15, 12);
 
         const bool isRead = bits(machInst, 20);
 
         switch (miscReg) {
           case MISCREG_NOP:
             return new NopInst(machInst);
-          case MISCREG_CP14_UNIMPL:
+          case MISCREG_UNKNOWN:
             return new FailUnimplemented(isRead ? "mrc unknown" : "mcr unknown",
                     machInst,
                     csprintf("miscreg crn:%d opc1:%d crm:%d opc2:%d %s unknown",
@@ -203,7 +203,7 @@
         const uint32_t opc2 = bits(machInst, 7, 5);
         const uint32_t crm = bits(machInst, 3, 0);
         const MiscRegIndex miscReg = decodeCP15Reg(crn, opc1, crm, opc2);
-        const IntRegIndex rt = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rt = (RegIndex)(uint32_t)bits(machInst, 15, 12);
         const bool isRead = bits(machInst, 20);
         uint32_t iss = mcrMrcIssBuild(isRead, crm, rt, crn, opc1, opc2);
 
@@ -211,7 +211,7 @@
           case MISCREG_NOP:
             return new McrMrcMiscInst(isRead ? "mrc nop" : "mcr nop",
                                       machInst, iss, MISCREG_NOP);
-          case MISCREG_CP15_UNIMPL:
+          case MISCREG_UNKNOWN:
             return new FailUnimplemented(isRead ? "mrc unkown" : "mcr unkown",
                     machInst,
                     csprintf("miscreg crn:%d opc1:%d crm:%d opc2:%d %s unknown",
@@ -247,6 +247,37 @@
             return new McrDccmvau(machInst, miscReg, rt, iss);
           case MISCREG_DCCIMVAC:
             return new McrDccimvac(machInst, miscReg, rt, iss);
+          case MISCREG_TLBIALL:
+          case MISCREG_TLBIALLIS:
+          case MISCREG_ITLBIALL:
+          case MISCREG_DTLBIALL:
+          case MISCREG_TLBIMVA:
+          case MISCREG_TLBIMVAL:
+          case MISCREG_TLBIMVAIS:
+          case MISCREG_TLBIMVALIS:
+          case MISCREG_TLBIASID:
+          case MISCREG_TLBIASIDIS:
+          case MISCREG_TLBIMVAA:
+          case MISCREG_TLBIMVAAL:
+          case MISCREG_TLBIMVAAIS:
+          case MISCREG_TLBIMVAALIS:
+          case MISCREG_TLBIMVAH:
+          case MISCREG_TLBIMVALH:
+          case MISCREG_TLBIMVAHIS:
+          case MISCREG_TLBIMVALHIS:
+          case MISCREG_TLBIIPAS2:
+          case MISCREG_TLBIIPAS2L:
+          case MISCREG_TLBIIPAS2IS:
+          case MISCREG_TLBIIPAS2LIS:
+          case MISCREG_ITLBIMVA:
+          case MISCREG_DTLBIMVA:
+          case MISCREG_ITLBIASID:
+          case MISCREG_DTLBIASID:
+          case MISCREG_TLBIALLNSNH:
+          case MISCREG_TLBIALLNSNHIS:
+          case MISCREG_TLBIALLH:
+          case MISCREG_TLBIALLHIS:
+            return new Tlbi(machInst, miscReg, rt, iss);
           default:
             if (miscRegInfo[miscReg][MISCREG_WARN_NOT_FAIL]) {
                 std::string full_mnem = csprintf("%s %s",
@@ -295,13 +326,13 @@
         const uint32_t crm = bits(machInst, 3, 0);
         const uint32_t opc1 = bits(machInst, 7, 4);
         const MiscRegIndex miscReg = decodeCP15Reg64(crm, opc1);
-        const IntRegIndex rt = (IntRegIndex) (uint32_t) bits(machInst, 15, 12);
-        const IntRegIndex rt2 = (IntRegIndex) (uint32_t) bits(machInst, 19, 16);
+        const RegIndex rt = (RegIndex) (uint32_t) bits(machInst, 15, 12);
+        const RegIndex rt2 = (RegIndex) (uint32_t) bits(machInst, 19, 16);
 
         const bool isRead = bits(machInst, 20);
 
         switch (miscReg) {
-          case MISCREG_CP15_UNIMPL:
+          case MISCREG_UNKNOWN:
             return new FailUnimplemented(isRead ? "mrc" : "mcr", machInst,
                     csprintf("miscreg crm:%d opc1:%d 64-bit %s unknown",
                     crm, opc1, isRead ? "read" : "write"));
diff --git a/src/arch/arm/isa/formats/mult.isa b/src/arch/arm/isa/formats/mult.isa
index dc91da3..3473287 100644
--- a/src/arch/arm/isa/formats/mult.isa
+++ b/src/arch/arm/isa/formats/mult.isa
@@ -40,10 +40,10 @@
         // ignored.
         const uint32_t op = bits(machInst, 23, 21);
         const bool s = bits(machInst, 20);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex ra = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex ra = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 11, 8);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         switch (op) {
             case 0x0:
               if (s) {
@@ -97,10 +97,10 @@
     {
         const uint32_t op1 = bits(machInst, 22, 21);
         const bool op = bits(machInst, 5);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex ra = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex ra = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 11, 8);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         switch (op1) {
           case 0x0:
             switch (bits(machInst, 6, 5)) {
@@ -167,10 +167,10 @@
     {
         const uint32_t op1 = bits(machInst, 22, 20);
         const uint32_t op2 = bits(machInst, 5, 4);
-        const IntRegIndex ra = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex ra = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 11, 8);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         if (op1 != 0x1 && bits(op2, 1) != 0) {
             return new Unknown(machInst);
         }
@@ -292,10 +292,10 @@
     {
         const uint32_t op1 = bits(machInst, 22, 20);
         const uint32_t op2 = bits(machInst, 7, 4);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rdlo = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
-        const IntRegIndex rdhi = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rdlo = (RegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rdhi = (RegIndex)(uint32_t)bits(machInst, 11, 8);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 3, 0);
         switch (op1) {
           case 0x0:
             if (op2 == 0x0) {
@@ -368,10 +368,10 @@
         // This is 7-5 in the manual, but bit 5 is always ignored.
         const uint32_t op2 = bits(machInst, 7, 6);
         const bool aIsF = (bits(machInst, 15, 12) == 0xf);
-        const IntRegIndex rd = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
-        const IntRegIndex rm = (IntRegIndex)(uint32_t)bits(machInst, 11, 8);
-        const IntRegIndex ra = (IntRegIndex)(uint32_t)bits(machInst, 15, 12);
+        const RegIndex rd = (RegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 3, 0);
+        const RegIndex rm = (RegIndex)(uint32_t)bits(machInst, 11, 8);
+        const RegIndex ra = (RegIndex)(uint32_t)bits(machInst, 15, 12);
         const bool m = bits(machInst, 5);
         switch (op1) {
           case 0x0:
diff --git a/src/arch/arm/isa/formats/neon64.isa b/src/arch/arm/isa/formats/neon64.isa
index 660d118..72b7e28 100644
--- a/src/arch/arm/isa/formats/neon64.isa
+++ b/src/arch/arm/isa/formats/neon64.isa
@@ -98,9 +98,9 @@
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opcode = bits(machInst, 15, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         uint8_t size_q = (size << 1) | q;
         uint8_t sz_q = size_q & 0x3;
@@ -513,9 +513,9 @@
         uint8_t size   = bits(machInst, 23, 22);
         uint8_t opcode = bits(machInst, 15, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         switch (opcode) {
           case 0x10:
@@ -569,9 +569,9 @@
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opcode = bits(machInst, 15, 12);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         switch (opcode) {
           case 0x0:
@@ -731,8 +731,8 @@
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opcode = bits(machInst, 16, 12);
 
-        IntRegIndex vd = (IntRegIndex)(uint8_t)bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex)(uint8_t)bits(machInst, 9, 5);
+        RegIndex vd = (RegIndex)(uint8_t)bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex)(uint8_t)bits(machInst, 9, 5);
 
         uint8_t size_q = (size << 1) | q;
         uint8_t sz_q = size_q & 0x3;
@@ -1109,8 +1109,8 @@
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opcode = bits(machInst, 16, 12);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t size_q = (size << 1) | q;
         uint8_t sz_q = size_q & 0x3;
@@ -1195,8 +1195,8 @@
         uint8_t imm5 = bits(machInst, 20, 16);
         uint8_t imm4 = bits(machInst, 14, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t imm5_pos = findLsbSet(imm5);
         uint8_t index1 = 0, index2 = 0;
@@ -1353,9 +1353,9 @@
         uint8_t opcode = bits(machInst, 15, 12);
         uint8_t H = bits(machInst, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm_bf = (IntRegIndex) (uint8_t) bits(machInst, 19, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm_bf = (RegIndex) (uint8_t) bits(machInst, 19, 16);
 
         uint8_t index = 0;
         uint8_t index_fp = 0;
@@ -1372,7 +1372,7 @@
             index = (H << 1) | L;
             vmh = M;
         }
-        IntRegIndex vm = (IntRegIndex) (uint8_t) (vmh << 4 | vm_bf);
+        RegIndex vm = (RegIndex) (uint8_t) (vmh << 4 | vm_bf);
 
         // Index and 2nd register operand for FP instructions
         vmh = M;
@@ -1381,7 +1381,7 @@
         } else if (L == 0) {
             index_fp = H;
         }
-        IntRegIndex vm_fp = (IntRegIndex) (uint8_t) (vmh << 4 | vm_bf);
+        RegIndex vm_fp = (RegIndex) (uint8_t) (vmh << 4 | vm_bf);
 
         switch (opcode) {
           case 0x0:
@@ -1582,7 +1582,7 @@
         uint8_t cmode = bits(machInst, 15, 12);
         uint8_t o2 = bits(machInst, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
 
         if (o2 == 0x1 || (op == 0x1 && cmode == 0xf && !q))
             return new Unknown64(machInst);
@@ -1698,8 +1698,8 @@
         uint8_t immb = bits(machInst, 18, 16);
         uint8_t opcode = bits(machInst, 15, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t immh3 = bits(machInst, 22);
         uint8_t immh3_q = (immh3 << 1) | q;
@@ -1877,9 +1877,9 @@
     {
         uint8_t q = bits(machInst, 30);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         uint8_t switchVal = bits(machInst, 14, 12);
 
@@ -1938,9 +1938,9 @@
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opcode = bits(machInst, 14, 12);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         switch (opcode) {
           case 0x1:
@@ -1974,9 +1974,9 @@
         uint8_t op2 = bits(machInst, 23, 22);
         uint8_t imm4 = bits(machInst, 14, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         if (op2 != 0 || (q == 0x0 && bits(imm4, 3) == 0x1))
             return new Unknown64(machInst);
@@ -1998,9 +1998,9 @@
         uint8_t opcode = bits(machInst, 15, 11);
         uint8_t s = bits(machInst, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         switch (opcode) {
           case 0x01:
@@ -2142,9 +2142,9 @@
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opcode = bits(machInst, 15, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         switch (opcode) {
           case 0x10:
@@ -2170,9 +2170,9 @@
 
         uint8_t opcode = bits(machInst, 15, 12);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         switch (opcode) {
           case 0x9:
@@ -2193,8 +2193,8 @@
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opcode = bits(machInst, 16, 12);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t switchVal = opcode | ((u ? 1 : 0) << 5);
         switch (switchVal) {
@@ -2391,8 +2391,8 @@
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opcode = bits(machInst, 16, 12);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         if (!u) {
             if (opcode == 0x1b && size == 0x3)
@@ -2436,8 +2436,8 @@
 
         uint8_t imm5 = bits(machInst, 20, 16);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t size = findLsbSet(imm5);
         if (size > 3)
@@ -2459,9 +2459,9 @@
         uint8_t opcode = bits(machInst, 15, 12);
         uint8_t H = bits(machInst, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex vm_bf = (IntRegIndex) (uint8_t) bits(machInst, 19, 16);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vm_bf = (RegIndex) (uint8_t) bits(machInst, 19, 16);
 
         uint8_t index = 0;
         uint8_t index_fp = 0;
@@ -2479,7 +2479,7 @@
             index = H;
             vmh = M;
         }
-        IntRegIndex vm = (IntRegIndex) (uint8_t) (vmh << 4 | vm_bf);
+        RegIndex vm = (RegIndex) (uint8_t) (vmh << 4 | vm_bf);
 
         // Index and 2nd register operand for FP instructions
         vmh = M;
@@ -2488,7 +2488,7 @@
         } else if (L == 0) {
             index_fp = H;
         }
-        IntRegIndex vm_fp = (IntRegIndex) (uint8_t) (vmh << 4 | vm_bf);
+        RegIndex vm_fp = (RegIndex) (uint8_t) (vmh << 4 | vm_bf);
 
         uint8_t u_opcode = opcode | u << 4;
 
@@ -2563,8 +2563,8 @@
         uint8_t immb = bits(machInst, 18, 16);
         uint8_t opcode = bits(machInst, 15, 11);
 
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex vn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex vn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t immh3 = bits(machInst, 22);
         uint8_t size = findMsbSet(immh);
@@ -2748,9 +2748,9 @@
                 return new Unknown64(machInst);
             }
 
-            IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex rn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-            IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+            RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex rn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
             if (load) {
                 return new VldMult64(machInst, rn, vd, rm, eSize, dataSize,
@@ -2797,9 +2797,9 @@
 
             uint8_t eSize = scale;
 
-            IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex rn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-            IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+            RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex rn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
             if (load) {
                 return new VldSingle64(machInst, rn, vd, rm, eSize, dataSize,
diff --git a/src/arch/arm/isa/formats/sve_2nd_level.isa b/src/arch/arm/isa/formats/sve_2nd_level.isa
index 53fd80d..cbd5466 100644
--- a/src/arch/arm/isa/formats/sve_2nd_level.isa
+++ b/src/arch/arm/isa/formats/sve_2nd_level.isa
@@ -43,9 +43,9 @@
     StaticInstPtr
     decodeSveIntArithBinPred(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         switch (bits(machInst, 20, 19)) {
           case 0x0:
@@ -147,9 +147,9 @@
     StaticInstPtr
     decodeSveIntReduc(ExtMachInst machInst)
     {
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         uint8_t size = bits(machInst, 23, 22);
 
@@ -221,10 +221,10 @@
     StaticInstPtr
     decodeSveIntMulAdd(ExtMachInst machInst)
     {
-        IntRegIndex zda = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zda = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opc = (bits(machInst, 15) << 1) | bits(machInst, 13);
@@ -248,8 +248,8 @@
     StaticInstPtr
     decodeSveShiftByImmPred0(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         uint8_t imm3 = (uint8_t) bits(machInst, 7, 5);
 
         uint8_t tsize = (bits(machInst, 23, 22) << 2) | bits(machInst, 9, 8);
@@ -304,9 +304,9 @@
     StaticInstPtr
     decodeSveShiftByVectorPred(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opc = bits(machInst, 18, 16);
         switch (opc) {
@@ -335,9 +335,9 @@
     StaticInstPtr
     decodeSveShiftByWideElemsPred(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opc = bits(machInst, 18, 16);
         switch (opc) {
@@ -373,9 +373,9 @@
     StaticInstPtr
     decodeSveIntArithUnaryPred(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         unsigned esize = bits(machInst, 23, 22);
         uint8_t opg = bits(machInst, 20, 19);
         uint8_t opc = bits(machInst, 18, 16);
@@ -451,9 +451,9 @@
     StaticInstPtr
     decodeSveIntArithUnpred(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         uint8_t opc = (uint8_t) bits(machInst, 12, 10);
         uint8_t size = (uint8_t) bits(machInst, 23, 22);
@@ -485,9 +485,9 @@
     StaticInstPtr
     decodeSveIntLogUnpred(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
         uint8_t opc = (uint8_t) (bits(machInst, 23, 22) << 3
                 | bits(machInst, 12, 10));
 
@@ -508,7 +508,7 @@
     StaticInstPtr
     decodeSveIndexGen(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
         uint8_t size = (uint8_t) bits(machInst, 23, 22);
         uint8_t grp = (uint8_t) bits(machInst, 11, 10);
 
@@ -536,7 +536,7 @@
             case 1:
                 { // INDEX (scalar, immediate)
                     int8_t imm5 = sext<5>(bits(machInst, 20, 16));
-                    IntRegIndex zn = (IntRegIndex) (uint8_t) bits(
+                    RegIndex zn = (RegIndex) (uint8_t) bits(
                             machInst, 9, 5);
                     switch (size) {
                         case 0:
@@ -557,7 +557,7 @@
             case 2:
                 { // INDEX (immediate, scalar)
                     int8_t imm5 = sext<5>(bits(machInst, 9, 5));
-                    IntRegIndex zm = (IntRegIndex) (uint8_t) bits(
+                    RegIndex zm = (RegIndex) (uint8_t) bits(
                             machInst, 20, 16);
                     switch (size) {
                         case 0:
@@ -577,9 +577,9 @@
                 }
             case 3:
                 { // INDEX (scalars)
-                    IntRegIndex zn = (IntRegIndex) (uint8_t) bits(
+                    RegIndex zn = (RegIndex) (uint8_t) bits(
                             machInst, 9, 5);
-                    IntRegIndex zm = (IntRegIndex) (uint8_t) bits(
+                    RegIndex zm = (RegIndex) (uint8_t) bits(
                             machInst, 20, 16);
                     switch (size) {
                         case 0:
@@ -606,10 +606,10 @@
         uint8_t b23_22 = bits(machInst, 23, 22);
         uint8_t b11 = bits(machInst, 11);
         if ((b23_22 & 0x2) == 0x0 && b11 == 0x0) {
-            IntRegIndex rd = makeSP(
-                (IntRegIndex) (uint8_t) bits(machInst, 4, 0));
-            IntRegIndex rn = makeSP(
-                (IntRegIndex) (uint8_t) bits(machInst, 20, 16));
+            RegIndex rd = makeSP(
+                (RegIndex) (uint8_t) bits(machInst, 4, 0));
+            RegIndex rn = makeSP(
+                (RegIndex) (uint8_t) bits(machInst, 20, 16));
             uint64_t imm = sext<6>(bits(machInst, 10, 5));
             if ((b23_22 & 0x1) == 0x0) {
                 return new AddvlXImm(machInst, rd, rn, imm);
@@ -617,7 +617,7 @@
                 return new AddplXImm(machInst, rd, rn, imm);
             }
         } else if (b23_22 == 0x2 && b11 == 0x0) {
-            IntRegIndex rd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex rd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
             uint64_t imm = sext<6>(bits(machInst, 10, 5));
             if (bits(machInst, 20, 16) == 0x1f) {
                 return new SveRdvl(machInst, rd, imm);
@@ -629,9 +629,9 @@
     StaticInstPtr
     decodeSveShiftByWideElemsUnpred(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opc = (uint8_t) bits(machInst, 11, 10);
         switch (opc) {
@@ -651,8 +651,8 @@
     StaticInstPtr
     decodeSveShiftByImmUnpredB(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
         uint8_t imm3 = (uint8_t) bits(machInst, 18, 16);
 
         uint8_t tsize = (bits(machInst, 23, 22) << 2) | bits(machInst, 20, 19);
@@ -712,9 +712,9 @@
     StaticInstPtr
     decodeSveCompVecAddr(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
         uint8_t mult = 1 << bits(machInst, 11, 10);
 
         uint8_t opc = bits(machInst, 23, 22);
@@ -739,8 +739,8 @@
     StaticInstPtr
     decodeSveIntMiscUnpred(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opc = bits(machInst, 11, 10);
@@ -751,7 +751,7 @@
                 if (size == 0) {
                     break;
                 }
-                IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst,
+                RegIndex zm = (RegIndex) (uint8_t) bits(machInst,
                                                               20, 16);
                 return decodeSveBinUnpredF<SveFtssel>(
                     size, machInst, zd, zn, zm);
@@ -783,7 +783,7 @@
         if (b13_12 == 0) {
             uint8_t pattern = (uint8_t) bits(machInst, 9, 5);
             uint8_t imm4 = (uint8_t) bits(machInst, 19, 16) + 1;
-            IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
             unsigned size = (unsigned) bits(machInst, 23, 22);
             if (opc20) {
                 if (opc11 == 0) {
@@ -817,7 +817,7 @@
         } else if (b13_12 == 3) {
             uint8_t pattern = (uint8_t) bits(machInst, 9, 5);
             uint8_t imm4 = (uint8_t) bits(machInst, 19, 16) + 1;
-            IntRegIndex rdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex rdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
             unsigned size = (unsigned) bits(machInst, 23, 22);
             switch (opc11_10) {
                 case 0:
@@ -856,7 +856,7 @@
         } else if (opc20 && b13_12 == 2 && !(opc11_10 & 0x2)) {
             uint8_t pattern = (uint8_t) bits(machInst, 9, 5);
             uint8_t imm4 = (uint8_t) bits(machInst, 19, 16) + 1;
-            IntRegIndex rdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex rdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
             unsigned size = (unsigned) bits(machInst, 23, 22);
             if (opc11_10 & 0x1) {
                 return decodeSveElemIntCountU<SveDec>(size, machInst,
@@ -868,7 +868,7 @@
         } else if (!opc20 && b13_12 == 2 && opc11_10 == 0) {
             uint8_t pattern = (uint8_t) bits(machInst, 9, 5);
             uint8_t imm4 = (uint8_t) bits(machInst, 19, 16) + 1;
-            IntRegIndex rd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex rd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
             unsigned size = (unsigned) bits(machInst, 23, 22);
             return decodeSveElemIntCountU<SveCntx>(size, machInst,
                     rd, pattern, imm4);
@@ -879,7 +879,7 @@
     StaticInstPtr
     decodeSveLogMaskImm(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
         bool n = bits(machInst, 17);
         uint8_t immr = bits(machInst, 16, 11);
         uint8_t imms = bits(machInst, 10, 5);
@@ -932,8 +932,8 @@
     StaticInstPtr
     decodeSveIntWideImmPred(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 19, 16);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 19, 16);
         uint8_t size = bits(machInst, 23, 22);
 
         if (bits(machInst, 15) == 0x0) {
@@ -982,8 +982,8 @@
         if (!b23_22) {
             uint8_t position =
                 bits(machInst, 20, 16) << 3 | bits(machInst, 12, 10);
-            IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
             return new SveExt<uint8_t>(machInst, zdn, zm, position);
         }
         return new Unknown64(machInst);
@@ -995,38 +995,38 @@
         uint8_t b12_10 = bits(machInst, 12, 10);
         if (b12_10 == 0x4) {
             unsigned size = (unsigned) bits(machInst, 23, 22);
-            IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-            IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+            RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
             return decodeSveBinUnpredU<SveTbl>(size, machInst, zd, zn, zm);
         } else if (bits(machInst, 20, 16) == 0x0 && b12_10 == 0x6) {
             uint8_t size = bits(machInst, 23, 22);
-            IntRegIndex rn = makeSP(
-                    (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-            IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex rn = makeSP(
+                    (RegIndex) (uint8_t) bits(machInst, 9, 5));
+            RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
             return decodeSveUnaryUnpredU<SveDupScalar>(size, machInst, zd, rn);
         } else if (bits(machInst, 20, 16) == 0x4 && b12_10 == 0x6) {
             uint8_t size = bits(machInst, 23, 22);
-            IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
             return decodeSveUnaryUnpredU<SveInsr>(size, machInst, zdn, rm);
         } else if (bits(machInst, 20, 16) == 0x14 && b12_10 == 0x6) {
             uint8_t size = bits(machInst, 23, 22);
-            IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex vm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex vm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
             return decodeSveUnaryUnpredU<SveInsrf>(size, machInst, zdn, vm);
         } else if (bits(machInst, 20, 16) == 0x18 && b12_10 == 0x6) {
             uint8_t size = bits(machInst, 23, 22);
-            IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
             return decodeSveUnaryUnpredU<SveRevv>(size, machInst, zd, zn);
         } else if (b12_10 == 0x0 && bits(machInst, 20, 16) != 0x0) {
             uint8_t imm =
                 bits(machInst, 23, 22) << 5 | // imm3h
                 bits(machInst, 20) << 4 |     // imm3l
                 bits(machInst, 19, 16);       // tsz
-            IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
             if (imm & 0x1) {
                 imm >>= 1;
                 return new SveDupIdx<uint8_t>(machInst, zd, zn, imm);
@@ -1047,8 +1047,8 @@
         } else if (bits(machInst, 23, 22) != 0x0 &&
                    bits(machInst, 20, 18) == 0x4 && b12_10 == 0x6) {
             unsigned size = (unsigned) bits(machInst, 23, 22);
-            IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
             if (bits(machInst, 17)) {
                 if (bits(machInst, 16)) {
                     return decodeSveUnpackU<SveUunpkhi>(size, machInst,
@@ -1075,9 +1075,9 @@
     {
         if (bits(machInst, 20) == 0x0 && bits(machInst, 12, 11) != 0x3 &&
                 bits(machInst, 9) == 0x0 && bits(machInst, 4) == 0x0) {
-            IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-            IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-            IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+            RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+            RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+            RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
             uint8_t size = bits(machInst, 23, 22);
 
@@ -1106,8 +1106,8 @@
         } else if (bits(machInst, 23, 22) == 0x0 &&
                 bits(machInst, 20, 17) == 0x8 && bits(machInst, 12, 9) == 0x0
                 && bits(machInst, 4) == 0x0) {
-            IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
-            IntRegIndex pn = (IntRegIndex) (uint8_t) bits(machInst, 8, 5);
+            RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
+            RegIndex pn = (RegIndex) (uint8_t) bits(machInst, 8, 5);
             if (bits(machInst, 16)) {
                 return new SvePunpkhi<uint8_t, uint16_t>(machInst, pd, pn);
             } else {
@@ -1116,8 +1116,8 @@
         } else if (bits(machInst, 20, 16) == 0x14 &&
                 bits(machInst, 12, 9) == 0x00 && bits(machInst, 4) == 0) {
             uint8_t size = bits(machInst, 23, 22);
-            IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
-            IntRegIndex pn = (IntRegIndex) (uint8_t) bits(machInst, 8, 5);
+            RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
+            RegIndex pn = (RegIndex) (uint8_t) bits(machInst, 8, 5);
             return decodeSveUnaryUnpredU<SveRevp>(size, machInst, pd, pn);
         }
         return new Unknown64(machInst);
@@ -1126,9 +1126,9 @@
     StaticInstPtr
     decodeSvePermIntlv(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         uint8_t size = bits(machInst, 23, 22);
 
@@ -1160,9 +1160,9 @@
           case 0x0:
             if (!b13) {
                 uint8_t size = bits(machInst, 23, 22);
-                IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-                IntRegIndex vn = (IntRegIndex)(uint8_t) bits(machInst, 9, 5);
-                IntRegIndex zd = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
+                RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+                RegIndex vn = (RegIndex)(uint8_t) bits(machInst, 9, 5);
+                RegIndex zd = (RegIndex)(uint8_t) bits(machInst, 4, 0);
                 return decodeSveUnaryPredU<SveCpySimdFpScalar>(size,
                         machInst, zd, vn, pg);
             }
@@ -1170,9 +1170,9 @@
           case 0x1:
             if (!b13 && b23) {
                 // sve_int_perm_compact
-                IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-                IntRegIndex zn = (IntRegIndex)(uint8_t) bits(machInst, 9, 5);
-                IntRegIndex zd = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
+                RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+                RegIndex zn = (RegIndex)(uint8_t) bits(machInst, 9, 5);
+                RegIndex zd = (RegIndex)(uint8_t) bits(machInst, 4, 0);
                 if (bits(machInst, 22)) {
                     return new SveCompact<uint64_t>(machInst, zd, zn, pg);
                 } else {
@@ -1183,10 +1183,10 @@
           case 0x8:
             if (b13) {
                 uint8_t size = bits(machInst, 23, 22);
-                IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-                IntRegIndex rn = makeSP(
-                        (IntRegIndex)(uint8_t) bits(machInst, 9, 5));
-                IntRegIndex zd = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
+                RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+                RegIndex rn = makeSP(
+                        (RegIndex)(uint8_t) bits(machInst, 9, 5));
+                RegIndex zd = (RegIndex)(uint8_t) bits(machInst, 4, 0);
                 return decodeSveUnaryPredU<SveCpyScalar>(size,
                         machInst, zd, rn, pg);
             }
@@ -1194,9 +1194,9 @@
           case 0xC:
             if (!b13) {
                 uint8_t size = bits(machInst, 23, 22);
-                IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-                IntRegIndex zdn = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
-                IntRegIndex zm = (IntRegIndex)(uint8_t) bits(machInst, 9, 5);
+                RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+                RegIndex zdn = (RegIndex)(uint8_t) bits(machInst, 4, 0);
+                RegIndex zm = (RegIndex)(uint8_t) bits(machInst, 9, 5);
                 return decodeSveBinDestrPredU<SveSplice>(size, machInst,
                         zdn, zm, pg);
             }
@@ -1207,9 +1207,9 @@
             if (b13) {
                 uint8_t AB = bits(machInst, 16);
                 uint8_t size = bits(machInst, 23, 22);
-                IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-                IntRegIndex zn = (IntRegIndex)(uint8_t) bits(machInst, 9, 5);
-                IntRegIndex rd = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
+                RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+                RegIndex zn = (RegIndex)(uint8_t) bits(machInst, 9, 5);
+                RegIndex rd = (RegIndex)(uint8_t) bits(machInst, 4, 0);
                 if (!AB) {
                     return decodeSveUnaryPredU<SveLasta>(size,
                             machInst, rd, zn, pg);
@@ -1223,9 +1223,9 @@
             if (!b13) {
                 uint8_t AB = bits(machInst, 16);
                 uint8_t size = bits(machInst, 23, 22);
-                IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-                IntRegIndex zn = (IntRegIndex)(uint8_t) bits(machInst, 9, 5);
-                IntRegIndex vd = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
+                RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+                RegIndex zn = (RegIndex)(uint8_t) bits(machInst, 9, 5);
+                RegIndex vd = (RegIndex)(uint8_t) bits(machInst, 4, 0);
                 if (!AB) {
                     return decodeSveUnaryPredU<SveLastaf>(size,
                             machInst, vd, zn, pg);
@@ -1239,9 +1239,9 @@
             if (!b13) {
                 uint8_t AB = bits(machInst, 16);
                 uint8_t size = bits(machInst, 23, 22);
-                IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-                IntRegIndex zm = (IntRegIndex)(uint8_t) bits(machInst, 9, 5);
-                IntRegIndex zdn = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
+                RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+                RegIndex zm = (RegIndex)(uint8_t) bits(machInst, 9, 5);
+                RegIndex zdn = (RegIndex)(uint8_t) bits(machInst, 4, 0);
                 if (!AB) {
                     return decodeSveUnaryPredU<SveClastav>(size,
                             machInst, zdn, zm, pg);
@@ -1255,9 +1255,9 @@
             if (!b13) {
                 uint8_t AB = bits(machInst, 16);
                 uint8_t size = bits(machInst, 23, 22);
-                IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-                IntRegIndex zm = (IntRegIndex)(uint8_t) bits(machInst, 9, 5);
-                IntRegIndex zdn = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
+                RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+                RegIndex zm = (RegIndex)(uint8_t) bits(machInst, 9, 5);
+                RegIndex zdn = (RegIndex)(uint8_t) bits(machInst, 4, 0);
                 if (!AB) {
                     return decodeSveUnaryPredU<SveClastaf>(size,
                             machInst, zdn, zm, pg);
@@ -1271,9 +1271,9 @@
             if (b13) {
                 uint8_t AB = bits(machInst, 16);
                 uint8_t size = bits(machInst, 23, 22);
-                IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-                IntRegIndex zm = (IntRegIndex)(uint8_t) bits(machInst, 9, 5);
-                IntRegIndex rdn = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
+                RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+                RegIndex zm = (RegIndex)(uint8_t) bits(machInst, 9, 5);
+                RegIndex rdn = (RegIndex)(uint8_t) bits(machInst, 4, 0);
                 if (!AB) {
                     return decodeSveUnaryPredU<SveClasta>(size,
                             machInst, rdn, zm, pg);
@@ -1286,9 +1286,9 @@
         }
         if (bits(machInst, 20, 18) == 0x1 && !b13) {
             unsigned size = (unsigned) bits(machInst, 23, 22);
-            IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-            IntRegIndex zn = (IntRegIndex)(uint8_t) bits(machInst, 9, 5);
-            IntRegIndex zd = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
+            RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+            RegIndex zn = (RegIndex)(uint8_t) bits(machInst, 9, 5);
+            RegIndex zd = (RegIndex)(uint8_t) bits(machInst, 4, 0);
             uint8_t opc17_16 = bits(machInst, 17, 16);
             switch (opc17_16) {
                 case 0x00:
@@ -1325,10 +1325,10 @@
     StaticInstPtr
     decodeSveSelVec(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 13, 10);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 13, 10);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         uint8_t size = bits(machInst, 23, 22);
 
@@ -1345,10 +1345,10 @@
             bits(machInst, 15) << 2 |
             bits(machInst, 13) << 1 |
             bits(machInst, 4);
-        IntRegIndex pd = (IntRegIndex) (uint8_t)bits(machInst, 3, 0);
-        IntRegIndex pg = (IntRegIndex) (uint8_t)bits(machInst, 12, 10);
-        IntRegIndex zn = (IntRegIndex) (uint8_t)bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t)bits(machInst, 20, 16);
+        RegIndex pd = (RegIndex) (uint8_t)bits(machInst, 3, 0);
+        RegIndex pg = (RegIndex) (uint8_t)bits(machInst, 12, 10);
+        RegIndex zn = (RegIndex) (uint8_t)bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t)bits(machInst, 20, 16);
         if (b14 && size != 3) {
             // sve_int_cmp_1
             switch (opc) {
@@ -1418,9 +1418,9 @@
     decodeSveIntCmpUImm(ExtMachInst machInst)
     {
         uint8_t cmp = bits(machInst, 13) << 1 | bits(machInst, 4);
-        IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         int64_t imm = (int64_t) bits(machInst, 20, 14);
         uint8_t size = bits(machInst, 23, 22);
         switch (cmp) {
@@ -1445,9 +1445,9 @@
     {
         uint8_t opc = bits(machInst, 15) << 2 | bits(machInst, 13) << 1 |
             bits(machInst, 4);
-        IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         int64_t imm = sext<5>(bits(machInst, 20, 16));
         uint8_t size = bits(machInst, 23, 22);
         switch (opc) {
@@ -1478,10 +1478,10 @@
     StaticInstPtr
     decodeSvePredLogicalOps(ExtMachInst machInst)
     {
-        IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
-        IntRegIndex pn = (IntRegIndex) (uint8_t) bits(machInst, 8, 5);
-        IntRegIndex pm = (IntRegIndex) (uint8_t) bits(machInst, 19, 16);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 13, 10);
+        RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
+        RegIndex pn = (RegIndex) (uint8_t) bits(machInst, 8, 5);
+        RegIndex pm = (RegIndex) (uint8_t) bits(machInst, 19, 16);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 13, 10);
         uint8_t opc = (bits(machInst, 23, 22) << 2) |
                       (bits(machInst, 9) << 1) |
                       bits(machInst, 4);
@@ -1526,10 +1526,10 @@
     {
         if (bits(machInst, 23) == 0x0 && bits(machInst, 9) == 0x0) {
             uint8_t opc = (bits(machInst, 22) << 1) | bits(machInst, 4);
-            IntRegIndex pm = (IntRegIndex)(uint8_t) bits(machInst, 19, 16);
-            IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 13, 10);
-            IntRegIndex pn = (IntRegIndex)(uint8_t) bits(machInst, 8, 5);
-            IntRegIndex pd = (IntRegIndex)(uint8_t) bits(machInst, 3, 0);
+            RegIndex pm = (RegIndex)(uint8_t) bits(machInst, 19, 16);
+            RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 13, 10);
+            RegIndex pn = (RegIndex)(uint8_t) bits(machInst, 8, 5);
+            RegIndex pd = (RegIndex)(uint8_t) bits(machInst, 3, 0);
             switch (opc) {
               case 0x0:
                 // BRKPA
@@ -1554,9 +1554,9 @@
         if (bits(machInst, 18, 16) == 0x0 && bits(machInst, 9) == 0x0) {
             bool flagset = bits(machInst, 22);
             bool merging = bits(machInst, 4);
-            IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 13, 10);
-            IntRegIndex pn = (IntRegIndex)(uint8_t) bits(machInst, 8, 5);
-            IntRegIndex pd = (IntRegIndex)(uint8_t) bits(machInst, 3, 0);
+            RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 13, 10);
+            RegIndex pn = (RegIndex)(uint8_t) bits(machInst, 8, 5);
+            RegIndex pd = (RegIndex)(uint8_t) bits(machInst, 3, 0);
             if (bits(machInst, 23)) {
                 if (flagset) {
                     if (!merging) {
@@ -1593,8 +1593,8 @@
         if (bits(machInst, 23, 22) == 0x1 &&
                 bits(machInst, 18, 16) == 0x0 &&
                 bits(machInst, 9) == 0x0) {
-            IntRegIndex pn = (IntRegIndex) (uint8_t) bits(machInst, 8, 5);
-            IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 13, 10);
+            RegIndex pn = (RegIndex) (uint8_t) bits(machInst, 8, 5);
+            RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 13, 10);
             return new SvePtest(machInst, pn, pg);
         }
         return new Unknown64(machInst);
@@ -1606,8 +1606,8 @@
         uint8_t size = bits(machInst, 23, 22);
         uint8_t opc18_16 = bits(machInst, 18, 16);
         uint8_t opc10_9 = bits(machInst, 10, 9);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 8, 5);
-        IntRegIndex pdn = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 8, 5);
+        RegIndex pdn = (RegIndex) (uint8_t) bits(machInst, 3, 0);
         if (opc18_16 == 0x1 && opc10_9 == 0x2) {
             return decodeSveUnaryPredU<SvePnext>(size,
                     machInst, pdn, pdn, pg);
@@ -1620,7 +1620,7 @@
     StaticInstPtr
     decodeSveInitPred(ExtMachInst machInst)
     {
-        IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
+        RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
         unsigned size = bits(machInst, 23, 22);
         uint8_t imm = bits(machInst, 9, 5);
 
@@ -1636,7 +1636,7 @@
     decodeSveZeroPredReg(ExtMachInst machInst)
     {
         if (bits(machInst, 23, 22) == 0x0 && bits(machInst, 18, 16) == 0x0) {
-            IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
+            RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
             return new SvePfalse(machInst, pd);
         }
         return new Unknown64(machInst);
@@ -1649,9 +1649,9 @@
                 bits(machInst, 18, 16) == 0x0 &&
                 bits(machInst, 9) == 0x0 &&
                 bits(machInst, 4) == 0x0) {
-            IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 13, 10);
-            IntRegIndex pn = (IntRegIndex)(uint8_t) bits(machInst, 8, 5);
-            IntRegIndex pdm = (IntRegIndex)(uint8_t) bits(machInst, 3, 0);
+            RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 13, 10);
+            RegIndex pn = (RegIndex)(uint8_t) bits(machInst, 8, 5);
+            RegIndex pdm = (RegIndex)(uint8_t) bits(machInst, 3, 0);
             if (bits(machInst, 22) == 0x0) {
                 return new SveBrkn(machInst, pdm, pn, pdm, pg);
             } else {
@@ -1668,8 +1668,8 @@
         if (bits(machInst, 23)) {
             return new Unknown64(machInst);
         }
-        IntRegIndex pd = (IntRegIndex)(uint8_t) bits(machInst, 3, 0);
-        IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 8, 5);
+        RegIndex pd = (RegIndex)(uint8_t) bits(machInst, 3, 0);
+        RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 8, 5);
         if (bits(machInst, 22)) {
             return new SveRdffrsPred(machInst, pd, pg);
         } else {
@@ -1683,7 +1683,7 @@
         if (bits(machInst, 23, 22) != 0) {
             return new Unknown64(machInst);
         }
-        IntRegIndex pd = (IntRegIndex)(uint8_t) bits(machInst, 3, 0);
+        RegIndex pd = (RegIndex)(uint8_t) bits(machInst, 3, 0);
         return new SveRdffrUnpred(machInst, pd);
     }  // decodeSveReadPredFromFFRUnpred
 
@@ -1756,9 +1756,9 @@
                     if (bits(machInst, 10, 9) != 0x0) {
                         return new Unknown64(machInst);
                     }
-                    IntRegIndex zdn = (IntRegIndex) (uint8_t)
+                    RegIndex zdn = (RegIndex) (uint8_t)
                         bits(machInst, 4, 0);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                         bits(machInst, 8, 5);
                     uint8_t esize = bits(machInst, 23, 22);
                     if (esize == 0x0) {
@@ -1797,9 +1797,9 @@
                 break;
               case 0x1:
                 {
-                    IntRegIndex rdn = (IntRegIndex) (uint8_t)
+                    RegIndex rdn = (RegIndex) (uint8_t)
                         bits(machInst, 4, 0);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                         bits(machInst, 8, 5);
                     uint8_t esize = bits(machInst, 23, 22);
                     uint8_t opc = bits(machInst, 18, 17);
@@ -1861,7 +1861,7 @@
                         bits(machInst, 4, 0) == 0x0) {
                     uint8_t opc = bits(machInst, 18, 16);
                     if (opc == 0x0) {
-                        IntRegIndex pn = (IntRegIndex)(uint8_t)
+                        RegIndex pn = (RegIndex)(uint8_t)
                             bits(machInst, 8, 5);
                         return new SveWrffr(machInst, pn);
                     } else if (opc == 0x4 && bits(machInst, 8, 5) == 0x0) {
@@ -1873,9 +1873,9 @@
         } else {
             uint8_t opc = bits(machInst, 18, 16);
             if (opc == 0 && bits(machInst, 9) == 0) {
-                IntRegIndex rd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-                IntRegIndex pn = (IntRegIndex) (uint8_t) bits(machInst, 8, 5);
-                IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 13,
+                RegIndex rd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+                RegIndex pn = (RegIndex) (uint8_t) bits(machInst, 8, 5);
+                RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 13,
                         10);
                 uint8_t esize = bits(machInst, 23, 22);
                 return decodeSveUnaryPredU<SveCntp>(esize,
@@ -1892,8 +1892,8 @@
             (bits(machInst, 23) << 8) | (bits(machInst, 13, 10) << 4) |
             bits(machInst, 3, 0);
         uint8_t b10 = (uint8_t) bits(machInst, 10);
-        IntRegIndex rn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex rn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
         if (b23_13_12_11_10_3_2_1_0 == 0x180) {
             uint8_t s64b = bits(machInst, 22);
             uint8_t ne = bits(machInst, 4);
@@ -1911,7 +1911,7 @@
                 }
             }
         } else if (b10) {
-            IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
+            RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
             uint8_t size = (uint8_t) bits(machInst, 23, 22);
             uint8_t s64b = (uint8_t) bits(machInst, 12);
             uint8_t opc = (uint8_t) bits(machInst, 11) << 1 |
@@ -1954,7 +1954,7 @@
     StaticInstPtr
     decodeSveIntWideImmUnpred0(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
         uint64_t imm = bits(machInst, 12, 5);
         uint8_t sh = bits(machInst, 13);
         uint8_t size = bits(machInst, 23, 22);
@@ -1996,7 +1996,7 @@
     StaticInstPtr
     decodeSveIntWideImmUnpred1(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
         uint64_t imm = bits(machInst, 12, 5);
         uint8_t size = bits(machInst, 23, 22);
 
@@ -2021,7 +2021,7 @@
     StaticInstPtr
     decodeSveIntWideImmUnpred2(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
         uint64_t imm = bits(machInst, 12, 5);
         uint8_t size = bits(machInst, 23, 22);
 
@@ -2036,7 +2036,7 @@
     StaticInstPtr
     decodeSveIntWideImmUnpred3(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
         uint64_t imm = bits(machInst, 12, 5);
         uint8_t sh = bits(machInst, 13);
         uint8_t size = bits(machInst, 23, 22);
@@ -2064,7 +2064,7 @@
     StaticInstPtr
     decodeSveIntWideImmUnpred4(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
         uint8_t size = bits(machInst, 23, 22);
 
         if (bits(machInst, 18, 17) == 0x0 && size != 0x0) {
@@ -2109,9 +2109,9 @@
     StaticInstPtr
     decodeSveMultiplyAddUnpred(ExtMachInst machInst)
     {
-        IntRegIndex zda = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex zda = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         uint8_t size = (uint8_t) bits(machInst, 23, 22);
 
@@ -2144,8 +2144,8 @@
     StaticInstPtr
     decodeSveMultiplyIndexed(ExtMachInst machInst)
     {
-        IntRegIndex zda = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zda = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t size = (uint8_t) bits(machInst, 23, 22);
 
@@ -2155,7 +2155,7 @@
 
         uint8_t usig = (uint8_t) bits(machInst, 10);
         if (size & 0x1) {
-            IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 19, 16);
+            RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 19, 16);
             uint8_t i1 = (uint8_t) bits(machInst, 20);
             if (usig) {
                 return new SveUdoti<uint16_t, uint64_t>(machInst,
@@ -2165,7 +2165,7 @@
                                                         zda, zn, zm, i1);
             }
         } else {
-            IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 18, 16);
+            RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 18, 16);
             uint8_t i2 = (uint8_t) bits(machInst, 20, 19);
             if (usig) {
                 return new SveUdoti<uint8_t, uint32_t>(machInst,
@@ -2181,9 +2181,9 @@
     StaticInstPtr
     decodeSveFpFastReduc(ExtMachInst machInst)
     {
-        IntRegIndex vd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex vd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         uint8_t size = bits(machInst, 23, 22);
 
@@ -2210,8 +2210,8 @@
     StaticInstPtr
     decodeSveFpUnaryUnpred(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t size = (uint8_t) bits(machInst, 23, 22);
         if (size == 0) {
@@ -2233,9 +2233,9 @@
     StaticInstPtr
     decodeSveFpCmpZero(ExtMachInst machInst)
     {
-        IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         uint8_t size = bits(machInst, 23, 22);
         if (size == 0) {
@@ -2275,9 +2275,9 @@
             return new Unknown64(machInst);
         }
 
-        IntRegIndex vdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex vdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         return decodeSveUnaryPredF<SveFadda>(size, machInst, vdn, zm, pg);
     }  // decodeSveFpAccumReduc
@@ -2285,9 +2285,9 @@
     StaticInstPtr
     decodeSveFpArithUnpred(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
 
         uint8_t size = bits(machInst, 23, 22);
         if (size == 0) {
@@ -2321,9 +2321,9 @@
     StaticInstPtr
     decodeSveFpArithPred0(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         uint8_t size = (uint8_t) bits(machInst, 23, 22);
         if (size == 0) {
@@ -2378,8 +2378,8 @@
     StaticInstPtr
     decodeSveFpTrigMAddCoeff(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
         uint8_t imm = (uint8_t) bits(machInst, 18, 16);
 
         uint8_t size = (uint8_t) bits(machInst, 23, 22);
@@ -2393,8 +2393,8 @@
     StaticInstPtr
     decodeSveFpArithImmPred(ExtMachInst machInst)
     {
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         uint64_t imm;
 
         uint8_t size = (uint8_t) bits(machInst, 23, 22);
@@ -2456,9 +2456,9 @@
     StaticInstPtr
     decodeSveFpUnaryPred(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         uint8_t size = (uint8_t) bits(machInst, 23, 22);
         if (size == 0) {
@@ -2656,10 +2656,10 @@
     StaticInstPtr
     decodeSveFpCmpVec(ExtMachInst machInst)
     {
-        IntRegIndex pd = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex pd = (RegIndex) (uint8_t) bits(machInst, 3, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         uint8_t size = bits(machInst, 23, 22);
         if (size == 0) {
@@ -2691,10 +2691,10 @@
     StaticInstPtr
     decodeSveFpFusedMulAdd(ExtMachInst machInst)
     {
-        IntRegIndex zda = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zda = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         uint8_t size = bits(machInst, 23, 22);
         if (size == 0) {
@@ -2736,9 +2736,9 @@
     {
         uint8_t size = bits(machInst, 23, 22);
         uint8_t rot = bits(machInst, 16) << 1 | 0x01;
-        IntRegIndex zdn = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zdn = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         switch (size) {
             case 1:
                 return new SveFcadd<uint16_t>(machInst,
@@ -2761,10 +2761,10 @@
             return new Unknown64(machInst);
         }
 
-        IntRegIndex zda = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
-        IntRegIndex zm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex zda = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
         uint8_t rot = bits(machInst, 14, 13);
         switch (size) {
             case 1:
@@ -2789,20 +2789,20 @@
             return new Unknown64(machInst);
         }
 
-        IntRegIndex zda = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
-        IntRegIndex zm;
+        RegIndex zda = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zm;
         uint8_t rot = bits(machInst, 11, 10);
         uint8_t imm;
 
         switch (size) {
             case 2:
-                zm = (IntRegIndex) (uint8_t) bits(machInst, 18, 16);
+                zm = (RegIndex) (uint8_t) bits(machInst, 18, 16);
                 imm = bits(machInst, 20, 19);
                 return new SveFcmlai<uint16_t>(machInst,
                         zda, zn, zm, rot, imm);
             case 3:
-                zm = (IntRegIndex) (uint8_t) bits(machInst, 19, 16);
+                zm = (RegIndex) (uint8_t) bits(machInst, 19, 16);
                 imm = bits(machInst, 20);
                 return new SveFcmlai<uint32_t>(machInst,
                         zda, zn, zm, rot, imm);
@@ -2813,8 +2813,8 @@
     StaticInstPtr
     decodeSveFpMulIndexed(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
 
         uint8_t size = bits(machInst, 23, 22);
         switch (size) {
@@ -2822,17 +2822,17 @@
           case 0x1:
             return new SveFmulIdx<uint16_t>(
                 machInst, zd, zn,
-                (IntRegIndex) (uint8_t) bits(machInst, 18, 16),
+                (RegIndex) (uint8_t) bits(machInst, 18, 16),
                 bits(machInst, 20, 19) | (bits(machInst, 22) << 2));
           case 0x2:
             return new SveFmulIdx<uint32_t>(
                 machInst, zd, zn,
-                (IntRegIndex) (uint8_t) bits(machInst, 18, 16),
+                (RegIndex) (uint8_t) bits(machInst, 18, 16),
                 bits(machInst, 20, 19));
           case 0x3:
             return new SveFmulIdx<uint64_t>(
                 machInst, zd, zn,
-                (IntRegIndex) (uint8_t) bits(machInst, 19, 16),
+                (RegIndex) (uint8_t) bits(machInst, 19, 16),
                 bits(machInst, 20));
           default:
             return new Unknown64(machInst);
@@ -2843,8 +2843,8 @@
     StaticInstPtr
     decodeSveFpMulAddIndexed(ExtMachInst machInst)
     {
-        IntRegIndex zd = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+        RegIndex zd = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
         const uint8_t op = bits(machInst, 10);
 
         uint8_t size = bits(machInst, 23, 22);
@@ -2854,36 +2854,36 @@
             if (op) {
                 return new SveFmlsIdx<uint16_t>(
                     machInst, zd, zn,
-                    (IntRegIndex) (uint8_t) bits(machInst, 18, 16),
+                    (RegIndex) (uint8_t) bits(machInst, 18, 16),
                     bits(machInst, 20, 19) | (bits(machInst, 22) << 2));
             } else {
                 return new SveFmlaIdx<uint16_t>(
                     machInst, zd, zn,
-                    (IntRegIndex) (uint8_t) bits(machInst, 18, 16),
+                    (RegIndex) (uint8_t) bits(machInst, 18, 16),
                     bits(machInst, 20, 19) | (bits(machInst, 22) << 2));
             }
           case 0x2:
             if (op) {
                 return new SveFmlsIdx<uint32_t>(
                     machInst, zd, zn,
-                    (IntRegIndex) (uint8_t) bits(machInst, 18, 16),
+                    (RegIndex) (uint8_t) bits(machInst, 18, 16),
                     bits(machInst, 20, 19));
             } else {
                 return new SveFmlaIdx<uint32_t>(
                     machInst, zd, zn,
-                    (IntRegIndex) (uint8_t) bits(machInst, 18, 16),
+                    (RegIndex) (uint8_t) bits(machInst, 18, 16),
                     bits(machInst, 20, 19));
             }
           case 0x3:
             if (op) {
                 return new SveFmlsIdx<uint64_t>(
                     machInst, zd, zn,
-                    (IntRegIndex) (uint8_t) bits(machInst, 19, 16),
+                    (RegIndex) (uint8_t) bits(machInst, 19, 16),
                     bits(machInst, 20));
             } else {
                 return new SveFmlaIdx<uint64_t>(
                     machInst, zd, zn,
-                    (IntRegIndex) (uint8_t) bits(machInst, 19, 16),
+                    (RegIndex) (uint8_t) bits(machInst, 19, 16),
                     bits(machInst, 20));
             }
           default:
@@ -2897,11 +2897,11 @@
         if (bits(machInst, 15)) {
             if (bits(machInst, 22)) {
                 // SVE load and broadcast element
-                IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-                IntRegIndex rn = makeSP(
-                        (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+                RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+                RegIndex rn = makeSP(
+                        (RegIndex) (uint8_t) bits(machInst, 9, 5));
                 uint64_t imm = bits(machInst, 21, 16);
-                IntRegIndex pg = (IntRegIndex) (uint8_t)
+                RegIndex pg = (RegIndex) (uint8_t)
                                  bits(machInst, 12, 10);
                 uint8_t dtype = (bits(machInst, 24, 23) << 2) |
                                 bits(machInst, 14, 13);
@@ -2910,12 +2910,12 @@
             } else {
                 if (bits(machInst, 21)) {
                     // SVE 32-bit gather load (vector plus immediate)
-                    IntRegIndex zt = (IntRegIndex) (uint8_t)
+                    RegIndex zt = (RegIndex) (uint8_t)
                                      bits(machInst, 4, 0);
-                    IntRegIndex zn = (IntRegIndex) (uint8_t)
+                    RegIndex zn = (RegIndex) (uint8_t)
                                      bits(machInst, 9, 5);
                     uint64_t imm = bits(machInst, 20, 16);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                                      bits(machInst, 12, 10);
                     uint8_t dtype = (bits(machInst, 24, 23) << 1) |
                                     bits(machInst, 14);
@@ -2938,12 +2938,12 @@
             uint8_t b24_23 = bits(machInst, 24, 23);
             if (b24_23 != 0x3 && bits(machInst, 21) == 0) {
                 // SVE 32-bit gather load (scalar plus 32-bit unscaled offsets)
-                IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-                IntRegIndex rn = makeSP(
-                        (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                IntRegIndex zm = (IntRegIndex) (uint8_t)
+                RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+                RegIndex rn = makeSP(
+                        (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                RegIndex zm = (RegIndex) (uint8_t)
                          bits(machInst, 20, 16);
-                IntRegIndex pg = (IntRegIndex) (uint8_t)
+                RegIndex pg = (RegIndex) (uint8_t)
                          bits(machInst, 12, 10);
                 uint8_t dtype = (bits(machInst, 24, 23) << 1) |
                                 bits(machInst, 14);
@@ -2964,13 +2964,13 @@
                 if (bits(machInst, 21)) {
                     // SVE 32-bit gather load halfwords (scalar plus 32-bit
                     // scaled offsets)
-                    IntRegIndex zt = (IntRegIndex) (uint8_t)
+                    RegIndex zt = (RegIndex) (uint8_t)
                              bits(machInst, 4, 0);
-                    IntRegIndex rn = makeSP(
-                            (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                    IntRegIndex zm = (IntRegIndex) (uint8_t)
+                    RegIndex rn = makeSP(
+                            (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                    RegIndex zm = (RegIndex) (uint8_t)
                              bits(machInst, 20, 16);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                              bits(machInst, 12, 10);
                     uint8_t xs = bits(machInst, 22);
                     uint8_t ff = bits(machInst, 13);
@@ -2995,13 +2995,13 @@
                 if (bits(machInst, 21)) {
                     // SVE 32-bit gather load words (scalar plus 32-bit scaled
                     // offsets)
-                    IntRegIndex zt = (IntRegIndex) (uint8_t)
+                    RegIndex zt = (RegIndex) (uint8_t)
                              bits(machInst, 4, 0);
-                    IntRegIndex rn = makeSP(
-                            (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                    IntRegIndex zm = (IntRegIndex) (uint8_t)
+                    RegIndex rn = makeSP(
+                            (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                    RegIndex zm = (RegIndex) (uint8_t)
                              bits(machInst, 20, 16);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                              bits(machInst, 12, 10);
                     uint8_t xs = bits(machInst, 22);
                     uint8_t ff = bits(machInst, 13);
@@ -3016,20 +3016,20 @@
                 if (bits(machInst, 22) == 0 && bits(machInst, 14, 13) == 0x0 &&
                         bits(machInst, 4) == 0) {
                     // SVE load predicate register
-                    IntRegIndex pt = (IntRegIndex) (uint8_t)
+                    RegIndex pt = (RegIndex) (uint8_t)
                         bits(machInst, 3, 0);
-                    IntRegIndex rn = makeSP(
-                            (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+                    RegIndex rn = makeSP(
+                            (RegIndex) (uint8_t) bits(machInst, 9, 5));
                     uint64_t imm = sext<9>((bits(machInst, 21, 16) << 3) |
                                            bits(machInst, 12, 10));
                     return new SveLdrPred(machInst, pt, rn, imm);
                 } else if (bits(machInst, 22) == 0 &&
                            bits(machInst, 14, 13) == 0x2) {
                     // SVE load vector register
-                    IntRegIndex zt = (IntRegIndex) (uint8_t)
+                    RegIndex zt = (RegIndex) (uint8_t)
                         bits(machInst, 4, 0);
-                    IntRegIndex rn = makeSP(
-                            (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+                    RegIndex rn = makeSP(
+                            (RegIndex) (uint8_t) bits(machInst, 9, 5));
                     uint64_t imm = sext<9>((bits(machInst, 21, 16) << 3) |
                                            bits(machInst, 12, 10));
                     return new SveLdrVec(machInst, zt, rn, imm);
@@ -3052,10 +3052,10 @@
             return new Unknown64(machInst);
         }
 
-        IntRegIndex zt = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP((IntRegIndex)(uint8_t) bits(machInst, 9, 5));
-        IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
-        IntRegIndex rm = (IntRegIndex)(uint8_t) bits(machInst, 20, 16);
+        RegIndex zt = (RegIndex)(uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP((RegIndex)(uint8_t) bits(machInst, 9, 5));
+        RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
+        RegIndex rm = (RegIndex)(uint8_t) bits(machInst, 20, 16);
         uint8_t msz = bits(machInst, 24, 23);
         switch (msz) {
             case 0:
@@ -3083,9 +3083,9 @@
             return new Unknown64(machInst);
         }
 
-        IntRegIndex zt = (IntRegIndex)(uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP((IntRegIndex)(uint8_t) bits(machInst, 9, 5));
-        IntRegIndex pg = (IntRegIndex)(uint8_t) bits(machInst, 12, 10);
+        RegIndex zt = (RegIndex)(uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP((RegIndex)(uint8_t) bits(machInst, 9, 5));
+        RegIndex pg = (RegIndex)(uint8_t) bits(machInst, 12, 10);
         uint64_t imm = sext<4>(bits(machInst, 19, 16));
         uint8_t msz = bits(machInst, 24, 23);
         switch (msz) {
@@ -3109,10 +3109,10 @@
     StaticInstPtr
     decodeSveContigLoadSS(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP((IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-        IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP((RegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         if (rm == 0x1f) {
             return new Unknown64(machInst);
@@ -3125,10 +3125,10 @@
     StaticInstPtr
     decodeSveContigFFLoadSS(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP((IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-        IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP((RegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         return decodeSveContigLoadSSInsts<SveContigFFLoadSS>(
             bits(machInst, 24, 21), machInst, zt, pg, rn, rm, true);
@@ -3137,10 +3137,10 @@
     StaticInstPtr
     decodeSveContigLoadSI(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP((IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP((RegIndex) (uint8_t) bits(machInst, 9, 5));
         uint64_t imm = sext<4>(bits(machInst, 19, 16));
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         return decodeSveContigLoadSIInsts<SveContigLoadSI>(
             bits(machInst, 24, 21), machInst, zt, pg, rn, imm, false);
@@ -3149,10 +3149,10 @@
     StaticInstPtr
     decodeSveContigNFLoadSI(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP((IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP((RegIndex) (uint8_t) bits(machInst, 9, 5));
         uint64_t imm = sext<4>(bits(machInst, 19, 16));
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         return decodeSveContigLoadSIInsts<SveContigNFLoadSI>(
             bits(machInst, 24, 21), machInst, zt, pg, rn, imm, true);
@@ -3167,11 +3167,11 @@
     StaticInstPtr
     decodeSveLoadStructsSS(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP(
-                (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-        IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP(
+                (RegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         uint8_t msz = bits(machInst, 24, 23);
         uint8_t num = bits(machInst, 22, 21);
 
@@ -3192,11 +3192,11 @@
     StaticInstPtr
     decodeSveLoadStructsSI(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP(
-                (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP(
+                (RegIndex) (uint8_t) bits(machInst, 9, 5));
         int64_t imm = sext<4>(bits(machInst, 19, 16));
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         uint8_t msz = bits(machInst, 24, 23);
         uint8_t num = bits(machInst, 22, 21);
 
@@ -3257,12 +3257,12 @@
             {
                 // SVE 64-bit gather load (scalar plus unpacked 32-bit unscaled
                 // offsets)
-                IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-                IntRegIndex rn = makeSP(
-                        (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                IntRegIndex zm = (IntRegIndex) (uint8_t)
+                RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+                RegIndex rn = makeSP(
+                        (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                RegIndex zm = (RegIndex) (uint8_t)
                          bits(machInst, 20, 16);
-                IntRegIndex pg = (IntRegIndex) (uint8_t)
+                RegIndex pg = (RegIndex) (uint8_t)
                          bits(machInst, 12, 10);
                 uint8_t dtype = (bits(machInst, 24, 23) << 1) |
                                 bits(machInst, 14);
@@ -3275,12 +3275,12 @@
           case 0x1:
             if (bits(machInst, 22)) {
                 // SVE 64-bit gather load (scalar plus 64-bit unscaled offsets)
-                IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-                IntRegIndex rn = makeSP(
-                        (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                IntRegIndex zm = (IntRegIndex) (uint8_t)
+                RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+                RegIndex rn = makeSP(
+                        (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                RegIndex zm = (RegIndex) (uint8_t)
                          bits(machInst, 20, 16);
-                IntRegIndex pg = (IntRegIndex) (uint8_t)
+                RegIndex pg = (RegIndex) (uint8_t)
                          bits(machInst, 12, 10);
                 uint8_t dtype = (bits(machInst, 24, 23) << 1) |
                                 bits(machInst, 14);
@@ -3299,12 +3299,12 @@
             if (bits(machInst, 24, 23) != 0x0) {
                 //  SVE 64-bit gather load (scalar plus unpacked 32-bit scaled
                 //  offsets)
-                IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-                IntRegIndex rn = makeSP(
-                        (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                IntRegIndex zm = (IntRegIndex) (uint8_t)
+                RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+                RegIndex rn = makeSP(
+                        (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                RegIndex zm = (RegIndex) (uint8_t)
                          bits(machInst, 20, 16);
-                IntRegIndex pg = (IntRegIndex) (uint8_t)
+                RegIndex pg = (RegIndex) (uint8_t)
                          bits(machInst, 12, 10);
                 uint8_t dtype = (bits(machInst, 24, 23) << 1) |
                                 bits(machInst, 14);
@@ -3322,10 +3322,10 @@
           case 0x3:
             if (bits(machInst, 22) == 0) {
                 // SVE 64-bit gather load (vector plus immediate)
-                IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-                IntRegIndex zn = (IntRegIndex) (uint8_t) bits(machInst, 9, 5);
+                RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+                RegIndex zn = (RegIndex) (uint8_t) bits(machInst, 9, 5);
                 uint64_t imm = bits(machInst, 20, 16);
-                IntRegIndex pg = (IntRegIndex) (uint8_t)
+                RegIndex pg = (RegIndex) (uint8_t)
                                  bits(machInst, 12, 10);
                 uint8_t dtype = (bits(machInst, 24, 23) << 1) |
                                 bits(machInst, 14);
@@ -3336,13 +3336,13 @@
                 if (bits(machInst, 24, 23) != 0x0) {
                     // SVE 64-bit gather load (scalar plus 64-bit scaled
                     // offsets)
-                    IntRegIndex zt = (IntRegIndex) (uint8_t)
+                    RegIndex zt = (RegIndex) (uint8_t)
                              bits(machInst, 4, 0);
-                    IntRegIndex rn = makeSP(
-                            (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                    IntRegIndex zm = (IntRegIndex) (uint8_t)
+                    RegIndex rn = makeSP(
+                            (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                    RegIndex zm = (RegIndex) (uint8_t)
                              bits(machInst, 20, 16);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                              bits(machInst, 12, 10);
                     uint8_t dtype = (bits(machInst, 24, 23) << 1) |
                                     bits(machInst, 14);
@@ -3364,10 +3364,10 @@
     StaticInstPtr
     decodeSveContigStoreSS(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP((IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-        IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP((RegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         if (rm == 0x1f) {
             return new Unknown64(machInst);
@@ -3380,10 +3380,10 @@
     StaticInstPtr
     decodeSveContigStoreSI(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP((IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP((RegIndex) (uint8_t) bits(machInst, 9, 5));
         int8_t imm = sext<4>(bits(machInst, 19, 16));
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
 
         return decodeSveContigStoreSIInsts<SveContigStoreSI>(
             bits(machInst, 24, 21), machInst, zt, pg, rn, imm);
@@ -3404,11 +3404,11 @@
     StaticInstPtr
     decodeSveStoreStructsSS(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP(
-                (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-        IntRegIndex rm = (IntRegIndex) (uint8_t) bits(machInst, 20, 16);
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP(
+                (RegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex rm = (RegIndex) (uint8_t) bits(machInst, 20, 16);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         uint8_t msz = bits(machInst, 24, 23);
         uint8_t num = bits(machInst, 22, 21);
 
@@ -3423,11 +3423,11 @@
     StaticInstPtr
     decodeSveStoreStructsSI(ExtMachInst machInst)
     {
-        IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-        IntRegIndex rn = makeSP(
-                (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+        RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+        RegIndex rn = makeSP(
+                (RegIndex) (uint8_t) bits(machInst, 9, 5));
         int64_t imm = sext<4>(bits(machInst, 19, 16));
-        IntRegIndex pg = (IntRegIndex) (uint8_t) bits(machInst, 12, 10);
+        RegIndex pg = (RegIndex) (uint8_t) bits(machInst, 12, 10);
         uint8_t msz = bits(machInst, 24, 23);
         uint8_t num = bits(machInst, 22, 21);
 
@@ -3446,9 +3446,9 @@
         switch (bits(machInst, 15, 13)) {
           case 0x0:
             if (bits(machInst, 24, 22) == 0x6 && bits(machInst, 4) == 0x0) {
-                IntRegIndex pt = (IntRegIndex) (uint8_t) bits(machInst, 3, 0);
-                IntRegIndex rn = makeSP(
-                    (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+                RegIndex pt = (RegIndex) (uint8_t) bits(machInst, 3, 0);
+                RegIndex rn = makeSP(
+                    (RegIndex) (uint8_t) bits(machInst, 9, 5));
                 int16_t imm = sext<9>((bits(machInst, 21, 16) << 3) |
                                       bits(machInst, 12, 10));
                 return new SveStrPred(machInst, pt, rn, imm);
@@ -3456,9 +3456,9 @@
             break;
           case 0x2:
             if (bits(machInst, 24, 22) == 0x6) {
-                IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-                IntRegIndex rn = makeSP(
-                    (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
+                RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+                RegIndex rn = makeSP(
+                    (RegIndex) (uint8_t) bits(machInst, 9, 5));
                 int16_t imm = sext<9>((bits(machInst, 21, 16) << 3) |
                                       bits(machInst, 12, 10));
                 return new SveStrVec(machInst, zt, rn, imm);
@@ -3475,12 +3475,12 @@
           case 0x4:
           case 0x6:
             {
-                IntRegIndex zt = (IntRegIndex) (uint8_t) bits(machInst, 4, 0);
-                IntRegIndex rn = makeSP(
-                        (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                IntRegIndex zm = (IntRegIndex) (uint8_t)
+                RegIndex zt = (RegIndex) (uint8_t) bits(machInst, 4, 0);
+                RegIndex rn = makeSP(
+                        (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                RegIndex zm = (RegIndex) (uint8_t)
                          bits(machInst, 20, 16);
-                IntRegIndex pg = (IntRegIndex) (uint8_t)
+                RegIndex pg = (RegIndex) (uint8_t)
                          bits(machInst, 12, 10);
                 uint8_t msz = bits(machInst, 24, 23);
                 uint8_t xs = bits(machInst, 22);
@@ -3525,13 +3525,13 @@
                 {
                     // SVE 64-bit scatter store (scalar plus 64-bit unscaled
                     // offsets)
-                    IntRegIndex zt = (IntRegIndex) (uint8_t)
+                    RegIndex zt = (RegIndex) (uint8_t)
                             bits(machInst, 4, 0);
-                    IntRegIndex rn = makeSP(
-                            (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                    IntRegIndex zm = (IntRegIndex) (uint8_t)
+                    RegIndex rn = makeSP(
+                            (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                    RegIndex zm = (RegIndex) (uint8_t)
                             bits(machInst, 20, 16);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                             bits(machInst, 12, 10);
                     uint8_t msz = bits(machInst, 24, 23);
 
@@ -3543,13 +3543,13 @@
                 if (bits(machInst, 24, 23) != 0x0) {
                     // SVE 64-bit scatter store (scalar plus 64-bit scaled
                     // offsets)
-                    IntRegIndex zt = (IntRegIndex) (uint8_t)
+                    RegIndex zt = (RegIndex) (uint8_t)
                             bits(machInst, 4, 0);
-                    IntRegIndex rn = makeSP(
-                            (IntRegIndex) (uint8_t) bits(machInst, 9, 5));
-                    IntRegIndex zm = (IntRegIndex) (uint8_t)
+                    RegIndex rn = makeSP(
+                            (RegIndex) (uint8_t) bits(machInst, 9, 5));
+                    RegIndex zm = (RegIndex) (uint8_t)
                             bits(machInst, 20, 16);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                             bits(machInst, 12, 10);
                     uint8_t msz = bits(machInst, 24, 23);
 
@@ -3561,12 +3561,12 @@
               case 0x2:
                 {
                     // SVE 64-bit scatter store (vector plus immediate)
-                    IntRegIndex zt = (IntRegIndex) (uint8_t)
+                    RegIndex zt = (RegIndex) (uint8_t)
                             bits(machInst, 4, 0);
-                    IntRegIndex zn = (IntRegIndex) (uint8_t)
+                    RegIndex zn = (RegIndex) (uint8_t)
                             bits(machInst, 9, 5);
                     uint64_t imm = bits(machInst, 20, 16);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                             bits(machInst, 12, 10);
                     uint8_t msz = bits(machInst, 24, 23);
 
@@ -3576,12 +3576,12 @@
               case 0x3:
                 if (bits(machInst, 24, 23) != 0x3) {
                     // SVE 32-bit scatter store (vector plus immediate)
-                    IntRegIndex zt = (IntRegIndex) (uint8_t)
+                    RegIndex zt = (RegIndex) (uint8_t)
                             bits(machInst, 4, 0);
-                    IntRegIndex zn = (IntRegIndex) (uint8_t)
+                    RegIndex zn = (RegIndex) (uint8_t)
                             bits(machInst, 9, 5);
                     uint64_t imm = bits(machInst, 20, 16);
-                    IntRegIndex pg = (IntRegIndex) (uint8_t)
+                    RegIndex pg = (RegIndex) (uint8_t)
                             bits(machInst, 12, 10);
                     uint8_t msz = bits(machInst, 24, 23);
 
diff --git a/src/arch/arm/isa/formats/uncond.isa b/src/arch/arm/isa/formats/uncond.isa
index dfa9214..813ca90 100644
--- a/src/arch/arm/isa/formats/uncond.isa
+++ b/src/arch/arm/isa/formats/uncond.isa
@@ -36,7 +36,7 @@
 def format ArmUnconditional() {{
     decode_block = '''
     {
-        const IntRegIndex rn = (IntRegIndex)(uint32_t)bits(machInst, 19, 16);
+        const RegIndex rn = (RegIndex)(uint32_t)bits(machInst, 19, 16);
         const uint32_t op1 = bits(machInst, 27, 20);
         if (bits(op1, 7) == 0) {
             const uint32_t op2 = bits(machInst, 7, 4);
@@ -63,10 +63,10 @@
                     const bool add = bits(machInst, 23);
                     const uint32_t imm12 = bits(machInst, 11, 0);
                     if (add) {
-                        return new %(pli_iadd)s(machInst, INTREG_ZERO,
+                        return new %(pli_iadd)s(machInst, int_reg::Zero,
                                                 rn, add, imm12);
                     } else {
-                        return new %(pli_isub)s(machInst, INTREG_ZERO,
+                        return new %(pli_isub)s(machInst, int_reg::Zero,
                                                 rn, add, imm12);
                     }
                 }
@@ -77,18 +77,18 @@
                     const uint32_t imm12 = bits(machInst, 11, 0);
                     if (pldw) {
                         if (add) {
-                            return new %(pldw_iadd)s(machInst, INTREG_ZERO,
+                            return new %(pldw_iadd)s(machInst, int_reg::Zero,
                                                      rn, add, imm12);
                         } else {
-                            return new %(pldw_isub)s(machInst, INTREG_ZERO,
+                            return new %(pldw_isub)s(machInst, int_reg::Zero,
                                                      rn, add, imm12);
                         }
                     } else {
                         if (add) {
-                            return new %(pld_iadd)s(machInst, INTREG_ZERO,
+                            return new %(pld_iadd)s(machInst, int_reg::Zero,
                                                     rn, add, imm12);
                         } else {
-                            return new %(pld_isub)s(machInst, INTREG_ZERO,
+                            return new %(pld_isub)s(machInst, int_reg::Zero,
                                                     rn, add, imm12);
                         }
                     }
@@ -114,14 +114,14 @@
                         const uint32_t imm5 = bits(machInst, 11, 7);
                         const uint32_t type = bits(machInst, 6, 5);
                         const bool add = bits(machInst, 23);
-                        const IntRegIndex rm =
-                            (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                        const RegIndex rm =
+                            (RegIndex)(uint32_t)bits(machInst, 3, 0);
                         if (add) {
-                            return new %(pli_radd)s(machInst, INTREG_ZERO, rn,
-                                                    add, imm5, type, rm);
+                            return new %(pli_radd)s(machInst, int_reg::Zero,
+                                                    rn, add, imm5, type, rm);
                         } else {
-                            return new %(pli_rsub)s(machInst, INTREG_ZERO, rn,
-                                                    add, imm5, type, rm);
+                            return new %(pli_rsub)s(machInst, int_reg::Zero,
+                                                    rn, add, imm5, type, rm);
                         }
                     }
                   case 0x71:
@@ -131,25 +131,29 @@
                         const uint32_t type = bits(machInst, 6, 5);
                         const bool add = bits(machInst, 23);
                         const bool pldw = bits(machInst, 22);
-                        const IntRegIndex rm =
-                            (IntRegIndex)(uint32_t)bits(machInst, 3, 0);
+                        const RegIndex rm =
+                            (RegIndex)(uint32_t)bits(machInst, 3, 0);
                         if (pldw) {
                             if (add) {
-                                return new %(pldw_radd)s(machInst, INTREG_ZERO,
+                                return new %(pldw_radd)s(machInst,
+                                                         int_reg::Zero,
                                                          rn, add, imm5,
                                                          type, rm);
                             } else {
-                                return new %(pldw_rsub)s(machInst, INTREG_ZERO,
+                                return new %(pldw_rsub)s(machInst,
+                                                         int_reg::Zero,
                                                          rn, add, imm5,
                                                          type, rm);
                             }
                         } else {
                             if (add) {
-                                return new %(pld_radd)s(machInst, INTREG_ZERO,
+                                return new %(pld_radd)s(machInst,
+                                                        int_reg::Zero,
                                                         rn, add, imm5,
                                                         type, rm);
                             } else {
-                                return new %(pld_rsub)s(machInst, INTREG_ZERO,
+                                return new %(pld_rsub)s(machInst,
+                                                        int_reg::Zero,
                                                         rn, add, imm5,
                                                         type, rm);
                             }
@@ -244,7 +248,7 @@
                         return decodeExtensionRegLoadStore(machInst);
                     }
                     if (bits(op1, 0) == 1) {
-                        if (rn == INTREG_PC) {
+                        if (rn == int_reg::Pc) {
                             if (bits(op1, 4, 3) != 0x0) {
                                 return new WarnUnimplemented(
                                         "ldc, ldc2 (literal)", machInst);
diff --git a/src/arch/arm/isa/insts/branch.isa b/src/arch/arm/isa/insts/branch.isa
index fd48bad..c14e1f8 100644
--- a/src/arch/arm/isa/insts/branch.isa
+++ b/src/arch/arm/isa/insts/branch.isa
@@ -122,7 +122,7 @@
             instFlags += ["IsCall"]
         else:
             linkStr = ""
-            isRasPop = "op1 == INTREG_LR"
+            isRasPop = "op1 == int_reg::Lr"
 
         if imm and link: #blx with imm
             branchStr = '''
@@ -161,7 +161,7 @@
     bxjIop = ArmInstObjParams("bxj", "BxjReg", "BranchRegCond",
                               { "code": bxjcode,
                                 "predicate_test": predicateTest,
-                                "is_ras_pop": "op1 == INTREG_LR" },
+                                "is_ras_pop": "op1 == int_reg::Lr" },
                               ["IsIndirectControl"])
     header_output += BranchRegCondDeclare.subst(bxjIop)
     decoder_output += BranchRegCondConstructor.subst(bxjIop)
diff --git a/src/arch/arm/isa/insts/data.isa b/src/arch/arm/isa/insts/data.isa
index d7a957d..9a14d97 100644
--- a/src/arch/arm/isa/insts/data.isa
+++ b/src/arch/arm/isa/insts/data.isa
@@ -285,14 +285,14 @@
     buildDataInst("and", "Dest = resTemp = Op1 & secondOp;")
     buildDataInst("eor", "Dest = resTemp = Op1 ^ secondOp;")
     buildDataInst("sub", "Dest = resTemp = Op1 - secondOp;", "sub",
-                  isBranch = "dest == INTREG_PC")
+                  isBranch = "dest == int_reg::Pc")
     buildDataInst("rsb", "Dest = resTemp = secondOp - Op1;", "rsb")
     buildDataInst("add", "Dest = resTemp = Op1 + secondOp;", "add",
-                  isBranch = "dest == INTREG_PC")
+                  isBranch = "dest == int_reg::Pc")
     buildImmDataInst("adr", '''
                                Dest = resTemp = (PC & ~0x3) +
                                (op1 ? secondOp : -secondOp);
-                            ''', isBranch = "dest == INTREG_PC")
+                            ''', isBranch = "dest == int_reg::Pc")
     buildDataInst("adc", "Dest = resTemp = Op1 + secondOp + %s;" % oldC, "add")
     buildDataInst("sbc", "Dest = resTemp = Op1 - secondOp - !%s;" % oldC, "sub")
     buildDataInst("rsc", "Dest = resTemp = secondOp - Op1 - !%s;" % oldC, "rsb")
@@ -303,7 +303,8 @@
     buildDataInst("orr", "Dest = resTemp = Op1 | secondOp;")
     buildDataInst("orn", "Dest = resTemp = Op1 | ~secondOp;", aiw = False)
     buildDataInst("mov", "Dest = resTemp = secondOp;", regRegAiw = False,
-                  isRasPop = "op2 == INTREG_LR", isBranch = "dest == INTREG_PC")
+                  isRasPop = "op2 == int_reg::Lr",
+                  isBranch = "dest == int_reg::Pc")
     buildDataInst("bic", "Dest = resTemp = Op1 & ~secondOp;")
     buildDataInst("mvn", "Dest = resTemp = ~secondOp;")
     buildDataInst("movt",
diff --git a/src/arch/arm/isa/insts/data64.isa b/src/arch/arm/isa/insts/data64.isa
index aafab40..922e923 100644
--- a/src/arch/arm/isa/insts/data64.isa
+++ b/src/arch/arm/isa/insts/data64.isa
@@ -1,6 +1,6 @@
 // -*- mode:c++ -*-
 
-// Copyright (c) 2011-2013, 2016-2020 ARM Limited
+// Copyright (c) 2011-2013, 2016-2021 Arm Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -368,6 +368,34 @@
     decoder_output += MiscRegRegOp64Constructor.subst(msrIop)
     exec_output += BasicExecute.subst(msrIop)
 
+    tlbiCode = msr_check_code + '''
+        performTlbi(xc, flat_idx, XOp1);
+    '''
+    msrTlbiIop = ArmInstObjParams("msr", "Tlbi64LocalHub", "TlbiOp64",
+                                  tlbiCode,
+                                  ["IsSerializeAfter", "IsNonSpeculative"])
+    header_output += MiscRegRegOp64Declare.subst(msrTlbiIop)
+    decoder_output += MiscRegRegOp64Constructor.subst(msrTlbiIop)
+    exec_output += BasicExecute.subst(msrTlbiIop)
+
+    dvmCode = '''
+    if (dvmEnabled) {
+        Request::Flags memAccessFlags =
+            Request::STRICT_ORDER | Request::TLBI;
+
+        fault = xc->initiateMemMgmtCmd(memAccessFlags);
+
+        PendingDvm = true;
+    }
+    '''
+    msrTlbiSIop = ArmInstObjParams("msr", "Tlbi64ShareableHub", "TlbiOp64",
+                                  { "code" : tlbiCode, "dvm_code" : dvmCode },
+                                  ["IsSerializeAfter", "IsNonSpeculative"])
+    header_output += DvmTlbiDeclare.subst(msrTlbiSIop)
+    decoder_output += DvmTlbiConstructor.subst(msrTlbiSIop)
+    exec_output += BasicExecute.subst(msrTlbiSIop)
+    exec_output += DvmInitiateAcc.subst(msrTlbiSIop)
+    exec_output += DvmCompleteAcc.subst(msrTlbiSIop)
 
     buildDataXRegInst("msrNZCV", 1, '''
         CPSR cpsr = XOp1;
diff --git a/src/arch/arm/isa/insts/fp.isa b/src/arch/arm/isa/insts/fp.isa
index bb65d5b..00f2b80 100644
--- a/src/arch/arm/isa/insts/fp.isa
+++ b/src/arch/arm/isa/insts/fp.isa
@@ -41,8 +41,8 @@
 class VfpMacroRegRegOp : public VfpMacroOp
 {
   public:
-    VfpMacroRegRegOp(ExtMachInst _machInst, IntRegIndex _dest,
-                     IntRegIndex _op1, bool _wide) :
+    VfpMacroRegRegOp(ExtMachInst _machInst, RegIndex _dest,
+                     RegIndex _op1, bool _wide) :
         VfpMacroOp("VfpMacroRegRegOp", _machInst, No_OpClass, _wide)
     {
         numMicroops = machInst.fpscrLen + 1;
@@ -63,7 +63,7 @@
 template <class VfpOp>
 StaticInstPtr
 decodeVfpRegRegOp(ExtMachInst machInst,
-        IntRegIndex dest, IntRegIndex op1, bool wide)
+        RegIndex dest, RegIndex op1, bool wide)
 {
     if (machInst.fpscrLen == 0 || VfpMacroOp::inScalarBank(dest)) {
         return new VfpOp(machInst, dest, op1);
@@ -76,7 +76,7 @@
 class VfpMacroRegImmOp : public VfpMacroOp
 {
   public:
-    VfpMacroRegImmOp(ExtMachInst _machInst, IntRegIndex _dest, uint64_t _imm,
+    VfpMacroRegImmOp(ExtMachInst _machInst, RegIndex _dest, uint64_t _imm,
                      bool _wide) :
         VfpMacroOp("VfpMacroRegImmOp", _machInst, No_OpClass, _wide)
     {
@@ -97,7 +97,7 @@
 template <class VfpOp>
 StaticInstPtr
 decodeVfpRegImmOp(ExtMachInst machInst,
-        IntRegIndex dest, uint64_t imm, bool wide)
+        RegIndex dest, uint64_t imm, bool wide)
 {
     if (machInst.fpscrLen == 0 || VfpMacroOp::inScalarBank(dest)) {
         return new VfpOp(machInst, dest, imm);
@@ -110,8 +110,8 @@
 class VfpMacroRegRegImmOp : public VfpMacroOp
 {
   public:
-    VfpMacroRegRegImmOp(ExtMachInst _machInst, IntRegIndex _dest,
-                        IntRegIndex _op1, uint64_t _imm, bool _wide) :
+    VfpMacroRegRegImmOp(ExtMachInst _machInst, RegIndex _dest,
+                        RegIndex _op1, uint64_t _imm, bool _wide) :
         VfpMacroOp("VfpMacroRegRegImmOp", _machInst, No_OpClass, _wide)
     {
         numMicroops = machInst.fpscrLen + 1;
@@ -130,8 +130,8 @@
 
 template <class VfpOp>
 StaticInstPtr
-decodeVfpRegRegImmOp(ExtMachInst machInst, IntRegIndex dest,
-                     IntRegIndex op1, uint64_t imm, bool wide)
+decodeVfpRegRegImmOp(ExtMachInst machInst, RegIndex dest,
+                     RegIndex op1, uint64_t imm, bool wide)
 {
     if (machInst.fpscrLen == 0 || VfpMacroOp::inScalarBank(dest)) {
         return new VfpOp(machInst, dest, op1, imm);
@@ -144,8 +144,8 @@
 class VfpMacroRegRegRegOp : public VfpMacroOp
 {
   public:
-    VfpMacroRegRegRegOp(ExtMachInst _machInst, IntRegIndex _dest,
-                        IntRegIndex _op1, IntRegIndex _op2, bool _wide) :
+    VfpMacroRegRegRegOp(ExtMachInst _machInst, RegIndex _dest,
+                        RegIndex _op1, RegIndex _op2, bool _wide) :
         VfpMacroOp("VfpMacroRegRegRegOp", _machInst, No_OpClass, _wide)
     {
         numMicroops = machInst.fpscrLen + 1;
@@ -164,8 +164,8 @@
 
 template <class VfpOp>
 StaticInstPtr
-decodeVfpRegRegRegOp(ExtMachInst machInst, IntRegIndex dest,
-                     IntRegIndex op1, IntRegIndex op2, bool wide)
+decodeVfpRegRegRegOp(ExtMachInst machInst, RegIndex dest,
+                     RegIndex op1, RegIndex op2, bool wide)
 {
     if (machInst.fpscrLen == 0 || VfpMacroOp::inScalarBank(dest)) {
         return new VfpOp(machInst, dest, op1, op2);
diff --git a/src/arch/arm/isa/insts/ldr.isa b/src/arch/arm/isa/insts/ldr.isa
index 1f011bc..2e82ab2 100644
--- a/src/arch/arm/isa/insts/ldr.isa
+++ b/src/arch/arm/isa/insts/ldr.isa
@@ -118,7 +118,8 @@
             self.codeBlobs["memacc_code"] = accCode
 
             wbDecl = None
-            pcDecl = "MicroUopSetPCCPSR(machInst, INTREG_UREG0, INTREG_UREG1, INTREG_UREG2);"
+            pcDecl = "MicroUopSetPCCPSR(machInst, int_reg::Ureg0, " \
+                     "int_reg::Ureg1, int_reg::Ureg2);"
 
             if self.writeback:
                 wbDecl = "MicroAddiUop(machInst, base, base, %d);" % wbDiff
diff --git a/src/arch/arm/isa/insts/macromem.isa b/src/arch/arm/isa/insts/macromem.isa
index 94acf41..edd7228 100644
--- a/src/arch/arm/isa/insts/macromem.isa
+++ b/src/arch/arm/isa/insts/macromem.isa
@@ -610,7 +610,7 @@
 
     microAddXiSpAlignUopIop = ArmInstObjParams('addxi_uop',
             'MicroAddXiSpAlignUop', 'MicroIntImmXOp', '''
-        if (isSP((IntRegIndex) urb) && bits(XURb, 3, 0) &&
+        if (isSP((RegIndex) urb) && bits(XURb, 3, 0) &&
             SPAlignmentCheckEnabled(xc->tcBase())) {
             return std::make_shared<SPAlignmentFault>();
         }
diff --git a/src/arch/arm/isa/insts/mem.isa b/src/arch/arm/isa/insts/mem.isa
index 760a354..a7add8a 100644
--- a/src/arch/arm/isa/insts/mem.isa
+++ b/src/arch/arm/isa/insts/mem.isa
@@ -66,7 +66,7 @@
                 codeBlobs["fa_code"] = faCode
             elif wbDecl == None:
                 codeBlobs["fa_code"] = '''
-                    if (dest != INTREG_PC) {
+                    if (dest != int_reg::Pc) {
                         fault->annotate(ArmISA::ArmFault::SAS, %s);
                         fault->annotate(ArmISA::ArmFault::SSE, %s);
                         fault->annotate(ArmISA::ArmFault::SRT, dest);
diff --git a/src/arch/arm/isa/insts/misc.isa b/src/arch/arm/isa/insts/misc.isa
index 7253f85..6083407 100644
--- a/src/arch/arm/isa/insts/misc.isa
+++ b/src/arch/arm/isa/insts/misc.isa
@@ -1,6 +1,6 @@
 // -*- mode:c++ -*-
 
-// Copyright (c) 2010-2013,2017-2020 ARM Limited
+// Copyright (c) 2010-2013,2017-2021 Arm Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -290,7 +290,7 @@
                 // you look at the generated C code you'll find that they are.
                 // However this is safe as DecodedBankedIntReg (which is used
                 // in operands.isa to get the index of DecodedBankedIntReg)
-                // will return INTREG_ZERO if its not a valid integer
+                // will return int_reg::Zero if its not a valid integer
                 // register, so redirecting the write to somewhere we don't
                 // care about.
                 DecodedBankedIntReg = Op1;
@@ -967,7 +967,7 @@
     exec_output += PredOpExecute.subst(mrc15Iop)
 
 
-    mcr15code = '''
+    mcr15CheckCode = '''
     int preFlatDest = snsBankedIndex(dest, xc->tcBase());
     MiscRegIndex miscReg = (MiscRegIndex)
                        xc->tcBase()->flattenRegId(RegId(MiscRegClass,
@@ -989,6 +989,8 @@
     if (fault != NoFault) {
         return fault;
     }
+    '''
+    mcr15code = mcr15CheckCode + '''
     MiscNsBankedDest = Op1;
     '''
     mcr15Iop = ArmInstObjParams("mcr", "Mcr15", "MiscRegRegImmOp",
@@ -999,6 +1001,17 @@
     decoder_output += MiscRegRegImmOpConstructor.subst(mcr15Iop)
     exec_output += PredOpExecute.subst(mcr15Iop)
 
+    mcrTlbiCode = mcr15CheckCode + '''
+    performTlbi(xc, miscReg, Op1);
+    '''
+    mcrTlbiIop = ArmInstObjParams("mcr", "Tlbi", "TlbiOp",
+                                  { "code": mcrTlbiCode,
+                                    "predicate_test": predicateTest },
+                                  ["IsSerializeAfter","IsNonSpeculative"])
+    header_output += MiscRegRegImmOpDeclare.subst(mcrTlbiIop)
+    decoder_output += MiscRegRegImmOpConstructor.subst(mcrTlbiIop)
+    exec_output += PredOpExecute.subst(mcrTlbiIop)
+
 
     mrrc15code = '''
     int preFlatOp1 = snsBankedIndex(op1, xc->tcBase());
@@ -1110,30 +1123,6 @@
     decoder_output += BasicConstructor.subst(clrexIop)
     exec_output += PredOpExecute.subst(clrexIop)
 
-    McrDcCheckCode = '''
-        int preFlatDest = snsBankedIndex(dest, xc->tcBase());
-        MiscRegIndex miscReg = (MiscRegIndex) xc->tcBase()->flattenRegId(
-            RegId(MiscRegClass, preFlatDest)).index();
-
-        bool hypTrap = mcrMrc15TrapToHyp(miscReg, xc->tcBase(), imm);
-
-        auto [can_write, undefined] = canWriteCoprocReg(miscReg, Scr, Cpsr,
-                                                        xc->tcBase());
-
-        // if we're in non secure PL1 mode then we can trap regardless
-        // of whether the register is accessible, in other modes we
-        // trap if only if the register IS accessible.
-        if (undefined || (!can_write & !(hypTrap & !inUserMode(Cpsr) &
-                                         !isSecure(xc->tcBase())))) {
-            return std::make_shared<UndefinedInstruction>(machInst, false,
-                                                          mnemonic);
-        }
-        if (hypTrap) {
-            return std::make_shared<HypervisorTrap>(machInst, imm,
-                                                    EC_TRAPPED_CP15_MCR_MRC);
-        }
-    '''
-
     McrDcimvacCode = '''
         const Request::Flags memAccessFlags(Request::INVALIDATE |
                                             Request::DST_POC);
@@ -1141,7 +1130,7 @@
     '''
     McrDcimvacIop = ArmInstObjParams("mcr", "McrDcimvac",
                                      "MiscRegRegImmOp",
-                                     {"memacc_code": McrDcCheckCode,
+                                     {"memacc_code": mcr15CheckCode,
                                       "postacc_code": "",
                                       "ea_code": McrDcimvacCode,
                                       "predicate_test": predicateTest},
@@ -1159,7 +1148,7 @@
     '''
     McrDccmvacIop = ArmInstObjParams("mcr", "McrDccmvac",
                                      "MiscRegRegImmOp",
-                                     {"memacc_code": McrDcCheckCode,
+                                     {"memacc_code": mcr15CheckCode,
                                       "postacc_code": "",
                                       "ea_code": McrDccmvacCode,
                                       "predicate_test": predicateTest},
@@ -1177,7 +1166,7 @@
     '''
     McrDccmvauIop = ArmInstObjParams("mcr", "McrDccmvau",
                                      "MiscRegRegImmOp",
-                                     {"memacc_code": McrDcCheckCode,
+                                     {"memacc_code": mcr15CheckCode,
                                       "postacc_code": "",
                                       "ea_code": McrDccmvauCode,
                                       "predicate_test": predicateTest},
@@ -1196,7 +1185,7 @@
     '''
     McrDccimvacIop = ArmInstObjParams("mcr", "McrDccimvac",
                                      "MiscRegRegImmOp",
-                                     {"memacc_code": McrDcCheckCode,
+                                     {"memacc_code": mcr15CheckCode,
                                       "postacc_code": "",
                                       "ea_code": McrDccimvacCode,
                                       "predicate_test": predicateTest},
diff --git a/src/arch/arm/isa/insts/misc64.isa b/src/arch/arm/isa/insts/misc64.isa
index d516c53..abe30fc 100644
--- a/src/arch/arm/isa/insts/misc64.isa
+++ b/src/arch/arm/isa/insts/misc64.isa
@@ -1,6 +1,6 @@
 // -*- mode:c++ -*-
 
-// Copyright (c) 2011-2013, 2016-2018, 2020 ARM Limited
+// Copyright (c) 2011-2013, 2016-2018, 2020-2021 Arm Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -172,12 +172,36 @@
     decoder_output += BasicConstructor64.subst(isbIop)
     exec_output += BasicExecute.subst(isbIop)
 
-    dsbIop = ArmInstObjParams("dsb", "Dsb64", "ArmStaticInst", "",
-                              ['IsReadBarrier', 'IsWriteBarrier',
-                               'IsSerializeAfter'])
-    header_output += BasicDeclare.subst(dsbIop)
-    decoder_output += BasicConstructor64.subst(dsbIop)
-    exec_output += BasicExecute.subst(dsbIop)
+    dsbLocalIop = ArmInstObjParams("dsb", "Dsb64Local", "ArmStaticInst", "",
+                                   ['IsReadBarrier', 'IsWriteBarrier',
+                                   'IsSerializeAfter'])
+    header_output += BasicDeclare.subst(dsbLocalIop)
+    decoder_output += BasicConstructor64.subst(dsbLocalIop)
+    exec_output += BasicExecute.subst(dsbLocalIop)
+
+    dvmCode = '''
+        if (dvmEnabled) {
+            Request::Flags memAccessFlags =
+                Request::STRICT_ORDER|Request::TLBI_SYNC;
+
+            if (!PendingDvm) {
+                memAccessFlags = memAccessFlags | Request::NO_ACCESS;
+            }
+
+            fault = xc->initiateMemMgmtCmd(memAccessFlags);
+
+            PendingDvm = false;
+        }
+    '''
+    dsbShareableIop = ArmInstObjParams("dsb", "Dsb64Shareable", "ArmStaticInst",
+                                       { "code" : "", "dvm_code" : dvmCode },
+                                       ['IsReadBarrier', 'IsWriteBarrier',
+                                        'IsSerializeAfter'])
+    header_output += DvmDeclare.subst(dsbShareableIop)
+    decoder_output += DvmConstructor.subst(dsbShareableIop)
+    exec_output += BasicExecute.subst(dsbShareableIop)
+    exec_output += DvmInitiateAcc.subst(dsbShareableIop)
+    exec_output += DvmCompleteAcc.subst(dsbShareableIop)
 
     dmbIop = ArmInstObjParams("dmb", "Dmb64", "ArmStaticInst", "",
                               ['IsReadBarrier', 'IsWriteBarrier'])
diff --git a/src/arch/arm/isa/insts/neon.isa b/src/arch/arm/isa/insts/neon.isa
index 756abdc..df2bd3a 100644
--- a/src/arch/arm/isa/insts/neon.isa
+++ b/src/arch/arm/isa/insts/neon.isa
@@ -39,8 +39,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUThreeUReg(unsigned size,
-                         ExtMachInst machInst, IntRegIndex dest,
-                         IntRegIndex op1, IntRegIndex op2)
+                         ExtMachInst machInst, RegIndex dest,
+                         RegIndex op1, RegIndex op2)
     {
         switch (size) {
           case 0:
@@ -59,8 +59,8 @@
     template <class BaseS, class BaseD>
     StaticInstPtr
     decodeNeonSizeSingleDouble(unsigned size,
-                         ExtMachInst machInst, IntRegIndex dest,
-                         IntRegIndex op1, IntRegIndex op2)
+                         ExtMachInst machInst, RegIndex dest,
+                         RegIndex op1, RegIndex op2)
     {
         switch (size) {
           case 2:
@@ -75,8 +75,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonSThreeUReg(unsigned size,
-                         ExtMachInst machInst, IntRegIndex dest,
-                         IntRegIndex op1, IntRegIndex op2)
+                         ExtMachInst machInst, RegIndex dest,
+                         RegIndex op1, RegIndex op2)
     {
         switch (size) {
           case 0:
@@ -95,8 +95,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUSThreeUReg(bool notSigned, unsigned size,
-                          ExtMachInst machInst, IntRegIndex dest,
-                          IntRegIndex op1, IntRegIndex op2)
+                          ExtMachInst machInst, RegIndex dest,
+                          RegIndex op1, RegIndex op2)
     {
         if (notSigned) {
             return decodeNeonUThreeUReg<Base>(size, machInst, dest, op1, op2);
@@ -108,8 +108,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUThreeUSReg(unsigned size,
-                          ExtMachInst machInst, IntRegIndex dest,
-                          IntRegIndex op1, IntRegIndex op2)
+                          ExtMachInst machInst, RegIndex dest,
+                          RegIndex op1, RegIndex op2)
     {
         switch (size) {
           case 0:
@@ -126,8 +126,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonSThreeUSReg(unsigned size,
-                          ExtMachInst machInst, IntRegIndex dest,
-                          IntRegIndex op1, IntRegIndex op2)
+                          ExtMachInst machInst, RegIndex dest,
+                          RegIndex op1, RegIndex op2)
     {
         switch (size) {
           case 0:
@@ -144,8 +144,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonSThreeHAndWReg(unsigned size, ExtMachInst machInst,
-                             IntRegIndex dest, IntRegIndex op1,
-                             IntRegIndex op2)
+                             RegIndex dest, RegIndex op1,
+                             RegIndex op2)
     {
         switch (size) {
           case 1:
@@ -160,8 +160,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonSThreeImmHAndWReg(unsigned size, ExtMachInst machInst,
-                                IntRegIndex dest, IntRegIndex op1,
-                                IntRegIndex op2, uint64_t imm)
+                                RegIndex dest, RegIndex op1,
+                                RegIndex op2, uint64_t imm)
     {
         switch (size) {
           case 1:
@@ -176,8 +176,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUSThreeUSReg(bool notSigned, unsigned size,
-                           ExtMachInst machInst, IntRegIndex dest,
-                           IntRegIndex op1, IntRegIndex op2)
+                           ExtMachInst machInst, RegIndex dest,
+                           RegIndex op1, RegIndex op2)
     {
         if (notSigned) {
             return decodeNeonUThreeUSReg<Base>(
@@ -192,8 +192,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUThreeSReg(bool q, unsigned size,
-                         ExtMachInst machInst, IntRegIndex dest,
-                         IntRegIndex op1, IntRegIndex op2)
+                         ExtMachInst machInst, RegIndex dest,
+                         RegIndex op1, RegIndex op2)
     {
         if (q) {
             return decodeNeonUThreeUSReg<BaseQ>(
@@ -208,8 +208,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSThreeSReg(bool q, unsigned size,
-                         ExtMachInst machInst, IntRegIndex dest,
-                         IntRegIndex op1, IntRegIndex op2)
+                         ExtMachInst machInst, RegIndex dest,
+                         RegIndex op1, RegIndex op2)
     {
         if (q) {
             return decodeNeonSThreeUSReg<BaseQ>(
@@ -224,8 +224,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSThreeXReg(bool q, unsigned size,
-                         ExtMachInst machInst, IntRegIndex dest,
-                         IntRegIndex op1, IntRegIndex op2)
+                         ExtMachInst machInst, RegIndex dest,
+                         RegIndex op1, RegIndex op2)
     {
         if (q) {
             return decodeNeonSThreeUReg<BaseQ>(
@@ -240,8 +240,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUThreeXReg(bool q, unsigned size,
-                         ExtMachInst machInst, IntRegIndex dest,
-                         IntRegIndex op1, IntRegIndex op2)
+                         ExtMachInst machInst, RegIndex dest,
+                         RegIndex op1, RegIndex op2)
     {
         if (q) {
             return decodeNeonUThreeUReg<BaseQ>(
@@ -256,8 +256,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUSThreeSReg(bool q, bool notSigned, unsigned size,
-                          ExtMachInst machInst, IntRegIndex dest,
-                          IntRegIndex op1, IntRegIndex op2)
+                          ExtMachInst machInst, RegIndex dest,
+                          RegIndex op1, RegIndex op2)
     {
         if (notSigned) {
             return decodeNeonUThreeSReg<BaseD, BaseQ>(
@@ -272,8 +272,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUThreeReg(bool q, unsigned size,
-                        ExtMachInst machInst, IntRegIndex dest,
-                        IntRegIndex op1, IntRegIndex op2)
+                        ExtMachInst machInst, RegIndex dest,
+                        RegIndex op1, RegIndex op2)
     {
         if (q) {
             return decodeNeonUThreeUReg<BaseQ>(
@@ -288,8 +288,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSThreeReg(bool q, unsigned size,
-                        ExtMachInst machInst, IntRegIndex dest,
-                        IntRegIndex op1, IntRegIndex op2)
+                        ExtMachInst machInst, RegIndex dest,
+                        RegIndex op1, RegIndex op2)
     {
         if (q) {
             return decodeNeonSThreeUReg<BaseQ>(
@@ -304,8 +304,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUSThreeReg(bool q, bool notSigned, unsigned size,
-                         ExtMachInst machInst, IntRegIndex dest,
-                         IntRegIndex op1, IntRegIndex op2)
+                         ExtMachInst machInst, RegIndex dest,
+                         RegIndex op1, RegIndex op2)
     {
         if (notSigned) {
             return decodeNeonUThreeReg<BaseD, BaseQ>(
@@ -320,7 +320,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUThreeFpReg(bool q, unsigned size, ExtMachInst machInst,
-                          IntRegIndex dest, IntRegIndex op1, IntRegIndex op2)
+                          RegIndex dest, RegIndex op1, RegIndex op2)
     {
         if (q) {
             if (size)
@@ -338,7 +338,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUThreeScFpReg(bool size, ExtMachInst machInst,
-                            IntRegIndex dest, IntRegIndex op1, IntRegIndex op2)
+                            RegIndex dest, RegIndex op1, RegIndex op2)
     {
         if (size)
             return new Base<uint64_t>(machInst, dest, op1, op2);
@@ -349,8 +349,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUThreeImmScFpReg(bool size, ExtMachInst machInst,
-                               IntRegIndex dest, IntRegIndex op1,
-                               IntRegIndex op2, uint64_t imm)
+                               RegIndex dest, RegIndex op1,
+                               RegIndex op2, uint64_t imm)
     {
         if (size)
             return new Base<uint64_t>(machInst, dest, op1, op2, imm);
@@ -362,8 +362,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUThreeImmHAndWReg(bool q, unsigned size, ExtMachInst machInst,
-                                IntRegIndex dest, IntRegIndex op1,
-                                IntRegIndex op2, uint64_t imm)
+                                RegIndex dest, RegIndex op1,
+                                RegIndex op2, uint64_t imm)
     {
         if (q) {
             switch (size) {
@@ -390,8 +390,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSThreeImmHAndWReg(bool q, unsigned size, ExtMachInst machInst,
-                                IntRegIndex dest, IntRegIndex op1,
-                                IntRegIndex op2, uint64_t imm)
+                                RegIndex dest, RegIndex op1,
+                                RegIndex op2, uint64_t imm)
     {
         if (q) {
             switch (size) {
@@ -418,8 +418,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUThreeImmFpReg(bool q, unsigned size, ExtMachInst machInst,
-                             IntRegIndex dest, IntRegIndex op1,
-                             IntRegIndex op2, uint64_t imm)
+                             RegIndex dest, RegIndex op1,
+                             RegIndex op2, uint64_t imm)
     {
         if (q) {
             if (size)
@@ -438,8 +438,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUTwoShiftReg(bool q, unsigned size,
-                           ExtMachInst machInst, IntRegIndex dest,
-                           IntRegIndex op1, uint64_t imm)
+                           ExtMachInst machInst, RegIndex dest,
+                           RegIndex op1, uint64_t imm)
     {
         if (q) {
             switch (size) {
@@ -474,8 +474,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSTwoShiftReg(bool q, unsigned size,
-                           ExtMachInst machInst, IntRegIndex dest,
-                           IntRegIndex op1, uint64_t imm)
+                           ExtMachInst machInst, RegIndex dest,
+                           RegIndex op1, uint64_t imm)
     {
         if (q) {
             switch (size) {
@@ -511,8 +511,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUSTwoShiftReg(bool q, bool notSigned, unsigned size,
-                            ExtMachInst machInst, IntRegIndex dest,
-                            IntRegIndex op1, uint64_t imm)
+                            ExtMachInst machInst, RegIndex dest,
+                            RegIndex op1, uint64_t imm)
     {
         if (notSigned) {
             return decodeNeonUTwoShiftReg<BaseD, BaseQ>(
@@ -526,8 +526,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUTwoShiftUSReg(unsigned size,
-                             ExtMachInst machInst, IntRegIndex dest,
-                             IntRegIndex op1, uint64_t imm)
+                             ExtMachInst machInst, RegIndex dest,
+                             RegIndex op1, uint64_t imm)
     {
         switch (size) {
           case 0:
@@ -544,8 +544,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUTwoShiftUReg(unsigned size,
-                            ExtMachInst machInst, IntRegIndex dest,
-                            IntRegIndex op1, uint64_t imm)
+                            ExtMachInst machInst, RegIndex dest,
+                            RegIndex op1, uint64_t imm)
     {
         switch (size) {
           case 0:
@@ -564,8 +564,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonSTwoShiftUReg(unsigned size,
-                            ExtMachInst machInst, IntRegIndex dest,
-                            IntRegIndex op1, uint64_t imm)
+                            ExtMachInst machInst, RegIndex dest,
+                            RegIndex op1, uint64_t imm)
     {
         switch (size) {
           case 0:
@@ -585,8 +585,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUTwoShiftSReg(bool q, unsigned size,
-                            ExtMachInst machInst, IntRegIndex dest,
-                            IntRegIndex op1, uint64_t imm)
+                            ExtMachInst machInst, RegIndex dest,
+                            RegIndex op1, uint64_t imm)
     {
         if (q) {
             return decodeNeonUTwoShiftUSReg<BaseQ>(
@@ -600,8 +600,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonSTwoShiftUSReg(unsigned size,
-                             ExtMachInst machInst, IntRegIndex dest,
-                             IntRegIndex op1, uint64_t imm)
+                             ExtMachInst machInst, RegIndex dest,
+                             RegIndex op1, uint64_t imm)
     {
         switch (size) {
           case 0:
@@ -619,8 +619,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSTwoShiftSReg(bool q, unsigned size,
-                            ExtMachInst machInst, IntRegIndex dest,
-                            IntRegIndex op1, uint64_t imm)
+                            ExtMachInst machInst, RegIndex dest,
+                            RegIndex op1, uint64_t imm)
     {
         if (q) {
             return decodeNeonSTwoShiftUSReg<BaseQ>(
@@ -635,8 +635,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUSTwoShiftSReg(bool q, bool notSigned, unsigned size,
-                             ExtMachInst machInst, IntRegIndex dest,
-                             IntRegIndex op1, uint64_t imm)
+                             ExtMachInst machInst, RegIndex dest,
+                             RegIndex op1, uint64_t imm)
     {
         if (notSigned) {
             return decodeNeonUTwoShiftSReg<BaseD, BaseQ>(
@@ -651,7 +651,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUTwoShiftXReg(bool q, unsigned size, ExtMachInst machInst,
-                            IntRegIndex dest, IntRegIndex op1, uint64_t imm)
+                            RegIndex dest, RegIndex op1, uint64_t imm)
     {
         if (q) {
             return decodeNeonUTwoShiftUReg<BaseQ>(
@@ -666,7 +666,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSTwoShiftXReg(bool q, unsigned size, ExtMachInst machInst,
-                            IntRegIndex dest, IntRegIndex op1, uint64_t imm)
+                            RegIndex dest, RegIndex op1, uint64_t imm)
     {
         if (q) {
             return decodeNeonSTwoShiftUReg<BaseQ>(
@@ -680,7 +680,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUTwoShiftUFpReg(unsigned size, ExtMachInst machInst,
-                              IntRegIndex dest, IntRegIndex op1, uint64_t imm)
+                              RegIndex dest, RegIndex op1, uint64_t imm)
     {
         if (size)
             return new Base<uint64_t>(machInst, dest, op1, imm);
@@ -692,7 +692,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUTwoShiftFpReg(bool q, unsigned size, ExtMachInst machInst,
-                             IntRegIndex dest, IntRegIndex op1, uint64_t imm)
+                             RegIndex dest, RegIndex op1, uint64_t imm)
     {
         if (q) {
             if (size)
@@ -710,8 +710,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUTwoMiscUSReg(unsigned size,
-                            ExtMachInst machInst, IntRegIndex dest,
-                            IntRegIndex op1)
+                            ExtMachInst machInst, RegIndex dest,
+                            RegIndex op1)
     {
         switch (size) {
           case 0:
@@ -728,8 +728,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonSTwoMiscUSReg(unsigned size,
-                            ExtMachInst machInst, IntRegIndex dest,
-                            IntRegIndex op1)
+                            ExtMachInst machInst, RegIndex dest,
+                            RegIndex op1)
     {
         switch (size) {
           case 0:
@@ -747,8 +747,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUTwoMiscSReg(bool q, unsigned size,
-                           ExtMachInst machInst, IntRegIndex dest,
-                           IntRegIndex op1)
+                           ExtMachInst machInst, RegIndex dest,
+                           RegIndex op1)
     {
         if (q) {
             return decodeNeonUTwoMiscUSReg<BaseQ>(size, machInst, dest, op1);
@@ -761,8 +761,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSTwoMiscSReg(bool q, unsigned size,
-                           ExtMachInst machInst, IntRegIndex dest,
-                           IntRegIndex op1)
+                           ExtMachInst machInst, RegIndex dest,
+                           RegIndex op1)
     {
         if (q) {
             return decodeNeonSTwoMiscUSReg<BaseQ>(size, machInst, dest, op1);
@@ -774,8 +774,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUTwoMiscUReg(unsigned size,
-                           ExtMachInst machInst, IntRegIndex dest,
-                           IntRegIndex op1)
+                           ExtMachInst machInst, RegIndex dest,
+                           RegIndex op1)
     {
         switch (size) {
           case 0:
@@ -794,8 +794,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonSTwoMiscUReg(unsigned size,
-                           ExtMachInst machInst, IntRegIndex dest,
-                           IntRegIndex op1)
+                           ExtMachInst machInst, RegIndex dest,
+                           RegIndex op1)
     {
         switch (size) {
           case 0:
@@ -815,8 +815,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSTwoMiscReg(bool q, unsigned size,
-                          ExtMachInst machInst, IntRegIndex dest,
-                          IntRegIndex op1)
+                          ExtMachInst machInst, RegIndex dest,
+                          RegIndex op1)
     {
         if (q) {
             return decodeNeonSTwoMiscUReg<BaseQ>(size, machInst, dest, op1);
@@ -829,8 +829,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUTwoMiscReg(bool q, unsigned size,
-                          ExtMachInst machInst, IntRegIndex dest,
-                          IntRegIndex op1)
+                          ExtMachInst machInst, RegIndex dest,
+                          RegIndex op1)
     {
         if (q) {
             return decodeNeonUTwoMiscUReg<BaseQ>(size, machInst, dest, op1);
@@ -843,8 +843,8 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUSTwoMiscSReg(bool q, bool notSigned, unsigned size,
-                            ExtMachInst machInst, IntRegIndex dest,
-                            IntRegIndex op1)
+                            ExtMachInst machInst, RegIndex dest,
+                            RegIndex op1)
     {
         if (notSigned) {
             return decodeNeonUTwoShiftSReg<BaseD, BaseQ>(
@@ -859,7 +859,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUTwoMiscXReg(bool q, unsigned size, ExtMachInst machInst,
-                           IntRegIndex dest, IntRegIndex op1)
+                           RegIndex dest, RegIndex op1)
     {
         if (q) {
             return decodeNeonUTwoMiscUReg<BaseQ>(size, machInst, dest, op1);
@@ -872,7 +872,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSTwoMiscXReg(bool q, unsigned size, ExtMachInst machInst,
-                           IntRegIndex dest, IntRegIndex op1)
+                           RegIndex dest, RegIndex op1)
     {
         if (q) {
             return decodeNeonSTwoMiscUReg<BaseQ>(size, machInst, dest, op1);
@@ -885,7 +885,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUTwoMiscFpReg(bool q, unsigned size, ExtMachInst machInst,
-                            IntRegIndex dest, IntRegIndex op1)
+                            RegIndex dest, RegIndex op1)
     {
         if (q) {
             if (size)
@@ -904,7 +904,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUTwoMiscPwiseScFpReg(unsigned size, ExtMachInst machInst,
-                                   IntRegIndex dest, IntRegIndex op1)
+                                   RegIndex dest, RegIndex op1)
     {
         if (size)
             return new BaseQ<uint64_t>(machInst, dest, op1);
@@ -915,7 +915,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeNeonUTwoMiscScFpReg(unsigned size, ExtMachInst machInst,
-                              IntRegIndex dest, IntRegIndex op1)
+                              RegIndex dest, RegIndex op1)
     {
         if (size)
             return new Base<uint64_t>(machInst, dest, op1);
@@ -927,7 +927,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonUAcrossLanesReg(bool q, unsigned size, ExtMachInst machInst,
-                              IntRegIndex dest, IntRegIndex op1)
+                              RegIndex dest, RegIndex op1)
     {
         if (q) {
             switch (size) {
@@ -957,7 +957,7 @@
               template <typename T> class BaseBQ>
     StaticInstPtr
     decodeNeonUAcrossLanesReg(bool q, unsigned size, ExtMachInst machInst,
-                              IntRegIndex dest, IntRegIndex op1)
+                              RegIndex dest, RegIndex op1)
     {
         if (q) {
             switch (size) {
@@ -986,7 +986,7 @@
               template <typename T> class BaseQ>
     StaticInstPtr
     decodeNeonSAcrossLanesReg(bool q, unsigned size, ExtMachInst machInst,
-                              IntRegIndex dest, IntRegIndex op1)
+                              RegIndex dest, RegIndex op1)
     {
         if (q) {
             switch (size) {
@@ -1016,7 +1016,7 @@
               template <typename T> class BaseBQ>
     StaticInstPtr
     decodeNeonUAcrossLanesLongReg(bool q, unsigned size, ExtMachInst machInst,
-                                  IntRegIndex dest, IntRegIndex op1)
+                                  RegIndex dest, RegIndex op1)
     {
         if (q) {
             switch (size) {
@@ -1046,7 +1046,7 @@
               template <typename T> class BaseBQ>
     StaticInstPtr
     decodeNeonSAcrossLanesLongReg(bool q, unsigned size, ExtMachInst machInst,
-                                  IntRegIndex dest, IntRegIndex op1)
+                                  RegIndex dest, RegIndex op1)
     {
         if (q) {
             switch (size) {
diff --git a/src/arch/arm/isa/insts/pauth.isa b/src/arch/arm/isa/insts/pauth.isa
index ee3f2d3..ea16ab9 100644
--- a/src/arch/arm/isa/insts/pauth.isa
+++ b/src/arch/arm/isa/insts/pauth.isa
@@ -45,13 +45,13 @@
     def pacEnabledCode(hint):
         if hint:
             code = """
-                if (!HavePACExt(xc->tcBase())) {
+                if (!HaveExt(xc->tcBase(), ArmExtension::FEAT_PAuth)) {
                     return NoFault;
                 }
                 """
         else:
             code = """
-                if (!HavePACExt(xc->tcBase())) {
+                if (!HaveExt(xc->tcBase(), ArmExtension::FEAT_PAuth)) {
                     return std::make_shared<UndefinedInstruction>(
                         machInst, true);
                 }
@@ -78,7 +78,8 @@
                                "op2": 'Op164',
                                "op":  opcode }
 
-        iop = ArmInstObjParams(mnem, mnem, templateBase+"Op", code, optArgs)
+        iop = ArmInstObjParams(mnem, mnem.capitalize(),
+                               templateBase+"Op", code, optArgs)
         header_output += eval(templateBase + "Declare").subst(iop)
         decoder_output += eval(templateBase + "Constructor").subst(iop)
         exec_output += BasicExecute.subst(iop)
@@ -94,48 +95,49 @@
             """
         regoptype = 'RegOp'
 
-        iop = ArmInstObjParams(mnem, mnem, regoptype, code, optArgs)
+        iop = ArmInstObjParams(mnem, mnem.capitalize(),
+                               regoptype, code, optArgs)
         header_output += eval(templateBase + "Declare").subst(iop)
         decoder_output += eval(templateBase + "Constructor").subst(iop)
         exec_output += BasicExecute.subst(iop)
 
 
-    buildPauthObject("Pacda",  "DataX1Reg", 'addPACDA', hint=False)
-    buildPauthObject("Pacdza", "DataX1Reg", 'addPACDA', hint=False)
-    buildPauthObject("Pacdb",  "DataX1Reg", 'addPACDB', hint=False)
-    buildPauthObject("Pacdzb", "DataX1Reg", 'addPACDB', hint=False)
-    buildPauthObject("Pacga",  "DataX2Reg", 'addPACGA', hint=False)
+    buildPauthObject("pacda",  "DataX1Reg", 'addPACDA', hint=False)
+    buildPauthObject("pacdza", "DataX1Reg", 'addPACDA', hint=False)
+    buildPauthObject("pacdb",  "DataX1Reg", 'addPACDB', hint=False)
+    buildPauthObject("pacdzb", "DataX1Reg", 'addPACDB', hint=False)
+    buildPauthObject("pacga",  "DataX2Reg", 'addPACGA', hint=False)
 
-    buildPauthObject("Pacia",     "DataX1Reg", 'addPACIA', hint=False)
-    buildPauthObject("Pacia1716", "DataX1Reg", 'addPACIA', hint=True)
-    buildPauthObject("Paciasp",   "DataX1Reg", 'addPACIA', hint=True)
-    buildPauthObject("Paciaz",    "DataX1Reg", 'addPACIA', hint=True)
-    buildPauthObject("Paciza",    "DataX1Reg", 'addPACIA', hint=False)
+    buildPauthObject("pacia",     "DataX1Reg", 'addPACIA', hint=False)
+    buildPauthObject("pacia1716", "DataX1Reg", 'addPACIA', hint=True)
+    buildPauthObject("paciasp",   "DataX1Reg", 'addPACIA', hint=True)
+    buildPauthObject("paciaz",    "DataX1Reg", 'addPACIA', hint=True)
+    buildPauthObject("paciza",    "DataX1Reg", 'addPACIA', hint=False)
 
-    buildPauthObject("Pacib",     "DataX1Reg", 'addPACIB', hint=False)
-    buildPauthObject("Pacib1716", "DataX1Reg", 'addPACIB', hint=True)
-    buildPauthObject("Pacibsp",   "DataX1Reg", 'addPACIB', hint=True)
-    buildPauthObject("Pacibz",    "DataX1Reg", 'addPACIB', hint=True)
-    buildPauthObject("Pacizb",    "DataX1Reg", 'addPACIB', hint=False)
+    buildPauthObject("pacib",     "DataX1Reg", 'addPACIB', hint=False)
+    buildPauthObject("pacib1716", "DataX1Reg", 'addPACIB', hint=True)
+    buildPauthObject("pacibsp",   "DataX1Reg", 'addPACIB', hint=True)
+    buildPauthObject("pacibz",    "DataX1Reg", 'addPACIB', hint=True)
+    buildPauthObject("pacizb",    "DataX1Reg", 'addPACIB', hint=False)
 
-    buildPauthObject("Autda",     "DataX1Reg", 'authDA', hint=False)
-    buildPauthObject("Autdza",    "DataX1Reg", 'authDA', hint=False)
-    buildPauthObject("Autdb",     "DataX1Reg", 'authDB', hint=False)
-    buildPauthObject("Autdzb",    "DataX1Reg", 'authDB', hint=False)
+    buildPauthObject("autda",     "DataX1Reg", 'authDA', hint=False)
+    buildPauthObject("autdza",    "DataX1Reg", 'authDA', hint=False)
+    buildPauthObject("autdb",     "DataX1Reg", 'authDB', hint=False)
+    buildPauthObject("autdzb",    "DataX1Reg", 'authDB', hint=False)
 
-    buildPauthObject("Autia",     "DataX1Reg", 'authIA', hint=False)
-    buildPauthObject("Autia1716", "DataX1Reg", 'authIA', hint=True)
-    buildPauthObject("Autiasp",   "DataX1Reg", 'authIA', hint=True)
-    buildPauthObject("Autiaz",    "DataX1Reg", 'authIA', hint=True)
-    buildPauthObject("Autiza",    "DataX1Reg", 'authIA', hint=False)
+    buildPauthObject("autia",     "DataX1Reg", 'authIA', hint=False)
+    buildPauthObject("autia1716", "DataX1Reg", 'authIA', hint=True)
+    buildPauthObject("autiasp",   "DataX1Reg", 'authIA', hint=True)
+    buildPauthObject("autiaz",    "DataX1Reg", 'authIA', hint=True)
+    buildPauthObject("autiza",    "DataX1Reg", 'authIA', hint=False)
 
-    buildPauthObject("Autib",     "DataX1Reg", 'authIB', hint=False)
-    buildPauthObject("Autib1716", "DataX1Reg", 'authIB', hint=True)
-    buildPauthObject("Autibsp",   "DataX1Reg", 'authIB', hint=True)
-    buildPauthObject("Autibz",    "DataX1Reg", 'authIB', hint=True)
-    buildPauthObject("Autizb",    "DataX1Reg", 'authIB', hint=False)
+    buildPauthObject("autib",     "DataX1Reg", 'authIB', hint=False)
+    buildPauthObject("autib1716", "DataX1Reg", 'authIB', hint=True)
+    buildPauthObject("autibsp",   "DataX1Reg", 'authIB', hint=True)
+    buildPauthObject("autibz",    "DataX1Reg", 'authIB', hint=True)
+    buildPauthObject("autizb",    "DataX1Reg", 'authIB', hint=False)
 
-    buildXPauthObject("Xpacd", hint=False)
-    buildXPauthObject("Xpaci", hint=False)
-    buildXPauthObject("Xpaclri", hint=True)
+    buildXPauthObject("xpacd", hint=False)
+    buildXPauthObject("xpaci", hint=False)
+    buildXPauthObject("xpaclri", hint=True)
 }};
diff --git a/src/arch/arm/isa/insts/str.isa b/src/arch/arm/isa/insts/str.isa
index 8700293..1583ef0 100644
--- a/src/arch/arm/isa/insts/str.isa
+++ b/src/arch/arm/isa/insts/str.isa
@@ -130,8 +130,10 @@
             wbDecl = None
             if self.writeback:
                 wbDecl = '''MicroAddiUop(machInst,
-                              intRegInMode((OperatingMode)regMode, INTREG_SP),
-                              intRegInMode((OperatingMode)regMode, INTREG_SP),
+                              int_reg::regInMode((OperatingMode)regMode,
+                                  int_reg::Sp),
+                              int_reg::regInMode((OperatingMode)regMode,
+                                  int_reg::Sp),
                               %d);''' % wbDiff
 
             (newHeader,
diff --git a/src/arch/arm/isa/insts/sve.isa b/src/arch/arm/isa/insts/sve.isa
index 7338eb2..7cb7331 100644
--- a/src/arch/arm/isa/insts/sve.isa
+++ b/src/arch/arm/isa/insts/sve.isa
@@ -43,7 +43,7 @@
               template <typename T> class BaseU>
     StaticInstPtr
     decodeSveUnaryPred(unsigned size, unsigned u, ExtMachInst machInst,
-                       IntRegIndex dest, IntRegIndex op1, IntRegIndex gp)
+                       RegIndex dest, RegIndex op1, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -81,7 +81,7 @@
               template <typename T1, typename T2> class BaseU>
     StaticInstPtr
     decodeSveWideningReduc(unsigned size, unsigned u, ExtMachInst machInst,
-                           IntRegIndex dest, IntRegIndex op1, IntRegIndex gp)
+                           RegIndex dest, RegIndex op1, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -115,7 +115,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveUnaryPredS(unsigned size, ExtMachInst machInst,
-                        IntRegIndex dest, IntRegIndex op1, IntRegIndex gp)
+                        RegIndex dest, RegIndex op1, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -136,7 +136,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveUnaryPredU(unsigned size, ExtMachInst machInst,
-                        IntRegIndex dest, IntRegIndex op1, IntRegIndex gp)
+                        RegIndex dest, RegIndex op1, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -159,7 +159,7 @@
               template <typename T> class BaseU>
     StaticInstPtr
     decodeSveUnaryPredSmall(unsigned size, unsigned u, ExtMachInst machInst,
-                            IntRegIndex dest, IntRegIndex op1, IntRegIndex gp)
+                            RegIndex dest, RegIndex op1, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -190,7 +190,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveUnaryPredF(unsigned size, ExtMachInst machInst,
-                        IntRegIndex dest, IntRegIndex op1, IntRegIndex gp)
+                        RegIndex dest, RegIndex op1, RegIndex gp)
     {
         switch (size) {
           case 1:
@@ -209,7 +209,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveUnaryUnpredU(unsigned size, ExtMachInst machInst,
-                          IntRegIndex dest, IntRegIndex op1)
+                          RegIndex dest, RegIndex op1)
     {
         switch (size) {
           case 0:
@@ -230,7 +230,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveUnaryUnpredF(unsigned size, ExtMachInst machInst,
-                          IntRegIndex dest, IntRegIndex op1)
+                          RegIndex dest, RegIndex op1)
     {
         switch (size) {
           case 1:
@@ -250,7 +250,7 @@
               template <typename T> class BaseU>
     StaticInstPtr
     decodeSveBinDestrPred(unsigned size, unsigned u, ExtMachInst machInst,
-                          IntRegIndex dest, IntRegIndex op2, IntRegIndex gp)
+                          RegIndex dest, RegIndex op2, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -287,7 +287,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveBinImmUnpredS(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, IntRegIndex op1, unsigned immediate)
+            RegIndex dest, RegIndex op1, unsigned immediate)
     {
         switch (size) {
           case 0:
@@ -309,7 +309,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveBinImmUnpredU(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, IntRegIndex op1, unsigned immediate)
+            RegIndex dest, RegIndex op1, unsigned immediate)
     {
         switch (size) {
           case 0:
@@ -329,8 +329,8 @@
     // SVE instructions, handling unsigned variants only.
     template <template <typename T> class Base>
     StaticInstPtr
-    decodeSveBinImmPredU(unsigned size, ExtMachInst machInst, IntRegIndex dest,
-            unsigned immediate, IntRegIndex gp)
+    decodeSveBinImmPredU(unsigned size, ExtMachInst machInst, RegIndex dest,
+            unsigned immediate, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -350,8 +350,8 @@
     // SVE instructions, handling signed variants only.
     template <template <typename T> class Base>
     StaticInstPtr
-    decodeSveBinImmPredS(unsigned size, ExtMachInst machInst, IntRegIndex dest,
-            unsigned immediate, IntRegIndex gp)
+    decodeSveBinImmPredS(unsigned size, ExtMachInst machInst, RegIndex dest,
+            unsigned immediate, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -371,8 +371,8 @@
     // SVE instructions, handling floating-point variants only.
     template <template <typename T> class Base>
     StaticInstPtr
-    decodeSveBinImmPredF(unsigned size, ExtMachInst machInst, IntRegIndex dest,
-            uint64_t immediate, IntRegIndex gp)
+    decodeSveBinImmPredF(unsigned size, ExtMachInst machInst, RegIndex dest,
+            uint64_t immediate, RegIndex gp)
     {
         switch (size) {
           case 1:
@@ -391,7 +391,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveWideImmUnpredU(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, uint64_t immediate)
+            RegIndex dest, uint64_t immediate)
     {
         switch (size) {
           case 0:
@@ -412,7 +412,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveWideImmUnpredS(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, uint64_t immediate)
+            RegIndex dest, uint64_t immediate)
     {
         switch (size) {
           case 0:
@@ -433,7 +433,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveWideImmUnpredF(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, uint64_t immediate)
+            RegIndex dest, uint64_t immediate)
     {
         switch (size) {
           case 1:
@@ -452,7 +452,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveWideImmPredU(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, uint64_t immediate, IntRegIndex gp,
+            RegIndex dest, uint64_t immediate, RegIndex gp,
             bool isMerging = true)
     {
         switch (size) {
@@ -478,7 +478,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveWideImmPredF(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, uint64_t immediate, IntRegIndex gp)
+            RegIndex dest, uint64_t immediate, RegIndex gp)
     {
         switch (size) {
           case 1:
@@ -497,7 +497,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveBinDestrPredU(unsigned size, ExtMachInst machInst,
-                           IntRegIndex dest, IntRegIndex op2, IntRegIndex gp)
+                           RegIndex dest, RegIndex op2, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -518,7 +518,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveBinDestrPredS(unsigned size, ExtMachInst machInst,
-                           IntRegIndex dest, IntRegIndex op2, IntRegIndex gp)
+                           RegIndex dest, RegIndex op2, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -539,7 +539,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveBinDestrPredF(unsigned size, ExtMachInst machInst,
-                           IntRegIndex dest, IntRegIndex op2, IntRegIndex gp)
+                           RegIndex dest, RegIndex op2, RegIndex gp)
     {
         switch (size) {
           case 1:
@@ -558,8 +558,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveBinConstrPredU(unsigned size, ExtMachInst machInst,
-                            IntRegIndex dest, IntRegIndex op1, IntRegIndex op2,
-                            IntRegIndex gp, SvePredType predType)
+                            RegIndex dest, RegIndex op1, RegIndex op2,
+                            RegIndex gp, SvePredType predType)
     {
         switch (size) {
           case 0:
@@ -579,7 +579,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveBinUnpred(unsigned size, unsigned u, ExtMachInst machInst,
-                       IntRegIndex dest, IntRegIndex op1, IntRegIndex op2)
+                       RegIndex dest, RegIndex op1, RegIndex op2)
     {
         switch (size) {
           case 0:
@@ -615,8 +615,8 @@
     // Unsigned instructions only.
     template <template <typename T> class Base>
     StaticInstPtr
-    decodeSveBinUnpredU(unsigned size, ExtMachInst machInst, IntRegIndex dest,
-            IntRegIndex op1, IntRegIndex op2)
+    decodeSveBinUnpredU(unsigned size, ExtMachInst machInst, RegIndex dest,
+            RegIndex op1, RegIndex op2)
     {
         switch (size) {
           case 0:
@@ -636,8 +636,8 @@
     // Signed instructions only.
     template <template <typename T> class Base>
     StaticInstPtr
-    decodeSveBinUnpredS(unsigned size, ExtMachInst machInst, IntRegIndex dest,
-            IntRegIndex op1, IntRegIndex op2)
+    decodeSveBinUnpredS(unsigned size, ExtMachInst machInst, RegIndex dest,
+            RegIndex op1, RegIndex op2)
     {
         switch (size) {
           case 0:
@@ -657,8 +657,8 @@
     // floating-point variants only.
     template <template <typename T> class Base>
     StaticInstPtr
-    decodeSveBinUnpredF(unsigned size, ExtMachInst machInst, IntRegIndex dest,
-            IntRegIndex op1, IntRegIndex op2)
+    decodeSveBinUnpredF(unsigned size, ExtMachInst machInst, RegIndex dest,
+            RegIndex op1, RegIndex op2)
     {
         switch (size) {
           case 1:
@@ -677,8 +677,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveCmpF(unsigned size, ExtMachInst machInst,
-                  IntRegIndex dest, IntRegIndex op1, IntRegIndex op2,
-                  IntRegIndex gp)
+                  RegIndex dest, RegIndex op1, RegIndex op2,
+                  RegIndex gp)
     {
         switch (size) {
           case 1:
@@ -698,8 +698,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveCmpImmF(unsigned size, ExtMachInst machInst,
-                     IntRegIndex dest, IntRegIndex op1, uint64_t imm,
-                     IntRegIndex gp)
+                     RegIndex dest, RegIndex op1, uint64_t imm,
+                     RegIndex gp)
     {
         switch (size) {
           case 1:
@@ -717,8 +717,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveTerPred(unsigned size, unsigned u, ExtMachInst machInst,
-                     IntRegIndex dest, IntRegIndex op1, IntRegIndex op2,
-                     IntRegIndex gp)
+                     RegIndex dest, RegIndex op1, RegIndex op2,
+                     RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -755,8 +755,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveTerPredWS(unsigned size, ExtMachInst machInst,
-                      IntRegIndex dest, IntRegIndex op1, IntRegIndex op2,
-                      IntRegIndex gp)
+                      RegIndex dest, RegIndex op1, RegIndex op2,
+                      RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -775,8 +775,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveTerPredWU(unsigned size, ExtMachInst machInst,
-                      IntRegIndex dest, IntRegIndex op1, IntRegIndex op2,
-                      IntRegIndex gp)
+                      RegIndex dest, RegIndex op1, RegIndex op2,
+                      RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -795,8 +795,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveTerPredS(unsigned size, ExtMachInst machInst,
-                      IntRegIndex dest, IntRegIndex op1, IntRegIndex op2,
-                      IntRegIndex gp)
+                      RegIndex dest, RegIndex op1, RegIndex op2,
+                      RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -817,8 +817,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveTerPredU(unsigned size, ExtMachInst machInst,
-                      IntRegIndex dest, IntRegIndex op1, IntRegIndex op2,
-                      IntRegIndex gp)
+                      RegIndex dest, RegIndex op1, RegIndex op2,
+                      RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -839,8 +839,8 @@
     template <template <typename TS, typename TD> class Base>
     StaticInstPtr
     decodeSveUnaryExtendFromBPredS(unsigned dsize, ExtMachInst machInst,
-                                   IntRegIndex dest, IntRegIndex op1,
-                                   IntRegIndex gp)
+                                   RegIndex dest, RegIndex op1,
+                                   RegIndex gp)
     {
         switch (dsize) {
           case 1:
@@ -858,8 +858,8 @@
     template <template <typename TS, typename TD> class Base>
     StaticInstPtr
     decodeSveUnaryExtendFromBPredU(unsigned dsize, ExtMachInst machInst,
-                                   IntRegIndex dest, IntRegIndex op1,
-                                   IntRegIndex gp)
+                                   RegIndex dest, RegIndex op1,
+                                   RegIndex gp)
     {
         switch (dsize) {
           case 1:
@@ -877,8 +877,8 @@
     template <template <typename TS, typename TD> class Base>
     StaticInstPtr
     decodeSveUnaryExtendFromHPredS(unsigned dsize, ExtMachInst machInst,
-                                   IntRegIndex dest, IntRegIndex op1,
-                                   IntRegIndex gp)
+                                   RegIndex dest, RegIndex op1,
+                                   RegIndex gp)
     {
         switch (dsize) {
           case 2:
@@ -894,8 +894,8 @@
     template <template <typename TS, typename TD> class Base>
     StaticInstPtr
     decodeSveUnaryExtendFromHPredU(unsigned dsize, ExtMachInst machInst,
-                                   IntRegIndex dest, IntRegIndex op1,
-                                   IntRegIndex gp)
+                                   RegIndex dest, RegIndex op1,
+                                   RegIndex gp)
     {
         switch (dsize) {
           case 2:
@@ -911,8 +911,8 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveTerPredF(unsigned size, ExtMachInst machInst,
-                      IntRegIndex dest, IntRegIndex op1, IntRegIndex op2,
-                      IntRegIndex gp)
+                      RegIndex dest, RegIndex op1, RegIndex op2,
+                      RegIndex gp)
     {
         switch (size) {
           case 1:
@@ -931,7 +931,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveTerImmUnpredF(unsigned size, ExtMachInst machInst,
-                           IntRegIndex dest, IntRegIndex op2, uint8_t imm)
+                           RegIndex dest, RegIndex op2, uint8_t imm)
     {
         switch (size) {
           case 1:
@@ -949,7 +949,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSvePtrue(unsigned size, ExtMachInst machInst,
-                   IntRegIndex dest, uint8_t imm)
+                   RegIndex dest, uint8_t imm)
     {
         switch (size) {
           case 0:
@@ -969,7 +969,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSvePredCountS(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, IntRegIndex op1)
+            RegIndex dest, RegIndex op1)
     {
         switch (size) {
             case 0:
@@ -989,7 +989,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSvePredCountU(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, IntRegIndex op1)
+            RegIndex dest, RegIndex op1)
     {
         switch (size) {
             case 0:
@@ -1009,7 +1009,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSvePredCountVS(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, IntRegIndex op1)
+            RegIndex dest, RegIndex op1)
     {
         switch (size) {
             case 1:
@@ -1027,7 +1027,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSvePredCountVU(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, IntRegIndex op1)
+            RegIndex dest, RegIndex op1)
     {
         switch (size) {
             case 1:
@@ -1046,7 +1046,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveTerImmPredU(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, IntRegIndex op1, int64_t imm, IntRegIndex gp)
+            RegIndex dest, RegIndex op1, int64_t imm, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -1067,7 +1067,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveTerImmPredS(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, IntRegIndex op1, int64_t imm, IntRegIndex gp)
+            RegIndex dest, RegIndex op1, int64_t imm, RegIndex gp)
     {
         switch (size) {
           case 0:
@@ -1088,7 +1088,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveElemIntCountS(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, uint8_t pattern, uint8_t imm4)
+            RegIndex dest, uint8_t pattern, uint8_t imm4)
     {
         switch (size) {
           case 0:
@@ -1109,7 +1109,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveElemIntCountU(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, uint8_t pattern, uint8_t imm4)
+            RegIndex dest, uint8_t pattern, uint8_t imm4)
     {
         switch (size) {
           case 0:
@@ -1130,7 +1130,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveElemIntCountLS(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, uint8_t pattern, uint8_t imm4)
+            RegIndex dest, uint8_t pattern, uint8_t imm4)
     {
         switch (size) {
           case 1:
@@ -1149,7 +1149,7 @@
     template <template <typename T> class Base>
     StaticInstPtr
     decodeSveElemIntCountLU(unsigned size, ExtMachInst machInst,
-            IntRegIndex dest, uint8_t pattern, uint8_t imm4)
+            RegIndex dest, uint8_t pattern, uint8_t imm4)
     {
         switch (size) {
           case 1:
@@ -1167,7 +1167,7 @@
     template <template <typename T1, typename T2> class Base>
     StaticInstPtr
     decodeSveUnpackS(unsigned size, ExtMachInst machInst,
-                    IntRegIndex dest, IntRegIndex op1)
+                    RegIndex dest, RegIndex op1)
     {
         switch (size) {
           case 1:
@@ -1185,7 +1185,7 @@
     template <template <typename T1, typename T2> class Base>
     StaticInstPtr
     decodeSveUnpackU(unsigned size, ExtMachInst machInst,
-                    IntRegIndex dest, IntRegIndex op1)
+                    RegIndex dest, RegIndex op1)
     {
         switch (size) {
           case 1:
diff --git a/src/arch/arm/isa/insts/sve_mem.isa b/src/arch/arm/isa/insts/sve_mem.isa
index 213f57d..8a73d13 100644
--- a/src/arch/arm/isa/insts/sve_mem.isa
+++ b/src/arch/arm/isa/insts/sve_mem.isa
@@ -41,8 +41,8 @@
     template <template <typename T1, typename T2> class Base>
     StaticInstPtr
     decodeSveContigLoadSSInsts(uint8_t dtype, ExtMachInst machInst,
-                               IntRegIndex zt, IntRegIndex pg, IntRegIndex rn,
-                               IntRegIndex rm, bool firstFaulting)
+                               RegIndex zt, RegIndex pg, RegIndex rn,
+                               RegIndex rm, bool firstFaulting)
     {
         const char* mn = firstFaulting ? "ldff1" : "ld1";
         switch (dtype) {
@@ -86,7 +86,7 @@
     template <template <typename T1, typename T2> class Base>
     StaticInstPtr
     decodeSveContigLoadSIInsts(uint8_t dtype, ExtMachInst machInst,
-                               IntRegIndex zt, IntRegIndex pg, IntRegIndex rn,
+                               RegIndex zt, RegIndex pg, RegIndex rn,
                                uint64_t imm, bool nonFaulting,
                                bool replicate = false)
     {
@@ -133,8 +133,8 @@
     template <template <typename T1, typename T2> class Base>
     StaticInstPtr
     decodeSveContigStoreSSInsts(uint8_t dtype, ExtMachInst machInst,
-                                IntRegIndex zt, IntRegIndex pg, IntRegIndex rn,
-                                IntRegIndex rm)
+                                RegIndex zt, RegIndex pg, RegIndex rn,
+                                RegIndex rm)
     {
         const char* mn = "st1";
         switch (dtype) {
@@ -166,7 +166,7 @@
     template <template <typename T1, typename T2> class Base>
     StaticInstPtr
     decodeSveContigStoreSIInsts(uint8_t dtype, ExtMachInst machInst,
-                                IntRegIndex zt, IntRegIndex pg, IntRegIndex rn,
+                                RegIndex zt, RegIndex pg, RegIndex rn,
                                 int8_t imm)
     {
         const char* mn = "st1";
@@ -205,7 +205,7 @@
     template <class etype>
     StaticInstPtr
     decodeSveStructLoadSIInstsByNReg(uint8_t esize, ExtMachInst machInst,
-            IntRegIndex zt, IntRegIndex pg, IntRegIndex xn,
+            RegIndex zt, RegIndex pg, RegIndex xn,
             int64_t imm, int numregs)
     {
         static const char* nm[5][4] = {
@@ -240,7 +240,7 @@
 
     StaticInstPtr
     decodeSveStructLoadSIInsts(uint8_t esize, ExtMachInst machInst,
-            IntRegIndex zt, IntRegIndex pg, IntRegIndex xn,
+            RegIndex zt, RegIndex pg, RegIndex xn,
             int64_t imm, int numregs)
     {
         switch (esize) {
@@ -263,7 +263,7 @@
     template <class etype>
     StaticInstPtr
     decodeSveStructStoreSIInstsByNReg(uint8_t esize, ExtMachInst machInst,
-            IntRegIndex zt, IntRegIndex pg, IntRegIndex xn,
+            RegIndex zt, RegIndex pg, RegIndex xn,
             int64_t imm, int numregs)
     {
         static const char* nm[5][4] = {
@@ -298,7 +298,7 @@
 
     StaticInstPtr
     decodeSveStructStoreSIInsts(uint8_t esize, ExtMachInst machInst,
-            IntRegIndex zt, IntRegIndex pg, IntRegIndex xn,
+            RegIndex zt, RegIndex pg, RegIndex xn,
             int64_t imm, int numregs)
     {
         switch (esize) {
@@ -321,8 +321,8 @@
     template <class etype>
     StaticInstPtr
     decodeSveStructLoadSSInstsByNReg(uint8_t esize, ExtMachInst machInst,
-            IntRegIndex zt, IntRegIndex pg, IntRegIndex xn,
-            IntRegIndex xm, int numregs)
+            RegIndex zt, RegIndex pg, RegIndex xn,
+            RegIndex xm, int numregs)
     {
         static const char* nm[5][4] = {
             { nullptr, nullptr, nullptr, nullptr},
@@ -356,8 +356,8 @@
 
     StaticInstPtr
     decodeSveStructLoadSSInsts(uint8_t esize, ExtMachInst machInst,
-            IntRegIndex zt, IntRegIndex pg, IntRegIndex xn,
-            IntRegIndex xm, int numregs)
+            RegIndex zt, RegIndex pg, RegIndex xn,
+            RegIndex xm, int numregs)
     {
         switch (esize) {
             case 0:
@@ -379,8 +379,8 @@
     template <class etype>
     StaticInstPtr
     decodeSveStructStoreSSInstsByNReg(uint8_t esize, ExtMachInst machInst,
-            IntRegIndex zt, IntRegIndex pg, IntRegIndex xn,
-            IntRegIndex xm, int numregs)
+            RegIndex zt, RegIndex pg, RegIndex xn,
+            RegIndex xm, int numregs)
     {
         static const char* nm[5][4] = {
             { nullptr, nullptr, nullptr, nullptr},
@@ -414,8 +414,8 @@
 
     StaticInstPtr
     decodeSveStructStoreSSInsts(uint8_t esize, ExtMachInst machInst,
-            IntRegIndex zt, IntRegIndex pg, IntRegIndex xn,
-            IntRegIndex xm, int numregs)
+            RegIndex zt, RegIndex pg, RegIndex xn,
+            RegIndex xm, int numregs)
     {
         switch (esize) {
             case 0:
@@ -436,7 +436,7 @@
 
     StaticInstPtr
     decodeSveGatherLoadVIInsts(uint8_t dtype, ExtMachInst machInst,
-                               IntRegIndex zt, IntRegIndex pg, IntRegIndex zn,
+                               RegIndex zt, RegIndex pg, RegIndex zn,
                                uint64_t imm, bool esizeIs32,
                                bool firstFault)
     {
@@ -526,8 +526,8 @@
 
     StaticInstPtr
     decodeSveGatherLoadSVInsts(uint8_t dtype, ExtMachInst machInst,
-                               IntRegIndex zt, IntRegIndex pg, IntRegIndex rn,
-                               IntRegIndex zm, bool esizeIs32, bool offsetIs32,
+                               RegIndex zt, RegIndex pg, RegIndex rn,
+                               RegIndex zm, bool esizeIs32, bool offsetIs32,
                                bool offsetIsSigned, bool offsetIsScaled,
                                bool firstFault)
     {
@@ -629,8 +629,8 @@
 
     StaticInstPtr
     decodeSveScatterStoreVIInsts(uint8_t msz, ExtMachInst machInst,
-                                 IntRegIndex zt, IntRegIndex pg,
-                                 IntRegIndex zn, uint64_t imm,
+                                 RegIndex zt, RegIndex pg,
+                                 RegIndex zn, uint64_t imm,
                                  bool esizeIs32)
     {
         const char* mn = "st1";
@@ -686,8 +686,8 @@
 
     StaticInstPtr
     decodeSveScatterStoreSVInsts(uint8_t msz, ExtMachInst machInst,
-                                 IntRegIndex zt, IntRegIndex pg,
-                                 IntRegIndex rn, IntRegIndex zm,
+                                 RegIndex zt, RegIndex pg,
+                                 RegIndex rn, RegIndex zm,
                                  bool esizeIs32, bool offsetIs32,
                                  bool offsetIsSigned, bool offsetIsScaled)
     {
diff --git a/src/arch/arm/isa/operands.isa b/src/arch/arm/isa/operands.isa
index 7655291..9c8deaf 100644
--- a/src/arch/arm/isa/operands.isa
+++ b/src/arch/arm/isa/operands.isa
@@ -1,5 +1,5 @@
 // -*- mode:c++ -*-
-// Copyright (c) 2010-2014, 2016-2018, 2021 ARM Limited
+// Copyright (c) 2010-2014, 2016-2018, 2021-2022 Arm Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -62,59 +62,10 @@
 }};
 
 let {{
-    maybePCRead = '''
-        ((%(reg_idx)s == PCReg) ? readPC(xc) : xc->%(func)s(this, %(op_idx)s))
-    '''
-    maybeAlignedPCRead = '''
-        ((%(reg_idx)s == PCReg) ? (roundDown(readPC(xc), 4)) :
-         xc->%(func)s(this, %(op_idx)s))
-    '''
-    maybePCWrite = '''
-        ((%(reg_idx)s == PCReg) ? setNextPC(xc, %(final_val)s) :
-         xc->%(func)s(this, %(op_idx)s, %(final_val)s))
-    '''
-    maybeIWPCWrite = '''
-        ((%(reg_idx)s == PCReg) ? setIWNextPC(xc, %(final_val)s) :
-         xc->%(func)s(this, %(op_idx)s, %(final_val)s))
-    '''
-    maybeAIWPCWrite = '''
-        if (%(reg_idx)s == PCReg) {
-            bool thumb = THUMB;
-            if (thumb) {
-                setNextPC(xc, %(final_val)s);
-            } else {
-                setIWNextPC(xc, %(final_val)s);
-            }
-        } else {
-            xc->%(func)s(this, %(op_idx)s, %(final_val)s);
-        }
-    '''
-    aarch64Read = '''
-        ((xc->%(func)s(this, %(op_idx)s)) & mask(intWidth))
-    '''
-    aarch64Write = '''
-        xc->%(func)s(this, %(op_idx)s, (%(final_val)s) & mask(intWidth))
-    '''
-    aarchX64Read = '''
-        ((xc->%(func)s(this, %(op_idx)s)) & mask(aarch64 ? 64 : 32))
-    '''
-    aarchX64Write = '''
-        xc->%(func)s(this, %(op_idx)s, (%(final_val)s) & mask(aarch64 ? 64 : 32))
-    '''
-    aarchW64Read = '''
-        ((xc->%(func)s(this, %(op_idx)s)) & mask(32))
-    '''
-    aarchW64Write = '''
-        xc->%(func)s(this, %(op_idx)s, (%(final_val)s) & mask(32))
-    '''
     cntrlNsBankedWrite = '''
         xc->setMiscReg(snsBankedIndex(dest, xc->tcBase()), %(final_val)s)
     '''
 
-    cntrlNsBankedRead = '''
-        xc->readMiscReg(snsBankedIndex(op1, xc->tcBase()))
-    '''
-
     #PCState operands need to have a sorting index (the number at the end)
     #less than all the integer registers which might update the PC. That way
     #if the flag bits of the pc state are updated and a branch happens through
@@ -126,230 +77,332 @@
     srtMode = 1
     srtEPC = 0
 
-    def vectorElem(idx, elem):
-        return ('VecElem', 'sf', (idx, elem), 'IsVectorElem', srtNormal)
+    class VectorElem(VecElemOp):
+        def __init__(self, idx):
+            flat_idx = f'((({idx}) / 4) * NumVecElemPerVecReg) + ({idx}) % 4'
+            super().__init__('sf', flat_idx, 'IsVectorElem', srtNormal)
 
-    def vectorReg(idx, base, suffix = ''):
-        elems = {
-            base + 'P0' + suffix : ('0', 'sf'),
-            base + 'P1' + suffix : ('1', 'sf'),
-            base + 'P2' + suffix : ('2', 'sf'),
-            base + 'P3' + suffix : ('3', 'sf'),
-            base + 'S' + suffix : ('0', 'sf'),
-            base + 'D' + suffix : ('0', 'df'),
-            base + 'Q' + suffix : ('0', 'tud')
-        }
-        return ('VecReg', 'vc', (idx, elems), 'IsVector', srtNormal)
+    class VectorReg(VecRegOp):
+        def __init__(self, idx, base, suffix=''):
+            elems = {
+                base + 'P0' + suffix : ('0', 'sf'),
+                base + 'P1' + suffix : ('1', 'sf'),
+                base + 'P2' + suffix : ('2', 'sf'),
+                base + 'P3' + suffix : ('3', 'sf'),
+                base + 'S' + suffix : ('0', 'sf'),
+                base + 'D' + suffix : ('0', 'df'),
+                base + 'Q' + suffix : ('0', 'tud')
+            }
+            super().__init__('vc', (idx, elems), 'IsVector', srtNormal)
 
-    def vecPredReg(idx):
-        return ('VecPredReg', 'pc', idx, None, srtNormal)
+    class VecPredReg(VecPredRegOp):
+        def __init__(self, idx):
+            super().__init__('pc', idx, sort_pri=srtNormal)
 
-    def intReg(idx):
-        return ('IntReg', 'uw', idx, 'IsInteger', srtNormal,
-                maybePCRead, maybePCWrite)
+    class IntRegNPC(IntRegOp):
+        @overrideInOperand
+        def regId(self):
+            return f'gem5::ArmISA::couldBeZero({self.reg_spec}) ? RegId() : ' \
+                   f'RegId({self.reg_class}, {self.reg_spec})'
+        def __init__(self, idx, ctype='uw', id=srtNormal):
+            super().__init__(ctype, idx, 'IsInteger', id)
 
-    def pIntReg(idx):
-        return ('IntReg', 'pint', idx, 'IsInteger', srtNormal,
-                maybePCRead, maybePCWrite)
+    class IntReg(IntRegNPC):
+        @overrideInOperand
+        def makeRead(self):
+            '''Maybe PC read'''
+            return f'{self.base_name} = ({self.reg_spec} == int_reg::Pc) ? ' \
+                   f'readPC(xc) : xc->getRegOperand(' \
+                   f'this, {self.src_reg_idx});\n'
+        @overrideInOperand
+        def makeWrite(self):
+            '''Maybe PC write'''
+            return f'''
+            if ({self.reg_spec} == int_reg::Pc)
+                setNextPC(xc, {self.base_name});
+            else
+                xc->setRegOperand(this, {self.dest_reg_idx}, {self.base_name});
+            if (traceData)
+                traceData->setData({self.base_name});
+            '''
 
-    def intReg64(idx):
-        return ('IntReg', 'ud', idx, 'IsInteger', srtNormal,
-                aarch64Read, aarch64Write)
+    class PIntReg(IntReg):
+        def __init__(self, idx):
+            super().__init__(idx, ctype='pint')
 
-    def intRegX64(idx, id = srtNormal):
-        return ('IntReg', 'ud', idx, 'IsInteger', id,
-                aarchX64Read, aarchX64Write)
+    class IntRegAPC(IntReg):
+        @overrideInOperand
+        def makeRead(self):
+            '''Maybe aligned PC read'''
+            return f'{self.base_name} = ({self.reg_spec} == int_reg::Pc) ? ' \
+                   f'(roundDown(readPC(xc), 4)) : ' \
+                   f'xc->getRegOperand(this, {self.src_reg_idx});\n'
 
-    def intRegW64(idx, id = srtNormal):
-        return ('IntReg', 'ud', idx, 'IsInteger', id,
-                aarchW64Read, aarchW64Write)
+    class IntRegIWPC(IntReg):
+        @overrideInOperand
+        def makeWrite(self):
+            '''Maybe interworking PC write'''
+            return f'''
+            if ({self.reg_spec} == int_reg::Pc)
+                setIWNextPC(xc, {self.base_name});
+            else
+                xc->setRegOperand(this, {self.dest_reg_idx}, {self.base_name});
+            if (traceData)
+                traceData->setData({self.base_name});
+            '''
 
-    def intRegNPC(idx):
-        return ('IntReg', 'uw', idx, 'IsInteger', srtNormal)
+    class IntRegAIWPC(IntReg):
+        @overrideInOperand
+        def makeWrite(self):
+            '''Maybe aligned interworking PC write'''
+            return f'''
+            if ({self.reg_spec} == int_reg::Pc) {"{"}
+                if ((bool)THUMB)
+                    setNextPC(xc, {self.base_name});
+                else
+                    setIWNextPC(xc, {self.base_name});
+            {"}"} else {"{"}
+                xc->setRegOperand(this, {self.dest_reg_idx}, {self.base_name});
+            {"}"}
+            if (traceData)
+                traceData->setData({self.base_name});
+            '''
 
-    def intRegAPC(idx, id = srtNormal):
-        return ('IntReg', 'uw', idx, 'IsInteger', id,
-                maybeAlignedPCRead, maybePCWrite)
+    class IntReg64(IntRegOp):
+        @overrideInOperand
+        def regId(self):
+            return f'gem5::ArmISA::couldBeZero({self.reg_spec}) ? RegId() : ' \
+                   f'RegId({self.reg_class}, {self.reg_spec})'
+        @overrideInOperand
+        def makeRead(self):
+            '''aarch64 read'''
+            return f'{self.base_name} = ' \
+                   f'(xc->getRegOperand(this, {self.src_reg_idx})) & ' \
+                   f'mask(intWidth);\n'
+        @overrideInOperand
+        def makeWrite(self):
+            '''aarch64 write'''
+            return f'''
+            xc->setRegOperand(this, {self.dest_reg_idx}, {self.base_name} &
+                mask(intWidth));
+            if (traceData)
+                traceData->setData({self.base_name});
+            '''
+        def __init__(self, idx, id=srtNormal):
+            super().__init__('ud', idx, 'IsInteger', id)
 
-    def intRegIWPC(idx):
-        return ('IntReg', 'uw', idx, 'IsInteger', srtNormal,
-                maybePCRead, maybeIWPCWrite)
+    class IntRegX64(IntReg64):
+        @overrideInOperand
+        def makeRead(self):
+            '''Maybe masked to 32 bit read'''
+            return f'{self.base_name} = ' \
+                   f'(xc->getRegOperand(this, {self.src_reg_idx}) & ' \
+                    'mask(aarch64 ? 64 : 32));\n'
+        @overrideInOperand
+        def makeWrite(self):
+            '''Maybe masked to 32 bit write'''
+            return f'''
+            xc->setRegOperand(this, {self.dest_reg_idx}, {self.base_name} &
+                    mask(aarch64 ? 64 : 32));
+            if (traceData)
+                traceData->setData({self.base_name});
+            '''
 
-    def intRegAIWPC(idx):
-        return ('IntReg', 'uw', idx, 'IsInteger', srtNormal,
-                maybePCRead, maybeAIWPCWrite)
+    class IntRegW64(IntReg64):
+        @overrideInOperand
+        def makeRead(self):
+            '''Masked to 32 bit read'''
+            return f'{self.base_name} = ' \
+                   f'(xc->getRegOperand(this, {self.src_reg_idx})) & ' \
+                   f'mask(32);\n'
+        @overrideInOperand
+        def makeWrite(self):
+            '''Masked to 32 bit write'''
+            return f'''
+            xc->setRegOperand(this, {self.dest_reg_idx}, {self.base_name} &
+                    mask(32));
+            if (traceData)
+                traceData->setData({self.base_name});
+            '''
 
-    def ccReg(idx):
-        return ('CCReg', 'uw', idx, None, srtNormal)
+    class CCReg(CCRegOp):
+        def __init__(self, idx):
+            super().__init__('uw', idx, sort_pri=srtNormal)
 
-    def cntrlReg(idx, id = srtNormal, type = 'uw'):
-        return ('ControlReg', type, idx, None, id)
+    class CntrlReg(ControlRegOp):
+        def __init__(self, idx, id=srtNormal, ctype='uw', flags=None):
+            super().__init__(ctype, idx, flags, id)
 
-    def cntrlNsBankedReg(idx, id = srtNormal, type = 'uw'):
-        return ('ControlReg', type, idx, (None, None, 'IsControl'), id, cntrlNsBankedRead, cntrlNsBankedWrite)
+    class CntrlReg64(CntrlReg):
+        def __init__(self, idx, id=srtNormal, ctype='ud'):
+            super().__init__(idx, id, ctype)
 
-    def cntrlNsBankedReg64(idx, id = srtNormal, type = 'ud'):
-        return ('ControlReg', type, idx, (None, None, 'IsControl'), id, cntrlNsBankedRead, cntrlNsBankedWrite)
+    class CntrlNsBankedReg(CntrlReg):
+        @overrideInOperand
+        def makeRead(self):
+            return f'{self.base_name} = ' \
+                   f'xc->readMiscReg(snsBankedIndex(op1, xc->tcBase()));\n'
+        @overrideInOperand
+        def makeWrite(self):
+            return f'''
+            xc->setMiscReg(snsBankedIndex(dest, xc->tcBase()),
+                           {self.base_name});
+            if (traceData)
+                traceData->setData({self.base_name});
+            '''
+        def __init__(self, idx, id=srtNormal, ctype='uw'):
+            super().__init__(idx, id, ctype, (None, None, 'IsControl'))
 
-    def cntrlRegNC(idx, id = srtNormal, type = 'uw'):
-        return ('ControlReg', type, idx, None, id)
+    class CntrlNsBankedReg64(CntrlNsBankedReg):
+        def __init__(self, idx, id=srtNormal, ctype='ud'):
+            super().__init__(idx, id, ctype)
 
-    def pcStateReg(idx, id):
-        return ('PCState', 'ud', idx, (None, None, 'IsControl'), id)
+    class CntrlRegNC(CntrlReg):
+        pass
+
+    class PCStateReg(PCStateOp):
+        def __init__(self, idx, id):
+            super().__init__('ud', idx, (None, None, 'IsControl'), id)
 }};
 
 def operands {{
     #Abstracted integer reg operands
-    'Dest': intReg('dest'),
-    'Dest64': intReg64('dest'),
-    'XDest': intRegX64('dest'),
-    'WDest': intRegW64('dest'),
-    'IWDest': intRegIWPC('dest'),
-    'AIWDest': intRegAIWPC('dest'),
-    'Dest2': intReg('dest2'),
-    'XDest2': intRegX64('dest2'),
-    'IWDest2': intRegIWPC('dest2'),
-    'Result': intReg('result'),
-    'XResult': intRegX64('result'),
-    'XResult2': intRegX64('result2'),
-    'XBase': intRegX64('base', id = srtBase),
-    'Base': intRegAPC('base', id = srtBase),
-    'XOffset': intRegX64('offset'),
-    'Index': intReg('index'),
-    'Shift': intReg('shift'),
-    'Op1': intReg('op1'),
-    'Op2': intReg('op2'),
-    'Op3': intReg('op3'),
-    'Op164': intReg64('op1'),
-    'Op264': intReg64('op2'),
-    'Op364': intReg64('op3'),
-    'XOp1': intRegX64('op1'),
-    'XOp2': intRegX64('op2'),
-    'XOp3': intRegX64('op3'),
-    'WOp1': intRegW64('op1'),
-    'WOp2': intRegW64('op2'),
-    'WOp3': intRegW64('op3'),
-    'Reg0': intReg('reg0'),
-    'Reg1': intReg('reg1'),
-    'Reg2': intReg('reg2'),
-    'Reg3': intReg('reg3'),
-    'PInt0': pIntReg('reg0'),
-    'PInt1': pIntReg('reg1'),
-    'PInt2': pIntReg('reg2'),
-    'PInt3': pIntReg('reg3'),
+    'Dest': IntReg('dest'),
+    'Dest64': IntReg64('dest'),
+    'XDest': IntRegX64('dest'),
+    'WDest': IntRegW64('dest'),
+    'IWDest': IntRegIWPC('dest'),
+    'AIWDest': IntRegAIWPC('dest'),
+    'Dest2': IntReg('dest2'),
+    'XDest2': IntRegX64('dest2'),
+    'IWDest2': IntRegIWPC('dest2'),
+    'Result': IntReg('result'),
+    'XResult': IntRegX64('result'),
+    'XResult2': IntRegX64('result2'),
+    'XBase': IntRegX64('base', id=srtBase),
+    'Base': IntRegAPC('base', id=srtBase),
+    'XOffset': IntRegX64('offset'),
+    'Index': IntReg('index'),
+    'Shift': IntReg('shift'),
+    'Op1': IntReg('op1'),
+    'Op2': IntReg('op2'),
+    'Op3': IntReg('op3'),
+    'Op164': IntReg64('op1'),
+    'Op264': IntReg64('op2'),
+    'Op364': IntReg64('op3'),
+    'XOp1': IntRegX64('op1'),
+    'XOp2': IntRegX64('op2'),
+    'XOp3': IntRegX64('op3'),
+    'WOp1': IntRegW64('op1'),
+    'WOp2': IntRegW64('op2'),
+    'WOp3': IntRegW64('op3'),
+    'Reg0': IntReg('reg0'),
+    'Reg1': IntReg('reg1'),
+    'Reg2': IntReg('reg2'),
+    'Reg3': IntReg('reg3'),
+    'PInt0': PIntReg('reg0'),
+    'PInt1': PIntReg('reg1'),
+    'PInt2': PIntReg('reg2'),
+    'PInt3': PIntReg('reg3'),
 
     #Fixed index integer reg operands
-    'SpMode': intRegNPC('intRegInMode((OperatingMode)regMode, INTREG_SP)'),
-    'DecodedBankedIntReg': intRegNPC('decodeMrsMsrBankedIntRegIndex(byteMask, r)'),
-    'LR': intRegNPC('INTREG_LR'),
-    'XLR': intRegX64('INTREG_X30'),
-    'R7': intRegNPC('7'),
+    'SpMode': IntRegNPC('int_reg::regInMode((OperatingMode)regMode, '
+                        'int_reg::Sp)'),
+    'DecodedBankedIntReg':
+        IntRegNPC('decodeMrsMsrBankedIntRegIndex(byteMask, r)'),
+    'LR': IntRegNPC('int_reg::Lr'),
+    'XLR': IntRegX64('int_reg::X30'),
+    'R7': IntRegNPC('7'),
     # First four arguments are passed in registers
-    'R0': intRegNPC('0'),
-    'R1': intRegNPC('1'),
-    'R2': intRegNPC('2'),
-    'R3': intRegNPC('3'),
-    'R4': intRegNPC('4'),
-    'R5': intRegNPC('5'),
-    'X0': intRegX64('0'),
-    'X1': intRegX64('1'),
-    'X2': intRegX64('2'),
-    'X3': intRegX64('3'),
-    'X4': intRegX64('4'),
-    'X5': intRegX64('5'),
+    'R0': IntRegNPC('0'),
+    'R1': IntRegNPC('1'),
+    'R2': IntRegNPC('2'),
+    'R3': IntRegNPC('3'),
+    'R4': IntRegNPC('4'),
+    'R5': IntRegNPC('5'),
+    'X0': IntRegX64('0'),
+    'X1': IntRegX64('1'),
+    'X2': IntRegX64('2'),
+    'X3': IntRegX64('3'),
+    'X4': IntRegX64('4'),
+    'X5': IntRegX64('5'),
 
     # Condition code registers
-    'CondCodesNZ': ccReg('CCREG_NZ'),
-    'CondCodesC': ccReg('CCREG_C'),
-    'CondCodesV': ccReg('CCREG_V'),
-    'CondCodesGE': ccReg('CCREG_GE'),
-    'OptCondCodesNZ': ccReg(
+    'CondCodesNZ': CCReg('cc_reg::Nz'),
+    'CondCodesC': CCReg('cc_reg::C'),
+    'CondCodesV': CCReg('cc_reg::V'),
+    'CondCodesGE': CCReg('cc_reg::Ge'),
+    'OptCondCodesNZ': CCReg(
             '''((condCode == COND_AL || condCode == COND_UC ||
                  condCode == COND_CC || condCode == COND_CS ||
                  condCode == COND_VS || condCode == COND_VC) ?
-                CCREG_ZERO : CCREG_NZ)'''),
-    'OptCondCodesC': ccReg(
+                cc_reg::Zero : cc_reg::Nz)'''),
+    'OptCondCodesC': CCReg(
              '''((condCode == COND_HI || condCode == COND_LS ||
                 condCode == COND_CS || condCode == COND_CC) ?
-               CCREG_C : CCREG_ZERO)'''),
-    'OptShiftRmCondCodesC': ccReg(
+               cc_reg::C : cc_reg::Zero)'''),
+    'OptShiftRmCondCodesC': CCReg(
             '''((condCode == COND_HI || condCode == COND_LS ||
                  condCode == COND_CS || condCode == COND_CC ||
                  shiftType == ROR) ?
-                CCREG_C : CCREG_ZERO)'''),
-    'OptCondCodesV': ccReg(
+                cc_reg::C : cc_reg::Zero)'''),
+    'OptCondCodesV': CCReg(
             '''((condCode == COND_VS || condCode == COND_VC ||
                  condCode == COND_GE || condCode == COND_LT ||
                  condCode == COND_GT || condCode == COND_LE) ?
-                CCREG_V : CCREG_ZERO)'''),
-    'FpCondCodes': ccReg('CCREG_FP'),
+                cc_reg::V : cc_reg::Zero)'''),
+    'FpCondCodes': CCReg('cc_reg::Fp'),
 
     #Abstracted floating point reg operands
-    'FpDest': vectorElem('dest / 4', 'dest % 4'),
-    'FpDestP0': vectorElem('(dest + 0) / 4', '(dest + 0) % 4'),
-    'FpDestP1': vectorElem('(dest + 1) / 4', '(dest + 1) % 4'),
-    'FpDestP2': vectorElem('(dest + 2) / 4', '(dest + 2) % 4'),
-    'FpDestP3': vectorElem('(dest + 3) / 4', '(dest + 3) % 4'),
-    'FpDestP4': vectorElem('(dest + 4) / 4', '(dest + 4) % 4'),
-    'FpDestP5': vectorElem('(dest + 5) / 4', '(dest + 5) % 4'),
-    'FpDestP6': vectorElem('(dest + 6) / 4', '(dest + 6) % 4'),
-    'FpDestP7': vectorElem('(dest + 7) / 4', '(dest + 7) % 4'),
+    'FpDest': VectorElem('dest'),
+    'FpDestP0': VectorElem('dest + 0'),
+    'FpDestP1': VectorElem('dest + 1'),
+    'FpDestP2': VectorElem('dest + 2'),
+    'FpDestP3': VectorElem('dest + 3'),
+    'FpDestP4': VectorElem('dest + 4'),
+    'FpDestP5': VectorElem('dest + 5'),
+    'FpDestP6': VectorElem('dest + 6'),
+    'FpDestP7': VectorElem('dest + 7'),
 
-    'FpDestS0P0': vectorElem(
-        '(dest + step * 0 + 0) / 4', '(dest + step * 0 + 0) % 4'),
-    'FpDestS0P1': vectorElem(
-        '(dest + step * 0 + 1) / 4', '(dest + step * 0 + 1) % 4'),
-    'FpDestS1P0': vectorElem(
-        '(dest + step * 1 + 0) / 4', '(dest + step * 1 + 0) % 4'),
-    'FpDestS1P1': vectorElem(
-        '(dest + step * 1 + 1) / 4', '(dest + step * 1 + 1) % 4'),
-    'FpDestS2P0': vectorElem(
-        '(dest + step * 2 + 0) / 4', '(dest + step * 2 + 0) % 4'),
-    'FpDestS2P1': vectorElem(
-        '(dest + step * 2 + 1) / 4', '(dest + step * 2 + 1) % 4'),
-    'FpDestS3P0': vectorElem(
-        '(dest + step * 3 + 0) / 4', '(dest + step * 3 + 0) % 4'),
-    'FpDestS3P1': vectorElem(
-        '(dest + step * 3 + 1) / 4', '(dest + step * 3 + 1) % 4'),
+    'FpDestS0P0': VectorElem('dest + step * 0 + 0'),
+    'FpDestS0P1': VectorElem('dest + step * 0 + 1'),
+    'FpDestS1P0': VectorElem('dest + step * 1 + 0'),
+    'FpDestS1P1': VectorElem('dest + step * 1 + 1'),
+    'FpDestS2P0': VectorElem('dest + step * 2 + 0'),
+    'FpDestS2P1': VectorElem('dest + step * 2 + 1'),
+    'FpDestS3P0': VectorElem('dest + step * 3 + 0'),
+    'FpDestS3P1': VectorElem('dest + step * 3 + 1'),
 
-    'FpDest2': vectorElem('dest2 / 4', 'dest2 % 4'),
-    'FpDest2P0': vectorElem('(dest2 + 0) / 4', '(dest2 + 0) % 4'),
-    'FpDest2P1': vectorElem('(dest2 + 1) / 4', '(dest2 + 1) % 4'),
-    'FpDest2P2': vectorElem('(dest2 + 2) / 4', '(dest2 + 2) % 4'),
-    'FpDest2P3': vectorElem('(dest2 + 3) / 4', '(dest2 + 3) % 4'),
+    'FpDest2': VectorElem('dest2'),
+    'FpDest2P0': VectorElem('dest2 + 0'),
+    'FpDest2P1': VectorElem('dest2 + 1'),
+    'FpDest2P2': VectorElem('dest2 + 2'),
+    'FpDest2P3': VectorElem('dest2 + 3'),
 
-    'FpOp1': vectorElem('op1 / 4', 'op1 % 4'),
-    'FpOp1P0': vectorElem('(op1 + 0) / 4', '(op1 + 0) % 4'),
-    'FpOp1P1': vectorElem('(op1 + 1) / 4', '(op1 + 1) % 4'),
-    'FpOp1P2': vectorElem('(op1 + 2) / 4', '(op1 + 2) % 4'),
-    'FpOp1P3': vectorElem('(op1 + 3) / 4', '(op1 + 3) % 4'),
-    'FpOp1P4': vectorElem('(op1 + 4) / 4', '(op1 + 4) % 4'),
-    'FpOp1P5': vectorElem('(op1 + 5) / 4', '(op1 + 5) % 4'),
-    'FpOp1P6': vectorElem('(op1 + 6) / 4', '(op1 + 6) % 4'),
-    'FpOp1P7': vectorElem('(op1 + 7) / 4', '(op1 + 7) % 4'),
+    'FpOp1': VectorElem('op1'),
+    'FpOp1P0': VectorElem('op1 + 0'),
+    'FpOp1P1': VectorElem('op1 + 1'),
+    'FpOp1P2': VectorElem('op1 + 2'),
+    'FpOp1P3': VectorElem('op1 + 3'),
+    'FpOp1P4': VectorElem('op1 + 4'),
+    'FpOp1P5': VectorElem('op1 + 5'),
+    'FpOp1P6': VectorElem('op1 + 6'),
+    'FpOp1P7': VectorElem('op1 + 7'),
 
-    'FpOp1S0P0': vectorElem(
-        '(op1 + step * 0 + 0) / 4', '(op1 + step * 0 + 0) % 4'),
-    'FpOp1S0P1': vectorElem(
-        '(op1 + step * 0 + 1) / 4', '(op1 + step * 0 + 1) % 4'),
-    'FpOp1S1P0': vectorElem(
-        '(op1 + step * 1 + 0) / 4', '(op1 + step * 1 + 0) % 4'),
-    'FpOp1S1P1': vectorElem(
-        '(op1 + step * 1 + 1) / 4', '(op1 + step * 1 + 1) % 4'),
-    'FpOp1S2P0': vectorElem(
-        '(op1 + step * 2 + 0) / 4', '(op1 + step * 2 + 0) % 4'),
-    'FpOp1S2P1': vectorElem(
-        '(op1 + step * 2 + 1) / 4', '(op1 + step * 2 + 1) % 4'),
-    'FpOp1S3P0': vectorElem(
-        '(op1 + step * 3 + 0) / 4', '(op1 + step * 3 + 0) % 4'),
-    'FpOp1S3P1': vectorElem(
-        '(op1 + step * 3 + 1) / 4', '(op1 + step * 3 + 1) % 4'),
+    'FpOp1S0P0': VectorElem('op1 + step * 0 + 0'),
+    'FpOp1S0P1': VectorElem('op1 + step * 0 + 1'),
+    'FpOp1S1P0': VectorElem('op1 + step * 1 + 0'),
+    'FpOp1S1P1': VectorElem('op1 + step * 1 + 1'),
+    'FpOp1S2P0': VectorElem('op1 + step * 2 + 0'),
+    'FpOp1S2P1': VectorElem('op1 + step * 2 + 1'),
+    'FpOp1S3P0': VectorElem('op1 + step * 3 + 0'),
+    'FpOp1S3P1': VectorElem('op1 + step * 3 + 1'),
 
-    'FpOp2': vectorElem('op2 / 4', 'op2 % 4'),
-    'FpOp2P0': vectorElem('(op2 + 0) / 4', '(op2 + 0) % 4'),
-    'FpOp2P1': vectorElem('(op2 + 1) / 4', '(op2 + 1) % 4'),
-    'FpOp2P2': vectorElem('(op2 + 2) / 4', '(op2 + 2) % 4'),
-    'FpOp2P3': vectorElem('(op2 + 3) / 4', '(op2 + 3) % 4'),
+    'FpOp2': VectorElem('op2'),
+    'FpOp2P0': VectorElem('op2 + 0'),
+    'FpOp2P1': VectorElem('op2 + 1'),
+    'FpOp2P2': VectorElem('op2 + 2'),
+    'FpOp2P3': VectorElem('op2 + 3'),
 
     # Create AArch64 unpacked view of the FP registers
     # Name   ::= 'AA64Vec' OpSpec [LaneSpec]
@@ -363,111 +416,112 @@
     # All the constituents are hierarchically defined as part of the Vector
     # Register they belong to
 
-    'AA64FpOp1': vectorReg('op1', 'AA64FpOp1'),
-    'AA64FpOp2': vectorReg('op2', 'AA64FpOp2'),
-    'AA64FpOp3': vectorReg('op3', 'AA64FpOp3'),
-    'AA64FpDest': vectorReg('dest', 'AA64FpDest'),
-    'AA64FpDest2': vectorReg('dest2', 'AA64FpDest2'),
-    'AA64FpOp1V0': vectorReg('op1', 'AA64FpOp1', 'V0'),
-    'AA64FpOp1V1': vectorReg('op1 + 1', 'AA64FpOp1', 'V1'),
-    'AA64FpOp1V2': vectorReg('op1 + 2', 'AA64FpOp1', 'V2'),
-    'AA64FpOp1V3': vectorReg('op1 + 3', 'AA64FpOp1', 'V3'),
-    'AA64FpOp1V0S': vectorReg('(op1 + 0) % 32', 'AA64FpOp1', 'V0S'),
-    'AA64FpOp1V1S': vectorReg('(op1 + 1) % 32', 'AA64FpOp1', 'V1S'),
-    'AA64FpOp1V2S': vectorReg('(op1 + 2) % 32', 'AA64FpOp1', 'V2S'),
-    'AA64FpOp1V3S': vectorReg('(op1 + 3) % 32', 'AA64FpOp1', 'V3S'),
-    'AA64FpDestV0': vectorReg('(dest + 0)', 'AA64FpDest', 'V0'),
-    'AA64FpDestV1': vectorReg('(dest + 1)', 'AA64FpDest', 'V1'),
-    'AA64FpDestV0L': vectorReg('(dest + 0) % 32', 'AA64FpDest', 'V0L'),
-    'AA64FpDestV1L': vectorReg('(dest + 1) % 32', 'AA64FpDest', 'V1L'),
+    'AA64FpOp1': VectorReg('op1', 'AA64FpOp1'),
+    'AA64FpOp2': VectorReg('op2', 'AA64FpOp2'),
+    'AA64FpOp3': VectorReg('op3', 'AA64FpOp3'),
+    'AA64FpDest': VectorReg('dest', 'AA64FpDest'),
+    'AA64FpDest2': VectorReg('dest2', 'AA64FpDest2'),
+    'AA64FpOp1V0': VectorReg('op1', 'AA64FpOp1', 'V0'),
+    'AA64FpOp1V1': VectorReg('op1 + 1', 'AA64FpOp1', 'V1'),
+    'AA64FpOp1V2': VectorReg('op1 + 2', 'AA64FpOp1', 'V2'),
+    'AA64FpOp1V3': VectorReg('op1 + 3', 'AA64FpOp1', 'V3'),
+    'AA64FpOp1V0S': VectorReg('(op1 + 0) % 32', 'AA64FpOp1', 'V0S'),
+    'AA64FpOp1V1S': VectorReg('(op1 + 1) % 32', 'AA64FpOp1', 'V1S'),
+    'AA64FpOp1V2S': VectorReg('(op1 + 2) % 32', 'AA64FpOp1', 'V2S'),
+    'AA64FpOp1V3S': VectorReg('(op1 + 3) % 32', 'AA64FpOp1', 'V3S'),
+    'AA64FpDestV0': VectorReg('(dest + 0)', 'AA64FpDest', 'V0'),
+    'AA64FpDestV1': VectorReg('(dest + 1)', 'AA64FpDest', 'V1'),
+    'AA64FpDestV0L': VectorReg('(dest + 0) % 32', 'AA64FpDest', 'V0L'),
+    'AA64FpDestV1L': VectorReg('(dest + 1) % 32', 'AA64FpDest', 'V1L'),
 
     # Temporary registers for SVE interleaving
-    'AA64IntrlvReg0': vectorReg('INTRLVREG0', 'AA64FpIntrlvReg0'),
-    'AA64IntrlvReg1': vectorReg('INTRLVREG1', 'AA64FpIntrlvReg1'),
-    'AA64IntrlvReg2': vectorReg('INTRLVREG2', 'AA64FpIntrlvReg2'),
-    'AA64IntrlvReg3': vectorReg('INTRLVREG3', 'AA64FpIntrlvReg3'),
-    'AA64FpDestMerge': vectorReg('dest', 'AA64FpDestMerge'),
-    'AA64FpBase': vectorReg('base', 'AA64FpBase'),
-    'AA64FpOffset': vectorReg('offset', 'AA64FpOffset'),
-    'AA64FpUreg0': vectorReg('VECREG_UREG0', 'AA64FpUreg0'),
+    'AA64IntrlvReg0': VectorReg('INTRLVREG0', 'AA64FpIntrlvReg0'),
+    'AA64IntrlvReg1': VectorReg('INTRLVREG1', 'AA64FpIntrlvReg1'),
+    'AA64IntrlvReg2': VectorReg('INTRLVREG2', 'AA64FpIntrlvReg2'),
+    'AA64IntrlvReg3': VectorReg('INTRLVREG3', 'AA64FpIntrlvReg3'),
+    'AA64FpDestMerge': VectorReg('dest', 'AA64FpDestMerge'),
+    'AA64FpBase': VectorReg('base', 'AA64FpBase'),
+    'AA64FpOffset': VectorReg('offset', 'AA64FpOffset'),
+    'AA64FpUreg0': VectorReg('VECREG_UREG0', 'AA64FpUreg0'),
 
     # Predicate register operands
-    'GpOp': vecPredReg('gp'),
-    'POp1': vecPredReg('op1'),
-    'POp2': vecPredReg('op2'),
-    'PDest': vecPredReg('dest'),
-    'PDestMerge': vecPredReg('dest'),
-    'Ffr': vecPredReg('PREDREG_FFR'),
-    'FfrAux': vecPredReg('PREDREG_FFR'),
-    'PUreg0': vecPredReg('PREDREG_UREG0'),
+    'GpOp': VecPredReg('gp'),
+    'POp1': VecPredReg('op1'),
+    'POp2': VecPredReg('op2'),
+    'PDest': VecPredReg('dest'),
+    'PDestMerge': VecPredReg('dest'),
+    'Ffr': VecPredReg('PREDREG_FFR'),
+    'FfrAux': VecPredReg('PREDREG_FFR'),
+    'PUreg0': VecPredReg('PREDREG_UREG0'),
 
     #Abstracted control reg operands
-    'MiscDest': cntrlReg('dest'),
-    'MiscOp1': cntrlReg('op1'),
-    'MiscNsBankedDest': cntrlNsBankedReg('dest'),
-    'MiscNsBankedOp1': cntrlNsBankedReg('op1'),
-    'MiscNsBankedDest64': cntrlNsBankedReg64('dest'),
-    'MiscNsBankedOp164': cntrlNsBankedReg64('op1'),
+    'MiscDest': CntrlReg('dest'),
+    'MiscOp1': CntrlReg('op1'),
+    'MiscNsBankedDest': CntrlNsBankedReg('dest'),
+    'MiscNsBankedOp1': CntrlNsBankedReg('op1'),
+    'MiscNsBankedDest64': CntrlNsBankedReg64('dest'),
+    'MiscNsBankedOp164': CntrlNsBankedReg64('op1'),
 
     #Fixed index control regs
-    'Cpsr': cntrlReg('MISCREG_CPSR', srtCpsr),
-    'CpsrQ': cntrlReg('MISCREG_CPSR_Q', srtCpsr),
-    'Spsr': cntrlRegNC('MISCREG_SPSR'),
-    'Fpsr': cntrlRegNC('MISCREG_FPSR'),
-    'Fpsid': cntrlRegNC('MISCREG_FPSID'),
-    'Fpscr': cntrlRegNC('MISCREG_FPSCR'),
-    'FpscrQc': cntrlRegNC('MISCREG_FPSCR_QC'),
-    'FpscrExc': cntrlRegNC('MISCREG_FPSCR_EXC'),
-    'Cpacr': cntrlReg('MISCREG_CPACR'),
-    'Cpacr64': cntrlReg('MISCREG_CPACR_EL1'),
-    'Fpexc': cntrlRegNC('MISCREG_FPEXC'),
-    'Nsacr': cntrlReg('MISCREG_NSACR'),
-    'ElrHyp': cntrlRegNC('MISCREG_ELR_HYP'),
-    'Hcr': cntrlReg('MISCREG_HCR'),
-    'Hcr64': cntrlReg('MISCREG_HCR_EL2'),
-    'CptrEl264': cntrlReg('MISCREG_CPTR_EL2'),
-    'CptrEl364': cntrlReg('MISCREG_CPTR_EL3'),
-    'Hstr': cntrlReg('MISCREG_HSTR'),
-    'Scr': cntrlReg('MISCREG_SCR'),
-    'Scr64': cntrlReg('MISCREG_SCR_EL3'),
-    'Sctlr': cntrlRegNC('MISCREG_SCTLR'),
-    'SevMailbox': cntrlRegNC('MISCREG_SEV_MAILBOX'),
-    'LLSCLock': cntrlRegNC('MISCREG_LOCKFLAG'),
-    'Dczid' : cntrlRegNC('MISCREG_DCZID_EL0'),
+    'Cpsr': CntrlReg('MISCREG_CPSR', srtCpsr),
+    'CpsrQ': CntrlReg('MISCREG_CPSR_Q', srtCpsr),
+    'Spsr': CntrlRegNC('MISCREG_SPSR'),
+    'Fpsr': CntrlRegNC('MISCREG_FPSR'),
+    'Fpsid': CntrlRegNC('MISCREG_FPSID'),
+    'Fpscr': CntrlRegNC('MISCREG_FPSCR'),
+    'FpscrQc': CntrlRegNC('MISCREG_FPSCR_QC'),
+    'FpscrExc': CntrlRegNC('MISCREG_FPSCR_EXC'),
+    'Cpacr': CntrlReg('MISCREG_CPACR'),
+    'Cpacr64': CntrlReg64('MISCREG_CPACR_EL1'),
+    'Fpexc': CntrlRegNC('MISCREG_FPEXC'),
+    'Nsacr': CntrlReg('MISCREG_NSACR'),
+    'ElrHyp': CntrlRegNC('MISCREG_ELR_HYP'),
+    'Hcr': CntrlReg('MISCREG_HCR'),
+    'Hcr64': CntrlReg64('MISCREG_HCR_EL2'),
+    'CptrEl264': CntrlReg64('MISCREG_CPTR_EL2'),
+    'CptrEl364': CntrlReg64('MISCREG_CPTR_EL3'),
+    'Hstr': CntrlReg('MISCREG_HSTR'),
+    'Scr': CntrlReg('MISCREG_SCR'),
+    'Scr64': CntrlReg64('MISCREG_SCR_EL3'),
+    'Sctlr': CntrlRegNC('MISCREG_SCTLR'),
+    'SevMailbox': CntrlRegNC('MISCREG_SEV_MAILBOX'),
+    'LLSCLock': CntrlRegNC('MISCREG_LOCKFLAG'),
+    'Dczid' : CntrlRegNC('MISCREG_DCZID_EL0'),
+    'PendingDvm': CntrlRegNC('MISCREG_TLBINEEDSYNC'),
 
     #Register fields for microops
-    'URa' : intReg('ura'),
-    'XURa' : intRegX64('ura'),
-    'WURa' : intRegW64('ura'),
-    'IWRa' : intRegIWPC('ura'),
-    'Fa' : vectorElem('ura / 4', 'ura % 4'),
-    'URb' : intReg('urb'),
-    'XURb' : intRegX64('urb'),
-    'URc' : intReg('urc'),
-    'XURc' : intRegX64('urc'),
+    'URa' : IntReg('ura'),
+    'XURa' : IntRegX64('ura'),
+    'WURa' : IntRegW64('ura'),
+    'IWRa' : IntRegIWPC('ura'),
+    'Fa' : VectorElem('ura'),
+    'URb' : IntReg('urb'),
+    'XURb' : IntRegX64('urb'),
+    'URc' : IntReg('urc'),
+    'XURc' : IntRegX64('urc'),
 
     #Memory Operand
-    'Mem': ('Mem', 'uw', None, (None, 'IsLoad', 'IsStore'), srtNormal),
+    'Mem': MemOp('uw', None, (None, 'IsLoad', 'IsStore'), srtNormal),
 
     #PCState fields
-    'RawPC': pcStateReg('pc', srtPC),
-    'PC': pcStateReg('instPC', srtPC),
-    'NPC': pcStateReg('instNPC', srtPC),
-    'pNPC': pcStateReg('instNPC', srtEPC),
-    'IWNPC': pcStateReg('instIWNPC', srtPC),
-    'Thumb': pcStateReg('thumb', srtPC),
-    'NextThumb': pcStateReg('nextThumb', srtMode),
-    'NextJazelle': pcStateReg('nextJazelle', srtMode),
-    'NextItState': pcStateReg('nextItstate', srtMode),
-    'Itstate': pcStateReg('itstate', srtMode),
-    'NextAArch64': pcStateReg('nextAArch64', srtMode),
+    'RawPC': PCStateReg('pc', srtPC),
+    'PC': PCStateReg('instPC', srtPC),
+    'NPC': PCStateReg('instNPC', srtPC),
+    'pNPC': PCStateReg('instNPC', srtEPC),
+    'IWNPC': PCStateReg('instIWNPC', srtPC),
+    'Thumb': PCStateReg('thumb', srtPC),
+    'NextThumb': PCStateReg('nextThumb', srtMode),
+    'NextJazelle': PCStateReg('nextJazelle', srtMode),
+    'NextItState': PCStateReg('nextItstate', srtMode),
+    'Itstate': PCStateReg('itstate', srtMode),
+    'NextAArch64': PCStateReg('nextAArch64', srtMode),
 
     #Register operands depending on a field in the instruction encoding. These
     #should be avoided since they may not be portable across different
     #encodings of the same instruction.
-    'Rd': intReg('RD'),
-    'Rm': intReg('RM'),
-    'Rs': intReg('RS'),
-    'Rn': intReg('RN'),
-    'Rt': intReg('RT')
+    'Rd': IntReg('RD'),
+    'Rm': IntReg('RM'),
+    'Rs': IntReg('RS'),
+    'Rn': IntReg('RN'),
+    'Rt': IntReg('RT')
 }};
diff --git a/src/arch/arm/isa/templates/branch.isa b/src/arch/arm/isa/templates/branch.isa
index b42a00f..b886a97 100644
--- a/src/arch/arm/isa/templates/branch.isa
+++ b/src/arch/arm/isa/templates/branch.isa
@@ -80,14 +80,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1,
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1,
                    ConditionCode _condCode);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template BranchRegCondConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _op1,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _op1,
                                    ConditionCode _condCode) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _op1, _condCode)
     {
@@ -114,7 +114,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1, IntRegIndex _op2);
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1, RegIndex _op2);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
     Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
     Fault completeAcc(PacketPtr, ExecContext *,
@@ -124,7 +124,7 @@
 
 def template BranchRegRegConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                   IntRegIndex _op1, IntRegIndex _op2) :
+                                   RegIndex _op1, RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _op1, _op2)
     {
         %(set_reg_idx_arr)s;
@@ -148,7 +148,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, int32_t imm, IntRegIndex _op1);
+    %(class_name)s(ExtMachInst machInst, int32_t imm, RegIndex _op1);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
     std::unique_ptr<PCStateBase> branchTarget(
             const PCStateBase &branch_pc) const override;
@@ -162,7 +162,7 @@
 // a register value even though the instruction is always unconditional.
 def template BranchImmRegConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst, int32_t _imm,
-                                   IntRegIndex _op1) :
+                                   RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _imm, _op1)
     {
         %(set_reg_idx_arr)s;
diff --git a/src/arch/arm/isa/templates/branch64.isa b/src/arch/arm/isa/templates/branch64.isa
index 5b38675..b3914e0 100644
--- a/src/arch/arm/isa/templates/branch64.isa
+++ b/src/arch/arm/isa/templates/branch64.isa
@@ -89,13 +89,13 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1);
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template BranchReg64Constructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _op1) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _op1)
     {
         %(set_reg_idx_arr)s;
@@ -111,14 +111,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1, IntRegIndex _op2);
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1, RegIndex _op2);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template BranchRegReg64Constructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _op1,
-                                   IntRegIndex _op2) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _op1,
+                                   RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _op1, _op2)
     {
         %(set_reg_idx_arr)s;
@@ -134,14 +134,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, int64_t imm, IntRegIndex _op1);
+    %(class_name)s(ExtMachInst machInst, int64_t imm, RegIndex _op1);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template BranchImmReg64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst, int64_t _imm,
-                                   IntRegIndex _op1) :
+                                   RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _imm, _op1)
     {
         %(set_reg_idx_arr)s;
@@ -158,7 +158,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst, int64_t _imm1, int64_t _imm2,
-                   IntRegIndex _op1);
+                   RegIndex _op1);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
@@ -166,7 +166,7 @@
 def template BranchImmImmReg64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
                                    int64_t _imm1, int64_t _imm2,
-                                   IntRegIndex _op1) :
+                                   RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _imm1, _imm2, _op1)
     {
diff --git a/src/arch/arm/isa/templates/data64.isa b/src/arch/arm/isa/templates/data64.isa
index 2b1e8c4..69c4b64 100644
--- a/src/arch/arm/isa/templates/data64.isa
+++ b/src/arch/arm/isa/templates/data64.isa
@@ -43,15 +43,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, uint64_t _imm);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, uint64_t _imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataXImmConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                   IntRegIndex _dest, IntRegIndex _op1,
+                                   RegIndex _dest, RegIndex _op1,
                                    uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm)
@@ -69,16 +69,16 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2,
                    int32_t _shiftAmt, ArmShiftType _shiftType);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataXSRegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
                                    int32_t _shiftAmt,
                                    ArmShiftType _shiftType) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
@@ -97,16 +97,16 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-            IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+            RegIndex _op1, RegIndex _op2,
             ArmExtendType _extendType, int32_t _shiftAmt);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataXERegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
                                    ArmExtendType _extendType,
                                    int32_t _shiftAmt) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
@@ -125,14 +125,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataX1RegConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                   IntRegIndex _dest, IntRegIndex _op1) :
+                                   RegIndex _dest, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _op1)
     {
         %(set_reg_idx_arr)s;
@@ -148,15 +148,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataX2RegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2)
     {
@@ -173,15 +173,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2, uint64_t _imm);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2, uint64_t _imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataX2RegImmConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
                                    uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _imm)
@@ -199,16 +199,16 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2, IntRegIndex _op3);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2, RegIndex _op3);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataX3RegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
-                                   IntRegIndex _op3) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
+                                   RegIndex _op3) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _op3)
     {
@@ -225,14 +225,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1,
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1,
                    uint64_t _imm, ConditionCode _condCode, uint8_t _defCc);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataXCondCompImmConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _op1,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _op1,
                                    uint64_t _imm, ConditionCode _condCode,
                                    uint8_t _defCc) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
@@ -251,15 +251,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1,
-                   IntRegIndex _op2, ConditionCode _condCode, uint8_t _defCc);
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1,
+                   RegIndex _op2, ConditionCode _condCode, uint8_t _defCc);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataXCondCompRegConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                   IntRegIndex _op1, IntRegIndex _op2,
+                                   RegIndex _op1, RegIndex _op2,
                                    ConditionCode _condCode, uint8_t _defCc) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _op1, _op2, _condCode, _defCc)
@@ -277,16 +277,16 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2,
                    ConditionCode _condCode);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataXCondSelConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
                                    ConditionCode _condCode) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _condCode)
diff --git a/src/arch/arm/isa/templates/macromem.isa b/src/arch/arm/isa/templates/macromem.isa
index 094158a..dfda85c 100644
--- a/src/arch/arm/isa/templates/macromem.isa
+++ b/src/arch/arm/isa/templates/macromem.isa
@@ -166,18 +166,18 @@
 
       public:
         %(class_name)s(ExtMachInst machInst,
-                       IntRegIndex _ura,
-                       IntRegIndex _urb,
-                       IntRegIndex _urc);
+                       RegIndex _ura,
+                       RegIndex _urb,
+                       RegIndex _urc);
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
     };
 }};
 
 def template MicroSetPCCPSRConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                   IntRegIndex _ura,
-                                   IntRegIndex _urb,
-                                   IntRegIndex _urc) :
+                                   RegIndex _ura,
+                                   RegIndex _urb,
+                                   RegIndex _urc) :
           %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                            _ura, _urb, _urc)
     {
@@ -461,13 +461,13 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex rn, bool index, bool up,
+    %(class_name)s(ExtMachInst machInst, RegIndex rn, bool index, bool up,
             bool user, bool writeback, bool load, uint32_t reglist);
 };
 }};
 
 def template MacroMemConstructor {{
-%(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex rn,
+%(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex rn,
         bool index, bool up, bool user, bool writeback, bool load,
         uint32_t reglist) :
     %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, rn,
@@ -493,13 +493,13 @@
   public:
     // Constructor
     %(class_name)s(const char *mnemonic, ExtMachInst machInst,
-                   bool load, IntRegIndex dest, IntRegIndex base, int64_t imm);
+                   bool load, RegIndex dest, RegIndex base, int64_t imm);
 };
 }};
 
 def template BigFpMemImmConstructor {{
 %(class_name)s::%(class_name)s(const char *mnemonic, ExtMachInst machInst,
-        bool load, IntRegIndex dest, IntRegIndex base, int64_t imm) :
+        bool load, RegIndex dest, RegIndex base, int64_t imm) :
     %(base_class)s(mnemonic, machInst, %(op_class)s, load, dest, base, imm)
 {
     %(set_reg_idx_arr)s;
@@ -516,15 +516,15 @@
   public:
     // Constructor
     %(class_name)s(const char *mnemonic, ExtMachInst machInst,
-                   bool load, IntRegIndex dest, IntRegIndex base,
-                   IntRegIndex offset, ArmExtendType type, int64_t imm);
+                   bool load, RegIndex dest, RegIndex base,
+                   RegIndex offset, ArmExtendType type, int64_t imm);
 };
 }};
 
 def template BigFpMemRegConstructor {{
 %(class_name)s::%(class_name)s(const char *mnemonic, ExtMachInst machInst,
-        bool load, IntRegIndex dest, IntRegIndex base,
-        IntRegIndex offset, ArmExtendType type, int64_t imm) :
+        bool load, RegIndex dest, RegIndex base,
+        RegIndex offset, ArmExtendType type, int64_t imm) :
     %(base_class)s(mnemonic, machInst, %(op_class)s, load, dest, base,
                    offset, type, imm)
 {
@@ -542,13 +542,13 @@
   public:
     // Constructor
     %(class_name)s(const char *mnemonic, ExtMachInst machInst,
-                   IntRegIndex dest, int64_t imm);
+                   RegIndex dest, int64_t imm);
 };
 }};
 
 def template BigFpMemLitConstructor {{
 %(class_name)s::%(class_name)s(const char *mnemonic, ExtMachInst machInst,
-        IntRegIndex dest, int64_t imm) :
+        RegIndex dest, int64_t imm) :
     %(base_class)s(mnemonic, machInst, %(op_class)s, dest, imm)
 {
     %(set_reg_idx_arr)s;
@@ -567,8 +567,8 @@
     %(class_name)s(const char *mnemonic, ExtMachInst machInst,
             uint32_t size, bool fp, bool load, bool noAlloc, bool signExt,
             bool exclusive, bool acrel, uint32_t imm,
-            AddrMode mode, IntRegIndex rn, IntRegIndex rt,
-            IntRegIndex rt2);
+            AddrMode mode, RegIndex rn, RegIndex rt,
+            RegIndex rt2);
 };
 }};
 
@@ -576,7 +576,7 @@
 %(class_name)s::%(class_name)s(const char *mnemonic, ExtMachInst machInst,
         uint32_t size, bool fp, bool load, bool noAlloc, bool signExt,
         bool exclusive, bool acrel, uint32_t imm, AddrMode mode,
-        IntRegIndex rn, IntRegIndex rt, IntRegIndex rt2) :
+        RegIndex rn, RegIndex rt, RegIndex rt2) :
     %(base_class)s(mnemonic, machInst, %(op_class)s, size,
                    fp, load, noAlloc, signExt, exclusive, acrel,
                    imm, mode, rn, rt, rt2)
@@ -659,14 +659,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex rn,
+    %(class_name)s(ExtMachInst machInst, RegIndex rn,
             RegIndex vd, bool single, bool up, bool writeback,
             bool load, uint32_t offset);
 };
 }};
 
 def template MacroVFPMemConstructor {{
-%(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex rn,
+%(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex rn,
         RegIndex vd, bool single, bool up, bool writeback, bool load,
         uint32_t offset) :
     %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, rn,
diff --git a/src/arch/arm/isa/templates/mem.isa b/src/arch/arm/isa/templates/mem.isa
index 72e858f..35af775 100644
--- a/src/arch/arm/isa/templates/mem.isa
+++ b/src/arch/arm/isa/templates/mem.isa
@@ -875,7 +875,7 @@
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
             uint32_t _base, int _mode, bool _wb) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                       (IntRegIndex)_base, (AddrMode)_mode, _wb)
+                       (RegIndex)_base, (AddrMode)_mode, _wb)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -931,7 +931,7 @@
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
             uint32_t _dest, uint32_t _op1, uint32_t _base) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, (IntRegIndex)_op1, (IntRegIndex)_base)
+                 (RegIndex)_dest, (RegIndex)_op1, (RegIndex)_base)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -948,8 +948,8 @@
             uint32_t _dest, uint32_t _dest2,
             uint32_t _base, bool _add, int32_t _imm) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, (IntRegIndex)_dest2,
-                 (IntRegIndex)_base, _add, _imm)
+                 (RegIndex)_dest, (RegIndex)_dest2,
+                 (RegIndex)_base, _add, _imm)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -975,8 +975,8 @@
             uint32_t _result, uint32_t _dest, uint32_t _dest2,
             uint32_t _base, bool _add, int32_t _imm) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_result, (IntRegIndex)_dest, (IntRegIndex)_dest2,
-                 (IntRegIndex)_base, _add, _imm)
+                 (RegIndex)_result, (RegIndex)_dest, (RegIndex)_dest2,
+                 (RegIndex)_base, _add, _imm)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -1002,7 +1002,7 @@
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
             uint32_t _dest, uint32_t _base, bool _add, int32_t _imm) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, (IntRegIndex)_base, _add, _imm)
+                 (RegIndex)_dest, (RegIndex)_base, _add, _imm)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -1028,8 +1028,8 @@
             uint32_t _result, uint32_t _dest, uint32_t _base,
             bool _add, int32_t _imm) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_result, (IntRegIndex)_dest,
-                 (IntRegIndex)_base, _add, _imm)
+                 (RegIndex)_result, (RegIndex)_dest,
+                 (RegIndex)_base, _add, _imm)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -1056,10 +1056,10 @@
             uint32_t _dest, uint32_t _dest2, uint32_t _base, bool _add,
             int32_t _shiftAmt, uint32_t _shiftType, uint32_t _index) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, (IntRegIndex)_dest2,
-                 (IntRegIndex)_base, _add,
+                 (RegIndex)_dest, (RegIndex)_dest2,
+                 (RegIndex)_base, _add,
                  _shiftAmt, (ArmShiftType)_shiftType,
-                 (IntRegIndex)_index)
+                 (RegIndex)_index)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -1086,9 +1086,9 @@
             uint32_t _dest, uint32_t _base, bool _add,
             int32_t _shiftAmt, uint32_t _shiftType, uint32_t _index) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, (IntRegIndex)_base, _add,
+                 (RegIndex)_dest, (RegIndex)_base, _add,
                  _shiftAmt, (ArmShiftType)_shiftType,
-                 (IntRegIndex)_index)
+                 (RegIndex)_index)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -1115,10 +1115,10 @@
             uint32_t _dest, uint32_t _dest2, uint32_t _base, bool _add,
             int32_t _shiftAmt, uint32_t _shiftType, uint32_t _index) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, (IntRegIndex)_dest2,
-                 (IntRegIndex)_base, _add,
+                 (RegIndex)_dest, (RegIndex)_dest2,
+                 (RegIndex)_base, _add,
                  _shiftAmt, (ArmShiftType)_shiftType,
-                 (IntRegIndex)_index)
+                 (RegIndex)_index)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -1131,8 +1131,8 @@
         assert(numMicroops >= 2);
         uops = new StaticInstPtr[numMicroops];
         if ((_dest == _index) || (_dest2 == _index)) {
-            IntRegIndex wbIndexReg = INTREG_UREG0;
-            uops[0] = new MicroUopRegMov(machInst, INTREG_UREG0, _index);
+            RegIndex wbIndexReg = int_reg::Ureg0;
+            uops[0] = new MicroUopRegMov(machInst, int_reg::Ureg0, _index);
             uops[0]->setDelayedCommit();
             uops[0]->setFirstMicroop();
             uops[1] = new %(acc_name)s(machInst, _dest, _dest2, _base, _add,
@@ -1141,7 +1141,7 @@
             uops[2] = new %(wb_decl)s;
             uops[2]->setLastMicroop();
         } else {
-            IntRegIndex wbIndexReg = index;
+            RegIndex wbIndexReg = index;
             uops[0] = new %(acc_name)s(machInst, _dest, _dest2, _base, _add,
                                        _shiftAmt, _shiftType, _index);
             uops[0]->setDelayedCommit();
@@ -1158,9 +1158,9 @@
             uint32_t _dest, uint32_t _base, bool _add,
             int32_t _shiftAmt, uint32_t _shiftType, uint32_t _index) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, (IntRegIndex)_base, _add,
+                 (RegIndex)_dest, (RegIndex)_base, _add,
                  _shiftAmt, (ArmShiftType)_shiftType,
-                 (IntRegIndex)_index)
+                 (RegIndex)_index)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -1174,15 +1174,16 @@
 #if %(use_uops)d
         assert(numMicroops >= 2);
         uops = new StaticInstPtr[numMicroops];
-        if (_dest == INTREG_PC && !isFloating() && !isVector()) {
-            IntRegIndex wbIndexReg = index;
-            uops[0] = new %(acc_name)s(machInst, INTREG_UREG0, _base, _add,
+        if (_dest == int_reg::Pc && !isFloating() && !isVector()) {
+            RegIndex wbIndexReg = index;
+            uops[0] = new %(acc_name)s(machInst, int_reg::Ureg0, _base, _add,
                                        _shiftAmt, _shiftType, _index);
             uops[0]->setDelayedCommit();
             uops[0]->setFirstMicroop();
             uops[1] = new %(wb_decl)s;
             uops[1]->setDelayedCommit();
-            uops[2] = new MicroUopRegMov(machInst, INTREG_PC, INTREG_UREG0);
+            uops[2] = new MicroUopRegMov(machInst, int_reg::Pc,
+                                         int_reg::Ureg0);
             uops[2]->setFlag(StaticInst::IsControl);
             uops[2]->setFlag(StaticInst::IsIndirectControl);
             if (conditional)
@@ -1191,8 +1192,8 @@
                 uops[2]->setFlag(StaticInst::IsUncondControl);
             uops[2]->setLastMicroop();
         } else if(_dest == _index) {
-            IntRegIndex wbIndexReg = INTREG_UREG0;
-            uops[0] = new MicroUopRegMov(machInst, INTREG_UREG0, _index);
+            RegIndex wbIndexReg = int_reg::Ureg0;
+            uops[0] = new MicroUopRegMov(machInst, int_reg::Ureg0, _index);
             uops[0]->setDelayedCommit();
             uops[0]->setFirstMicroop();
             uops[1] = new %(acc_name)s(machInst, _dest, _base, _add,
@@ -1201,7 +1202,7 @@
             uops[2] = new %(wb_decl)s;
             uops[2]->setLastMicroop();
         } else {
-            IntRegIndex wbIndexReg = index;
+            RegIndex wbIndexReg = index;
             uops[0] = new %(acc_name)s(machInst, _dest, _base, _add,
                                       _shiftAmt, _shiftType, _index);
             uops[0]->setDelayedCommit();
@@ -1211,7 +1212,7 @@
 
         }
 #else
-        if (_dest == INTREG_PC && !isFloating() && !isVector()) {
+        if (_dest == int_reg::Pc && !isFloating() && !isVector()) {
             flags[IsControl] = true;
             flags[IsIndirectControl] = true;
             if (conditional)
@@ -1227,7 +1228,7 @@
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
             uint32_t _dest, uint32_t _base, bool _add, int32_t _imm) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, (IntRegIndex)_base, _add, _imm)
+                 (RegIndex)_dest, (RegIndex)_base, _add, _imm)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -1241,14 +1242,15 @@
 #if %(use_uops)d
         assert(numMicroops >= 2);
         uops = new StaticInstPtr[numMicroops];
-        if (_dest == INTREG_PC && !isFloating() && !isVector()) {
-            uops[0] = new %(acc_name)s(machInst, INTREG_UREG0, _base, _add,
+        if (_dest == int_reg::Pc && !isFloating() && !isVector()) {
+            uops[0] = new %(acc_name)s(machInst, int_reg::Ureg0, _base, _add,
                                    _imm);
             uops[0]->setDelayedCommit();
             uops[0]->setFirstMicroop();
             uops[1] = new %(wb_decl)s;
             uops[1]->setDelayedCommit();
-            uops[2] = new MicroUopRegMov(machInst, INTREG_PC, INTREG_UREG0);
+            uops[2] = new MicroUopRegMov(
+                    machInst, int_reg::Pc, int_reg::Ureg0);
             uops[2]->setFlag(StaticInst::IsControl);
             uops[2]->setFlag(StaticInst::IsIndirectControl);
             /* Also set flags on the macroop so that pre-microop decomposition
@@ -1262,7 +1264,7 @@
                 uops[2]->setFlag(StaticInst::IsUncondControl);
                 setFlag(StaticInst::IsUncondControl);
             }
-            if (_base == INTREG_SP && _add && _imm == 4 && %(is_ras_pop)s) {
+            if (_base == int_reg::Sp && _add && _imm == 4 && %(is_ras_pop)s) {
                 uops[2]->setFlag(StaticInst::IsReturn);
                 setFlag(StaticInst::IsReturn);
             }
@@ -1275,7 +1277,7 @@
             uops[1]->setLastMicroop();
         }
 #else
-        if (_dest == INTREG_PC && !isFloating() && !isVector()) {
+        if (_dest == int_reg::Pc && !isFloating() && !isVector()) {
             flags[IsControl] = true;
             flags[IsIndirectControl] = true;
             if (conditional)
diff --git a/src/arch/arm/isa/templates/mem64.isa b/src/arch/arm/isa/templates/mem64.isa
index 444560a..5097fb0 100644
--- a/src/arch/arm/isa/templates/mem64.isa
+++ b/src/arch/arm/isa/templates/mem64.isa
@@ -290,7 +290,7 @@
 
       public:
         /// Constructor.
-        %(class_name)s(ExtMachInst machInst, IntRegIndex _base,
+        %(class_name)s(ExtMachInst machInst, RegIndex _base,
                        MiscRegIndex _dest, uint64_t _imm);
 
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
@@ -307,7 +307,7 @@
 }};
 
 def template DCStore64Constructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _base,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _base,
                                    MiscRegIndex _dest, uint64_t _imm) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                         _base, _dest, _imm)
@@ -385,7 +385,7 @@
       public:
         /// Constructor.
         %(class_name)s(ExtMachInst machInst,
-                IntRegIndex _dest, IntRegIndex _base, int64_t _imm);
+                RegIndex _dest, RegIndex _base, int64_t _imm);
 
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
         Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
@@ -409,7 +409,7 @@
       public:
         /// Constructor.
         %(class_name)s(ExtMachInst machInst,
-                IntRegIndex _dest, IntRegIndex _base, int64_t _imm,
+                RegIndex _dest, RegIndex _base, int64_t _imm,
                 bool noAlloc = false, bool exclusive = false,
                 bool acrel = false);
 
@@ -435,7 +435,7 @@
       public:
         /// Constructor.
         %(class_name)s(ExtMachInst machInst,
-                IntRegIndex _dest, IntRegIndex _dest2, IntRegIndex _base,
+                RegIndex _dest, RegIndex _dest2, RegIndex _base,
                 int64_t _imm = 0, bool noAlloc = false, bool exclusive = false,
                 bool acrel = false);
 
@@ -464,8 +464,8 @@
       public:
         /// Constructor.
         %(class_name)s(ExtMachInst machInst,
-                IntRegIndex _result, IntRegIndex _dest, IntRegIndex _dest2,
-                IntRegIndex _base, int64_t _imm = 0);
+                RegIndex _result, RegIndex _dest, RegIndex _dest2,
+                RegIndex _base, int64_t _imm = 0);
 
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
         Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
@@ -484,7 +484,7 @@
       public:
         /// Constructor.
         %(class_name)s(ExtMachInst machInst,
-                IntRegIndex _dest, IntRegIndex _base, IntRegIndex _offset,
+                RegIndex _dest, RegIndex _base, RegIndex _offset,
                 ArmExtendType _type, uint32_t _shiftAmt);
 
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
@@ -509,7 +509,7 @@
       public:
         /// Constructor.
         %(class_name)s(ExtMachInst machInst,
-                IntRegIndex _dest, IntRegIndex _base, IntRegIndex _offset,
+                RegIndex _dest, RegIndex _base, RegIndex _offset,
                 ArmExtendType _type, uint32_t _shiftAmt,
                 bool noAlloc = false, bool exclusive = false,
                 bool acrel = false);
@@ -535,8 +535,8 @@
 
       public:
         /// Constructor.
-        %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                       IntRegIndex _base);
+        %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                       RegIndex _base);
 
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
         Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
@@ -559,8 +559,8 @@
 
       public:
         /// Constructor.
-        %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                       IntRegIndex _base, IntRegIndex _result);
+        %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                       RegIndex _base, RegIndex _result);
 
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
         Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
@@ -583,7 +583,7 @@
 
       public:
         /// Constructor.
-        %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, int64_t _imm);
+        %(class_name)s(ExtMachInst machInst, RegIndex _dest, int64_t _imm);
 
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
         Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
@@ -606,7 +606,7 @@
 
       public:
         /// Constructor.
-        %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, int64_t _imm,
+        %(class_name)s(ExtMachInst machInst, RegIndex _dest, int64_t _imm,
                 bool noAlloc = false, bool exclusive = false,
                 bool acrel = false);
 
@@ -625,9 +625,9 @@
 
 def template LoadStoreImm64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, IntRegIndex _base, int64_t _imm) :
+            RegIndex _dest, RegIndex _base, int64_t _imm) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                        (IntRegIndex)_dest, (IntRegIndex)_base, _imm)
+                        (RegIndex)_dest, (RegIndex)_base, _imm)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -645,7 +645,7 @@
 
 def template LoadStoreImmU64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, IntRegIndex _base, int64_t _imm,
+            RegIndex _dest, RegIndex _base, int64_t _imm,
             bool noAlloc, bool exclusive, bool acrel) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                         _dest, _base, _imm)
@@ -659,7 +659,7 @@
 
 def template LoadStoreImmDU64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, IntRegIndex _dest2, IntRegIndex _base,
+            RegIndex _dest, RegIndex _dest2, RegIndex _base,
             int64_t _imm, bool noAlloc, bool exclusive, bool acrel) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                         _dest, _dest2, _base, _imm)
@@ -673,8 +673,8 @@
 
 def template StoreImmDEx64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _result, IntRegIndex _dest, IntRegIndex _dest2,
-            IntRegIndex _base, int64_t _imm) :
+            RegIndex _result, RegIndex _dest, RegIndex _dest2,
+            RegIndex _base, int64_t _imm) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                         _result, _dest, _dest2, _base, _imm)
     {
@@ -687,7 +687,7 @@
 
 def template LoadStoreReg64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, IntRegIndex _base, IntRegIndex _offset,
+            RegIndex _dest, RegIndex _base, RegIndex _offset,
             ArmExtendType _type, uint32_t _shiftAmt) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                         _dest, _base, _offset, _type, _shiftAmt)
@@ -709,7 +709,7 @@
 
 def template LoadStoreRegU64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, IntRegIndex _base, IntRegIndex _offset,
+            RegIndex _dest, RegIndex _base, RegIndex _offset,
             ArmExtendType _type, uint32_t _shiftAmt,
             bool noAlloc, bool exclusive, bool acrel) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
@@ -724,7 +724,7 @@
 
 def template LoadStoreRaw64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, IntRegIndex _base) :
+            RegIndex _dest, RegIndex _base) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _base)
     {
         %(set_reg_idx_arr)s;
@@ -734,7 +734,7 @@
 
 def template LoadStoreEx64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, IntRegIndex _base, IntRegIndex _result) :
+            RegIndex _dest, RegIndex _base, RegIndex _result) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                         _dest, _base, _result)
     {
@@ -745,9 +745,9 @@
 
 def template LoadStoreLit64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, int64_t _imm) :
+            RegIndex _dest, int64_t _imm) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, _imm)
+                 (RegIndex)_dest, _imm)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -765,10 +765,10 @@
 
 def template LoadStoreLitU64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, int64_t _imm,
+            RegIndex _dest, int64_t _imm,
             bool noAlloc, bool exclusive, bool acrel) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
-                 (IntRegIndex)_dest, _imm)
+                 (RegIndex)_dest, _imm)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
@@ -859,8 +859,8 @@
 
       public:
         /// Constructor.
-        %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                       IntRegIndex _base, IntRegIndex _result);
+        %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                       RegIndex _base, RegIndex _result);
 
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
         Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
@@ -878,7 +878,7 @@
 
 def template AmoOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, IntRegIndex _base, IntRegIndex _result) :
+            RegIndex _dest, RegIndex _base, RegIndex _result) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                         _dest, _base, _result)
     {
@@ -898,8 +898,8 @@
       public:
         bool isXZR ;
         /// Constructor.
-        %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                       IntRegIndex _base, IntRegIndex _result);
+        %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                       RegIndex _base, RegIndex _result);
 
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
         Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
@@ -916,7 +916,7 @@
 
 def template AmoArithmeticOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, IntRegIndex _base, IntRegIndex _result) :
+            RegIndex _dest, RegIndex _base, RegIndex _result) :
          %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                           _dest,  _base, _result)
     {
diff --git a/src/arch/arm/isa/templates/misc.isa b/src/arch/arm/isa/templates/misc.isa
index 3d3393d..36c78f6 100644
--- a/src/arch/arm/isa/templates/misc.isa
+++ b/src/arch/arm/isa/templates/misc.isa
@@ -43,13 +43,13 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template MrsConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest)
     {
         %(set_reg_idx_arr)s;
@@ -74,14 +74,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
                    uint8_t _sysM, bool _r);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template MrsBankedRegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
                                    uint8_t _sysM, bool _r) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest),
         byteMask(_sysM), r(_r)
@@ -107,14 +107,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1,
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1,
                    uint8_t _sysM, bool _r);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template MsrBankedRegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _op1,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _op1,
                                    uint8_t _sysM, bool _r) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _op1, _sysM),
         r(_r)
@@ -137,13 +137,13 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1, uint8_t mask);
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1, uint8_t mask);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template MsrRegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _op1,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _op1,
                                    uint8_t mask) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _op1, mask)
     {
@@ -194,14 +194,14 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst, MiscRegIndex _op1,
-                   IntRegIndex _dest, IntRegIndex _dest2, uint32_t imm);
+                   RegIndex _dest, RegIndex _dest2, uint32_t imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template MrrcOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst, MiscRegIndex op1,
-                                   IntRegIndex dest, IntRegIndex dest2,
+                                   RegIndex dest, RegIndex dest2,
                                    uint32_t imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, op1, dest,
                        dest2, imm)
@@ -224,15 +224,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1, RegIndex _op2,
                    MiscRegIndex _dest, uint32_t imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template McrrOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex op1,
-                                   IntRegIndex op2, MiscRegIndex dest,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex op1,
+                                   RegIndex op2, MiscRegIndex dest,
                                    uint32_t imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, op1, op2,
                        dest, imm)
@@ -282,14 +282,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, uint64_t _imm);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, uint64_t _imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegImmOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, uint64_t _imm) :
+            RegIndex _dest, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _imm)
     {
         %(set_reg_idx_arr)s;
@@ -310,14 +310,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegRegOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                   IntRegIndex _dest, IntRegIndex _op1) :
+                                   RegIndex _dest, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _op1)
     {
         %(set_reg_idx_arr)s;
@@ -338,15 +338,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2, uint64_t _imm);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2, uint64_t _imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegRegRegImmOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
                                    uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _imm)
@@ -369,16 +369,16 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2, IntRegIndex _op3);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2, RegIndex _op3);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegRegRegRegOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
-                                   IntRegIndex _op3) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
+                                   RegIndex _op3) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _op3)
     {
@@ -400,15 +400,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegRegRegOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2)
     {
@@ -430,15 +430,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1,
                    uint64_t _imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegRegImmOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, uint64_t _imm) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm)
     {
@@ -460,7 +460,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, MiscRegIndex _dest, IntRegIndex _op1,
+    %(class_name)s(ExtMachInst machInst, MiscRegIndex _dest, RegIndex _op1,
                    uint64_t _imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
@@ -468,7 +468,7 @@
 
 def template MiscRegRegImmOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst, MiscRegIndex _dest,
-                                   IntRegIndex _op1, uint64_t _imm) :
+                                   RegIndex _op1, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm)
     {
@@ -490,14 +490,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, MiscRegIndex _op1,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, MiscRegIndex _op1,
                    uint64_t _imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegMiscRegImmOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
                                    MiscRegIndex _op1, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm)
@@ -520,14 +520,14 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
                    uint64_t _imm1, uint64_t _imm2);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegImmImmOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
                                    uint64_t _imm1, uint64_t _imm2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _imm1, _imm2)
@@ -550,7 +550,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1,
                    uint64_t _imm1, uint64_t _imm2);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
@@ -558,7 +558,7 @@
 
 def template RegRegImmImmOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                   IntRegIndex _dest, IntRegIndex _op1,
+                                   RegIndex _dest, RegIndex _op1,
                                    uint64_t _imm1, uint64_t _imm2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm1, _imm2)
@@ -581,15 +581,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   uint64_t _imm, IntRegIndex _op1);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   uint64_t _imm, RegIndex _op1);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegImmRegOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   uint64_t _imm, IntRegIndex _op1) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   uint64_t _imm, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _imm, _op1)
     {
@@ -611,16 +611,16 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, uint64_t _imm,
-                   IntRegIndex _op1, int32_t _shiftAmt,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, uint64_t _imm,
+                   RegIndex _op1, int32_t _shiftAmt,
                    ArmShiftType _shiftType);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegImmRegShiftOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   uint64_t _imm, IntRegIndex _op1,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   uint64_t _imm, RegIndex _op1,
                                    int32_t _shiftAmt,
                                    ArmShiftType _shiftType) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
@@ -645,7 +645,7 @@
       public:
         // Constructor
         %(class_name)s(ExtMachInst machInst, MiscRegIndex _dest,
-                       IntRegIndex _op1, uint64_t _imm);
+                       RegIndex _op1, uint64_t _imm);
         Fault execute(ExecContext *, Trace::InstRecord *) const override;
         Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
         Fault completeAcc(PacketPtr, ExecContext *,
diff --git a/src/arch/arm/isa/templates/misc64.isa b/src/arch/arm/isa/templates/misc64.isa
index faad349..7c15c39 100644
--- a/src/arch/arm/isa/templates/misc64.isa
+++ b/src/arch/arm/isa/templates/misc64.isa
@@ -1,6 +1,6 @@
 // -*- mode:c++ -*-
 
-// Copyright (c) 2011,2017-2020 ARM Limited
+// Copyright (c) 2011,2017-2022 Arm Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -67,7 +67,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1,
+                   RegIndex _dest, RegIndex _op1,
                    uint64_t _imm1, uint64_t _imm2);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
@@ -75,7 +75,7 @@
 
 def template RegRegImmImmOp64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                   IntRegIndex _dest, IntRegIndex _op1,
+                                   RegIndex _dest, RegIndex _op1,
                                    uint64_t _imm1, uint64_t _imm2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm1, _imm2)
@@ -93,15 +93,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1,
-                   IntRegIndex _op2, uint64_t _imm);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1,
+                   RegIndex _op2, uint64_t _imm);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template RegRegRegImmOp64Constructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
                                    uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _imm)
@@ -144,7 +144,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst, MiscRegIndex _dest,
-            IntRegIndex _op1, uint64_t _imm);
+            RegIndex _op1, uint64_t _imm);
 
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
@@ -152,7 +152,7 @@
 
 def template MiscRegRegOp64Constructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst, MiscRegIndex _dest,
-                                   IntRegIndex _op1, uint64_t _imm) :
+                                   RegIndex _op1, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm)
     {
@@ -169,7 +169,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
             MiscRegIndex _op1, uint64_t _imm);
 
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
@@ -177,7 +177,7 @@
 }};
 
 def template RegMiscRegOp64Constructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
                                    MiscRegIndex _op1, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm)
@@ -196,13 +196,13 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template XPauthOpRegRegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest)
     {
         %(set_reg_idx_arr)s;
@@ -219,17 +219,116 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest);
 
     Fault execute(ExecContext *, Trace::InstRecord *) const;
 };
 }};
 
 def template RegNoneConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest)
     {
         %(set_reg_idx_arr)s;
         %(constructor)s;
     }
 }};
+
+def template DvmTlbiDeclare {{
+class %(class_name)s : public %(base_class)s
+{
+  private:
+    %(reg_idx_arr_decl)s;
+    const bool dvmEnabled;
+
+  public:
+    // Constructor
+    %(class_name)s(ExtMachInst machInst, MiscRegIndex _dest,
+            RegIndex _op1, uint64_t _imm, bool dvm_enabled);
+
+    Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
+    Fault completeAcc(PacketPtr, ExecContext *,
+                      Trace::InstRecord *) const override;
+    Fault execute(ExecContext *, Trace::InstRecord *) const override;
+};
+}};
+
+def template DvmDeclare {{
+    /**
+     * Static instruction class for "%(mnemonic)s".
+     */
+    class %(class_name)s : public %(base_class)s
+    {
+      private:
+        %(reg_idx_arr_decl)s;
+        const bool dvmEnabled;
+
+      public:
+        /// Constructor.
+        %(class_name)s(ExtMachInst machInst, bool dvm_enabled);
+        Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override;
+        Fault completeAcc(PacketPtr, ExecContext *,
+                          Trace::InstRecord *) const override;
+        Fault execute(ExecContext *, Trace::InstRecord *) const override;
+    };
+}};
+
+def template DvmTlbiConstructor {{
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, MiscRegIndex _dest,
+                                   RegIndex _op1, uint64_t _imm,
+                                   bool dvm_enabled) :
+        %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
+                       _dest, _op1, _imm),
+        dvmEnabled(dvm_enabled)
+    {
+        %(set_reg_idx_arr)s;
+        %(constructor)s;
+
+        if (dvmEnabled) {
+            flags[IsLoad] = true;
+        }
+    }
+}};
+
+def template DvmConstructor {{
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, bool dvm_enabled) :
+        %(base_class)s("%(mnemonic)s", machInst, %(op_class)s),
+        dvmEnabled(dvm_enabled)
+    {
+        %(set_reg_idx_arr)s;
+        %(constructor)s;
+
+        if (dvmEnabled) {
+            flags[IsLoad] = true;
+        }
+    }
+}};
+
+def template DvmInitiateAcc {{
+    Fault
+    %(class_name)s::initiateAcc(ExecContext *xc,
+                                Trace::InstRecord *traceData) const
+    {
+        Fault fault = NoFault;
+
+        %(op_decl)s;
+        %(op_rd)s;
+        %(code)s;
+
+        %(dvm_code)s;
+
+        if (fault == NoFault) {
+            %(op_wb)s;
+        }
+        return fault;
+    }
+}};
+
+def template DvmCompleteAcc {{
+    Fault
+    %(class_name)s::completeAcc(PacketPtr pkt, ExecContext *xc,
+                                Trace::InstRecord *traceData) const
+    {
+        return NoFault;
+    }
+}};
diff --git a/src/arch/arm/isa/templates/mult.isa b/src/arch/arm/isa/templates/mult.isa
index f30bc79..e94cc93 100644
--- a/src/arch/arm/isa/templates/mult.isa
+++ b/src/arch/arm/isa/templates/mult.isa
@@ -43,15 +43,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _reg0,
-                   IntRegIndex _reg1, IntRegIndex _reg2);
+    %(class_name)s(ExtMachInst machInst, RegIndex _reg0,
+                   RegIndex _reg1, RegIndex _reg2);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template Mult3Constructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _reg0,
-                                   IntRegIndex _reg1, IntRegIndex _reg2) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _reg0,
+                                   RegIndex _reg1, RegIndex _reg2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _reg0, _reg1, _reg2)
     {
@@ -74,16 +74,16 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _reg0, IntRegIndex _reg1,
-                   IntRegIndex _reg2, IntRegIndex _reg3);
+                   RegIndex _reg0, RegIndex _reg1,
+                   RegIndex _reg2, RegIndex _reg3);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template Mult4Constructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _reg0,
-                                   IntRegIndex _reg1, IntRegIndex _reg2,
-                                   IntRegIndex _reg3) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _reg0,
+                                   RegIndex _reg1, RegIndex _reg2,
+                                   RegIndex _reg3) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _reg0, _reg1, _reg2, _reg3)
     {
diff --git a/src/arch/arm/isa/templates/neon.isa b/src/arch/arm/isa/templates/neon.isa
index 7e9b2b1..2a64424 100644
--- a/src/arch/arm/isa/templates/neon.isa
+++ b/src/arch/arm/isa/templates/neon.isa
@@ -61,7 +61,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2)
     {
@@ -91,7 +91,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
                    uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _imm)
@@ -122,7 +122,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, uint64_t _imm) :
+                   RegIndex _dest, RegIndex _op1, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm)
     {
@@ -151,7 +151,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, uint64_t _imm) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _imm)
     {
         %(set_reg_idx_arr)s;
@@ -180,7 +180,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1) :
+                   RegIndex _dest, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1)
     {
diff --git a/src/arch/arm/isa/templates/neon64.isa b/src/arch/arm/isa/templates/neon64.isa
index f19f89d..f315f1e 100644
--- a/src/arch/arm/isa/templates/neon64.isa
+++ b/src/arch/arm/isa/templates/neon64.isa
@@ -52,7 +52,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2)
     {
@@ -77,7 +77,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
                    uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _imm)
@@ -102,7 +102,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _op1)
     {
         %(set_reg_idx_arr)s;
@@ -126,7 +126,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, uint64_t _imm) :
+                   RegIndex _dest, RegIndex _op1, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm)
     {
@@ -151,7 +151,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, uint64_t _imm1,
+                   RegIndex _dest, RegIndex _op1, uint64_t _imm1,
                    uint64_t _imm2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm1, _imm2)
@@ -176,7 +176,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, uint64_t _imm) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _imm)
     {
         %(set_reg_idx_arr)s;
diff --git a/src/arch/arm/isa/templates/pred.isa b/src/arch/arm/isa/templates/pred.isa
index b461aaf..3acc724 100644
--- a/src/arch/arm/isa/templates/pred.isa
+++ b/src/arch/arm/isa/templates/pred.isa
@@ -56,15 +56,15 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-            IntRegIndex _op1, uint32_t _imm, bool _rotC=true);
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+            RegIndex _op1, uint32_t _imm, bool _rotC=true);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataImmConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, uint32_t _imm,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, uint32_t _imm,
                                    bool _rotC) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm, _rotC)
@@ -96,16 +96,16 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-            IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+            RegIndex _op1, RegIndex _op2,
             int32_t _shiftAmt, ArmShiftType _shiftType);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataRegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
                                    int32_t _shiftAmt,
                                    ArmShiftType _shiftType) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
@@ -143,17 +143,17 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-            IntRegIndex _op1, IntRegIndex _op2, IntRegIndex _shift,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+            RegIndex _op1, RegIndex _op2, RegIndex _shift,
             ArmShiftType _shiftType);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template DataRegRegConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
-                                   IntRegIndex _shift,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
+                                   RegIndex _shift,
                                    ArmShiftType _shiftType) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _shift, _shiftType)
diff --git a/src/arch/arm/isa/templates/sve.isa b/src/arch/arm/isa/templates/sve.isa
index cf77b71..b52a189 100644
--- a/src/arch/arm/isa/templates/sve.isa
+++ b/src/arch/arm/isa/templates/sve.isa
@@ -61,7 +61,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _gp) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _gp)
     {
@@ -87,7 +87,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _gp) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _gp)
     {
@@ -112,7 +112,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _op1)
     {
         %(set_reg_idx_arr)s;
@@ -136,7 +136,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, %(isSimdFp)s)
     {
@@ -161,7 +161,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, uint64_t _imm) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _imm)
     {
         %(set_reg_idx_arr)s;
@@ -186,7 +186,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, uint64_t _imm, IntRegIndex _gp,
+                   RegIndex _dest, uint64_t _imm, RegIndex _gp,
                    bool _isMerging=true) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _imm, _gp, _isMerging)
@@ -213,7 +213,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, uint64_t _imm)
+                   RegIndex _dest, RegIndex _op1, uint64_t _imm)
         : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                          _dest, _op1, _imm)
     {
@@ -239,7 +239,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, uint64_t _imm, IntRegIndex _gp) :
+                   RegIndex _dest, uint64_t _imm, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _imm, _gp)
     {
@@ -265,7 +265,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op2, IntRegIndex _gp) :
+                   RegIndex _dest, RegIndex _op2, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op2, _gp)
     {
@@ -291,8 +291,8 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-                   IntRegIndex _gp, SvePredType _predType) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
+                   RegIndex _gp, SvePredType _predType) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _gp, _predType)
     {
@@ -318,7 +318,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2)
     {
@@ -343,8 +343,8 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2, uint8_t _index) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2, uint8_t _index) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _index)
     {
@@ -370,8 +370,8 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-                   IntRegIndex _gp, bool _isSel=false) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
+                   RegIndex _gp, bool _isSel=false) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _gp, _isSel)
     {
@@ -396,8 +396,8 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _gp)
     {
@@ -422,8 +422,8 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _gp, %(op2IsWide)s)
     {
@@ -448,8 +448,8 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, uint64_t _imm, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, uint64_t _imm, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm, _gp)
     {
@@ -474,8 +474,8 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _gp)
     {
@@ -500,8 +500,8 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, uint64_t _imm) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm)
     {
@@ -526,8 +526,8 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _gp)
     {
@@ -555,8 +555,8 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _gp)
     {
@@ -581,7 +581,7 @@
 
   public:
     // Constructor
-    SveIndexII(ExtMachInst machInst, IntRegIndex _dest,
+    SveIndexII(ExtMachInst machInst, RegIndex _dest,
                int8_t _imm1, int8_t _imm2) :
         SveIndexIIOp("%(mnemonic)s", machInst, %(op_class)s,
                      _dest, _imm1, _imm2)
@@ -607,8 +607,8 @@
 
   public:
     // Constructor
-    SveIndexIR(ExtMachInst machInst, IntRegIndex _dest,
-               int8_t _imm, IntRegIndex _op) :
+    SveIndexIR(ExtMachInst machInst, RegIndex _dest,
+               int8_t _imm, RegIndex _op) :
         SveIndexIROp("%(mnemonic)s", machInst, %(op_class)s,
                      _dest, _imm, _op)
     {
@@ -633,8 +633,8 @@
 
   public:
     // Constructor
-    SveIndexRI(ExtMachInst machInst, IntRegIndex _dest,
-               IntRegIndex _op, int8_t _imm) :
+    SveIndexRI(ExtMachInst machInst, RegIndex _dest,
+               RegIndex _op, int8_t _imm) :
         SveIndexRIOp("%(mnemonic)s", machInst, %(op_class)s,
                      _dest, _op, _imm)
     {
@@ -659,8 +659,8 @@
 
   public:
     // Constructor
-    SveIndexRR(ExtMachInst machInst, IntRegIndex _dest,
-               IntRegIndex _op1, IntRegIndex _op2) :
+    SveIndexRR(ExtMachInst machInst, RegIndex _dest,
+               RegIndex _op1, RegIndex _op2) :
         SveIndexRROp("%(mnemonic)s", machInst, %(op_class)s,
                      _dest, _op1, _op2)
     {
@@ -684,7 +684,7 @@
     typedef _Element TPElem;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, %(srcIs32b)s, %(destIsVec)s)
     {
@@ -708,8 +708,8 @@
     typedef _Element TPElem;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _gp)
     {
@@ -735,7 +735,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, uint8_t _imm) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, uint8_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _imm)
     {
         %(set_reg_idx_arr)s;
@@ -760,7 +760,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-        IntRegIndex _dest, IntRegIndex _base, IntRegIndex _offset,
+        RegIndex _dest, RegIndex _base, RegIndex _offset,
         uint8_t _mult, SveAdrOffsetFormat _offsetFormat) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _base, _offset, _mult, _offsetFormat)
@@ -785,8 +785,8 @@
     typedef _Element TPElem;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, %(srcIs32b)s)
     {
@@ -810,7 +810,7 @@
     typedef _Element TPElem;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1, IntRegIndex _op2) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1, RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _op1, _op2)
     {
         %(set_reg_idx_arr)s;
@@ -833,8 +833,8 @@
     typedef _Element TPElem;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1,
-            int64_t _op2, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1,
+            int64_t _op2, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _op1,
                 _op2, _gp)
     {
@@ -858,7 +858,7 @@
     typedef _Element TPElem;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
             uint8_t _pattern, uint8_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest,
                 _pattern, _imm, %(dstIsVec)s, %(dstIs32b)s)
@@ -879,8 +879,8 @@
     %(reg_idx_arr_decl)s;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _gp,
-            IntRegIndex _op1) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _gp,
+            RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest,
                 _gp, _op1, %(isMerging)s)
     {
@@ -901,8 +901,8 @@
     %(reg_idx_arr_decl)s;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-            IntRegIndex _op1, IntRegIndex _op2, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+            RegIndex _op1, RegIndex _op2, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest,
                 _op1, _op2, _gp)
     {
@@ -926,8 +926,8 @@
     typedef _Element TPElem;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-            IntRegIndex _op1, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+            RegIndex _op1, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest,
                 _op1, _gp, %(isCond)s, %(isScalar)s, %(isSimdFp)s)
     {
@@ -955,7 +955,7 @@
     typedef _DElement TPDElem;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _op1)
     {
         %(set_reg_idx_arr)s;
@@ -973,7 +973,7 @@
     %(reg_idx_arr_decl)s;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _op1, _gp)
     {
         %(set_reg_idx_arr)s;
@@ -991,7 +991,7 @@
     %(reg_idx_arr_decl)s;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest)
     {
         %(set_reg_idx_arr)s;
@@ -1009,7 +1009,7 @@
     %(reg_idx_arr_decl)s;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _gp) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest, RegIndex _gp) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _dest, _gp)
     {
         %(set_reg_idx_arr)s;
@@ -1027,7 +1027,7 @@
     %(reg_idx_arr_decl)s;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _op1) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _op1) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, _op1)
     {
         %(set_reg_idx_arr)s;
@@ -1071,8 +1071,8 @@
     typedef _DElement TPDElem;
 
   public:
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                   IntRegIndex _op1, IntRegIndex _op2, uint64_t _imm) :
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                   RegIndex _op1, RegIndex _op2, uint64_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _imm)
     {
@@ -1101,7 +1101,7 @@
 
   public:
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2)
     {
@@ -1128,8 +1128,8 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-                   IntRegIndex _gp, uint8_t _rot) :
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
+                   RegIndex _gp, uint8_t _rot) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _gp, _rot)
     {
@@ -1155,7 +1155,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
                    uint8_t _rot, uint8_t _imm) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _rot, _imm)
diff --git a/src/arch/arm/isa/templates/sve_mem.isa b/src/arch/arm/isa/templates/sve_mem.isa
index 017ace8..5eb8975 100644
--- a/src/arch/arm/isa/templates/sve_mem.isa
+++ b/src/arch/arm/isa/templates/sve_mem.isa
@@ -46,7 +46,7 @@
 
       public:
         %(class_name)s(ExtMachInst machInst,
-                IntRegIndex _dest, IntRegIndex _base, uint64_t _imm) :
+                RegIndex _dest, RegIndex _base, uint64_t _imm) :
             %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 _dest, _base, _imm)
         {
@@ -79,8 +79,8 @@
 
       public:
         %(class_name)s(const char* mnem, ExtMachInst machInst,
-                IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
-                IntRegIndex _offset) :
+                RegIndex _dest, RegIndex _gp, RegIndex _base,
+                RegIndex _offset) :
             %(base_class)s(mnem, machInst, %(op_class)s,
                     _dest, _gp, _base, _offset)
         {
@@ -113,7 +113,7 @@
 
       public:
         %(class_name)s(const char* mnem, ExtMachInst machInst,
-                IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
+                RegIndex _dest, RegIndex _gp, RegIndex _base,
                 uint64_t _imm) :
             %(base_class)s(mnem, machInst, %(op_class)s,
                     _dest, _gp, _base, _imm)
@@ -418,9 +418,9 @@
       protected:
         typedef RegElemType TPElem;
 
-        IntRegIndex dest;
-        IntRegIndex gp;
-        IntRegIndex base;
+        RegIndex dest;
+        RegIndex gp;
+        RegIndex base;
         uint64_t imm;
 
         int elemIndex;
@@ -431,8 +431,8 @@
 
       public:
         %(class_name)s(const char* mnem, ExtMachInst machInst,
-                OpClass __opClass, IntRegIndex _dest, IntRegIndex _gp,
-                IntRegIndex _base, uint64_t _imm, int _elemIndex,
+                OpClass __opClass, RegIndex _dest, RegIndex _gp,
+                RegIndex _base, uint64_t _imm, int _elemIndex,
                 int _numElems, bool _firstFault) :
             %(base_class)s(mnem, machInst, %(op_class)s),
             dest(_dest), gp(_gp), base(_base), imm(_imm),
@@ -500,10 +500,10 @@
       protected:
         typedef RegElemType TPElem;
 
-        IntRegIndex dest;
-        IntRegIndex gp;
-        IntRegIndex base;
-        IntRegIndex offset;
+        RegIndex dest;
+        RegIndex gp;
+        RegIndex base;
+        RegIndex offset;
 
         bool offsetIs32;
         bool offsetIsSigned;
@@ -517,8 +517,8 @@
 
       public:
         %(class_name)s(const char* mnem, ExtMachInst machInst,
-                OpClass __opClass, IntRegIndex _dest, IntRegIndex _gp,
-                IntRegIndex _base, IntRegIndex _offset, bool _offsetIs32,
+                OpClass __opClass, RegIndex _dest, RegIndex _gp,
+                RegIndex _base, RegIndex _offset, bool _offsetIs32,
                 bool _offsetIsSigned, bool _offsetIsScaled, int _elemIndex,
                 int _numElems, bool _firstFault) :
             %(base_class)s(mnem, machInst, %(op_class)s),
@@ -834,13 +834,13 @@
         %(reg_idx_arr_decl)s;
 
       protected:
-        IntRegIndex op1;
+        RegIndex op1;
 
         StaticInst *macroOp;
 
       public:
         SveGatherLoadCpySrcVecMicroop(const char* mnem, ExtMachInst machInst,
-                IntRegIndex _op1, StaticInst *_macroOp) :
+                RegIndex _op1, StaticInst *_macroOp) :
             MicroOp(mnem, machInst, SimdAluOp), op1(_op1), macroOp(_macroOp)
         {
             %(set_reg_idx_arr)s;
@@ -891,8 +891,8 @@
         typedef _Element TPElem;
 
         RegIndex dest;
-        IntRegIndex gp;
-        IntRegIndex base;
+        RegIndex gp;
+        RegIndex base;
         int64_t imm;
 
         uint8_t numRegs;
@@ -904,7 +904,7 @@
 
       public:
         %(class_name)s(const char* mnem, ExtMachInst machInst,
-                RegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
+                RegIndex _dest, RegIndex _gp, RegIndex _base,
                 int64_t _imm, uint8_t _numRegs, int _regIndex) :
             %(base_class)s(mnem, machInst, %(op_class)s),
             dest(_dest), gp(_gp), base(_base), imm(_imm),
@@ -1168,9 +1168,9 @@
         typedef _Element TPElem;
 
         RegIndex dest;
-        IntRegIndex gp;
-        IntRegIndex base;
-        IntRegIndex offset;
+        RegIndex gp;
+        RegIndex base;
+        RegIndex offset;
 
         uint8_t numRegs;
         int regIndex;
@@ -1181,8 +1181,8 @@
 
       public:
         %(class_name)s(const char* mnem, ExtMachInst machInst,
-                RegIndex _dest, IntRegIndex _gp, IntRegIndex _base,
-                IntRegIndex _offset, uint8_t _numRegs, int _regIndex) :
+                RegIndex _dest, RegIndex _gp, RegIndex _base,
+                RegIndex _offset, uint8_t _numRegs, int _regIndex) :
             %(base_class)s(mnem, machInst, %(op_class)s),
             dest(_dest), gp(_gp), base(_base), offset(_offset),
             numRegs(_numRegs), regIndex(_regIndex),
@@ -1253,8 +1253,8 @@
       protected:
         typedef _Element Element;
         typedef _Element TPElem;
-        IntRegIndex dest;
-        IntRegIndex op1;
+        RegIndex dest;
+        RegIndex op1;
         uint8_t numRegs;
         int regIndex;
 
@@ -1262,7 +1262,7 @@
 
       public:
         %(class_name)s(const char* mnem, ExtMachInst machInst,
-                IntRegIndex _dest, IntRegIndex _op1,
+                RegIndex _dest, RegIndex _op1,
                 uint8_t _numRegs, int _regIndex, StaticInst *_macroOp) :
             MicroOp(mnem, machInst, SimdAluOp), dest(_dest), op1(_op1),
             numRegs(_numRegs), regIndex(_regIndex), macroOp(_macroOp)
@@ -1295,7 +1295,7 @@
       protected:
         typedef _Element Element;
         typedef _Element TPElem;
-        IntRegIndex dest;
+        RegIndex dest;
         uint8_t numRegs;
         int regIndex;
 
@@ -1303,7 +1303,7 @@
 
       public:
         %(class_name)s(const char* mnem, ExtMachInst machInst,
-                IntRegIndex _dest, uint8_t _numRegs, int _regIndex,
+                RegIndex _dest, uint8_t _numRegs, int _regIndex,
                 StaticInst *_macroOp) :
             MicroOp(mnem, machInst, SimdAluOp), dest(_dest),
             numRegs(_numRegs), regIndex(_regIndex), macroOp(_macroOp)
diff --git a/src/arch/arm/isa/templates/vfp.isa b/src/arch/arm/isa/templates/vfp.isa
index 26303cb..2f59f4f 100644
--- a/src/arch/arm/isa/templates/vfp.isa
+++ b/src/arch/arm/isa/templates/vfp.isa
@@ -104,7 +104,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1,
+                   RegIndex _dest, RegIndex _op1,
                    VfpMicroMode mode = VfpNotAMicroop);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
@@ -112,7 +112,7 @@
 
 def template FpRegRegOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                          IntRegIndex _dest, IntRegIndex _op1,
+                                          RegIndex _dest, RegIndex _op1,
                                           VfpMicroMode mode)
         : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 _dest, _op1, mode)
@@ -135,7 +135,7 @@
 
   public:
     // Constructor
-    %(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
+    %(class_name)s(ExtMachInst machInst, RegIndex _dest,
             uint64_t _imm, VfpMicroMode mode = VfpNotAMicroop);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
@@ -143,7 +143,7 @@
 
 def template FpRegImmOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, uint64_t _imm, VfpMicroMode mode)
+            RegIndex _dest, uint64_t _imm, VfpMicroMode mode)
         : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 _dest, _imm, mode)
     {
@@ -166,7 +166,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1,
+                   RegIndex _dest, RegIndex _op1,
                    uint64_t _imm, VfpMicroMode mode = VfpNotAMicroop);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
@@ -174,8 +174,8 @@
 
 def template FpRegRegImmOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                          IntRegIndex _dest,
-                                          IntRegIndex _op1,
+                                          RegIndex _dest,
+                                          RegIndex _op1,
                                           uint64_t _imm,
                                           VfpMicroMode mode)
         : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
@@ -200,7 +200,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
                    VfpMicroMode mode = VfpNotAMicroop);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
@@ -208,9 +208,9 @@
 
 def template FpRegRegRegOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                          IntRegIndex _dest,
-                                          IntRegIndex _op1,
-                                          IntRegIndex _op2,
+                                          RegIndex _dest,
+                                          RegIndex _op1,
+                                          RegIndex _op2,
                                           VfpMicroMode mode)
         : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                          _dest, _op1, _op2, mode)
@@ -234,7 +234,7 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
                   ConditionCode _cond,
                    VfpMicroMode mode = VfpNotAMicroop);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
@@ -243,9 +243,9 @@
 
 def template FpRegRegRegCondOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                          IntRegIndex _dest,
-                                          IntRegIndex _op1,
-                                          IntRegIndex _op2,
+                                          RegIndex _dest,
+                                          RegIndex _op1,
+                                          RegIndex _op2,
                                           ConditionCode _cond,
                                           VfpMicroMode mode)
         : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
diff --git a/src/arch/arm/isa/templates/vfp64.isa b/src/arch/arm/isa/templates/vfp64.isa
index 2548940..883fd43 100644
--- a/src/arch/arm/isa/templates/vfp64.isa
+++ b/src/arch/arm/isa/templates/vfp64.isa
@@ -37,7 +37,7 @@
 
 def template AA64FpRegRegOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-                                   IntRegIndex _dest, IntRegIndex _op1,
+                                   RegIndex _dest, RegIndex _op1,
                                    VfpMicroMode mode) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 _dest, _op1, mode)
@@ -49,7 +49,7 @@
 
 def template AA64FpRegImmOpConstructor {{
     %(class_name)s::%(class_name)s(ExtMachInst machInst,
-            IntRegIndex _dest, uint64_t _imm, VfpMicroMode mode) :
+            RegIndex _dest, uint64_t _imm, VfpMicroMode mode) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                 _dest, _imm, mode)
     {
@@ -59,8 +59,8 @@
 }};
 
 def template AA64FpRegRegImmOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, uint64_t _imm,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, uint64_t _imm,
                                    VfpMicroMode mode) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _imm, mode)
@@ -71,8 +71,8 @@
 }};
 
 def template AA64FpRegRegRegOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
                                    VfpMicroMode mode) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, mode)
@@ -91,16 +91,16 @@
   public:
     // Constructor
     %(class_name)s(ExtMachInst machInst,
-                   IntRegIndex _dest, IntRegIndex _op1, IntRegIndex _op2,
-                   IntRegIndex _op3, VfpMicroMode mode=VfpNotAMicroop);
+                   RegIndex _dest, RegIndex _op1, RegIndex _op2,
+                   RegIndex _op3, VfpMicroMode mode=VfpNotAMicroop);
     Fault execute(ExecContext *, Trace::InstRecord *) const override;
 };
 }};
 
 def template AA64FpRegRegRegRegOpConstructor {{
-    %(class_name)s::%(class_name)s(ExtMachInst machInst, IntRegIndex _dest,
-                                   IntRegIndex _op1, IntRegIndex _op2,
-                                   IntRegIndex _op3, VfpMicroMode mode) :
+    %(class_name)s::%(class_name)s(ExtMachInst machInst, RegIndex _dest,
+                                   RegIndex _op1, RegIndex _op2,
+                                   RegIndex _op3, VfpMicroMode mode) :
         %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
                        _dest, _op1, _op2, _op3, mode)
     {
diff --git a/src/arch/arm/kvm/BaseArmKvmCPU.py b/src/arch/arm/kvm/BaseArmKvmCPU.py
index 63a11d8..364fe44 100644
--- a/src/arch/arm/kvm/BaseArmKvmCPU.py
+++ b/src/arch/arm/kvm/BaseArmKvmCPU.py
@@ -34,10 +34,14 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from m5.params import *
+from m5.objects.ArmCPU import ArmCPU
+from m5.objects.ArmMMU import ArmMMU
 from m5.objects.BaseKvmCPU import BaseKvmCPU
 
-class BaseArmKvmCPU(BaseKvmCPU):
+class BaseArmKvmCPU(BaseKvmCPU, ArmCPU):
     type = 'BaseArmKvmCPU'
     cxx_header = "arch/arm/kvm/base_cpu.hh"
     cxx_class = 'gem5::BaseArmKvmCPU'
     abstract = True
+
+    mmu = ArmMMU()
diff --git a/src/arch/arm/kvm/KvmGic.py b/src/arch/arm/kvm/KvmGic.py
index c435f44..de86653 100644
--- a/src/arch/arm/kvm/KvmGic.py
+++ b/src/arch/arm/kvm/KvmGic.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015, 2017 ARM Limited
+# Copyright (c) 2015, 2017, 2021 Arm Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -36,12 +36,22 @@
 from m5.params import *
 from m5.proxy import *
 
-from m5.objects.Gic import GicV2
+from m5.objects.Gic import GicV2, Gicv3
 
-class MuxingKvmGic(GicV2):
-    type = 'MuxingKvmGic'
+class MuxingKvmGicV2(GicV2):
+    type = 'MuxingKvmGicV2'
     cxx_header = "arch/arm/kvm/gic.hh"
-    cxx_class = 'gem5::MuxingKvmGic'
+    cxx_class = 'gem5::MuxingKvmGic<gem5::GicV2Types>'
+    cxx_template_params = [ 'class Types' ]
+
+    simulate_gic = Param.Bool(False,
+        "Forcing the simulation to use the gem5 GIC instead of the host GIC")
+
+class MuxingKvmGicV3(Gicv3):
+    type = 'MuxingKvmGicV3'
+    cxx_header = "arch/arm/kvm/gic.hh"
+    cxx_class = 'gem5::MuxingKvmGic<gem5::GicV3Types>'
+    cxx_template_params = [ 'class Types' ]
 
     simulate_gic = Param.Bool(False,
         "Forcing the simulation to use the gem5 GIC instead of the host GIC")
diff --git a/src/arch/arm/kvm/SConscript b/src/arch/arm/kvm/SConscript
index 44134a4..131c457 100644
--- a/src/arch/arm/kvm/SConscript
+++ b/src/arch/arm/kvm/SConscript
@@ -40,10 +40,11 @@
 import platform
 host_isa = platform.machine()
 
-if not (env['USE_KVM'] and env['KVM_ISA'] == 'arm'):
+if not (env['CONF']['USE_KVM'] and env['CONF']['KVM_ISA'] == 'arm'):
     Return()
 
-SimObject('KvmGic.py', sim_objects=['MuxingKvmGic'], tags='arm isa')
+SimObject('KvmGic.py',
+    sim_objects=['MuxingKvmGicV2', 'MuxingKvmGicV3'], tags='arm isa')
 Source('gic.cc', tags='arm isa')
 
 SimObject('BaseArmKvmCPU.py', sim_objects=['BaseArmKvmCPU'], tags='arm isa')
diff --git a/src/arch/arm/kvm/arm_cpu.cc b/src/arch/arm/kvm/arm_cpu.cc
index ecdc602..42aeea6 100644
--- a/src/arch/arm/kvm/arm_cpu.cc
+++ b/src/arch/arm/kvm/arm_cpu.cc
@@ -191,7 +191,7 @@
 }
 
 constexpr KvmIntRegInfo
-regCore32(off_t offset, ArmISA::IntRegIndex idx, const char *name)
+regCore32(off_t offset, RegIndex idx, const char *name)
 {
     return { KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_CORE | offset,
              idx, name };
@@ -285,43 +285,43 @@
 
 
 ArmKvmCPU::KvmIntRegInfo ArmKvmCPU::kvmIntRegs[] = {
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r0), INTREG_R0, "R0"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r1), INTREG_R1, "R1"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r2), INTREG_R2, "R2"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r3), INTREG_R3, "R3"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r4), INTREG_R4, "R4"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r5), INTREG_R5, "R5"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r6), INTREG_R6, "R6"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r7), INTREG_R7, "R7"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r8), INTREG_R8, "R8"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r9), INTREG_R9, "R9"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r10), INTREG_R10, "R10"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_fp), INTREG_R11, "R11"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_ip), INTREG_R12, "R12"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_sp), INTREG_R13, "R13(USR)"),
-    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_lr), INTREG_R14, "R14(USR)"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r0), int_reg::R0, "R0"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r1), int_reg::R1, "R1"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r2), int_reg::R2, "R2"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r3), int_reg::R3, "R3"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r4), int_reg::R4, "R4"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r5), int_reg::R5, "R5"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r6), int_reg::R6, "R6"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r7), int_reg::R7, "R7"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r8), int_reg::R8, "R8"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r9), int_reg::R9, "R9"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_r10), int_reg::R10, "R10"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_fp), int_reg::R11, "R11"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_ip), int_reg::R12, "R12"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_sp), int_reg::R13, "R13(USR)"),
+    regCore32(KVM_REG_ARM_CORE_REG(usr_regs.ARM_lr), int_reg::R14, "R14(USR)"),
 
-    regCore32(KVM_REG_ARM_CORE_REG(svc_regs[0]), INTREG_SP_SVC, "R13(SVC)"),
-    regCore32(KVM_REG_ARM_CORE_REG(svc_regs[1]), INTREG_LR_SVC, "R14(SVC)"),
+    regCore32(KVM_REG_ARM_CORE_REG(svc_regs[0]), int_reg::SpSvc, "R13(SVC)"),
+    regCore32(KVM_REG_ARM_CORE_REG(svc_regs[1]), int_reg::LrSvc, "R14(SVC)"),
 
-    regCore32(KVM_REG_ARM_CORE_REG(abt_regs[0]), INTREG_SP_ABT, "R13(ABT)"),
-    regCore32(KVM_REG_ARM_CORE_REG(abt_regs[1]), INTREG_LR_ABT, "R14(ABT)"),
+    regCore32(KVM_REG_ARM_CORE_REG(abt_regs[0]), int_reg::SpAbt, "R13(ABT)"),
+    regCore32(KVM_REG_ARM_CORE_REG(abt_regs[1]), int_reg::LrAbt, "R14(ABT)"),
 
-    regCore32(KVM_REG_ARM_CORE_REG(und_regs[0]), INTREG_SP_UND, "R13(UND)"),
-    regCore32(KVM_REG_ARM_CORE_REG(und_regs[1]), INTREG_LR_UND, "R14(UND)"),
+    regCore32(KVM_REG_ARM_CORE_REG(und_regs[0]), int_reg::SpUnd, "R13(UND)"),
+    regCore32(KVM_REG_ARM_CORE_REG(und_regs[1]), int_reg::LrUnd, "R14(UND)"),
 
-    regCore32(KVM_REG_ARM_CORE_REG(irq_regs[0]), INTREG_SP_IRQ, "R13(IRQ)"),
-    regCore32(KVM_REG_ARM_CORE_REG(irq_regs[1]), INTREG_LR_IRQ, "R14(IRQ)"),
+    regCore32(KVM_REG_ARM_CORE_REG(irq_regs[0]), int_reg::SpIrq, "R13(IRQ)"),
+    regCore32(KVM_REG_ARM_CORE_REG(irq_regs[1]), int_reg::LrIrq, "R14(IRQ)"),
 
 
-    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[0]), INTREG_R8_FIQ, "R8(FIQ)"),
-    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[1]), INTREG_R9_FIQ, "R9(FIQ)"),
-    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[2]), INTREG_R10_FIQ, "R10(FIQ)"),
-    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[3]), INTREG_R11_FIQ, "R11(FIQ)"),
-    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[4]), INTREG_R12_FIQ, "R12(FIQ)"),
-    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[5]), INTREG_R13_FIQ, "R13(FIQ)"),
-    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[6]), INTREG_R14_FIQ, "R14(FIQ)"),
-    { 0, NUM_INTREGS, NULL }
+    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[0]), int_reg::R8Fiq, "R8(FIQ)"),
+    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[1]), int_reg::R9Fiq, "R9(FIQ)"),
+    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[2]), int_reg::R10Fiq, "R10(FIQ)"),
+    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[3]), int_reg::R11Fiq, "R11(FIQ)"),
+    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[4]), int_reg::R12Fiq, "R12(FIQ)"),
+    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[5]), int_reg::R13Fiq, "R13(FIQ)"),
+    regCore32(KVM_REG_ARM_CORE_REG(fiq_regs[6]), int_reg::R14Fiq, "R14(FIQ)"),
+    { 0, int_reg::NumRegs, NULL }
 };
 
 ArmKvmCPU::KvmCoreMiscRegInfo ArmKvmCPU::kvmCoreMiscRegs[] = {
@@ -374,12 +374,12 @@
     if (fiqAsserted != simFIQ) {
         fiqAsserted = simFIQ;
         DPRINTF(KvmInt, "KVM: Update FIQ state: %i\n", simFIQ);
-        vm.setIRQLine(interruptVcpuFiq(vcpuID), simFIQ);
+        vm->setIRQLine(interruptVcpuFiq(vcpuID), simFIQ);
     }
     if (irqAsserted != simIRQ) {
         irqAsserted = simIRQ;
         DPRINTF(KvmInt, "KVM: Update IRQ state: %i\n", simIRQ);
-        vm.setIRQLine(interruptVcpuIrq(vcpuID), simIRQ);
+        vm->setIRQLine(interruptVcpuIrq(vcpuID), simIRQ);
     }
 
     return BaseKvmCPU::kvmRun(ticks);
@@ -545,7 +545,7 @@
     inform("PC: 0x%x\n", pc);
 
     for (const KvmIntRegInfo *ri(kvmIntRegs);
-         ri->idx != NUM_INTREGS; ++ri) {
+         ri->idx != int_reg::NumRegs; ++ri) {
 
         uint32_t value(getOneRegU32(ri->id));
         inform("%s: 0x%x\n", ri->name, value);
@@ -662,9 +662,9 @@
 ArmKvmCPU::updateKvmStateCore()
 {
     for (const KvmIntRegInfo *ri(kvmIntRegs);
-         ri->idx != NUM_INTREGS; ++ri) {
+         ri->idx != init_reg::NumRegs; ++ri) {
 
-        uint64_t value = tc->readIntRegFlat(ri->idx);
+        uint64_t value = tc->getRegFlat(RegId(IntRegClass, ri->idx));
         DPRINTF(KvmContext, "kvm(%s) := 0x%x\n", ri->name, value);
         setOneReg(ri->id, value);
     }
@@ -773,8 +773,8 @@
         const unsigned idx_hi = idx_base + 1;
         const unsigned idx_lo = idx_base + 0;
         uint64_t value =
-            ((uint64_t)tc->readFloatRegFlat(idx_hi) << 32) |
-            tc->readFloatRegFlat(idx_lo);
+            ((uint64_t)tc->getRegFlat(RegId(FloatRegClass, idx_hi)) << 32) |
+            tc->getRegFlat(RegId(FloatRegClass, idx_lo));
 
         setOneReg(id, value);
     } else if (regIsVfpCtrl(id)) {
@@ -802,9 +802,9 @@
 ArmKvmCPU::updateTCStateCore()
 {
     for (const KvmIntRegInfo *ri(kvmIntRegs);
-         ri->idx != NUM_INTREGS; ++ri) {
+         ri->idx != int_reg::NumRegs; ++ri) {
 
-        tc->setIntRegFlat(ri->idx, getOneRegU32(ri->id));
+        tc->setRegFlat(RegId(IntRegClass, ri->idx), getOneRegU32(ri->id));
     }
 
     for (const KvmCoreMiscRegInfo *ri(kvmCoreMiscRegs);
@@ -915,8 +915,9 @@
         const unsigned idx_lo = idx_base + 0;
         uint64_t value = getOneRegU64(id);
 
-        tc->setFloatRegFlat(idx_hi, (value >> 32) & 0xFFFFFFFF);
-        tc->setFloatRegFlat(idx_lo, value & 0xFFFFFFFF);
+        tc->setRegFlat(RegId(FloatRegClass, idx_hi),
+                (value >> 32) & 0xFFFFFFFF);
+        tc->setRegFlat(RegId(FloatRegClass, idx_lo), value & 0xFFFFFFFF);
     } else if (regIsVfpCtrl(id)) {
         MiscRegIndex idx = decodeVFPCtrlReg(id);
         if (idx == NUM_MISCREGS) {
diff --git a/src/arch/arm/kvm/arm_cpu.hh b/src/arch/arm/kvm/arm_cpu.hh
index a16a095..849aa76 100644
--- a/src/arch/arm/kvm/arm_cpu.hh
+++ b/src/arch/arm/kvm/arm_cpu.hh
@@ -76,7 +76,7 @@
         /** KVM ID */
         const uint64_t id;
         /** gem5 index */
-        const ArmISA::IntRegIndex idx;
+        const RegIndex idx;
         /** Name in debug output */
         const char *name;
     };
diff --git a/src/arch/arm/kvm/armv8_cpu.cc b/src/arch/arm/kvm/armv8_cpu.cc
index 937b9c6..a059e14 100644
--- a/src/arch/arm/kvm/armv8_cpu.cc
+++ b/src/arch/arm/kvm/armv8_cpu.cc
@@ -49,7 +49,7 @@
 
 // Unlike gem5, kvm doesn't count the SP as a normal integer register,
 // which means we only have 31 normal integer registers.
-constexpr static unsigned NUM_XREGS = NUM_ARCH_INTREGS - 1;
+constexpr static unsigned NUM_XREGS = int_reg::NumArchRegs - 1;
 static_assert(NUM_XREGS == 31, "Unexpected number of aarch64 int. regs.");
 
 // The KVM interface accesses vector registers of 4 single precision
@@ -104,8 +104,8 @@
 #define FP_REGS_PER_VFP_REG 4
 
 const std::vector<ArmV8KvmCPU::IntRegInfo> ArmV8KvmCPU::intRegMap = {
-    { INT_REG(regs.sp), INTREG_SP0, "SP(EL0)" },
-    { INT_REG(sp_el1), INTREG_SP1, "SP(EL1)" },
+    { INT_REG(regs.sp), int_reg::Sp0, "SP(EL0)" },
+    { INT_REG(sp_el1), int_reg::Sp1, "SP(EL1)" },
 };
 
 const std::vector<ArmV8KvmCPU::MiscRegInfo> ArmV8KvmCPU::miscRegMap = {
@@ -225,11 +225,11 @@
 
     // update pstate register state
     CPSR cpsr(tc->readMiscReg(MISCREG_CPSR));
-    cpsr.nz = tc->readCCReg(CCREG_NZ);
-    cpsr.c = tc->readCCReg(CCREG_C);
-    cpsr.v = tc->readCCReg(CCREG_V);
+    cpsr.nz = tc->getReg(cc_reg::Nz);
+    cpsr.c = tc->getReg(cc_reg::C);
+    cpsr.v = tc->getReg(cc_reg::V);
     if (cpsr.width) {
-        cpsr.ge = tc->readCCReg(CCREG_GE);
+        cpsr.ge = tc->getReg(cc_reg::Ge);
     } else {
         cpsr.ge = 0;
     }
@@ -243,13 +243,13 @@
     }
 
     for (int i = 0; i < NUM_XREGS; ++i) {
-        const uint64_t value(tc->readIntReg(INTREG_X0 + i));
+        const uint64_t value = tc->getReg(int_reg::x(i));
         DPRINTF(KvmContext, "  X%i := 0x%x\n", i, value);
         setOneReg(kvmXReg(i), value);
     }
 
     for (const auto &ri : intRegMap) {
-        const uint64_t value(tc->readIntReg(ri.idx));
+        const uint64_t value = tc->getReg(RegId(IntRegClass, ri.idx));
         DPRINTF(KvmContext, "  %s := 0x%x\n", ri.name, value);
         setOneReg(ri.kvm, value);
     }
@@ -258,7 +258,9 @@
         KvmFPReg reg;
         if (!inAArch64(tc))
             syncVecElemsToRegs(tc);
-        auto v = tc->readVecReg(RegId(VecRegClass, i)).as<VecElem>();
+        ArmISA::VecRegContainer vc;
+        tc->getReg(RegId(VecRegClass, i), &vc);
+        auto v = vc.as<VecElem>();
         for (int j = 0; j < FP_REGS_PER_VFP_REG; j++)
             reg.s[j].i = v[j];
 
@@ -295,11 +297,11 @@
     const CPSR cpsr(getOneRegU64(INT_REG(regs.pstate)));
     DPRINTF(KvmContext, "  %s := 0x%x\n", "PSTATE", cpsr);
     tc->setMiscRegNoEffect(MISCREG_CPSR, cpsr);
-    tc->setCCReg(CCREG_NZ, cpsr.nz);
-    tc->setCCReg(CCREG_C, cpsr.c);
-    tc->setCCReg(CCREG_V, cpsr.v);
+    tc->setReg(cc_reg::Nz, cpsr.nz);
+    tc->setReg(cc_reg::C, cpsr.c);
+    tc->setReg(cc_reg::V, cpsr.v);
     if (cpsr.width) {
-        tc->setCCReg(CCREG_GE, cpsr.ge);
+        tc->setReg(cc_reg::Ge, cpsr.ge);
     }
 
     // Update core misc regs first as they
@@ -316,16 +318,16 @@
         // KVM64 returns registers in 64-bit layout. If we are in aarch32
         // mode, we need to map these to banked ARM32 registers.
         if (inAArch64(tc)) {
-            tc->setIntReg(INTREG_X0 + i, value);
+            tc->setReg(int_reg::x(i), value);
         } else {
-            tc->setIntRegFlat(IntReg64Map[INTREG_X0 + i], value);
+            tc->setRegFlat(int_reg::x(i), value);
         }
     }
 
     for (const auto &ri : intRegMap) {
         const auto value(getOneRegU64(ri.kvm));
         DPRINTF(KvmContext, "  %s := 0x%x\n", ri.name, value);
-        tc->setIntReg(ri.idx, value);
+        tc->setReg(RegId(IntRegClass, ri.idx), value);
     }
 
     for (int i = 0; i < NUM_QREGS; ++i) {
diff --git a/src/arch/arm/kvm/armv8_cpu.hh b/src/arch/arm/kvm/armv8_cpu.hh
index adca379..c66d2d2 100644
--- a/src/arch/arm/kvm/armv8_cpu.hh
+++ b/src/arch/arm/kvm/armv8_cpu.hh
@@ -97,13 +97,13 @@
     /** Mapping between integer registers in gem5 and KVM */
     struct IntRegInfo
     {
-        IntRegInfo(uint64_t _kvm, ArmISA::IntRegIndex _idx, const char *_name)
+        IntRegInfo(uint64_t _kvm, RegIndex _idx, const char *_name)
             : kvm(_kvm), idx(_idx), name(_name) {}
 
         /** Register index in KVM */
         uint64_t kvm;
         /** Register index in gem5 */
-        ArmISA::IntRegIndex idx;
+        RegIndex idx;
         /** Name to use in debug dumps */
         const char *name;
     };
diff --git a/src/arch/arm/kvm/base_cpu.cc b/src/arch/arm/kvm/base_cpu.cc
index fe922a1..d5c76aa 100644
--- a/src/arch/arm/kvm/base_cpu.cc
+++ b/src/arch/arm/kvm/base_cpu.cc
@@ -102,15 +102,15 @@
     struct kvm_vcpu_init target_config;
     memset(&target_config, 0, sizeof(target_config));
 
-    vm.kvmArmPreferredTarget(target_config);
+    vm->kvmArmPreferredTarget(target_config);
     if (!((ArmSystem *)system)->highestELIs64()) {
         target_config.features[0] |= (1 << KVM_ARM_VCPU_EL1_32BIT);
     }
     kvmArmVCpuInit(target_config);
 
-    if (!vm.hasKernelIRQChip())
+    if (!vm->hasKernelIRQChip())
         virtTimerPin = static_cast<ArmSystem *>(system)\
-            ->getGenericTimer()->params().int_virt->get(tc);
+            ->getGenericTimer()->params().int_el1_virt->get(tc);
 }
 
 Tick
@@ -120,14 +120,14 @@
     const bool simFIQ(interrupt->checkRaw(INT_FIQ));
     const bool simIRQ(interrupt->checkRaw(INT_IRQ));
 
-    if (!vm.hasKernelIRQChip()) {
+    if (!vm->hasKernelIRQChip()) {
         if (fiqAsserted != simFIQ) {
             DPRINTF(KvmInt, "KVM: Update FIQ state: %i\n", simFIQ);
-            vm.setIRQLine(INTERRUPT_VCPU_FIQ(vcpuID), simFIQ);
+            vm->setIRQLine(INTERRUPT_VCPU_FIQ(vcpuID), simFIQ);
         }
         if (irqAsserted != simIRQ) {
             DPRINTF(KvmInt, "KVM: Update IRQ state: %i\n", simIRQ);
-            vm.setIRQLine(INTERRUPT_VCPU_IRQ(vcpuID), simIRQ);
+            vm->setIRQLine(INTERRUPT_VCPU_IRQ(vcpuID), simIRQ);
         }
     } else {
         warn_if(simFIQ && !fiqAsserted,
@@ -144,7 +144,7 @@
 
     Tick kvmRunTicks = BaseKvmCPU::kvmRun(ticks);
 
-    if (!vm.hasKernelIRQChip()) {
+    if (!vm->hasKernelIRQChip()) {
         uint64_t device_irq_level =
             getKvmRunState()->s.regs.device_irq_level;
 
@@ -220,7 +220,8 @@
 BaseArmKvmCPU::kvmArmVCpuInit(const struct kvm_vcpu_init &init)
 {
     if (ioctl(KVM_ARM_VCPU_INIT, (void *)&init) == -1)
-        panic("KVM: Failed to initialize vCPU\n");
+        panic("KVM: Failed to initialize vCPU; errno %d (%s)\n",
+            errno, strerror(errno));
 }
 
 bool
diff --git a/src/arch/arm/kvm/gic.cc b/src/arch/arm/kvm/gic.cc
index ac7a7f5..e87bdde 100644
--- a/src/arch/arm/kvm/gic.cc
+++ b/src/arch/arm/kvm/gic.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2017 ARM Limited
+ * Copyright (c) 2015-2017, 2021 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -40,75 +40,90 @@
 #include <linux/kvm.h>
 
 #include "arch/arm/kvm/base_cpu.hh"
+#include "arch/arm/regs/misc.hh"
 #include "debug/GIC.hh"
 #include "debug/Interrupt.hh"
-#include "params/MuxingKvmGic.hh"
+#include "params/MuxingKvmGicV2.hh"
 
 namespace gem5
 {
 
-KvmKernelGicV2::KvmKernelGicV2(KvmVM &_vm, Addr cpu_addr, Addr dist_addr,
-                               unsigned it_lines)
-    : cpuRange(RangeSize(cpu_addr, KVM_VGIC_V2_CPU_SIZE)),
-      distRange(RangeSize(dist_addr, KVM_VGIC_V2_DIST_SIZE)),
-      vm(_vm),
-      kdev(vm.createDevice(KVM_DEV_TYPE_ARM_VGIC_V2))
+KvmKernelGic::KvmKernelGic(KvmVM &_vm, uint32_t dev, unsigned it_lines)
+    : vm(_vm),
+      kdev(vm.createDevice(dev))
 {
     // Tell the VM that we will emulate the GIC in the kernel. This
     // disables IRQ and FIQ handling in the KVM CPU model.
     vm.enableKernelIRQChip();
 
-    kdev.setAttr<uint64_t>(
-        KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_DIST, dist_addr);
-    kdev.setAttr<uint64_t>(
-        KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_CPU, cpu_addr);
-
     kdev.setAttr<uint32_t>(KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, it_lines);
 }
 
-KvmKernelGicV2::~KvmKernelGicV2()
+KvmKernelGic::~KvmKernelGic()
 {
 }
 
 void
-KvmKernelGicV2::setSPI(unsigned spi)
+KvmKernelGic::setSPI(unsigned spi)
 {
     setIntState(KVM_ARM_IRQ_TYPE_SPI, 0, spi, true);
 }
 
 void
-KvmKernelGicV2::clearSPI(unsigned spi)
+KvmKernelGic::clearSPI(unsigned spi)
 {
     setIntState(KVM_ARM_IRQ_TYPE_SPI, 0, spi, false);
 }
 
 void
-KvmKernelGicV2::setPPI(unsigned vcpu, unsigned ppi)
+KvmKernelGic::setPPI(unsigned vcpu, unsigned ppi)
 {
     setIntState(KVM_ARM_IRQ_TYPE_PPI, vcpu, ppi, true);
 }
 
 void
-KvmKernelGicV2::clearPPI(unsigned vcpu, unsigned ppi)
+KvmKernelGic::clearPPI(unsigned vcpu, unsigned ppi)
 {
     setIntState(KVM_ARM_IRQ_TYPE_PPI, vcpu, ppi, false);
 }
 
 void
-KvmKernelGicV2::setIntState(unsigned type, unsigned vcpu, unsigned irq,
-                            bool high)
+KvmKernelGic::setIntState(unsigned type, unsigned vcpu, unsigned irq,
+                          bool high)
 {
+    const unsigned vcpu_index = vcpu & 0xff;
+    const unsigned vcpu2_index = (vcpu >> 8) & 0xff;
+
+    static const bool vcpu2_enabled = vm.kvm->capIRQLineLayout2();
+    uint32_t kvm_vcpu = (vcpu_index << KVM_ARM_IRQ_VCPU_SHIFT);
+    if (vcpu2_enabled)
+        kvm_vcpu |= vcpu2_index << KVM_ARM_IRQ_VCPU2_SHIFT;
+
+    panic_if((!vcpu2_enabled && vcpu2_index) || kvm_vcpu > 0xffff,
+              "VCPU out of range");
+
     assert(type <= KVM_ARM_IRQ_TYPE_MASK);
-    assert(vcpu <= KVM_ARM_IRQ_VCPU_MASK);
     assert(irq <= KVM_ARM_IRQ_NUM_MASK);
     const uint32_t line(
+        kvm_vcpu |
         (type << KVM_ARM_IRQ_TYPE_SHIFT) |
-        (vcpu << KVM_ARM_IRQ_VCPU_SHIFT) |
         (irq << KVM_ARM_IRQ_NUM_SHIFT));
 
     vm.setIRQLine(line, high);
 }
 
+KvmKernelGicV2::KvmKernelGicV2(KvmVM &_vm,
+                               const MuxingKvmGicV2Params &p)
+    : KvmKernelGic(_vm, KVM_DEV_TYPE_ARM_VGIC_V2, p.it_lines),
+      cpuRange(RangeSize(p.cpu_addr, KVM_VGIC_V2_CPU_SIZE)),
+      distRange(RangeSize(p.dist_addr, KVM_VGIC_V2_DIST_SIZE))
+{
+    kdev.setAttr<uint64_t>(
+        KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_DIST, p.dist_addr);
+    kdev.setAttr<uint64_t>(
+        KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_CPU, p.cpu_addr);
+}
+
 uint32_t
 KvmKernelGicV2::getGicReg(unsigned group, unsigned vcpu, unsigned offset)
 {
@@ -165,269 +180,260 @@
     setGicReg(KVM_DEV_ARM_VGIC_GRP_CPU_REGS, vcpu, daddr, data);
 }
 
+#ifndef SZ_64K
+#define SZ_64K 0x00000040
+#endif
 
+KvmKernelGicV3::KvmKernelGicV3(KvmVM &_vm,
+                               const MuxingKvmGicV3Params &p)
+    : KvmKernelGic(_vm, KVM_DEV_TYPE_ARM_VGIC_V3, p.it_lines),
+      redistRange(RangeSize(p.redist_addr, KVM_VGIC_V3_REDIST_SIZE)),
+      distRange(RangeSize(p.dist_addr, KVM_VGIC_V3_DIST_SIZE))
+{
+    kdev.setAttr<uint64_t>(
+        KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, p.dist_addr);
+    kdev.setAttr<uint64_t>(
+        KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST, p.redist_addr);
+}
 
-MuxingKvmGic::MuxingKvmGic(const MuxingKvmGicParams &p)
-    : GicV2(p),
-      system(*p.system),
-      kernelGic(nullptr),
-      usingKvm(false)
+void
+KvmKernelGicV3::init()
+{
+    kdev.setAttr<uint64_t>(
+        KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_VGIC_CTRL_INIT, 0);
+}
+
+template <typename Ret>
+Ret
+KvmKernelGicV3::getGicReg(unsigned group, unsigned mpidr, unsigned offset)
+{
+    Ret reg;
+
+    assert(mpidr <= KVM_DEV_ARM_VGIC_V3_MPIDR_MASK);
+    const uint64_t attr(
+        ((uint64_t)mpidr << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT) |
+        (offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT));
+
+    kdev.getAttrPtr(group, attr, &reg);
+    return reg;
+}
+
+template <typename Arg>
+void
+KvmKernelGicV3::setGicReg(unsigned group, unsigned mpidr, unsigned offset,
+                          Arg value)
+{
+    Arg reg = value;
+
+    assert(mpidr <= KVM_DEV_ARM_VGIC_V3_MPIDR_MASK);
+    const uint64_t attr(
+        ((uint64_t)mpidr << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT) |
+        (offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT));
+
+    kdev.setAttrPtr(group, attr, &reg);
+}
+
+uint32_t
+KvmKernelGicV3::readDistributor(Addr daddr)
+{
+    return getGicReg<uint32_t>(KVM_DEV_ARM_VGIC_GRP_DIST_REGS, 0, daddr);
+}
+
+uint32_t
+KvmKernelGicV3::readRedistributor(const ArmISA::Affinity &aff, Addr daddr)
+{
+    return getGicReg<uint32_t>(KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, aff, daddr);
+}
+
+RegVal
+KvmKernelGicV3::readCpu(const ArmISA::Affinity &aff,
+                        ArmISA::MiscRegIndex misc_reg)
+{
+    auto sys_reg = ArmISA::encodeAArch64SysReg(misc_reg).packed();
+    return getGicReg<RegVal>(KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, aff, sys_reg);
+}
+
+void
+KvmKernelGicV3::writeDistributor(Addr daddr, uint32_t data)
+{
+    setGicReg<uint32_t>(KVM_DEV_ARM_VGIC_GRP_DIST_REGS, 0, daddr, data);
+}
+
+void
+KvmKernelGicV3::writeRedistributor(const ArmISA::Affinity &aff, Addr daddr,
+                                   uint32_t data)
+{
+    setGicReg<uint32_t>(KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, aff, daddr, data);
+}
+
+void
+KvmKernelGicV3::writeCpu(const ArmISA::Affinity &aff,
+                         ArmISA::MiscRegIndex misc_reg,
+                         RegVal data)
+{
+    auto sys_reg = ArmISA::encodeAArch64SysReg(misc_reg).packed();
+    setGicReg<RegVal>(KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, aff, sys_reg, data);
+}
+
+template <class Types>
+MuxingKvmGic<Types>::MuxingKvmGic(const Params &p)
+  : SimGic(p),
+    system(*p.system),
+    kernelGic(nullptr),
+    usingKvm(false)
 {
     auto vm = system.getKvmVM();
     if (vm && !p.simulate_gic) {
-        kernelGic = new KvmKernelGicV2(*vm, p.cpu_addr, p.dist_addr,
-                                       p.it_lines);
+        kernelGic = new KvmGic(*vm, p);
     }
 }
 
-MuxingKvmGic::~MuxingKvmGic()
-{
-}
-
+template <class Types>
 void
-MuxingKvmGic::startup()
+MuxingKvmGic<Types>::startup()
 {
-    GicV2::startup();
-    usingKvm = (kernelGic != nullptr) && system.validKvmEnvironment();
-    if (usingKvm)
-        fromGicV2ToKvm();
+    SimGic::startup();
+
+    if (kernelGic) {
+        kernelGic->init();
+
+        KvmVM *vm = system.getKvmVM();
+        if (vm && vm->validEnvironment()) {
+            usingKvm = true;
+            fromGicToKvm();
+        }
+    }
 }
 
+template <class Types>
 DrainState
-MuxingKvmGic::drain()
+MuxingKvmGic<Types>::drain()
 {
     if (usingKvm)
-        fromKvmToGicV2();
-    return GicV2::drain();
+        fromKvmToGic();
+    return SimGic::drain();
 }
 
+template <class Types>
 void
-MuxingKvmGic::drainResume()
+MuxingKvmGic<Types>::drainResume()
 {
-    GicV2::drainResume();
-    bool use_kvm = (kernelGic != nullptr) && system.validKvmEnvironment();
+    SimGic::drainResume();
+
+    KvmVM *vm = system.getKvmVM();
+    bool use_kvm = kernelGic && vm && vm->validEnvironment();
     if (use_kvm != usingKvm) {
         // Should only occur due to CPU switches
         if (use_kvm) // from simulation to KVM emulation
-            fromGicV2ToKvm();
+            fromGicToKvm();
         // otherwise, drain() already sync'd the state back to the GicV2
 
         usingKvm = use_kvm;
     }
 }
 
+template <class Types>
 Tick
-MuxingKvmGic::read(PacketPtr pkt)
+MuxingKvmGic<Types>::read(PacketPtr pkt)
 {
     if (!usingKvm)
-        return GicV2::read(pkt);
+        return SimGic::read(pkt);
 
     panic("MuxingKvmGic: PIO from gem5 is currently unsupported\n");
 }
 
+template <class Types>
 Tick
-MuxingKvmGic::write(PacketPtr pkt)
+MuxingKvmGic<Types>::write(PacketPtr pkt)
 {
     if (!usingKvm)
-        return GicV2::write(pkt);
+        return SimGic::write(pkt);
 
     panic("MuxingKvmGic: PIO from gem5 is currently unsupported\n");
 }
 
+template <class Types>
 void
-MuxingKvmGic::sendInt(uint32_t num)
+MuxingKvmGic<Types>::sendInt(uint32_t num)
 {
     if (!usingKvm)
-        return GicV2::sendInt(num);
+        return SimGic::sendInt(num);
 
     DPRINTF(Interrupt, "Set SPI %d\n", num);
     kernelGic->setSPI(num);
 }
 
+template <class Types>
 void
-MuxingKvmGic::clearInt(uint32_t num)
+MuxingKvmGic<Types>::clearInt(uint32_t num)
 {
     if (!usingKvm)
-        return GicV2::clearInt(num);
+        return SimGic::clearInt(num);
 
     DPRINTF(Interrupt, "Clear SPI %d\n", num);
     kernelGic->clearSPI(num);
 }
 
+template <class Types>
 void
-MuxingKvmGic::sendPPInt(uint32_t num, uint32_t cpu)
+MuxingKvmGic<Types>::sendPPInt(uint32_t num, uint32_t cpu)
 {
     if (!usingKvm)
-        return GicV2::sendPPInt(num, cpu);
+        return SimGic::sendPPInt(num, cpu);
     DPRINTF(Interrupt, "Set PPI %d:%d\n", cpu, num);
     kernelGic->setPPI(cpu, num);
 }
 
+template <class Types>
 void
-MuxingKvmGic::clearPPInt(uint32_t num, uint32_t cpu)
+MuxingKvmGic<Types>::clearPPInt(uint32_t num, uint32_t cpu)
 {
     if (!usingKvm)
-        return GicV2::clearPPInt(num, cpu);
+        return SimGic::clearPPInt(num, cpu);
 
     DPRINTF(Interrupt, "Clear PPI %d:%d\n", cpu, num);
     kernelGic->clearPPI(cpu, num);
 }
 
-void
-MuxingKvmGic::updateIntState(int hint)
+template <class Types>
+bool
+MuxingKvmGic<Types>::blockIntUpdate() const
 {
-    // During Kvm->GicV2 state transfer, writes to the GicV2 will call
+    // During Kvm->Gic state transfer, writes to the Gic will call
     // updateIntState() which can post an interrupt.  Since we're only
-    // using the GicV2 model for holding state in this circumstance, we
+    // using the Gic model for holding state in this circumstance, we
     // short-circuit this behavior, as the GicV2 is not actually active.
-    if (!usingKvm)
-        return GicV2::updateIntState(hint);
+    return usingKvm;
 }
 
+template <class Types>
 void
-MuxingKvmGic::copyDistRegister(BaseGicRegisters* from, BaseGicRegisters* to,
-                               ContextID ctx, Addr daddr)
+MuxingKvmGic<Types>::fromGicToKvm()
 {
-    auto val = from->readDistributor(ctx, daddr);
-    DPRINTF(GIC, "copy dist 0x%x 0x%08x\n", daddr, val);
-    to->writeDistributor(ctx, daddr, val);
+    this->copyGicState(static_cast<SimGic*>(this),
+                       static_cast<KvmGic*>(kernelGic));
 }
 
+template <class Types>
 void
-MuxingKvmGic::copyCpuRegister(BaseGicRegisters* from, BaseGicRegisters* to,
-                               ContextID ctx, Addr daddr)
+MuxingKvmGic<Types>::fromKvmToGic()
 {
-    auto val = from->readCpu(ctx, daddr);
-    DPRINTF(GIC, "copy cpu  0x%x 0x%08x\n", daddr, val);
-    to->writeCpu(ctx, daddr, val);
-}
-
-void
-MuxingKvmGic::copyBankedDistRange(BaseGicRegisters* from, BaseGicRegisters* to,
-                                  Addr daddr, size_t size)
-{
-    for (int ctx = 0; ctx < system.threads.size(); ++ctx)
-        for (auto a = daddr; a < daddr + size; a += 4)
-            copyDistRegister(from, to, ctx, a);
-}
-
-void
-MuxingKvmGic::clearBankedDistRange(BaseGicRegisters* to,
-                                   Addr daddr, size_t size)
-{
-    for (int ctx = 0; ctx < system.threads.size(); ++ctx)
-        for (auto a = daddr; a < daddr + size; a += 4)
-            to->writeDistributor(ctx, a, 0xFFFFFFFF);
-}
-
-void
-MuxingKvmGic::copyDistRange(BaseGicRegisters* from, BaseGicRegisters* to,
-                            Addr daddr, size_t size)
-{
-    for (auto a = daddr; a < daddr + size; a += 4)
-        copyDistRegister(from, to, 0, a);
-}
-
-void
-MuxingKvmGic::clearDistRange(BaseGicRegisters* to,
-                             Addr daddr, size_t size)
-{
-    for (auto a = daddr; a < daddr + size; a += 4)
-        to->writeDistributor(0, a, 0xFFFFFFFF);
-}
-
-void
-MuxingKvmGic::copyGicState(BaseGicRegisters* from, BaseGicRegisters* to)
-{
-    Addr set, clear;
-    size_t size;
-
-    /// CPU state (GICC_*)
-    // Copy CPU Interface Control Register (CTLR),
-    //      Interrupt Priority Mask Register (PMR), and
-    //      Binary Point Register (BPR)
-    for (int ctx = 0; ctx < system.threads.size(); ++ctx) {
-        copyCpuRegister(from, to, ctx, GICC_CTLR);
-        copyCpuRegister(from, to, ctx, GICC_PMR);
-        copyCpuRegister(from, to, ctx, GICC_BPR);
-    }
-
-
-    /// Distributor state (GICD_*)
-    // Copy Distributor Control Register (CTLR)
-    copyDistRegister(from, to, 0, GICD_CTLR);
-
-    // Copy interrupt-enabled statuses (I[CS]ENABLERn; R0 is per-CPU banked)
-    set   = GicV2::GICD_ISENABLER.start();
-    clear = GicV2::GICD_ICENABLER.start();
-    size  = GicV2::itLines / 8;
-    clearBankedDistRange(to, clear, 4);
-    copyBankedDistRange(from, to, set, 4);
-
-    set += 4, clear += 4, size -= 4;
-    clearDistRange(to, clear, size);
-    copyDistRange(from, to, set, size);
-
-    // Copy pending interrupts (I[CS]PENDRn; R0 is per-CPU banked)
-    set   = GicV2::GICD_ISPENDR.start();
-    clear = GicV2::GICD_ICPENDR.start();
-    size  = GicV2::itLines / 8;
-    clearBankedDistRange(to, clear, 4);
-    copyBankedDistRange(from, to, set, 4);
-
-    set += 4, clear += 4, size -= 4;
-    clearDistRange(to, clear, size);
-    copyDistRange(from, to, set, size);
-
-    // Copy active interrupts (I[CS]ACTIVERn; R0 is per-CPU banked)
-    set   = GicV2::GICD_ISACTIVER.start();
-    clear = GicV2::GICD_ICACTIVER.start();
-    size  = GicV2::itLines / 8;
-    clearBankedDistRange(to, clear, 4);
-    copyBankedDistRange(from, to, set, 4);
-
-    set += 4, clear += 4, size -= 4;
-    clearDistRange(to, clear, size);
-    copyDistRange(from, to, set, size);
-
-    // Copy interrupt priorities (IPRIORITYRn; R0-7 are per-CPU banked)
-    set   = GicV2::GICD_IPRIORITYR.start();
-    copyBankedDistRange(from, to, set, 32);
-
-    set += 32;
-    size = GicV2::itLines - 32;
-    copyDistRange(from, to, set, size);
-
-    // Copy interrupt processor target regs (ITARGETRn; R0-7 are read-only)
-    set = GicV2::GICD_ITARGETSR.start() + 32;
-    size = GicV2::itLines - 32;
-    copyDistRange(from, to, set, size);
-
-    // Copy interrupt configuration registers (ICFGRn)
-    set = GicV2::GICD_ICFGR.start();
-    size = GicV2::itLines / 4;
-    copyDistRange(from, to, set, size);
-}
-
-void
-MuxingKvmGic::fromGicV2ToKvm()
-{
-    copyGicState(static_cast<GicV2*>(this), kernelGic);
-}
-
-void
-MuxingKvmGic::fromKvmToGicV2()
-{
-    copyGicState(kernelGic, static_cast<GicV2*>(this));
+    this->copyGicState(static_cast<KvmGic*>(kernelGic),
+                       static_cast<SimGic*>(this));
 
     // the values read for the Interrupt Priority Mask Register (PMR)
     // have been shifted by three bits due to its having been emulated by
     // a VGIC with only 5 PMR bits in its VMCR register.  Presently the
     // Linux kernel does not repair this inaccuracy, so we correct it here.
-    for (int cpu = 0; cpu < system.threads.size(); ++cpu) {
-       cpuPriority[cpu] <<= 3;
-       assert((cpuPriority[cpu] & ~0xff) == 0);
+    if constexpr(std::is_same<SimGic, GicV2>::value) {
+        for (int cpu = 0; cpu < system.threads.size(); ++cpu) {
+           this->cpuPriority[cpu] <<= 3;
+           assert((this->cpuPriority[cpu] & ~0xff) == 0);
+        }
     }
 }
 
+template class MuxingKvmGic<GicV2Types>;
+template class MuxingKvmGic<GicV3Types>;
+
 } // namespace gem5
diff --git a/src/arch/arm/kvm/gic.hh b/src/arch/arm/kvm/gic.hh
index 465ecdf..f864945 100644
--- a/src/arch/arm/kvm/gic.hh
+++ b/src/arch/arm/kvm/gic.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2017 ARM Limited
+ * Copyright (c) 2015-2017, 2021-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -42,8 +42,12 @@
 #include "cpu/kvm/device.hh"
 #include "cpu/kvm/vm.hh"
 #include "dev/arm/gic_v2.hh"
+#include "dev/arm/gic_v3.hh"
 #include "dev/platform.hh"
 
+#include "params/MuxingKvmGicV2.hh"
+#include "params/MuxingKvmGicV3.hh"
+
 namespace gem5
 {
 
@@ -54,7 +58,7 @@
  * model. It exposes an API that is similar to that of
  * software-emulated GIC models in gem5.
  */
-class KvmKernelGicV2 : public BaseGicRegisters
+class KvmKernelGic
 {
   public:
     /**
@@ -68,14 +72,15 @@
      * @param dist_addr GIC distributor base address
      * @param it_lines Number of interrupt lines to support
      */
-    KvmKernelGicV2(KvmVM &vm, Addr cpu_addr, Addr dist_addr,
-                   unsigned it_lines);
-    virtual ~KvmKernelGicV2();
+    KvmKernelGic(KvmVM &vm, uint32_t dev, unsigned it_lines);
+    virtual ~KvmKernelGic();
 
-    KvmKernelGicV2(const KvmKernelGicV2 &other) = delete;
-    KvmKernelGicV2(const KvmKernelGicV2 &&other) = delete;
-    KvmKernelGicV2 &operator=(const KvmKernelGicV2 &&rhs) = delete;
-    KvmKernelGicV2 &operator=(const KvmKernelGicV2 &rhs) = delete;
+    KvmKernelGic(const KvmKernelGic &other) = delete;
+    KvmKernelGic(const KvmKernelGic &&other) = delete;
+    KvmKernelGic &operator=(const KvmKernelGic &&rhs) = delete;
+    KvmKernelGic &operator=(const KvmKernelGic &rhs) = delete;
+
+    virtual void init() {}
 
   public:
     /**
@@ -112,19 +117,6 @@
      */
     void clearPPI(unsigned vcpu, unsigned ppi);
 
-    /** Address range for the CPU interfaces */
-    const AddrRange cpuRange;
-    /** Address range for the distributor interface */
-    const AddrRange distRange;
-
-    /** BaseGicRegisters interface */
-    uint32_t readDistributor(ContextID ctx, Addr daddr) override;
-    uint32_t readCpu(ContextID ctx, Addr daddr) override;
-
-    void writeDistributor(ContextID ctx, Addr daddr,
-                          uint32_t data) override;
-    void writeCpu(ContextID ctx, Addr daddr, uint32_t data) override;
-
     /* @} */
 
   protected:
@@ -138,6 +130,37 @@
      */
     void setIntState(unsigned type, unsigned vcpu, unsigned irq, bool high);
 
+    /** KVM VM in the parent system */
+    KvmVM &vm;
+
+    /** Kernel interface to the GIC */
+    KvmDevice kdev;
+};
+
+class KvmKernelGicV2 : public KvmKernelGic, public GicV2Registers
+{
+  public:
+    /**
+     * Instantiate a KVM in-kernel GICv2 model.
+     *
+     * This constructor instantiates an in-kernel GICv2 model and wires
+     * it up to the virtual memory system.
+     *
+     * @param vm KVM VM representing this system
+     * @param params MuxingKvmGicV2 params
+     */
+    KvmKernelGicV2(KvmVM &vm,
+                   const MuxingKvmGicV2Params &params);
+
+  public: // GicV2Registers
+    uint32_t readDistributor(ContextID ctx, Addr daddr) override;
+    uint32_t readCpu(ContextID ctx, Addr daddr) override;
+
+    void writeDistributor(ContextID ctx, Addr daddr,
+                          uint32_t data) override;
+    void writeCpu(ContextID ctx, Addr daddr, uint32_t data) override;
+
+  protected:
     /**
      * Get value of GIC register "from" a cpu
      *
@@ -158,21 +181,96 @@
     void setGicReg(unsigned group, unsigned vcpu, unsigned offset,
                    unsigned value);
 
-    /** KVM VM in the parent system */
-    KvmVM &vm;
-
-    /** Kernel interface to the GIC */
-    KvmDevice kdev;
+  private:
+    /** Address range for the CPU interfaces */
+    const AddrRange cpuRange;
+    /** Address range for the distributor */
+    const AddrRange distRange;
 };
 
-
-struct MuxingKvmGicParams;
-
-class MuxingKvmGic : public GicV2
+class KvmKernelGicV3 : public KvmKernelGic, public Gicv3Registers
 {
+  public:
+    /**
+     * Instantiate a KVM in-kernel GICv3 model.
+     *
+     * This constructor instantiates an in-kernel GICv3 model and wires
+     * it up to the virtual memory system.
+     *
+     * @param vm KVM VM representing this system
+     * @param params MuxingKvmGicV3 parameters
+     */
+    KvmKernelGicV3(KvmVM &vm,
+                   const MuxingKvmGicV3Params &params);
+
+    void init() override;
+
+  public: // Gicv3Registers
+    uint32_t readDistributor(Addr daddr) override;
+    uint32_t readRedistributor(const ArmISA::Affinity &aff,
+                               Addr daddr) override;
+    RegVal readCpu(const ArmISA::Affinity &aff,
+                   ArmISA::MiscRegIndex misc_reg) override;
+
+    void writeDistributor(Addr daddr, uint32_t data) override;
+    void writeRedistributor(const ArmISA::Affinity &aff,
+                            Addr daddr, uint32_t data) override;
+    void writeCpu(const ArmISA::Affinity &aff,
+                  ArmISA::MiscRegIndex misc_reg, RegVal data) override;
+
+  protected:
+    /**
+     * Get value of GIC register "from" a cpu
+     *
+     * @param group Distributor or CPU (KVM_DEV_ARM_VGIC_GRP_{DIST,CPU}_REGS)
+     * @param mpidr CPU affinity numbers
+     * @param offset register offset
+     */
+    template <typename Ret>
+    Ret getGicReg(unsigned group, unsigned mpidr, unsigned offset);
+
+    /**
+     * Set value of GIC register "from" a cpu
+     *
+     * @param group Distributor or CPU (KVM_DEV_ARM_VGIC_GRP_{DIST,CPU}_REGS)
+     * @param mpidr CPU affinity numbers
+     * @param offset register offset
+     * @param value value to set register to
+     */
+    template <typename Arg>
+    void setGicReg(unsigned group, unsigned mpidr, unsigned offset,
+                   Arg value);
+
+  private:
+    /** Address range for the redistributor */
+    const AddrRange redistRange;
+    /** Address range for the distributor */
+    const AddrRange distRange;
+};
+
+struct GicV2Types
+{
+    using SimGic = GicV2;
+    using KvmGic = KvmKernelGicV2;
+    using Params = MuxingKvmGicV2Params;
+};
+
+struct GicV3Types
+{
+    using SimGic = Gicv3;
+    using KvmGic = KvmKernelGicV3;
+    using Params = MuxingKvmGicV3Params;
+};
+
+template <class Types>
+class MuxingKvmGic : public Types::SimGic
+{
+    using SimGic = typename Types::SimGic;
+    using KvmGic = typename Types::KvmGic;
+    using Params = typename Types::Params;
+
   public: // SimObject / Serializable / Drainable
-    MuxingKvmGic(const MuxingKvmGicParams &p);
-    ~MuxingKvmGic();
+    MuxingKvmGic(const Params &p);
 
     void startup() override;
     DrainState drain() override;
@@ -189,38 +287,22 @@
     void sendPPInt(uint32_t num, uint32_t cpu) override;
     void clearPPInt(uint32_t num, uint32_t cpu) override;
 
-  protected: // GicV2
-    void updateIntState(int hint) override;
+  protected: // BaseGic
+    bool blockIntUpdate() const override;
 
   protected:
     /** System this interrupt controller belongs to */
     System &system;
 
     /** Kernel GIC device */
-    KvmKernelGicV2 *kernelGic;
+    KvmKernelGic *kernelGic;
 
   private:
     bool usingKvm;
 
     /** Multiplexing implementation */
-    void fromGicV2ToKvm();
-    void fromKvmToGicV2();
-
-    void copyGicState(BaseGicRegisters* from, BaseGicRegisters* to);
-
-    void copyDistRegister(BaseGicRegisters* from, BaseGicRegisters* to,
-                          ContextID ctx, Addr daddr);
-    void copyCpuRegister(BaseGicRegisters* from, BaseGicRegisters* to,
-                         ContextID ctx, Addr daddr);
-
-    void copyBankedDistRange(BaseGicRegisters* from, BaseGicRegisters* to,
-                             Addr daddr, size_t size);
-    void clearBankedDistRange(BaseGicRegisters* to,
-                              Addr daddr, size_t size);
-    void copyDistRange(BaseGicRegisters* from, BaseGicRegisters* to,
-                       Addr daddr, size_t size);
-    void clearDistRange(BaseGicRegisters* to,
-                        Addr daddr, size_t size);
+    void fromGicToKvm();
+    void fromKvmToGic();
 };
 
 } // namespace gem5
diff --git a/src/arch/arm/linux/fs_workload.cc b/src/arch/arm/linux/fs_workload.cc
index d512067..26146ef 100644
--- a/src/arch/arm/linux/fs_workload.cc
+++ b/src/arch/arm/linux/fs_workload.cc
@@ -91,6 +91,21 @@
     bool dtb_file_specified = params().dtb_filename != "";
 
     if (kernel_has_fdt_support && dtb_file_specified) {
+        bool initrd_file_specified = params().initrd_filename != "";
+        size_t initrd_len = 0;
+
+        if (initrd_file_specified) {
+            inform("Loading initrd file: %s at address %#x\n",
+                    params().initrd_filename, params().initrd_addr);
+
+            loader::ImageFileDataPtr initrd_file_data(
+                new loader::ImageFileData(params().initrd_filename));
+            system->physProxy.writeBlob(params().initrd_addr,
+                                        initrd_file_data->data(),
+                                        initrd_file_data->len());
+            initrd_len = initrd_file_data->len();
+        }
+
         // Kernel supports flattened device tree and dtb file specified.
         // Using Device Tree Blob to describe system configuration.
         inform("Loading DTB file: %s at address %#x\n", params().dtb_filename,
@@ -98,8 +113,8 @@
 
         auto *dtb_file = new loader::DtbFile(params().dtb_filename);
 
-        if (!dtb_file->addBootCmdLine(
-                    commandLine.c_str(), commandLine.size())) {
+        if (!dtb_file->addBootData(commandLine.c_str(), commandLine.size(),
+                                   params().initrd_addr, initrd_len)) {
             warn("couldn't append bootargs to DTB file: %s\n",
                  params().dtb_filename);
         }
@@ -164,15 +179,15 @@
         // originally done because the entry offset changed in kernel v5.8.
         // Previously the bootloader just used a hardcoded address.
         for (auto *tc: system->threads) {
-            tc->setIntReg(0, params().dtb_addr);
-            tc->setIntReg(5, params().cpu_release_addr);
+            tc->setReg(int_reg::X0, params().dtb_addr);
+            tc->setReg(int_reg::X5, params().cpu_release_addr);
         }
     } else {
         // Kernel boot requirements to set up r0, r1 and r2 in ARMv7
         for (auto *tc: system->threads) {
-            tc->setIntReg(0, 0);
-            tc->setIntReg(1, params().machine_type);
-            tc->setIntReg(2, params().dtb_addr);
+            tc->setReg(int_reg::R0, (RegVal)0);
+            tc->setReg(int_reg::R1, params().machine_type);
+            tc->setReg(int_reg::R2, params().dtb_addr);
         }
     }
 }
@@ -286,7 +301,7 @@
     uint32_t &tgid, std::string &next_task_str, int32_t &mm) {
 
     linux::ThreadInfo ti(tc);
-    Addr task_descriptor = tc->readIntReg(2);
+    Addr task_descriptor = tc->getReg(int_reg::R2);
     pid = ti.curTaskPID(task_descriptor);
     tgid = ti.curTaskTGID(task_descriptor);
     next_task_str = ti.curTaskName(task_descriptor);
@@ -308,7 +323,7 @@
     uint32_t &tgid, std::string &next_task_str, int32_t &mm) {
 
     linux::ThreadInfo ti(tc);
-    Addr task_struct = tc->readIntReg(1);
+    Addr task_struct = tc->getReg(int_reg::X1);
     pid = ti.curTaskPIDFromTaskStruct(task_struct);
     tgid = ti.curTaskTGIDFromTaskStruct(task_struct);
     next_task_str = ti.curTaskNameFromTaskStruct(task_struct);
diff --git a/src/arch/arm/linux/linux.hh b/src/arch/arm/linux/linux.hh
index 66c2f9e..6c19981 100644
--- a/src/arch/arm/linux/linux.hh
+++ b/src/arch/arm/linux/linux.hh
@@ -290,7 +290,7 @@
         ArmLinux::archClone(flags, pp, cp, ptc, ctc, stack, tls);
 
         if (stack)
-            ctc->setIntReg(ArmISA::INTREG_SP, stack);
+            ctc->setReg(ArmISA::int_reg::Sp, stack);
     }
 };
 
@@ -544,7 +544,7 @@
         ArmLinux::archClone(flags, pp, cp, ptc, ctc, stack, tls);
 
         if (stack)
-            ctc->setIntReg(ArmISA::INTREG_SP0, stack);
+            ctc->setReg(ArmISA::int_reg::Sp0, stack);
     }
 };
 
diff --git a/src/arch/arm/linux/se_workload.cc b/src/arch/arm/linux/se_workload.cc
index b511e02..c919d85 100644
--- a/src/arch/arm/linux/se_workload.cc
+++ b/src/arch/arm/linux/se_workload.cc
@@ -488,6 +488,7 @@
         { base + 363, "sys_rt_tgsigqueueinfo" },
         { base + 364, "sys_perf_event_open" },
         { base + 365, "sys_recvmmsg" },
+        { base + 384, "getrandom", getrandomFunc<ArmLinux32> }
     })
     {}
 };
@@ -758,6 +759,29 @@
         {  base + 269, "sendmmsg" },
         {  base + 270, "process_vm_readv" },
         {  base + 271, "process_vm_writev" },
+        {  base + 272, "kcmp" },
+        {  base + 273, "finit_module" },
+        {  base + 274, "sched_setattr"},
+        {  base + 275, "sched_getattr"},
+        {  base + 276, "renameat2"},
+        {  base + 277, "seccomp"},
+        {  base + 278, "getrandom", getrandomFunc<ArmLinux64> },
+        {  base + 279, "memfd_create" },
+        {  base + 280, "bpf" },
+        {  base + 281, "execveat"},
+        {  base + 282, "userfaultfd"},
+        {  base + 283, "membarrier"},
+        {  base + 284, "mlock2"},
+        {  base + 285, "copy_file_range"},
+        {  base + 286, "preadv2"},
+        {  base + 287, "pwritev2"},
+        {  base + 288, "pkey_mprotect"},
+        {  base + 289, "pkey_alloc"},
+        {  base + 290, "pkey_free"},
+        {  base + 291, "statx"},
+        {  base + 292, "io_pgetevents"},
+        {  base + 293, "rseq", ignoreWarnOnceFunc },
+        {  base + 294, "kexec_file_load"},
         { base + 1024, "open", openFunc<ArmLinux64> },
         { base + 1025, "link" },
         { base + 1026, "unlink", unlinkFunc },
@@ -848,14 +872,14 @@
 
     SyscallDesc *desc = nullptr;
     if (dynamic_cast<ArmLinuxProcess64 *>(process)) {
-        int num = tc->readIntReg(INTREG_X8);
+        int num = tc->getReg(int_reg::X8);
         desc = syscallDescs64Low.get(num, false);
         if (!desc)
             desc = syscallDescs64Low.get(num, false);
         if (!desc)
             desc = privSyscallDescs64.get(num);
     } else {
-        int num = tc->readIntReg(INTREG_R7);
+        int num = tc->getReg(int_reg::R7);
         desc = syscallDescs32Low.get(num, false);
         if (!desc)
             desc = syscallDescs32Low.get(num, false);
diff --git a/src/arch/arm/linux/se_workload.hh b/src/arch/arm/linux/se_workload.hh
index f0af647..0939af1 100644
--- a/src/arch/arm/linux/se_workload.hh
+++ b/src/arch/arm/linux/se_workload.hh
@@ -74,9 +74,9 @@
     static void
     store(ThreadContext *tc, const SyscallReturn &ret)
     {
-        tc->setIntReg(ArmISA::ReturnValueReg, ret.encodedValue());
+        tc->setReg(ArmISA::ReturnValueReg, ret.encodedValue());
         if (ret.count() > 1)
-            tc->setIntReg(ArmISA::SyscallPseudoReturnReg, ret.value2());
+            tc->setReg(ArmISA::SyscallPseudoReturnReg, ret.value2());
     }
 };
 
diff --git a/src/arch/arm/mmu.cc b/src/arch/arm/mmu.cc
index 79a5240..ff55c3c 100644
--- a/src/arch/arm/mmu.cc
+++ b/src/arch/arm/mmu.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013, 2016-2021 Arm Limited
+ * Copyright (c) 2010-2013, 2016-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -197,6 +197,8 @@
 MMU::invalidateMiscReg()
 {
     s1State.miscRegValid = false;
+    s1State.computeAddrTop.flush();
+    s2State.computeAddrTop.flush();
 }
 
 Fault
@@ -237,11 +239,12 @@
     updateMiscReg(tc, NormalTran, state.isStage2);
     Addr vaddr_tainted = req->getVaddr();
     Addr vaddr = 0;
-    if (state.aarch64)
+    if (state.aarch64) {
         vaddr = purifyTaggedAddr(vaddr_tainted, tc, state.aarch64EL,
-                                 (TCR)state.ttbcr, mode==Execute);
-    else
+            static_cast<TCR>(state.ttbcr), mode==Execute, state);
+    } else {
         vaddr = vaddr_tainted;
+    }
     Request::Flags flags = req->getFlags();
 
     bool is_fetch = (mode == Execute);
@@ -480,7 +483,7 @@
 
     Addr vaddr_tainted = req->getVaddr();
     Addr vaddr = purifyTaggedAddr(vaddr_tainted, tc, state.aarch64EL,
-        (TCR)state.ttbcr, mode==Execute);
+        static_cast<TCR>(state.ttbcr), mode==Execute, state);
 
     Request::Flags flags = req->getFlags();
     bool is_fetch  = (mode == Execute);
@@ -771,8 +774,7 @@
     // gem5)
     // 4) Instructions to be treated as unprivileged, unless
     // HCR_EL2.{E2H, TGE} == {1, 0}
-    const AA64MMFR1 mmfr1 = tc->readMiscReg(MISCREG_ID_AA64MMFR1_EL1);
-    if (mmfr1.pan && state.cpsr.pan && (ap & 0x1) &&
+    if (HaveExt(tc, ArmExtension::FEAT_PAN) && state.cpsr.pan && (ap & 0x1) &&
         mode != BaseMMU::Execute) {
 
         if (req->isCacheMaintenance() &&
@@ -789,6 +791,18 @@
     return false;
 }
 
+Addr
+MMU::purifyTaggedAddr(Addr vaddr_tainted, ThreadContext *tc, ExceptionLevel el,
+                      TCR tcr, bool is_inst, CachedState& state)
+{
+    const bool selbit = bits(vaddr_tainted, 55);
+
+    // Call the memoized version of computeAddrTop
+    const auto topbit = state.computeAddrTop(tc, selbit, is_inst, tcr, el);
+
+    return maskTaggedAddr(vaddr_tainted, tc, el, topbit);
+}
+
 Fault
 MMU::translateMmuOff(ThreadContext *tc, const RequestPtr &req, Mode mode,
         ArmTranslationType tran_type, Addr vaddr, bool long_desc_format,
@@ -835,8 +849,8 @@
     // Set memory attributes
     TlbEntry temp_te;
     temp_te.ns = !state.isSecure;
-    bool dc = (HaveVirtHostExt(tc)
-               && state.hcr.e2h == 1 && state.hcr.tge == 1) ? 0: state.hcr.dc;
+    bool dc = (HaveExt(tc, ArmExtension::FEAT_VHE) &&
+               state.hcr.e2h == 1 && state.hcr.tge == 1) ? 0: state.hcr.dc;
     bool i_cacheability = state.sctlr.i && !state.sctlr.m;
     if (state.isStage2 || !dc || state.isSecure ||
        (state.isHyp && !(tran_type & S1CTran))) {
@@ -947,11 +961,12 @@
 
     Addr vaddr_tainted = req->getVaddr();
     Addr vaddr = 0;
-    if (state.aarch64)
+    if (state.aarch64) {
         vaddr = purifyTaggedAddr(vaddr_tainted, tc, state.aarch64EL,
-            (TCR)state.ttbcr, mode==Execute);
-    else
+            static_cast<TCR>(state.ttbcr), mode==Execute, state);
+    } else {
         vaddr = vaddr_tainted;
+    }
     Request::Flags flags = req->getFlags();
 
     bool is_fetch  = (mode == Execute);
@@ -992,7 +1007,8 @@
     }
 
     bool vm = state.hcr.vm;
-    if (HaveVirtHostExt(tc) && state.hcr.e2h == 1 && state.hcr.tge ==1)
+    if (HaveExt(tc, ArmExtension::FEAT_VHE) &&
+        state.hcr.e2h == 1 && state.hcr.tge == 1)
         vm = 0;
     else if (state.hcr.dc == 1)
         vm = 1;
@@ -1217,7 +1233,8 @@
         // determine EL we need to translate in
         switch (aarch64EL) {
           case EL0:
-            if (HaveVirtHostExt(tc) && hcr.tge == 1 && hcr.e2h == 1) {
+            if (HaveExt(tc, ArmExtension::FEAT_VHE) &&
+                hcr.tge == 1 && hcr.e2h == 1) {
                 // VHE code for EL2&0 regime
                 sctlr = tc->readMiscReg(MISCREG_SCTLR_EL2);
                 ttbcr = tc->readMiscReg(MISCREG_TCR_EL2);
@@ -1279,7 +1296,8 @@
             isHyp &= (tran_type & S1S2NsTran) == 0;
             isHyp &= (tran_type & S1CTran)    == 0;
             bool vm = hcr.vm;
-            if (HaveVirtHostExt(tc) && hcr.e2h == 1 && hcr.tge ==1) {
+            if (HaveExt(tc, ArmExtension::FEAT_VHE) &&
+                hcr.e2h == 1 && hcr.tge ==1) {
                 vm = 0;
             }
 
@@ -1443,7 +1461,7 @@
     ExceptionLevel target_el = state.aarch64 ? state.aarch64EL : EL1;
     if (state.aarch64) {
         vaddr = purifyTaggedAddr(vaddr_tainted, tc, target_el,
-            (TCR)state.ttbcr, mode==Execute);
+            static_cast<TCR>(state.ttbcr), mode==Execute, state);
     } else {
         vaddr = vaddr_tainted;
     }
diff --git a/src/arch/arm/mmu.hh b/src/arch/arm/mmu.hh
index fc5b307..171dbf5 100644
--- a/src/arch/arm/mmu.hh
+++ b/src/arch/arm/mmu.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013, 2016, 2019-2021 Arm Limited
+ * Copyright (c) 2010-2013, 2016, 2019-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -43,8 +43,9 @@
 
 #include "arch/arm/page_size.hh"
 #include "arch/arm/tlb.hh"
+#include "arch/arm/utility.hh"
 #include "arch/generic/mmu.hh"
-
+#include "base/memoizer.hh"
 #include "enums/ArmLookupLevel.hh"
 
 #include "params/ArmMMU.hh"
@@ -130,11 +131,44 @@
         S12E1Tran = 0x100
     };
 
-    struct CachedState {
-        explicit CachedState(MMU *_mmu, bool stage2)
-          : mmu(_mmu), isStage2(stage2)
+    struct CachedState
+    {
+        CachedState(MMU *_mmu, bool stage2)
+          : mmu(_mmu), isStage2(stage2),
+            computeAddrTop(ArmISA::computeAddrTop)
         {}
 
+        CachedState&
+        operator=(const CachedState &rhs)
+        {
+            isStage2 = rhs.isStage2;
+            cpsr = rhs.cpsr;
+            aarch64 = rhs.aarch64;
+            aarch64EL = EL0;
+            sctlr = rhs.sctlr;
+            scr = rhs.scr;
+            isPriv = rhs.isPriv;
+            isSecure = rhs.isSecure;
+            isHyp = rhs.isHyp;
+            ttbcr = rhs.ttbcr;
+            asid = rhs.asid;
+            vmid = rhs.vmid;
+            prrr = rhs.prrr;
+            nmrr = rhs.nmrr;
+            hcr = rhs.hcr;
+            dacr = rhs.dacr;
+            miscRegValid = rhs.miscRegValid;
+            curTranType = rhs.curTranType;
+            stage2Req = rhs.stage2Req;
+            stage2DescReq = rhs.stage2DescReq;
+            directToStage2 = rhs.directToStage2;
+
+            // When we copy we just flush the memoizer cache
+            computeAddrTop.flush();
+
+            return *this;
+        }
+
         void updateMiscReg(ThreadContext *tc, ArmTranslationType tran_type);
 
         /** Returns the current VMID
@@ -173,6 +207,9 @@
         // Indicates whether all translation requests should
         // be routed directly to the stage 2 TLB
         bool directToStage2 = false;
+
+        Memoizer<int, ThreadContext*, bool,
+                 bool, TCR, ExceptionLevel> computeAddrTop;
     };
 
     MMU(const ArmMMUParams &p);
@@ -397,7 +434,12 @@
                              ThreadContext *tc, bool stage2);
     Fault checkPermissions64(TlbEntry *te, const RequestPtr &req, Mode mode,
                              ThreadContext *tc, CachedState &state);
+
   protected:
+    Addr purifyTaggedAddr(Addr vaddr_tainted, ThreadContext *tc,
+                          ExceptionLevel el,
+                          TCR tcr, bool is_inst, CachedState& state);
+
     bool checkPAN(ThreadContext *tc, uint8_t ap, const RequestPtr &req,
                   Mode mode, const bool is_priv, CachedState &state);
 
diff --git a/src/arch/arm/nativetrace.cc b/src/arch/arm/nativetrace.cc
index 8d0313a..0303ff6 100644
--- a/src/arch/arm/nativetrace.cc
+++ b/src/arch/arm/nativetrace.cc
@@ -109,7 +109,7 @@
 
     // Regular int regs
     for (int i = 0; i < 15; i++) {
-        newState[i] = tc->readIntReg(i);
+        newState[i] = tc->getReg(RegId(IntRegClass, i));
         changed[i] = (oldState[i] != newState[i]);
     }
 
@@ -119,21 +119,23 @@
 
     //CPSR
     CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
-    cpsr.nz = tc->readCCReg(CCREG_NZ);
-    cpsr.c = tc->readCCReg(CCREG_C);
-    cpsr.v = tc->readCCReg(CCREG_V);
-    cpsr.ge = tc->readCCReg(CCREG_GE);
+    cpsr.nz = tc->getReg(cc_reg::Nz);
+    cpsr.c = tc->getReg(cc_reg::C);
+    cpsr.v = tc->getReg(cc_reg::V);
+    cpsr.ge = tc->getReg(cc_reg::Ge);
 
     newState[STATE_CPSR] = cpsr;
     changed[STATE_CPSR] = (newState[STATE_CPSR] != oldState[STATE_CPSR]);
 
     for (int i = 0; i < NumVecV7ArchRegs; i++) {
-        auto *vec = tc->readVecReg(RegId(VecRegClass,i)).as<uint64_t>();
+        ArmISA::VecRegContainer vec_container;
+        tc->getReg(RegId(VecRegClass, i), &vec_container);
+        auto *vec = vec_container.as<uint64_t>();
         newState[STATE_F0 + 2*i] = vec[0];
         newState[STATE_F0 + 2*i + 1] = vec[1];
     }
     newState[STATE_FPSCR] = tc->readMiscRegNoEffect(MISCREG_FPSCR) |
-                            tc->readCCReg(CCREG_FP);
+                            tc->getReg(cc_reg::Fp);
 }
 
 void
diff --git a/src/arch/arm/process.cc b/src/arch/arm/process.cc
index a2b0b60..9770ea6 100644
--- a/src/arch/arm/process.cc
+++ b/src/arch/arm/process.cc
@@ -107,7 +107,7 @@
 ArmProcess32::initState()
 {
     Process::initState();
-    argsInit<uint32_t>(PageBytes, INTREG_SP);
+    argsInit<uint32_t>(PageBytes, int_reg::Sp);
     for (auto id: contextIds) {
         ThreadContext *tc = system->threads[id];
         CPACR cpacr = tc->readMiscReg(MISCREG_CPACR);
@@ -126,7 +126,7 @@
 ArmProcess64::initState()
 {
     Process::initState();
-    argsInit<uint64_t>(PageBytes, INTREG_SP0);
+    argsInit<uint64_t>(PageBytes, int_reg::Sp0);
     for (auto id: contextIds) {
         ThreadContext *tc = system->threads[id];
         CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
@@ -257,7 +257,7 @@
 
 template <class IntType>
 void
-ArmProcess::argsInit(int pageSize, IntRegIndex spIndex)
+ArmProcess::argsInit(int pageSize, const RegId &spId)
 {
     int intSize = sizeof(IntType);
 
@@ -449,22 +449,22 @@
 
     ThreadContext *tc = system->threads[contextIds[0]];
     //Set the stack pointer register
-    tc->setIntReg(spIndex, memState->getStackMin());
+    tc->setReg(spId, memState->getStackMin());
     //A pointer to a function to run when the program exits. We'll set this
     //to zero explicitly to make sure this isn't used.
-    tc->setIntReg(ArgumentReg0, 0);
+    tc->setReg(ArgumentReg0, (RegVal)0);
     //Set argument regs 1 and 2 to argv[0] and envp[0] respectively
     if (argv.size() > 0) {
-        tc->setIntReg(ArgumentReg1, arg_data_base + arg_data_size -
-                                    argv[argv.size() - 1].size() - 1);
+        tc->setReg(ArgumentReg1, arg_data_base + arg_data_size -
+                                 argv[argv.size() - 1].size() - 1);
     } else {
-        tc->setIntReg(ArgumentReg1, 0);
+        tc->setReg(ArgumentReg1, (RegVal)0);
     }
     if (envp.size() > 0) {
-        tc->setIntReg(ArgumentReg2, env_data_base + env_data_size -
-                                    envp[envp.size() - 1].size() - 1);
+        tc->setReg(ArgumentReg2, env_data_base + env_data_size -
+                                 envp[envp.size() - 1].size() - 1);
     } else {
-        tc->setIntReg(ArgumentReg2, 0);
+        tc->setReg(ArgumentReg2, (RegVal)0);
     }
 
     PCState pc;
diff --git a/src/arch/arm/process.hh b/src/arch/arm/process.hh
index de6d171..6bdabef 100644
--- a/src/arch/arm/process.hh
+++ b/src/arch/arm/process.hh
@@ -60,7 +60,7 @@
     ArmProcess(const ProcessParams &params, loader::ObjectFile *objFile,
                loader::Arch _arch);
     template<class IntType>
-    void argsInit(int pageSize, ArmISA::IntRegIndex spIndex);
+    void argsInit(int pageSize, const RegId &spId);
 
     template<class IntType>
     IntType
diff --git a/src/arch/arm/regs/cc.hh b/src/arch/arm/regs/cc.hh
index 9f3aaa1..2aa55fa 100644
--- a/src/arch/arm/regs/cc.hh
+++ b/src/arch/arm/regs/cc.hh
@@ -38,24 +38,37 @@
 #ifndef __ARCH_ARM_REGS_CC_HH__
 #define __ARCH_ARM_REGS_CC_HH__
 
+#include "cpu/reg_class.hh"
+
 namespace gem5
 {
 
 namespace ArmISA
 {
 
-enum ccRegIndex
+namespace cc_reg
 {
-    CCREG_NZ,
-    CCREG_C,
-    CCREG_V,
-    CCREG_GE,
-    CCREG_FP,
-    CCREG_ZERO,
-    NUM_CCREGS
+
+enum : RegIndex
+{
+    _NzIdx,
+    _CIdx,
+    _VIdx,
+    _GeIdx,
+    _FpIdx,
+    _ZeroIdx,
+    NumRegs
 };
 
-const char * const ccRegName[NUM_CCREGS] = {
+inline constexpr RegId
+    Nz(CCRegClass, _NzIdx),
+    C(CCRegClass, _CIdx),
+    V(CCRegClass, _VIdx),
+    Ge(CCRegClass, _GeIdx),
+    Fp(CCRegClass, _FpIdx),
+    Zero(CCRegClass, _ZeroIdx);
+
+const char * const RegName[NumRegs] = {
     "nz",
     "c",
     "v",
@@ -64,6 +77,8 @@
     "zero"
 };
 
+} // namespace cc_reg
+
 enum ConditionCode
 {
     COND_EQ  =   0,
diff --git a/src/arch/arm/regs/int.hh b/src/arch/arm/regs/int.hh
index 59f8a6d..34c2685 100644
--- a/src/arch/arm/regs/int.hh
+++ b/src/arch/arm/regs/int.hh
@@ -45,6 +45,7 @@
 
 #include "arch/arm/types.hh"
 #include "base/logging.hh"
+#include "cpu/reg_class.hh"
 #include "sim/core.hh"
 
 namespace gem5
@@ -62,445 +63,516 @@
     SignedBitfield<31, 0> sw;
 EndBitUnion(PackedIntReg)
 
-enum IntRegIndex
+namespace int_reg
+{
+
+enum : RegIndex
 {
     /* All the unique register indices. */
-    INTREG_R0,
-    INTREG_R1,
-    INTREG_R2,
-    INTREG_R3,
-    INTREG_R4,
-    INTREG_R5,
-    INTREG_R6,
-    INTREG_R7,
-    INTREG_R8,
-    INTREG_R9,
-    INTREG_R10,
-    INTREG_R11,
-    INTREG_R12,
-    INTREG_R13,
-    INTREG_SP = INTREG_R13,
-    INTREG_R14,
-    INTREG_LR = INTREG_R14,
-    INTREG_R15,
-    INTREG_PC = INTREG_R15,
+    _R0Idx,
+    _R1Idx,
+    _R2Idx,
+    _R3Idx,
+    _R4Idx,
+    _R5Idx,
+    _R6Idx,
+    _R7Idx,
+    _R8Idx,
+    _R9Idx,
+    _R10Idx,
+    _R11Idx,
+    _R12Idx,
+    _R13Idx,
+    _R14Idx,
+    _R15Idx,
 
-    INTREG_R13_SVC,
-    INTREG_SP_SVC = INTREG_R13_SVC,
-    INTREG_R14_SVC,
-    INTREG_LR_SVC = INTREG_R14_SVC,
+    _R13SvcIdx,
+    _R14SvcIdx,
 
-    INTREG_R13_MON,
-    INTREG_SP_MON = INTREG_R13_MON,
-    INTREG_R14_MON,
-    INTREG_LR_MON = INTREG_R14_MON,
+    _R13MonIdx,
+    _R14MonIdx,
 
-    INTREG_R13_HYP,
-    INTREG_SP_HYP = INTREG_R13_HYP,
+    _R13HypIdx,
 
-    INTREG_R13_ABT,
-    INTREG_SP_ABT = INTREG_R13_ABT,
-    INTREG_R14_ABT,
-    INTREG_LR_ABT = INTREG_R14_ABT,
+    _R13AbtIdx,
+    _R14AbtIdx,
 
-    INTREG_R13_UND,
-    INTREG_SP_UND = INTREG_R13_UND,
-    INTREG_R14_UND,
-    INTREG_LR_UND = INTREG_R14_UND,
+    _R13UndIdx,
+    _R14UndIdx,
 
-    INTREG_R13_IRQ,
-    INTREG_SP_IRQ = INTREG_R13_IRQ,
-    INTREG_R14_IRQ,
-    INTREG_LR_IRQ = INTREG_R14_IRQ,
+    _R13IrqIdx,
+    _R14IrqIdx,
 
-    INTREG_R8_FIQ,
-    INTREG_R9_FIQ,
-    INTREG_R10_FIQ,
-    INTREG_R11_FIQ,
-    INTREG_R12_FIQ,
-    INTREG_R13_FIQ,
-    INTREG_SP_FIQ = INTREG_R13_FIQ,
-    INTREG_R14_FIQ,
-    INTREG_LR_FIQ = INTREG_R14_FIQ,
+    _R8FiqIdx,
+    _R9FiqIdx,
+    _R10FiqIdx,
+    _R11FiqIdx,
+    _R12FiqIdx,
+    _R13FiqIdx,
+    _R14FiqIdx,
 
-    INTREG_ZERO,
-    INTREG_UREG0,
-    INTREG_UREG1,
-    INTREG_UREG2,
+    _ZeroIdx,
+    _Ureg0Idx,
+    _Ureg1Idx,
+    _Ureg2Idx,
 
-    INTREG_SP0,
-    INTREG_SP1,
-    INTREG_SP2,
-    INTREG_SP3,
+    _Sp0Idx,
+    _Sp1Idx,
+    _Sp2Idx,
+    _Sp3Idx,
 
-    NUM_INTREGS,
-    NUM_ARCH_INTREGS = 32,
+    NumRegs,
+    _SpxIdx = NumRegs,
 
-    /* AArch64 registers */
-    INTREG_X0 = 0,
-    INTREG_X1,
-    INTREG_X2,
-    INTREG_X3,
-    INTREG_X4,
-    INTREG_X5,
-    INTREG_X6,
-    INTREG_X7,
-    INTREG_X8,
-    INTREG_X9,
-    INTREG_X10,
-    INTREG_X11,
-    INTREG_X12,
-    INTREG_X13,
-    INTREG_X14,
-    INTREG_X15,
-    INTREG_X16,
-    INTREG_X17,
-    INTREG_X18,
-    INTREG_X19,
-    INTREG_X20,
-    INTREG_X21,
-    INTREG_X22,
-    INTREG_X23,
-    INTREG_X24,
-    INTREG_X25,
-    INTREG_X26,
-    INTREG_X27,
-    INTREG_X28,
-    INTREG_X29,
-    INTREG_X30,
-    INTREG_X31,
+    NumArchRegs = 32,
 
-    INTREG_SPX = NUM_INTREGS,
+    _X0Idx = 0,
+    _X1Idx,
+    _X2Idx,
+    _X3Idx,
+    _X4Idx,
+    _X5Idx,
+    _X6Idx,
+    _X7Idx,
+    _X8Idx,
+    _X9Idx,
+    _X10Idx,
+    _X11Idx,
+    _X12Idx,
+    _X13Idx,
+    _X14Idx,
+    _X15Idx,
+    _X16Idx,
+    _X17Idx,
+    _X18Idx,
+    _X19Idx,
+    _X20Idx,
+    _X21Idx,
+    _X22Idx,
+    _X23Idx,
+    _X24Idx,
+    _X25Idx,
+    _X26Idx,
+    _X27Idx,
+    _X28Idx,
+    _X29Idx,
+    _X30Idx,
+    _X31Idx
+};
 
-    /* All the aliased indexes. */
+inline constexpr RegId
+    /* All the unique register indices. */
+    R0(IntRegClass, _R0Idx),
+    R1(IntRegClass, _R1Idx),
+    R2(IntRegClass, _R2Idx),
+    R3(IntRegClass, _R3Idx),
+    R4(IntRegClass, _R4Idx),
+    R5(IntRegClass, _R5Idx),
+    R6(IntRegClass, _R6Idx),
+    R7(IntRegClass, _R7Idx),
+    R8(IntRegClass, _R8Idx),
+    R9(IntRegClass, _R9Idx),
+    R10(IntRegClass, _R10Idx),
+    R11(IntRegClass, _R11Idx),
+    R12(IntRegClass, _R12Idx),
+    R13(IntRegClass, _R13Idx),
+    R14(IntRegClass, _R14Idx),
+    R15(IntRegClass, _R15Idx),
+
+    R13Svc(IntRegClass, _R13SvcIdx),
+    R14Svc(IntRegClass, _R14SvcIdx),
+
+    R13Mon(IntRegClass, _R13MonIdx),
+    R14Mon(IntRegClass, _R14MonIdx),
+
+    R13Hyp(IntRegClass, _R13HypIdx),
+
+    R13Abt(IntRegClass, _R13AbtIdx),
+    R14Abt(IntRegClass, _R14AbtIdx),
+
+    R13Und(IntRegClass, _R13UndIdx),
+    R14Und(IntRegClass, _R14UndIdx),
+
+    R13Irq(IntRegClass, _R13IrqIdx),
+    R14Irq(IntRegClass, _R14IrqIdx),
+
+    R8Fiq(IntRegClass, _R8FiqIdx),
+    R9Fiq(IntRegClass, _R9FiqIdx),
+    R10Fiq(IntRegClass, _R10FiqIdx),
+    R11Fiq(IntRegClass, _R11FiqIdx),
+    R12Fiq(IntRegClass, _R12FiqIdx),
+    R13Fiq(IntRegClass, _R13FiqIdx),
+    R14Fiq(IntRegClass, _R14FiqIdx),
+
+    Zero(IntRegClass, _ZeroIdx),
+    Ureg0(IntRegClass, _Ureg0Idx),
+    Ureg1(IntRegClass, _Ureg1Idx),
+    Ureg2(IntRegClass, _Ureg2Idx),
+
+    Sp0(IntRegClass, _Sp0Idx),
+    Sp1(IntRegClass, _Sp1Idx),
+    Sp2(IntRegClass, _Sp2Idx),
+    Sp3(IntRegClass, _Sp3Idx),
+
+    Spx(IntRegClass, _SpxIdx),
+
+    X0(IntRegClass, _X0Idx),
+    X1(IntRegClass, _X1Idx),
+    X2(IntRegClass, _X2Idx),
+    X3(IntRegClass, _X3Idx),
+    X4(IntRegClass, _X4Idx),
+    X5(IntRegClass, _X5Idx),
+    X6(IntRegClass, _X6Idx),
+    X7(IntRegClass, _X7Idx),
+    X8(IntRegClass, _X8Idx),
+    X9(IntRegClass, _X9Idx),
+    X10(IntRegClass, _X10Idx),
+    X11(IntRegClass, _X11Idx),
+    X12(IntRegClass, _X12Idx),
+    X13(IntRegClass, _X13Idx),
+    X14(IntRegClass, _X14Idx),
+    X15(IntRegClass, _X15Idx),
+    X16(IntRegClass, _X16Idx),
+    X17(IntRegClass, _X17Idx),
+    X18(IntRegClass, _X18Idx),
+    X19(IntRegClass, _X19Idx),
+    X20(IntRegClass, _X20Idx),
+    X21(IntRegClass, _X21Idx),
+    X22(IntRegClass, _X22Idx),
+    X23(IntRegClass, _X23Idx),
+    X24(IntRegClass, _X24Idx),
+    X25(IntRegClass, _X25Idx),
+    X26(IntRegClass, _X26Idx),
+    X27(IntRegClass, _X27Idx),
+    X28(IntRegClass, _X28Idx),
+    X29(IntRegClass, _X29Idx),
+    X30(IntRegClass, _X30Idx),
+    X31(IntRegClass, _X31Idx);
+
+inline constexpr auto
+    &Sp = R13,
+    &Lr = R14,
+    &Pc = R15,
+
+    &SpSvc = R13Svc,
+    &LRSvc = R14Svc,
+
+    &SPMon = R13Mon,
+    &LRMon = R14Mon,
+
+    &SPHyp = R13Hyp,
+
+    &SPAbt = R13Abt,
+    &LRAbt = R14Abt,
+
+    &SPUnd = R13Und,
+    &LRUnd = R14Und,
+
+    &SPIrq = R13Irq,
+    &LRIrq = R14Irq,
+
+    &SPFiq = R13Fiq,
+    &LRFiq = R14Fiq,
 
     /* USR mode */
-    INTREG_R0_USR = INTREG_R0,
-    INTREG_R1_USR = INTREG_R1,
-    INTREG_R2_USR = INTREG_R2,
-    INTREG_R3_USR = INTREG_R3,
-    INTREG_R4_USR = INTREG_R4,
-    INTREG_R5_USR = INTREG_R5,
-    INTREG_R6_USR = INTREG_R6,
-    INTREG_R7_USR = INTREG_R7,
-    INTREG_R8_USR = INTREG_R8,
-    INTREG_R9_USR = INTREG_R9,
-    INTREG_R10_USR = INTREG_R10,
-    INTREG_R11_USR = INTREG_R11,
-    INTREG_R12_USR = INTREG_R12,
-    INTREG_R13_USR = INTREG_R13,
-    INTREG_SP_USR = INTREG_SP,
-    INTREG_R14_USR = INTREG_R14,
-    INTREG_LR_USR = INTREG_LR,
-    INTREG_R15_USR = INTREG_R15,
-    INTREG_PC_USR = INTREG_PC,
+    &R0Usr = R0,
+    &R1Usr = R1,
+    &R2Usr = R2,
+    &R3Usr = R3,
+    &R4Usr = R4,
+    &R5Usr = R5,
+    &R6Usr = R6,
+    &R7Usr = R7,
+    &R8Usr = R8,
+    &R9Usr = R9,
+    &R10Usr = R10,
+    &R11Usr = R11,
+    &R12Usr = R12,
+    &R13Usr = R13,
+    &SPUsr = Sp,
+    &R14Usr = R14,
+    &LRUsr = Lr,
+    &R15Usr = R15,
+    &PcUsr = Pc,
 
     /* SVC mode */
-    INTREG_R0_SVC = INTREG_R0,
-    INTREG_R1_SVC = INTREG_R1,
-    INTREG_R2_SVC = INTREG_R2,
-    INTREG_R3_SVC = INTREG_R3,
-    INTREG_R4_SVC = INTREG_R4,
-    INTREG_R5_SVC = INTREG_R5,
-    INTREG_R6_SVC = INTREG_R6,
-    INTREG_R7_SVC = INTREG_R7,
-    INTREG_R8_SVC = INTREG_R8,
-    INTREG_R9_SVC = INTREG_R9,
-    INTREG_R10_SVC = INTREG_R10,
-    INTREG_R11_SVC = INTREG_R11,
-    INTREG_R12_SVC = INTREG_R12,
-    INTREG_PC_SVC = INTREG_PC,
-    INTREG_R15_SVC = INTREG_R15,
+    &R0Svc = R0,
+    &R1Svc = R1,
+    &R2Svc = R2,
+    &R3Svc = R3,
+    &R4Svc = R4,
+    &R5Svc = R5,
+    &R6Svc = R6,
+    &R7Svc = R7,
+    &R8Svc = R8,
+    &R9Svc = R9,
+    &R10Svc = R10,
+    &R11Svc = R11,
+    &R12Svc = R12,
+    &PcSvc = Pc,
+    &R15Svc = R15,
 
     /* MON mode */
-    INTREG_R0_MON = INTREG_R0,
-    INTREG_R1_MON = INTREG_R1,
-    INTREG_R2_MON = INTREG_R2,
-    INTREG_R3_MON = INTREG_R3,
-    INTREG_R4_MON = INTREG_R4,
-    INTREG_R5_MON = INTREG_R5,
-    INTREG_R6_MON = INTREG_R6,
-    INTREG_R7_MON = INTREG_R7,
-    INTREG_R8_MON = INTREG_R8,
-    INTREG_R9_MON = INTREG_R9,
-    INTREG_R10_MON = INTREG_R10,
-    INTREG_R11_MON = INTREG_R11,
-    INTREG_R12_MON = INTREG_R12,
-    INTREG_PC_MON = INTREG_PC,
-    INTREG_R15_MON = INTREG_R15,
+    &R0Mon = R0,
+    &R1Mon = R1,
+    &R2Mon = R2,
+    &R3Mon = R3,
+    &R4Mon = R4,
+    &R5Mon = R5,
+    &R6Mon = R6,
+    &R7Mon = R7,
+    &R8Mon = R8,
+    &R9Mon = R9,
+    &R10Mon = R10,
+    &R11Mon = R11,
+    &R12Mon = R12,
+    &PcMon = Pc,
+    &R15Mon = R15,
 
     /* ABT mode */
-    INTREG_R0_ABT = INTREG_R0,
-    INTREG_R1_ABT = INTREG_R1,
-    INTREG_R2_ABT = INTREG_R2,
-    INTREG_R3_ABT = INTREG_R3,
-    INTREG_R4_ABT = INTREG_R4,
-    INTREG_R5_ABT = INTREG_R5,
-    INTREG_R6_ABT = INTREG_R6,
-    INTREG_R7_ABT = INTREG_R7,
-    INTREG_R8_ABT = INTREG_R8,
-    INTREG_R9_ABT = INTREG_R9,
-    INTREG_R10_ABT = INTREG_R10,
-    INTREG_R11_ABT = INTREG_R11,
-    INTREG_R12_ABT = INTREG_R12,
-    INTREG_PC_ABT = INTREG_PC,
-    INTREG_R15_ABT = INTREG_R15,
+    &R0Abt = R0,
+    &R1Abt = R1,
+    &R2Abt = R2,
+    &R3Abt = R3,
+    &R4Abt = R4,
+    &R5Abt = R5,
+    &R6Abt = R6,
+    &R7Abt = R7,
+    &R8Abt = R8,
+    &R9Abt = R9,
+    &R10Abt = R10,
+    &R11Abt = R11,
+    &R12Abt = R12,
+    &PcAbt = Pc,
+    &R15Abt = R15,
 
     /* HYP mode */
-    INTREG_R0_HYP = INTREG_R0,
-    INTREG_R1_HYP = INTREG_R1,
-    INTREG_R2_HYP = INTREG_R2,
-    INTREG_R3_HYP = INTREG_R3,
-    INTREG_R4_HYP = INTREG_R4,
-    INTREG_R5_HYP = INTREG_R5,
-    INTREG_R6_HYP = INTREG_R6,
-    INTREG_R7_HYP = INTREG_R7,
-    INTREG_R8_HYP = INTREG_R8,
-    INTREG_R9_HYP = INTREG_R9,
-    INTREG_R10_HYP = INTREG_R10,
-    INTREG_R11_HYP = INTREG_R11,
-    INTREG_R12_HYP = INTREG_R12,
-    INTREG_LR_HYP = INTREG_LR,
-    INTREG_R14_HYP = INTREG_R14,
-    INTREG_PC_HYP = INTREG_PC,
-    INTREG_R15_HYP = INTREG_R15,
+    &R0Hyp = R0,
+    &R1Hyp = R1,
+    &R2Hyp = R2,
+    &R3Hyp = R3,
+    &R4Hyp = R4,
+    &R5Hyp = R5,
+    &R6Hyp = R6,
+    &R7Hyp = R7,
+    &R8Hyp = R8,
+    &R9Hyp = R9,
+    &R10Hyp = R10,
+    &R11Hyp = R11,
+    &R12Hyp = R12,
+    &LRHyp = Lr,
+    &R14Hyp = R14,
+    &PcHyp = Pc,
+    &R15Hyp = R15,
 
     /* UND mode */
-    INTREG_R0_UND = INTREG_R0,
-    INTREG_R1_UND = INTREG_R1,
-    INTREG_R2_UND = INTREG_R2,
-    INTREG_R3_UND = INTREG_R3,
-    INTREG_R4_UND = INTREG_R4,
-    INTREG_R5_UND = INTREG_R5,
-    INTREG_R6_UND = INTREG_R6,
-    INTREG_R7_UND = INTREG_R7,
-    INTREG_R8_UND = INTREG_R8,
-    INTREG_R9_UND = INTREG_R9,
-    INTREG_R10_UND = INTREG_R10,
-    INTREG_R11_UND = INTREG_R11,
-    INTREG_R12_UND = INTREG_R12,
-    INTREG_PC_UND = INTREG_PC,
-    INTREG_R15_UND = INTREG_R15,
+    &R0Und = R0,
+    &R1Und = R1,
+    &R2Und = R2,
+    &R3Und = R3,
+    &R4Und = R4,
+    &R5Und = R5,
+    &R6Und = R6,
+    &R7Und = R7,
+    &R8Und = R8,
+    &R9Und = R9,
+    &R10Und = R10,
+    &R11Und = R11,
+    &R12Und = R12,
+    &PcUnd = Pc,
+    &R15Und = R15,
 
     /* IRQ mode */
-    INTREG_R0_IRQ = INTREG_R0,
-    INTREG_R1_IRQ = INTREG_R1,
-    INTREG_R2_IRQ = INTREG_R2,
-    INTREG_R3_IRQ = INTREG_R3,
-    INTREG_R4_IRQ = INTREG_R4,
-    INTREG_R5_IRQ = INTREG_R5,
-    INTREG_R6_IRQ = INTREG_R6,
-    INTREG_R7_IRQ = INTREG_R7,
-    INTREG_R8_IRQ = INTREG_R8,
-    INTREG_R9_IRQ = INTREG_R9,
-    INTREG_R10_IRQ = INTREG_R10,
-    INTREG_R11_IRQ = INTREG_R11,
-    INTREG_R12_IRQ = INTREG_R12,
-    INTREG_PC_IRQ = INTREG_PC,
-    INTREG_R15_IRQ = INTREG_R15,
+    &R0Irq = R0,
+    &R1Irq = R1,
+    &R2Irq = R2,
+    &R3Irq = R3,
+    &R4Irq = R4,
+    &R5Irq = R5,
+    &R6Irq = R6,
+    &R7Irq = R7,
+    &R8Irq = R8,
+    &R9Irq = R9,
+    &R10Irq = R10,
+    &R11Irq = R11,
+    &R12Irq = R12,
+    &PcIrq = Pc,
+    &R15Irq = R15,
 
     /* FIQ mode */
-    INTREG_R0_FIQ = INTREG_R0,
-    INTREG_R1_FIQ = INTREG_R1,
-    INTREG_R2_FIQ = INTREG_R2,
-    INTREG_R3_FIQ = INTREG_R3,
-    INTREG_R4_FIQ = INTREG_R4,
-    INTREG_R5_FIQ = INTREG_R5,
-    INTREG_R6_FIQ = INTREG_R6,
-    INTREG_R7_FIQ = INTREG_R7,
-    INTREG_PC_FIQ = INTREG_PC,
-    INTREG_R15_FIQ = INTREG_R15
+    &R0Fiq = R0,
+    &R1Fiq = R1,
+    &R2Fiq = R2,
+    &R3Fiq = R3,
+    &R4Fiq = R4,
+    &R5Fiq = R5,
+    &R6Fiq = R6,
+    &R7Fiq = R7,
+    &PcFiq = Pc,
+    &R15Fiq = R15;
+
+typedef const RegId RegMap[NumArchRegs];
+
+const RegMap Reg64Map = {
+    R0,     R1,     R2,     R3,     R4,     R5,     R6,     R7,
+    R8Usr,  R9Usr,  R10Usr, R11Usr, R12Usr, R13Usr, R14Usr, R13Hyp,
+    R14Irq, R13Irq, R14Svc, R13Svc, R14Abt, R13Abt, R14Und, R13Und,
+    R8Fiq,  R9Fiq,  R10Fiq, R11Fiq, R12Fiq, R13Fiq, R14Fiq, Zero
 };
 
-typedef IntRegIndex IntRegMap[NUM_ARCH_INTREGS];
-
-const IntRegMap IntReg64Map = {
-    INTREG_R0,      INTREG_R1,      INTREG_R2,      INTREG_R3,
-    INTREG_R4,      INTREG_R5,      INTREG_R6,      INTREG_R7,
-    INTREG_R8_USR,  INTREG_R9_USR,  INTREG_R10_USR, INTREG_R11_USR,
-    INTREG_R12_USR, INTREG_R13_USR, INTREG_R14_USR, INTREG_R13_HYP,
-    INTREG_R14_IRQ, INTREG_R13_IRQ, INTREG_R14_SVC, INTREG_R13_SVC,
-    INTREG_R14_ABT, INTREG_R13_ABT, INTREG_R14_UND, INTREG_R13_UND,
-    INTREG_R8_FIQ,  INTREG_R9_FIQ,  INTREG_R10_FIQ, INTREG_R11_FIQ,
-    INTREG_R12_FIQ, INTREG_R13_FIQ, INTREG_R14_FIQ, INTREG_ZERO
-};
-
-const IntRegMap IntRegUsrMap = {
-    INTREG_R0_USR,  INTREG_R1_USR,  INTREG_R2_USR,  INTREG_R3_USR,
-    INTREG_R4_USR,  INTREG_R5_USR,  INTREG_R6_USR,  INTREG_R7_USR,
-    INTREG_R8_USR,  INTREG_R9_USR,  INTREG_R10_USR, INTREG_R11_USR,
-    INTREG_R12_USR, INTREG_R13_USR, INTREG_R14_USR, INTREG_R15_USR,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO
-};
-
-static inline IntRegIndex
-INTREG_USR(unsigned index)
+static inline RegId
+x(unsigned index)
 {
-    assert(index < NUM_ARCH_INTREGS);
-    return IntRegUsrMap[index];
+    assert(index < NumArchRegs);
+    return RegId(IntRegClass, _X0Idx + index);
 }
 
-const IntRegMap IntRegHypMap = {
-    INTREG_R0_HYP,  INTREG_R1_HYP,  INTREG_R2_HYP,  INTREG_R3_HYP,
-    INTREG_R4_HYP,  INTREG_R5_HYP,  INTREG_R6_HYP,  INTREG_R7_HYP,
-    INTREG_R8_HYP,  INTREG_R9_HYP,  INTREG_R10_HYP, INTREG_R11_HYP,
-    INTREG_R12_HYP, INTREG_R13_HYP, INTREG_R14_HYP, INTREG_R15_HYP,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO
+const RegMap RegUsrMap = {
+    R0Usr,  R1Usr,  R2Usr,  R3Usr,  R4Usr,  R5Usr,  R6Usr,  R7Usr,
+    R8Usr,  R9Usr,  R10Usr, R11Usr, R12Usr, R13Usr, R14Usr, R15Usr,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero
 };
 
-static inline IntRegIndex
-INTREG_HYP(unsigned index)
+static inline const RegId &
+usr(unsigned index)
 {
-    assert(index < NUM_ARCH_INTREGS);
-    return IntRegHypMap[index];
+    assert(index < NumArchRegs);
+    return RegUsrMap[index];
 }
 
-const IntRegMap IntRegSvcMap = {
-    INTREG_R0_SVC,  INTREG_R1_SVC,  INTREG_R2_SVC,  INTREG_R3_SVC,
-    INTREG_R4_SVC,  INTREG_R5_SVC,  INTREG_R6_SVC,  INTREG_R7_SVC,
-    INTREG_R8_SVC,  INTREG_R9_SVC,  INTREG_R10_SVC, INTREG_R11_SVC,
-    INTREG_R12_SVC, INTREG_R13_SVC, INTREG_R14_SVC, INTREG_R15_SVC,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO
+const RegMap RegHypMap = {
+    R0Hyp,  R1Hyp,  R2Hyp,  R3Hyp,  R4Hyp,  R5Hyp,  R6Hyp,  R7Hyp,
+    R8Hyp,  R9Hyp,  R10Hyp, R11Hyp, R12Hyp, R13Hyp, R14Hyp, R15Hyp,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero
 };
 
-static inline IntRegIndex
-INTREG_SVC(unsigned index)
+static inline const RegId &
+hyp(unsigned index)
 {
-    assert(index < NUM_ARCH_INTREGS);
-    return IntRegSvcMap[index];
+    assert(index < NumArchRegs);
+    return RegHypMap[index];
 }
 
-const IntRegMap IntRegMonMap = {
-    INTREG_R0_MON,  INTREG_R1_MON,  INTREG_R2_MON,  INTREG_R3_MON,
-    INTREG_R4_MON,  INTREG_R5_MON,  INTREG_R6_MON,  INTREG_R7_MON,
-    INTREG_R8_MON,  INTREG_R9_MON,  INTREG_R10_MON, INTREG_R11_MON,
-    INTREG_R12_MON, INTREG_R13_MON, INTREG_R14_MON, INTREG_R15_MON,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO
+const RegMap RegSvcMap = {
+    R0Svc,  R1Svc,  R2Svc,  R3Svc,  R4Svc,  R5Svc,  R6Svc,  R7Svc,
+    R8Svc,  R9Svc,  R10Svc, R11Svc, R12Svc, R13Svc, R14Svc, R15Svc,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero
 };
 
-static inline IntRegIndex
-INTREG_MON(unsigned index)
+static inline const RegId &
+svc(unsigned index)
 {
-    assert(index < NUM_ARCH_INTREGS);
-    return IntRegMonMap[index];
+    assert(index < NumArchRegs);
+    return RegSvcMap[index];
 }
 
-const IntRegMap IntRegAbtMap = {
-    INTREG_R0_ABT,  INTREG_R1_ABT,  INTREG_R2_ABT,  INTREG_R3_ABT,
-    INTREG_R4_ABT,  INTREG_R5_ABT,  INTREG_R6_ABT,  INTREG_R7_ABT,
-    INTREG_R8_ABT,  INTREG_R9_ABT,  INTREG_R10_ABT, INTREG_R11_ABT,
-    INTREG_R12_ABT, INTREG_R13_ABT, INTREG_R14_ABT, INTREG_R15_ABT,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO
+const RegMap RegMonMap = {
+    R0Mon,  R1Mon,  R2Mon,  R3Mon,  R4Mon,  R5Mon,  R6Mon,  R7Mon,
+    R8Mon,  R9Mon,  R10Mon, R11Mon, R12Mon, R13Mon, R14Mon, R15Mon,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero
 };
 
-static inline IntRegIndex
-INTREG_ABT(unsigned index)
+static inline const RegId &
+mon(unsigned index)
 {
-    assert(index < NUM_ARCH_INTREGS);
-    return IntRegAbtMap[index];
+    assert(index < NumArchRegs);
+    return RegMonMap[index];
 }
 
-const IntRegMap IntRegUndMap = {
-    INTREG_R0_UND,  INTREG_R1_UND,  INTREG_R2_UND,  INTREG_R3_UND,
-    INTREG_R4_UND,  INTREG_R5_UND,  INTREG_R6_UND,  INTREG_R7_UND,
-    INTREG_R8_UND,  INTREG_R9_UND,  INTREG_R10_UND, INTREG_R11_UND,
-    INTREG_R12_UND, INTREG_R13_UND, INTREG_R14_UND, INTREG_R15_UND,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO
+const RegMap RegAbtMap = {
+    R0Abt,  R1Abt,  R2Abt,  R3Abt,  R4Abt,  R5Abt,  R6Abt,  R7Abt,
+    R8Abt,  R9Abt,  R10Abt, R11Abt, R12Abt, R13Abt, R14Abt, R15Abt,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero
 };
 
-static inline IntRegIndex
-INTREG_UND(unsigned index)
+static inline const RegId &
+abt(unsigned index)
 {
-    assert(index < NUM_ARCH_INTREGS);
-    return IntRegUndMap[index];
+    assert(index < NumArchRegs);
+    return RegAbtMap[index];
 }
 
-const IntRegMap IntRegIrqMap = {
-    INTREG_R0_IRQ,  INTREG_R1_IRQ,  INTREG_R2_IRQ,  INTREG_R3_IRQ,
-    INTREG_R4_IRQ,  INTREG_R5_IRQ,  INTREG_R6_IRQ,  INTREG_R7_IRQ,
-    INTREG_R8_IRQ,  INTREG_R9_IRQ,  INTREG_R10_IRQ, INTREG_R11_IRQ,
-    INTREG_R12_IRQ, INTREG_R13_IRQ, INTREG_R14_IRQ, INTREG_R15_IRQ,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO
+const RegMap RegUndMap = {
+    R0Und,  R1Und,  R2Und,  R3Und,  R4Und,  R5Und,  R6Und,  R7Und,
+    R8Und,  R9Und,  R10Und, R11Und, R12Und, R13Und, R14Und, R15Und,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero
 };
 
-static inline IntRegIndex
-INTREG_IRQ(unsigned index)
+static inline const RegId &
+und(unsigned index)
 {
-    assert(index < NUM_ARCH_INTREGS);
-    return IntRegIrqMap[index];
+    assert(index < NumArchRegs);
+    return RegUndMap[index];
 }
 
-const IntRegMap IntRegFiqMap = {
-    INTREG_R0_FIQ,  INTREG_R1_FIQ,  INTREG_R2_FIQ,  INTREG_R3_FIQ,
-    INTREG_R4_FIQ,  INTREG_R5_FIQ,  INTREG_R6_FIQ,  INTREG_R7_FIQ,
-    INTREG_R8_FIQ,  INTREG_R9_FIQ,  INTREG_R10_FIQ, INTREG_R11_FIQ,
-    INTREG_R12_FIQ, INTREG_R13_FIQ, INTREG_R14_FIQ, INTREG_R15_FIQ,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,
-    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO,    INTREG_ZERO
+const RegMap RegIrqMap = {
+    R0Irq,  R1Irq,  R2Irq,  R3Irq,  R4Irq,  R5Irq,  R6Irq,  R7Irq,
+    R8Irq,  R9Irq,  R10Irq, R11Irq, R12Irq, R13Irq, R14Irq, R15Irq,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero
 };
 
-static inline IntRegIndex
-INTREG_FIQ(unsigned index)
+static inline const RegId &
+irq(unsigned index)
 {
-    assert(index < NUM_ARCH_INTREGS);
-    return IntRegFiqMap[index];
+    assert(index < NumArchRegs);
+    return RegIrqMap[index];
 }
 
-static const unsigned intRegsPerMode = NUM_INTREGS;
+const RegMap RegFiqMap = {
+    R0Fiq,  R1Fiq,  R2Fiq,  R3Fiq,  R4Fiq,  R5Fiq,  R6Fiq,  R7Fiq,
+    R8Fiq,  R9Fiq,  R10Fiq, R11Fiq, R12Fiq, R13Fiq, R14Fiq, R15Fiq,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,
+    Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero,   Zero
+};
+
+static inline const RegId &
+fiq(unsigned index)
+{
+    assert(index < NumArchRegs);
+    return RegFiqMap[index];
+}
+
+static const unsigned regsPerMode = NumRegs;
 
 static inline int
-intRegInMode(OperatingMode mode, int reg)
+regInMode(OperatingMode mode, int reg)
 {
-    assert(reg < NUM_ARCH_INTREGS);
-    return mode * intRegsPerMode + reg;
+    assert(reg < NumArchRegs);
+    return mode * regsPerMode + reg;
 }
 
+} // namespace int_reg
+
 static inline int
 flattenIntRegModeIndex(int reg)
 {
-    int mode = reg / intRegsPerMode;
-    reg = reg % intRegsPerMode;
+    int mode = reg / int_reg::regsPerMode;
+    reg = reg % int_reg::regsPerMode;
     switch (mode) {
       case MODE_USER:
       case MODE_SYSTEM:
-        return INTREG_USR(reg);
+        return int_reg::usr(reg);
       case MODE_FIQ:
-        return INTREG_FIQ(reg);
+        return int_reg::fiq(reg);
       case MODE_IRQ:
-        return INTREG_IRQ(reg);
+        return int_reg::irq(reg);
       case MODE_SVC:
-        return INTREG_SVC(reg);
+        return int_reg::svc(reg);
       case MODE_MON:
-        return INTREG_MON(reg);
+        return int_reg::mon(reg);
       case MODE_ABORT:
-        return INTREG_ABT(reg);
+        return int_reg::abt(reg);
       case MODE_HYP:
-        return INTREG_HYP(reg);
+        return int_reg::hyp(reg);
       case MODE_UNDEFINED:
-        return INTREG_UND(reg);
+        return int_reg::und(reg);
       default:
         panic("%d: Flattening into an unknown mode: reg:%#x mode:%#x\n",
                 curTick(), reg, mode);
@@ -508,46 +580,62 @@
 }
 
 
-static inline IntRegIndex
-makeSP(IntRegIndex reg)
+static inline RegIndex
+makeSP(RegIndex reg)
 {
-    if (reg == INTREG_X31)
-        reg = INTREG_SPX;
-    return reg;
-}
-
-static inline IntRegIndex
-makeZero(IntRegIndex reg)
-{
-    if (reg == INTREG_X31)
-        reg = INTREG_ZERO;
+    if (reg == int_reg::X31)
+        reg = int_reg::Spx;
     return reg;
 }
 
 static inline bool
-isSP(IntRegIndex reg)
+couldBeSP(RegIndex reg)
 {
-    return reg == INTREG_SPX;
+    return (reg == int_reg::X31 || reg == int_reg::Spx);
+}
+
+static inline bool
+isSP(RegIndex reg)
+{
+    return reg == int_reg::Spx;
+}
+
+static inline bool
+couldBeZero(RegIndex reg)
+{
+    return (reg == int_reg::X31 || reg == int_reg::Zero);
+}
+
+static inline bool
+isZero(RegIndex reg)
+{
+    return reg == int_reg::Zero;
+}
+
+static inline RegIndex
+makeZero(RegIndex reg)
+{
+    if (reg == int_reg::X31)
+        reg = int_reg::Zero;
+    return reg;
 }
 
 // Semantically meaningful register indices
-const int ReturnValueReg = 0;
-const int ReturnValueReg1 = 1;
-const int ReturnValueReg2 = 2;
-const int NumArgumentRegs = 4;
-const int NumArgumentRegs64 = 8;
-const int ArgumentReg0 = 0;
-const int ArgumentReg1 = 1;
-const int ArgumentReg2 = 2;
-const int ArgumentReg3 = 3;
-const int FramePointerReg = 11;
-const int StackPointerReg = INTREG_SP;
-const int ReturnAddressReg = INTREG_LR;
-const int PCReg = INTREG_PC;
+inline constexpr size_t NumArgumentRegs = 4;
+inline constexpr size_t NumArgumentRegs64 = 8;
+inline constexpr auto
+    &ReturnValueReg = int_reg::X0,
+    &ReturnValueReg1 = int_reg::X1,
+    &ArgumentReg0 = int_reg::X0,
+    &ArgumentReg1 = int_reg::X1,
+    &ArgumentReg2 = int_reg::X2,
+    &FramePointerReg = int_reg::X11,
+    &StackPointerReg = int_reg::Sp,
+    &ReturnAddressReg = int_reg::Lr,
 
-const int SyscallNumReg = ReturnValueReg;
-const int SyscallPseudoReturnReg = ReturnValueReg;
-const int SyscallSuccessReg = ReturnValueReg;
+    &SyscallNumReg = ReturnValueReg,
+    &SyscallPseudoReturnReg = ReturnValueReg,
+    &SyscallSuccessReg = ReturnValueReg;
 
 } // namespace ArmISA
 } // namespace gem5
diff --git a/src/arch/arm/regs/misc.cc b/src/arch/arm/regs/misc.cc
index d586e0f..00d5501 100644
--- a/src/arch/arm/regs/misc.cc
+++ b/src/arch/arm/regs/misc.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013, 2015-2020 ARM Limited
+ * Copyright (c) 2010-2013, 2015-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -50,1159 +50,512 @@
 namespace ArmISA
 {
 
+namespace
+{
+
+std::unordered_map<MiscRegNum32, MiscRegIndex> miscRegNum32ToIdx{
+    // MCR/MRC regs
+    { MiscRegNum32(14, 0, 0, 0, 0), MISCREG_DBGDIDR },
+    { MiscRegNum32(14, 0, 0, 0, 2), MISCREG_DBGDTRRXext },
+    { MiscRegNum32(14, 0, 0, 0, 4), MISCREG_DBGBVR0 },
+    { MiscRegNum32(14, 0, 0, 0, 5), MISCREG_DBGBCR0 },
+    { MiscRegNum32(14, 0, 0, 0, 6), MISCREG_DBGWVR0 },
+    { MiscRegNum32(14, 0, 0, 0, 7), MISCREG_DBGWCR0 },
+    { MiscRegNum32(14, 0, 0, 1, 0), MISCREG_DBGDSCRint },
+    { MiscRegNum32(14, 0, 0, 1, 4), MISCREG_DBGBVR1 },
+    { MiscRegNum32(14, 0, 0, 1, 5), MISCREG_DBGBCR1 },
+    { MiscRegNum32(14, 0, 0, 1, 6), MISCREG_DBGWVR1 },
+    { MiscRegNum32(14, 0, 0, 1, 7), MISCREG_DBGWCR1 },
+    { MiscRegNum32(14, 0, 0, 2, 2), MISCREG_DBGDSCRext },
+    { MiscRegNum32(14, 0, 0, 2, 4), MISCREG_DBGBVR2 },
+    { MiscRegNum32(14, 0, 0, 2, 5), MISCREG_DBGBCR2 },
+    { MiscRegNum32(14, 0, 0, 2, 6), MISCREG_DBGWVR2 },
+    { MiscRegNum32(14, 0, 0, 2, 7), MISCREG_DBGWCR2 },
+    { MiscRegNum32(14, 0, 0, 3, 2), MISCREG_DBGDTRTXext },
+    { MiscRegNum32(14, 0, 0, 3, 4), MISCREG_DBGBVR3 },
+    { MiscRegNum32(14, 0, 0, 3, 5), MISCREG_DBGBCR3 },
+    { MiscRegNum32(14, 0, 0, 3, 6), MISCREG_DBGWVR3 },
+    { MiscRegNum32(14, 0, 0, 3, 7), MISCREG_DBGWCR3 },
+    { MiscRegNum32(14, 0, 0, 4, 4), MISCREG_DBGBVR4 },
+    { MiscRegNum32(14, 0, 0, 4, 5), MISCREG_DBGBCR4 },
+    { MiscRegNum32(14, 0, 0, 4, 6), MISCREG_DBGWVR4 },
+    { MiscRegNum32(14, 0, 0, 4, 7), MISCREG_DBGWCR4 },
+    { MiscRegNum32(14, 0, 0, 5, 4), MISCREG_DBGBVR5 },
+    { MiscRegNum32(14, 0, 0, 5, 5), MISCREG_DBGBCR5 },
+    { MiscRegNum32(14, 0, 0, 5, 6), MISCREG_DBGWVR5 },
+    { MiscRegNum32(14, 0, 0, 5, 7), MISCREG_DBGWCR5 },
+    { MiscRegNum32(14, 0, 0, 6, 2), MISCREG_DBGOSECCR },
+    { MiscRegNum32(14, 0, 0, 6, 4), MISCREG_DBGBVR6 },
+    { MiscRegNum32(14, 0, 0, 6, 5), MISCREG_DBGBCR6 },
+    { MiscRegNum32(14, 0, 0, 6, 6), MISCREG_DBGWVR6 },
+    { MiscRegNum32(14, 0, 0, 6, 7), MISCREG_DBGWCR6 },
+    { MiscRegNum32(14, 0, 0, 7, 0), MISCREG_DBGVCR },
+    { MiscRegNum32(14, 0, 0, 7, 4), MISCREG_DBGBVR7 },
+    { MiscRegNum32(14, 0, 0, 7, 5), MISCREG_DBGBCR7 },
+    { MiscRegNum32(14, 0, 0, 7, 6), MISCREG_DBGWVR7 },
+    { MiscRegNum32(14, 0, 0, 7, 7), MISCREG_DBGWCR7 },
+    { MiscRegNum32(14, 0, 0, 8, 4), MISCREG_DBGBVR8 },
+    { MiscRegNum32(14, 0, 0, 8, 5), MISCREG_DBGBCR8 },
+    { MiscRegNum32(14, 0, 0, 8, 6), MISCREG_DBGWVR8 },
+    { MiscRegNum32(14, 0, 0, 8, 7), MISCREG_DBGWCR8 },
+    { MiscRegNum32(14, 0, 0, 9, 4), MISCREG_DBGBVR9 },
+    { MiscRegNum32(14, 0, 0, 9, 5), MISCREG_DBGBCR9 },
+    { MiscRegNum32(14, 0, 0, 9, 6), MISCREG_DBGWVR9 },
+    { MiscRegNum32(14, 0, 0, 9, 7), MISCREG_DBGWCR9 },
+    { MiscRegNum32(14, 0, 0, 10, 4), MISCREG_DBGBVR10 },
+    { MiscRegNum32(14, 0, 0, 10, 5), MISCREG_DBGBCR10 },
+    { MiscRegNum32(14, 0, 0, 10, 6), MISCREG_DBGWVR10 },
+    { MiscRegNum32(14, 0, 0, 10, 7), MISCREG_DBGWCR10 },
+    { MiscRegNum32(14, 0, 0, 11, 4), MISCREG_DBGBVR11 },
+    { MiscRegNum32(14, 0, 0, 11, 5), MISCREG_DBGBCR11 },
+    { MiscRegNum32(14, 0, 0, 11, 6), MISCREG_DBGWVR11 },
+    { MiscRegNum32(14, 0, 0, 11, 7), MISCREG_DBGWCR11 },
+    { MiscRegNum32(14, 0, 0, 12, 4), MISCREG_DBGBVR12 },
+    { MiscRegNum32(14, 0, 0, 12, 5), MISCREG_DBGBCR12 },
+    { MiscRegNum32(14, 0, 0, 12, 6), MISCREG_DBGWVR12 },
+    { MiscRegNum32(14, 0, 0, 12, 7), MISCREG_DBGWCR12 },
+    { MiscRegNum32(14, 0, 0, 13, 4), MISCREG_DBGBVR13 },
+    { MiscRegNum32(14, 0, 0, 13, 5), MISCREG_DBGBCR13 },
+    { MiscRegNum32(14, 0, 0, 13, 6), MISCREG_DBGWVR13 },
+    { MiscRegNum32(14, 0, 0, 13, 7), MISCREG_DBGWCR13 },
+    { MiscRegNum32(14, 0, 0, 14, 4), MISCREG_DBGBVR14 },
+    { MiscRegNum32(14, 0, 0, 14, 5), MISCREG_DBGBCR14 },
+    { MiscRegNum32(14, 0, 0, 14, 6), MISCREG_DBGWVR14 },
+    { MiscRegNum32(14, 0, 0, 14, 7), MISCREG_DBGWCR14 },
+    { MiscRegNum32(14, 0, 0, 15, 4), MISCREG_DBGBVR15 },
+    { MiscRegNum32(14, 0, 0, 15, 5), MISCREG_DBGBCR15 },
+    { MiscRegNum32(14, 0, 0, 15, 6), MISCREG_DBGWVR15 },
+    { MiscRegNum32(14, 0, 0, 15, 7), MISCREG_DBGWCR15 },
+    { MiscRegNum32(14, 0, 1, 0, 1), MISCREG_DBGBXVR0 },
+    { MiscRegNum32(14, 0, 1, 0, 4), MISCREG_DBGOSLAR },
+    { MiscRegNum32(14, 0, 1, 1, 1), MISCREG_DBGBXVR1 },
+    { MiscRegNum32(14, 0, 1, 1, 4), MISCREG_DBGOSLSR },
+    { MiscRegNum32(14, 0, 1, 2, 1), MISCREG_DBGBXVR2 },
+    { MiscRegNum32(14, 0, 1, 3, 1), MISCREG_DBGBXVR3 },
+    { MiscRegNum32(14, 0, 1, 3, 4), MISCREG_DBGOSDLR },
+    { MiscRegNum32(14, 0, 1, 4, 1), MISCREG_DBGBXVR4 },
+    { MiscRegNum32(14, 0, 1, 4, 4), MISCREG_DBGPRCR },
+    { MiscRegNum32(14, 0, 1, 5, 1), MISCREG_DBGBXVR5 },
+    { MiscRegNum32(14, 0, 1, 6, 1), MISCREG_DBGBXVR6 },
+    { MiscRegNum32(14, 0, 1, 7, 1), MISCREG_DBGBXVR7 },
+    { MiscRegNum32(14, 0, 1, 8, 1), MISCREG_DBGBXVR8 },
+    { MiscRegNum32(14, 0, 1, 9, 1), MISCREG_DBGBXVR9 },
+    { MiscRegNum32(14, 0, 1, 10, 1), MISCREG_DBGBXVR10 },
+    { MiscRegNum32(14, 0, 1, 11, 1), MISCREG_DBGBXVR11 },
+    { MiscRegNum32(14, 0, 1, 12, 1), MISCREG_DBGBXVR12 },
+    { MiscRegNum32(14, 0, 1, 13, 1), MISCREG_DBGBXVR13 },
+    { MiscRegNum32(14, 0, 1, 14, 1), MISCREG_DBGBXVR14 },
+    { MiscRegNum32(14, 0, 1, 15, 1), MISCREG_DBGBXVR15 },
+    { MiscRegNum32(14, 6, 1, 0, 0), MISCREG_TEEHBR },
+    { MiscRegNum32(14, 7, 0, 0, 0), MISCREG_JIDR },
+    { MiscRegNum32(14, 7, 1, 0, 0), MISCREG_JOSCR },
+    { MiscRegNum32(14, 7, 2, 0, 0), MISCREG_JMCR },
+    { MiscRegNum32(15, 0, 0, 0, 0), MISCREG_MIDR },
+    { MiscRegNum32(15, 0, 0, 0, 1), MISCREG_CTR },
+    { MiscRegNum32(15, 0, 0, 0, 2), MISCREG_TCMTR },
+    { MiscRegNum32(15, 0, 0, 0, 3), MISCREG_TLBTR },
+    { MiscRegNum32(15, 0, 0, 0, 4), MISCREG_MIDR },
+    { MiscRegNum32(15, 0, 0, 0, 5), MISCREG_MPIDR },
+    { MiscRegNum32(15, 0, 0, 0, 6), MISCREG_REVIDR },
+    { MiscRegNum32(15, 0, 0, 0, 7), MISCREG_MIDR },
+    { MiscRegNum32(15, 0, 0, 1, 0), MISCREG_ID_PFR0 },
+    { MiscRegNum32(15, 0, 0, 1, 1), MISCREG_ID_PFR1 },
+    { MiscRegNum32(15, 0, 0, 1, 2), MISCREG_ID_DFR0 },
+    { MiscRegNum32(15, 0, 0, 1, 3), MISCREG_ID_AFR0 },
+    { MiscRegNum32(15, 0, 0, 1, 4), MISCREG_ID_MMFR0 },
+    { MiscRegNum32(15, 0, 0, 1, 5), MISCREG_ID_MMFR1 },
+    { MiscRegNum32(15, 0, 0, 1, 6), MISCREG_ID_MMFR2 },
+    { MiscRegNum32(15, 0, 0, 1, 7), MISCREG_ID_MMFR3 },
+    { MiscRegNum32(15, 0, 0, 2, 0), MISCREG_ID_ISAR0 },
+    { MiscRegNum32(15, 0, 0, 2, 1), MISCREG_ID_ISAR1 },
+    { MiscRegNum32(15, 0, 0, 2, 2), MISCREG_ID_ISAR2 },
+    { MiscRegNum32(15, 0, 0, 2, 3), MISCREG_ID_ISAR3 },
+    { MiscRegNum32(15, 0, 0, 2, 4), MISCREG_ID_ISAR4 },
+    { MiscRegNum32(15, 0, 0, 2, 5), MISCREG_ID_ISAR5 },
+    { MiscRegNum32(15, 0, 0, 2, 6), MISCREG_ID_MMFR4 },
+    { MiscRegNum32(15, 0, 0, 2, 7), MISCREG_ID_ISAR6 },
+    { MiscRegNum32(15, 0, 0, 3, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 3, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 3, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 3, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 3, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 3, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 3, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 3, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 4, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 4, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 4, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 4, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 4, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 4, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 4, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 4, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 5, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 5, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 5, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 5, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 5, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 5, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 5, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 5, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 6, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 6, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 6, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 6, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 6, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 6, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 6, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 6, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 7, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 7, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 7, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 7, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 7, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 7, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 7, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 7, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 8, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 8, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 8, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 8, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 8, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 8, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 8, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 8, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 9, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 9, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 9, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 9, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 9, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 9, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 9, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 9, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 10, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 10, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 10, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 10, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 10, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 10, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 10, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 10, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 11, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 11, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 11, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 11, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 11, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 11, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 11, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 11, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 12, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 12, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 12, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 12, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 12, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 12, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 12, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 12, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 13, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 13, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 13, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 13, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 13, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 13, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 13, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 13, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 14, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 14, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 14, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 14, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 14, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 14, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 14, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 14, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 15, 0), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 15, 1), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 15, 2), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 15, 3), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 15, 4), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 15, 5), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 15, 6), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 0, 15, 7), MISCREG_RAZ },
+    { MiscRegNum32(15, 0, 1, 0, 0), MISCREG_SCTLR },
+    { MiscRegNum32(15, 0, 1, 0, 1), MISCREG_ACTLR },
+    { MiscRegNum32(15, 0, 1, 0, 2), MISCREG_CPACR },
+    { MiscRegNum32(15, 0, 1, 1, 0), MISCREG_SCR },
+    { MiscRegNum32(15, 0, 1, 1, 1), MISCREG_SDER },
+    { MiscRegNum32(15, 0, 1, 1, 2), MISCREG_NSACR },
+    { MiscRegNum32(15, 0, 1, 3, 1), MISCREG_SDCR },
+    { MiscRegNum32(15, 0, 2, 0, 0), MISCREG_TTBR0 },
+    { MiscRegNum32(15, 0, 2, 0, 1), MISCREG_TTBR1 },
+    { MiscRegNum32(15, 0, 2, 0, 2), MISCREG_TTBCR },
+    { MiscRegNum32(15, 0, 3, 0, 0), MISCREG_DACR },
+    { MiscRegNum32(15, 0, 4, 6, 0), MISCREG_ICC_PMR },
+    { MiscRegNum32(15, 0, 5, 0, 0), MISCREG_DFSR },
+    { MiscRegNum32(15, 0, 5, 0, 1), MISCREG_IFSR },
+    { MiscRegNum32(15, 0, 5, 1, 0), MISCREG_ADFSR },
+    { MiscRegNum32(15, 0, 5, 1, 1), MISCREG_AIFSR },
+    { MiscRegNum32(15, 0, 6, 0, 0), MISCREG_DFAR },
+    { MiscRegNum32(15, 0, 6, 0, 2), MISCREG_IFAR },
+    { MiscRegNum32(15, 0, 7, 0, 4), MISCREG_NOP },
+    { MiscRegNum32(15, 0, 7, 1, 0), MISCREG_ICIALLUIS },
+    { MiscRegNum32(15, 0, 7, 1, 6), MISCREG_BPIALLIS },
+    { MiscRegNum32(15, 0, 7, 2, 7), MISCREG_DBGDEVID0 },
+    { MiscRegNum32(15, 0, 7, 4, 0), MISCREG_PAR },
+    { MiscRegNum32(15, 0, 7, 5, 0), MISCREG_ICIALLU },
+    { MiscRegNum32(15, 0, 7, 5, 1), MISCREG_ICIMVAU },
+    { MiscRegNum32(15, 0, 7, 5, 4), MISCREG_CP15ISB },
+    { MiscRegNum32(15, 0, 7, 5, 6), MISCREG_BPIALL },
+    { MiscRegNum32(15, 0, 7, 5, 7), MISCREG_BPIMVA },
+    { MiscRegNum32(15, 0, 7, 6, 1), MISCREG_DCIMVAC },
+    { MiscRegNum32(15, 0, 7, 6, 2), MISCREG_DCISW },
+    { MiscRegNum32(15, 0, 7, 8, 0), MISCREG_ATS1CPR },
+    { MiscRegNum32(15, 0, 7, 8, 1), MISCREG_ATS1CPW },
+    { MiscRegNum32(15, 0, 7, 8, 2), MISCREG_ATS1CUR },
+    { MiscRegNum32(15, 0, 7, 8, 3), MISCREG_ATS1CUW },
+    { MiscRegNum32(15, 0, 7, 8, 4), MISCREG_ATS12NSOPR },
+    { MiscRegNum32(15, 0, 7, 8, 5), MISCREG_ATS12NSOPW },
+    { MiscRegNum32(15, 0, 7, 8, 6), MISCREG_ATS12NSOUR },
+    { MiscRegNum32(15, 0, 7, 8, 7), MISCREG_ATS12NSOUW },
+    { MiscRegNum32(15, 0, 7, 10, 1), MISCREG_DCCMVAC },
+    { MiscRegNum32(15, 0, 7, 10, 2), MISCREG_DCCSW },
+    { MiscRegNum32(15, 0, 7, 10, 4), MISCREG_CP15DSB },
+    { MiscRegNum32(15, 0, 7, 10, 5), MISCREG_CP15DMB },
+    { MiscRegNum32(15, 0, 7, 11, 1), MISCREG_DCCMVAU },
+    { MiscRegNum32(15, 0, 7, 13, 1), MISCREG_NOP },
+    { MiscRegNum32(15, 0, 7, 14, 1), MISCREG_DCCIMVAC },
+    { MiscRegNum32(15, 0, 7, 14, 2), MISCREG_DCCISW },
+    { MiscRegNum32(15, 0, 8, 3, 0), MISCREG_TLBIALLIS },
+    { MiscRegNum32(15, 0, 8, 3, 1), MISCREG_TLBIMVAIS },
+    { MiscRegNum32(15, 0, 8, 3, 2), MISCREG_TLBIASIDIS },
+    { MiscRegNum32(15, 0, 8, 3, 3), MISCREG_TLBIMVAAIS },
+    { MiscRegNum32(15, 0, 8, 3, 5), MISCREG_TLBIMVALIS },
+    { MiscRegNum32(15, 0, 8, 3, 7), MISCREG_TLBIMVAALIS },
+    { MiscRegNum32(15, 0, 8, 5, 0), MISCREG_ITLBIALL },
+    { MiscRegNum32(15, 0, 8, 5, 1), MISCREG_ITLBIMVA },
+    { MiscRegNum32(15, 0, 8, 5, 2), MISCREG_ITLBIASID },
+    { MiscRegNum32(15, 0, 8, 6, 0), MISCREG_DTLBIALL },
+    { MiscRegNum32(15, 0, 8, 6, 1), MISCREG_DTLBIMVA },
+    { MiscRegNum32(15, 0, 8, 6, 2), MISCREG_DTLBIASID },
+    { MiscRegNum32(15, 0, 8, 7, 0), MISCREG_TLBIALL },
+    { MiscRegNum32(15, 0, 8, 7, 1), MISCREG_TLBIMVA },
+    { MiscRegNum32(15, 0, 8, 7, 2), MISCREG_TLBIASID },
+    { MiscRegNum32(15, 0, 8, 7, 3), MISCREG_TLBIMVAA },
+    { MiscRegNum32(15, 0, 8, 7, 5), MISCREG_TLBIMVAL },
+    { MiscRegNum32(15, 0, 8, 7, 7), MISCREG_TLBIMVAAL },
+    { MiscRegNum32(15, 0, 9, 12, 0), MISCREG_PMCR },
+    { MiscRegNum32(15, 0, 9, 12, 1), MISCREG_PMCNTENSET },
+    { MiscRegNum32(15, 0, 9, 12, 2), MISCREG_PMCNTENCLR },
+    { MiscRegNum32(15, 0, 9, 12, 3), MISCREG_PMOVSR },
+    { MiscRegNum32(15, 0, 9, 12, 4), MISCREG_PMSWINC },
+    { MiscRegNum32(15, 0, 9, 12, 5), MISCREG_PMSELR },
+    { MiscRegNum32(15, 0, 9, 12, 6), MISCREG_PMCEID0 },
+    { MiscRegNum32(15, 0, 9, 12, 7), MISCREG_PMCEID1 },
+    { MiscRegNum32(15, 0, 9, 13, 0), MISCREG_PMCCNTR },
+    { MiscRegNum32(15, 0, 9, 13, 1), MISCREG_PMXEVTYPER_PMCCFILTR },
+    { MiscRegNum32(15, 0, 9, 13, 2), MISCREG_PMXEVCNTR },
+    { MiscRegNum32(15, 0, 9, 14, 0), MISCREG_PMUSERENR },
+    { MiscRegNum32(15, 0, 9, 14, 1), MISCREG_PMINTENSET },
+    { MiscRegNum32(15, 0, 9, 14, 2), MISCREG_PMINTENCLR },
+    { MiscRegNum32(15, 0, 9, 14, 3), MISCREG_PMOVSSET },
+    { MiscRegNum32(15, 0, 10, 2, 0), MISCREG_PRRR_MAIR0 },
+    { MiscRegNum32(15, 0, 10, 2, 1), MISCREG_NMRR_MAIR1 },
+    { MiscRegNum32(15, 0, 10, 3, 0), MISCREG_AMAIR0 },
+    { MiscRegNum32(15, 0, 10, 3, 1), MISCREG_AMAIR1 },
+    { MiscRegNum32(15, 0, 12, 0, 0), MISCREG_VBAR },
+    { MiscRegNum32(15, 0, 12, 0, 1), MISCREG_MVBAR },
+    { MiscRegNum32(15, 0, 12, 1, 0), MISCREG_ISR },
+    { MiscRegNum32(15, 0, 12, 8, 0), MISCREG_ICC_IAR0 },
+    { MiscRegNum32(15, 0, 12, 8, 1), MISCREG_ICC_EOIR0 },
+    { MiscRegNum32(15, 0, 12, 8, 2), MISCREG_ICC_HPPIR0 },
+    { MiscRegNum32(15, 0, 12, 8, 3), MISCREG_ICC_BPR0 },
+    { MiscRegNum32(15, 0, 12, 8, 4), MISCREG_ICC_AP0R0 },
+    { MiscRegNum32(15, 0, 12, 8, 5), MISCREG_ICC_AP0R1 },
+    { MiscRegNum32(15, 0, 12, 8, 6), MISCREG_ICC_AP0R2 },
+    { MiscRegNum32(15, 0, 12, 8, 7), MISCREG_ICC_AP0R3 },
+    { MiscRegNum32(15, 0, 12, 9, 0), MISCREG_ICC_AP1R0 },
+    { MiscRegNum32(15, 0, 12, 9, 1), MISCREG_ICC_AP1R1 },
+    { MiscRegNum32(15, 0, 12, 9, 2), MISCREG_ICC_AP1R2 },
+    { MiscRegNum32(15, 0, 12, 9, 3), MISCREG_ICC_AP1R3 },
+    { MiscRegNum32(15, 0, 12, 11, 1), MISCREG_ICC_DIR },
+    { MiscRegNum32(15, 0, 12, 11, 3), MISCREG_ICC_RPR },
+    { MiscRegNum32(15, 0, 12, 12, 0), MISCREG_ICC_IAR1 },
+    { MiscRegNum32(15, 0, 12, 12, 1), MISCREG_ICC_EOIR1 },
+    { MiscRegNum32(15, 0, 12, 12, 2), MISCREG_ICC_HPPIR1 },
+    { MiscRegNum32(15, 0, 12, 12, 3), MISCREG_ICC_BPR1 },
+    { MiscRegNum32(15, 0, 12, 12, 4), MISCREG_ICC_CTLR },
+    { MiscRegNum32(15, 0, 12, 12, 5), MISCREG_ICC_SRE },
+    { MiscRegNum32(15, 0, 12, 12, 6), MISCREG_ICC_IGRPEN0 },
+    { MiscRegNum32(15, 0, 12, 12, 7), MISCREG_ICC_IGRPEN1 },
+    { MiscRegNum32(15, 0, 13, 0, 0), MISCREG_FCSEIDR },
+    { MiscRegNum32(15, 0, 13, 0, 1), MISCREG_CONTEXTIDR },
+    { MiscRegNum32(15, 0, 13, 0, 2), MISCREG_TPIDRURW },
+    { MiscRegNum32(15, 0, 13, 0, 3), MISCREG_TPIDRURO },
+    { MiscRegNum32(15, 0, 13, 0, 4), MISCREG_TPIDRPRW },
+    { MiscRegNum32(15, 0, 14, 0, 0), MISCREG_CNTFRQ },
+    { MiscRegNum32(15, 0, 14, 1, 0), MISCREG_CNTKCTL },
+    { MiscRegNum32(15, 0, 14, 2, 0), MISCREG_CNTP_TVAL },
+    { MiscRegNum32(15, 0, 14, 2, 1), MISCREG_CNTP_CTL },
+    { MiscRegNum32(15, 0, 14, 3, 0), MISCREG_CNTV_TVAL },
+    { MiscRegNum32(15, 0, 14, 3, 1), MISCREG_CNTV_CTL },
+    { MiscRegNum32(15, 1, 0, 0, 0), MISCREG_CCSIDR },
+    { MiscRegNum32(15, 1, 0, 0, 1), MISCREG_CLIDR },
+    { MiscRegNum32(15, 1, 0, 0, 7), MISCREG_AIDR },
+    { MiscRegNum32(15, 2, 0, 0, 0), MISCREG_CSSELR },
+    { MiscRegNum32(15, 4, 0, 0, 0), MISCREG_VPIDR },
+    { MiscRegNum32(15, 4, 0, 0, 5), MISCREG_VMPIDR },
+    { MiscRegNum32(15, 4, 1, 0, 0), MISCREG_HSCTLR },
+    { MiscRegNum32(15, 4, 1, 0, 1), MISCREG_HACTLR },
+    { MiscRegNum32(15, 4, 1, 1, 0), MISCREG_HCR },
+    { MiscRegNum32(15, 4, 1, 1, 1), MISCREG_HDCR },
+    { MiscRegNum32(15, 4, 1, 1, 2), MISCREG_HCPTR },
+    { MiscRegNum32(15, 4, 1, 1, 3), MISCREG_HSTR },
+    { MiscRegNum32(15, 4, 1, 1, 4), MISCREG_HCR2 },
+    { MiscRegNum32(15, 4, 1, 1, 7), MISCREG_HACR },
+    { MiscRegNum32(15, 4, 2, 0, 2), MISCREG_HTCR },
+    { MiscRegNum32(15, 4, 2, 1, 2), MISCREG_VTCR },
+    { MiscRegNum32(15, 4, 5, 1, 0), MISCREG_HADFSR },
+    { MiscRegNum32(15, 4, 5, 1, 1), MISCREG_HAIFSR },
+    { MiscRegNum32(15, 4, 5, 2, 0), MISCREG_HSR },
+    { MiscRegNum32(15, 4, 6, 0, 0), MISCREG_HDFAR },
+    { MiscRegNum32(15, 4, 6, 0, 2), MISCREG_HIFAR },
+    { MiscRegNum32(15, 4, 6, 0, 4), MISCREG_HPFAR },
+    { MiscRegNum32(15, 4, 7, 8, 0), MISCREG_ATS1HR },
+    { MiscRegNum32(15, 4, 7, 8, 1), MISCREG_ATS1HW },
+    { MiscRegNum32(15, 4, 8, 0, 1), MISCREG_TLBIIPAS2IS },
+    { MiscRegNum32(15, 4, 8, 0, 5), MISCREG_TLBIIPAS2LIS },
+    { MiscRegNum32(15, 4, 8, 3, 0), MISCREG_TLBIALLHIS },
+    { MiscRegNum32(15, 4, 8, 3, 1), MISCREG_TLBIMVAHIS },
+    { MiscRegNum32(15, 4, 8, 3, 4), MISCREG_TLBIALLNSNHIS },
+    { MiscRegNum32(15, 4, 8, 3, 5), MISCREG_TLBIMVALHIS },
+    { MiscRegNum32(15, 4, 8, 4, 1), MISCREG_TLBIIPAS2 },
+    { MiscRegNum32(15, 4, 8, 4, 5), MISCREG_TLBIIPAS2L },
+    { MiscRegNum32(15, 4, 8, 7, 0), MISCREG_TLBIALLH },
+    { MiscRegNum32(15, 4, 8, 7, 1), MISCREG_TLBIMVAH },
+    { MiscRegNum32(15, 4, 8, 7, 4), MISCREG_TLBIALLNSNH },
+    { MiscRegNum32(15, 4, 8, 7, 5), MISCREG_TLBIMVALH },
+    { MiscRegNum32(15, 4, 10, 2, 0), MISCREG_HMAIR0 },
+    { MiscRegNum32(15, 4, 10, 2, 1), MISCREG_HMAIR1 },
+    { MiscRegNum32(15, 4, 10, 3, 0), MISCREG_HAMAIR0 },
+    { MiscRegNum32(15, 4, 10, 3, 1), MISCREG_HAMAIR1 },
+    { MiscRegNum32(15, 4, 12, 0, 0), MISCREG_HVBAR },
+    { MiscRegNum32(15, 4, 12, 8, 0), MISCREG_ICH_AP0R0 },
+    { MiscRegNum32(15, 4, 12, 8, 1), MISCREG_ICH_AP0R1 },
+    { MiscRegNum32(15, 4, 12, 8, 2), MISCREG_ICH_AP0R2 },
+    { MiscRegNum32(15, 4, 12, 8, 3), MISCREG_ICH_AP0R3 },
+    { MiscRegNum32(15, 4, 12, 9, 0), MISCREG_ICH_AP1R0 },
+    { MiscRegNum32(15, 4, 12, 9, 1), MISCREG_ICH_AP1R1 },
+    { MiscRegNum32(15, 4, 12, 9, 2), MISCREG_ICH_AP1R2 },
+    { MiscRegNum32(15, 4, 12, 9, 3), MISCREG_ICH_AP1R3 },
+    { MiscRegNum32(15, 4, 12, 9, 5), MISCREG_ICC_HSRE },
+    { MiscRegNum32(15, 4, 12, 11, 0), MISCREG_ICH_HCR },
+    { MiscRegNum32(15, 4, 12, 11, 1), MISCREG_ICH_VTR },
+    { MiscRegNum32(15, 4, 12, 11, 2), MISCREG_ICH_MISR },
+    { MiscRegNum32(15, 4, 12, 11, 3), MISCREG_ICH_EISR },
+    { MiscRegNum32(15, 4, 12, 11, 5), MISCREG_ICH_ELRSR },
+    { MiscRegNum32(15, 4, 12, 11, 7), MISCREG_ICH_VMCR },
+    { MiscRegNum32(15, 4, 12, 12, 0), MISCREG_ICH_LR0 },
+    { MiscRegNum32(15, 4, 12, 12, 1), MISCREG_ICH_LR1 },
+    { MiscRegNum32(15, 4, 12, 12, 2), MISCREG_ICH_LR2 },
+    { MiscRegNum32(15, 4, 12, 12, 3), MISCREG_ICH_LR3 },
+    { MiscRegNum32(15, 4, 12, 12, 4), MISCREG_ICH_LR4 },
+    { MiscRegNum32(15, 4, 12, 12, 5), MISCREG_ICH_LR5 },
+    { MiscRegNum32(15, 4, 12, 12, 6), MISCREG_ICH_LR6 },
+    { MiscRegNum32(15, 4, 12, 12, 7), MISCREG_ICH_LR7 },
+    { MiscRegNum32(15, 4, 12, 13, 0), MISCREG_ICH_LR8 },
+    { MiscRegNum32(15, 4, 12, 13, 1), MISCREG_ICH_LR9 },
+    { MiscRegNum32(15, 4, 12, 13, 2), MISCREG_ICH_LR10 },
+    { MiscRegNum32(15, 4, 12, 13, 3), MISCREG_ICH_LR11 },
+    { MiscRegNum32(15, 4, 12, 13, 4), MISCREG_ICH_LR12 },
+    { MiscRegNum32(15, 4, 12, 13, 5), MISCREG_ICH_LR13 },
+    { MiscRegNum32(15, 4, 12, 13, 6), MISCREG_ICH_LR14 },
+    { MiscRegNum32(15, 4, 12, 13, 7), MISCREG_ICH_LR15 },
+    { MiscRegNum32(15, 4, 12, 14, 0), MISCREG_ICH_LRC0 },
+    { MiscRegNum32(15, 4, 12, 14, 1), MISCREG_ICH_LRC1 },
+    { MiscRegNum32(15, 4, 12, 14, 2), MISCREG_ICH_LRC2 },
+    { MiscRegNum32(15, 4, 12, 14, 3), MISCREG_ICH_LRC3 },
+    { MiscRegNum32(15, 4, 12, 14, 4), MISCREG_ICH_LRC4 },
+    { MiscRegNum32(15, 4, 12, 14, 5), MISCREG_ICH_LRC5 },
+    { MiscRegNum32(15, 4, 12, 14, 6), MISCREG_ICH_LRC6 },
+    { MiscRegNum32(15, 4, 12, 14, 7), MISCREG_ICH_LRC7 },
+    { MiscRegNum32(15, 4, 12, 15, 0), MISCREG_ICH_LRC8 },
+    { MiscRegNum32(15, 4, 12, 15, 1), MISCREG_ICH_LRC9 },
+    { MiscRegNum32(15, 4, 12, 15, 2), MISCREG_ICH_LRC10 },
+    { MiscRegNum32(15, 4, 12, 15, 3), MISCREG_ICH_LRC11 },
+    { MiscRegNum32(15, 4, 12, 15, 4), MISCREG_ICH_LRC12 },
+    { MiscRegNum32(15, 4, 12, 15, 5), MISCREG_ICH_LRC13 },
+    { MiscRegNum32(15, 4, 12, 15, 6), MISCREG_ICH_LRC14 },
+    { MiscRegNum32(15, 4, 12, 15, 7), MISCREG_ICH_LRC15 },
+    { MiscRegNum32(15, 4, 13, 0, 2), MISCREG_HTPIDR },
+    { MiscRegNum32(15, 4, 14, 1, 0), MISCREG_CNTHCTL },
+    { MiscRegNum32(15, 4, 14, 2, 0), MISCREG_CNTHP_TVAL },
+    { MiscRegNum32(15, 4, 14, 2, 1), MISCREG_CNTHP_CTL },
+    { MiscRegNum32(15, 6, 12, 12, 4), MISCREG_ICC_MCTLR },
+    { MiscRegNum32(15, 6, 12, 12, 5), MISCREG_ICC_MSRE },
+    { MiscRegNum32(15, 6, 12, 12, 7), MISCREG_ICC_MGRPEN1 },
+    // MCRR/MRRC regs
+    { MiscRegNum32(15, 0, 2), MISCREG_TTBR0 },
+    { MiscRegNum32(15, 0, 7), MISCREG_PAR },
+    { MiscRegNum32(15, 0, 12), MISCREG_ICC_SGI1R },
+    { MiscRegNum32(15, 0, 14), MISCREG_CNTPCT },
+    { MiscRegNum32(15, 0, 15), MISCREG_CPUMERRSR },
+    { MiscRegNum32(15, 1, 2), MISCREG_TTBR1 },
+    { MiscRegNum32(15, 1, 12), MISCREG_ICC_ASGI1R },
+    { MiscRegNum32(15, 1, 14), MISCREG_CNTVCT },
+    { MiscRegNum32(15, 1, 15), MISCREG_L2MERRSR },
+    { MiscRegNum32(15, 2, 12), MISCREG_ICC_SGI0R },
+    { MiscRegNum32(15, 2, 14), MISCREG_CNTP_CVAL },
+    { MiscRegNum32(15, 3, 14), MISCREG_CNTV_CVAL },
+    { MiscRegNum32(15, 4, 2), MISCREG_HTTBR },
+    { MiscRegNum32(15, 4, 14), MISCREG_CNTVOFF },
+    { MiscRegNum32(15, 6, 2), MISCREG_VTTBR },
+    { MiscRegNum32(15, 6, 14), MISCREG_CNTHP_CVAL },
+};
+
+}
+
 MiscRegIndex
 decodeCP14Reg(unsigned crn, unsigned opc1, unsigned crm, unsigned opc2)
 {
-    switch(crn) {
-      case 0:
-        switch (opc1) {
-          case 0:
-            switch (opc2) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    return MISCREG_DBGDIDR;
-                  case 1:
-                    return MISCREG_DBGDSCRint;
-                  case 7:
-                    return MISCREG_DBGVCR;
-                }
-                break;
-              case 2:
-                switch (crm) {
-                  case 0:
-                    return MISCREG_DBGDTRRXext;
-                  case 2:
-                    return MISCREG_DBGDSCRext;
-                  case 3:
-                    return MISCREG_DBGDTRTXext;
-                  case 6:
-                    return MISCREG_DBGOSECCR;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    return MISCREG_DBGBVR0;
-                  case 1:
-                    return MISCREG_DBGBVR1;
-                  case 2:
-                    return MISCREG_DBGBVR2;
-                  case 3:
-                    return MISCREG_DBGBVR3;
-                  case 4:
-                    return MISCREG_DBGBVR4;
-                  case 5:
-                    return MISCREG_DBGBVR5;
-                  case 6:
-                    return MISCREG_DBGBVR6;
-                  case 7:
-                    return MISCREG_DBGBVR7;
-                  case 8:
-                    return MISCREG_DBGBVR8;
-                  case 9:
-                    return MISCREG_DBGBVR9;
-                  case 10:
-                    return MISCREG_DBGBVR10;
-                  case 11:
-                    return MISCREG_DBGBVR11;
-                  case 12:
-                    return MISCREG_DBGBVR12;
-                  case 13:
-                    return MISCREG_DBGBVR13;
-                  case 14:
-                    return MISCREG_DBGBVR14;
-                  case 15:
-                    return MISCREG_DBGBVR15;
-                }
-                break;
-              case 5:
-                switch (crm) {
-                  case 0:
-                    return MISCREG_DBGBCR0;
-                  case 1:
-                    return MISCREG_DBGBCR1;
-                  case 2:
-                    return MISCREG_DBGBCR2;
-                  case 3:
-                    return MISCREG_DBGBCR3;
-                  case 4:
-                    return MISCREG_DBGBCR4;
-                  case 5:
-                    return MISCREG_DBGBCR5;
-                  case 6:
-                    return MISCREG_DBGBCR6;
-                  case 7:
-                    return MISCREG_DBGBCR7;
-                  case 8:
-                    return MISCREG_DBGBCR8;
-                  case 9:
-                    return MISCREG_DBGBCR9;
-                  case 10:
-                    return MISCREG_DBGBCR10;
-                  case 11:
-                    return MISCREG_DBGBCR11;
-                  case 12:
-                    return MISCREG_DBGBCR12;
-                  case 13:
-                    return MISCREG_DBGBCR13;
-                  case 14:
-                    return MISCREG_DBGBCR14;
-                  case 15:
-                    return MISCREG_DBGBCR15;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 0:
-                    return MISCREG_DBGWVR0;
-                  case 1:
-                    return MISCREG_DBGWVR1;
-                  case 2:
-                    return MISCREG_DBGWVR2;
-                  case 3:
-                    return MISCREG_DBGWVR3;
-                  case 4:
-                    return MISCREG_DBGWVR4;
-                  case 5:
-                    return MISCREG_DBGWVR5;
-                  case 6:
-                    return MISCREG_DBGWVR6;
-                  case 7:
-                    return MISCREG_DBGWVR7;
-                  case 8:
-                    return MISCREG_DBGWVR8;
-                  case 9:
-                    return MISCREG_DBGWVR9;
-                  case 10:
-                    return MISCREG_DBGWVR10;
-                  case 11:
-                    return MISCREG_DBGWVR11;
-                  case 12:
-                    return MISCREG_DBGWVR12;
-                  case 13:
-                    return MISCREG_DBGWVR13;
-                  case 14:
-                    return MISCREG_DBGWVR14;
-                  case 15:
-                    return MISCREG_DBGWVR15;
-                    break;
-                }
-                break;
-              case 7:
-                switch (crm) {
-                  case 0:
-                    return MISCREG_DBGWCR0;
-                  case 1:
-                    return MISCREG_DBGWCR1;
-                  case 2:
-                    return MISCREG_DBGWCR2;
-                  case 3:
-                    return MISCREG_DBGWCR3;
-                  case 4:
-                    return MISCREG_DBGWCR4;
-                  case 5:
-                    return MISCREG_DBGWCR5;
-                  case 6:
-                    return MISCREG_DBGWCR6;
-                  case 7:
-                    return MISCREG_DBGWCR7;
-                  case 8:
-                    return MISCREG_DBGWCR8;
-                  case 9:
-                    return MISCREG_DBGWCR9;
-                  case 10:
-                    return MISCREG_DBGWCR10;
-                  case 11:
-                    return MISCREG_DBGWCR11;
-                  case 12:
-                    return MISCREG_DBGWCR12;
-                  case 13:
-                    return MISCREG_DBGWCR13;
-                  case 14:
-                    return MISCREG_DBGWCR14;
-                  case 15:
-                    return MISCREG_DBGWCR15;
-                }
-                break;
-            }
-            break;
-          case 7:
-            switch (opc2) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    return MISCREG_JIDR;
-                }
-              break;
-            }
-            break;
-        }
-        break;
-      case 1:
-        switch (opc1) {
-          case 0:
-            switch(opc2) {
-              case 1:
-                switch(crm) {
-                  case 0:
-                      return MISCREG_DBGBXVR0;
-                  case 1:
-                      return MISCREG_DBGBXVR1;
-                  case 2:
-                      return MISCREG_DBGBXVR2;
-                  case 3:
-                      return MISCREG_DBGBXVR3;
-                  case 4:
-                      return MISCREG_DBGBXVR4;
-                  case 5:
-                      return MISCREG_DBGBXVR5;
-                  case 6:
-                      return MISCREG_DBGBXVR6;
-                  case 7:
-                      return MISCREG_DBGBXVR7;
-                  case 8:
-                      return MISCREG_DBGBXVR8;
-                  case 9:
-                      return MISCREG_DBGBXVR9;
-                  case 10:
-                      return MISCREG_DBGBXVR10;
-                  case 11:
-                      return MISCREG_DBGBXVR11;
-                  case 12:
-                      return MISCREG_DBGBXVR12;
-                  case 13:
-                      return MISCREG_DBGBXVR13;
-                  case 14:
-                      return MISCREG_DBGBXVR14;
-                  case 15:
-                      return MISCREG_DBGBXVR15;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    return MISCREG_DBGOSLAR;
-                  case 1:
-                    return MISCREG_DBGOSLSR;
-                  case 3:
-                    return MISCREG_DBGOSDLR;
-                  case 4:
-                    return MISCREG_DBGPRCR;
-                }
-                break;
-            }
-            break;
-          case 6:
-            switch (crm) {
-              case 0:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_TEEHBR;
-                }
-                break;
-            }
-            break;
-          case 7:
-            switch (crm) {
-              case 0:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_JOSCR;
-                }
-                break;
-            }
-            break;
-        }
-        break;
-      case 2:
-        switch (opc1) {
-          case 7:
-            switch (crm) {
-              case 0:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_JMCR;
-                }
-                break;
-            }
-            break;
-        }
-        break;
+    MiscRegNum32 cop_reg(14, opc1, crn, crm, opc2);
+    auto it = miscRegNum32ToIdx.find(cop_reg);
+    if (it != miscRegNum32ToIdx.end()) {
+        return it->second;
+    } else {
+        warn("CP14 unimplemented crn[%d], opc1[%d], crm[%d], opc2[%d]",
+             crn, opc1, crm, opc2);
+        return MISCREG_UNKNOWN;
     }
-    // If we get here then it must be a register that we haven't implemented
-    warn("CP14 unimplemented crn[%d], opc1[%d], crm[%d], opc2[%d]",
-         crn, opc1, crm, opc2);
-    return MISCREG_CP14_UNIMPL;
 }
 
 MiscRegIndex
 decodeCP15Reg(unsigned crn, unsigned opc1, unsigned crm, unsigned opc2)
 {
-    switch (crn) {
-      case 0:
-        switch (opc1) {
-          case 0:
-            switch (crm) {
-              case 0:
-                switch (opc2) {
-                  case 1:
-                    return MISCREG_CTR;
-                  case 2:
-                    return MISCREG_TCMTR;
-                  case 3:
-                    return MISCREG_TLBTR;
-                  case 5:
-                    return MISCREG_MPIDR;
-                  case 6:
-                    return MISCREG_REVIDR;
-                  default:
-                    return MISCREG_MIDR;
-                }
-                break;
-              case 1:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_ID_PFR0;
-                  case 1:
-                    return MISCREG_ID_PFR1;
-                  case 2:
-                    return MISCREG_ID_DFR0;
-                  case 3:
-                    return MISCREG_ID_AFR0;
-                  case 4:
-                    return MISCREG_ID_MMFR0;
-                  case 5:
-                    return MISCREG_ID_MMFR1;
-                  case 6:
-                    return MISCREG_ID_MMFR2;
-                  case 7:
-                    return MISCREG_ID_MMFR3;
-                }
-                break;
-              case 2:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_ID_ISAR0;
-                  case 1:
-                    return MISCREG_ID_ISAR1;
-                  case 2:
-                    return MISCREG_ID_ISAR2;
-                  case 3:
-                    return MISCREG_ID_ISAR3;
-                  case 4:
-                    return MISCREG_ID_ISAR4;
-                  case 5:
-                    return MISCREG_ID_ISAR5;
-                  case 6:
-                    return MISCREG_ID_MMFR4;
-                  case 7:
-                    return MISCREG_ID_ISAR6;
-                }
-                break;
-              default:
-                return MISCREG_RAZ; // read as zero
-            }
-            break;
-          case 1:
-            if (crm == 0) {
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_CCSIDR;
-                  case 1:
-                    return MISCREG_CLIDR;
-                  case 7:
-                    return MISCREG_AIDR;
-                }
-            }
-            break;
-          case 2:
-            if (crm == 0 && opc2 == 0) {
-                return MISCREG_CSSELR;
-            }
-            break;
-          case 4:
-            if (crm == 0) {
-                if (opc2 == 0)
-                    return MISCREG_VPIDR;
-                else if (opc2 == 5)
-                    return MISCREG_VMPIDR;
-            }
-            break;
-        }
-        break;
-      case 1:
-        if (opc1 == 0) {
-            if (crm == 0) {
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_SCTLR;
-                  case 1:
-                    return MISCREG_ACTLR;
-                  case 0x2:
-                    return MISCREG_CPACR;
-                }
-            } else if (crm == 1) {
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_SCR;
-                  case 1:
-                    return MISCREG_SDER;
-                  case 2:
-                    return MISCREG_NSACR;
-                }
-            } else if (crm == 3) {
-                if ( opc2 == 1)
-                    return MISCREG_SDCR;
-            }
-        } else if (opc1 == 4) {
-            if (crm == 0) {
-                if (opc2 == 0)
-                    return MISCREG_HSCTLR;
-                else if (opc2 == 1)
-                    return MISCREG_HACTLR;
-            } else if (crm == 1) {
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_HCR;
-                  case 1:
-                    return MISCREG_HDCR;
-                  case 2:
-                    return MISCREG_HCPTR;
-                  case 4:
-                    return MISCREG_HCR2;
-                  case 3:
-                    return MISCREG_HSTR;
-                  case 7:
-                    return MISCREG_HACR;
-                }
-            }
-        }
-        break;
-      case 2:
-        if (opc1 == 0 && crm == 0) {
-            switch (opc2) {
-              case 0:
-                return MISCREG_TTBR0;
-              case 1:
-                return MISCREG_TTBR1;
-              case 2:
-                return MISCREG_TTBCR;
-            }
-        } else if (opc1 == 4) {
-            if (crm == 0 && opc2 == 2)
-                return MISCREG_HTCR;
-            else if (crm == 1 && opc2 == 2)
-                return MISCREG_VTCR;
-        }
-        break;
-      case 3:
-        if (opc1 == 0 && crm == 0 && opc2 == 0) {
-            return MISCREG_DACR;
-        }
-        break;
-      case 4:
-        if (opc1 == 0 && crm == 6 && opc2 == 0) {
-            return MISCREG_ICC_PMR;
-        }
-        break;
-      case 5:
-        if (opc1 == 0) {
-            if (crm == 0) {
-                if (opc2 == 0) {
-                    return MISCREG_DFSR;
-                } else if (opc2 == 1) {
-                    return MISCREG_IFSR;
-                }
-            } else if (crm == 1) {
-                if (opc2 == 0) {
-                    return MISCREG_ADFSR;
-                } else if (opc2 == 1) {
-                    return MISCREG_AIFSR;
-                }
-            }
-        } else if (opc1 == 4) {
-            if (crm == 1) {
-                if (opc2 == 0)
-                    return MISCREG_HADFSR;
-                else if (opc2 == 1)
-                    return MISCREG_HAIFSR;
-            } else if (crm == 2 && opc2 == 0) {
-                return MISCREG_HSR;
-            }
-        }
-        break;
-      case 6:
-        if (opc1 == 0 && crm == 0) {
-            switch (opc2) {
-              case 0:
-                return MISCREG_DFAR;
-              case 2:
-                return MISCREG_IFAR;
-            }
-        } else if (opc1 == 4 && crm == 0) {
-            switch (opc2) {
-              case 0:
-                return MISCREG_HDFAR;
-              case 2:
-                return MISCREG_HIFAR;
-              case 4:
-                return MISCREG_HPFAR;
-            }
-        }
-        break;
-      case 7:
-        if (opc1 == 0) {
-            switch (crm) {
-              case 0:
-                if (opc2 == 4) {
-                    return MISCREG_NOP;
-                }
-                break;
-              case 1:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_ICIALLUIS;
-                  case 6:
-                    return MISCREG_BPIALLIS;
-                }
-                break;
-              case 2:
-                switch (opc2) {
-                  case 7:
-                    return MISCREG_DBGDEVID0;
-                }
-                break;
-              case 4:
-                if (opc2 == 0) {
-                    return MISCREG_PAR;
-                }
-                break;
-              case 5:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_ICIALLU;
-                  case 1:
-                    return MISCREG_ICIMVAU;
-                  case 4:
-                    return MISCREG_CP15ISB;
-                  case 6:
-                    return MISCREG_BPIALL;
-                  case 7:
-                    return MISCREG_BPIMVA;
-                }
-                break;
-              case 6:
-                if (opc2 == 1) {
-                    return MISCREG_DCIMVAC;
-                } else if (opc2 == 2) {
-                    return MISCREG_DCISW;
-                }
-                break;
-              case 8:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_ATS1CPR;
-                  case 1:
-                    return MISCREG_ATS1CPW;
-                  case 2:
-                    return MISCREG_ATS1CUR;
-                  case 3:
-                    return MISCREG_ATS1CUW;
-                  case 4:
-                    return MISCREG_ATS12NSOPR;
-                  case 5:
-                    return MISCREG_ATS12NSOPW;
-                  case 6:
-                    return MISCREG_ATS12NSOUR;
-                  case 7:
-                    return MISCREG_ATS12NSOUW;
-                }
-                break;
-              case 10:
-                switch (opc2) {
-                  case 1:
-                    return MISCREG_DCCMVAC;
-                  case 2:
-                    return MISCREG_DCCSW;
-                  case 4:
-                    return MISCREG_CP15DSB;
-                  case 5:
-                    return MISCREG_CP15DMB;
-                }
-                break;
-              case 11:
-                if (opc2 == 1) {
-                    return MISCREG_DCCMVAU;
-                }
-                break;
-              case 13:
-                if (opc2 == 1) {
-                    return MISCREG_NOP;
-                }
-                break;
-              case 14:
-                if (opc2 == 1) {
-                    return MISCREG_DCCIMVAC;
-                } else if (opc2 == 2) {
-                    return MISCREG_DCCISW;
-                }
-                break;
-            }
-        } else if (opc1 == 4 && crm == 8) {
-            if (opc2 == 0)
-                return MISCREG_ATS1HR;
-            else if (opc2 == 1)
-                return MISCREG_ATS1HW;
-        }
-        break;
-      case 8:
-        if (opc1 == 0) {
-            switch (crm) {
-              case 3:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_TLBIALLIS;
-                  case 1:
-                    return MISCREG_TLBIMVAIS;
-                  case 2:
-                    return MISCREG_TLBIASIDIS;
-                  case 3:
-                    return MISCREG_TLBIMVAAIS;
-                  case 5:
-                    return MISCREG_TLBIMVALIS;
-                  case 7:
-                    return MISCREG_TLBIMVAALIS;
-                }
-                break;
-              case 5:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_ITLBIALL;
-                  case 1:
-                    return MISCREG_ITLBIMVA;
-                  case 2:
-                    return MISCREG_ITLBIASID;
-                }
-                break;
-              case 6:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_DTLBIALL;
-                  case 1:
-                    return MISCREG_DTLBIMVA;
-                  case 2:
-                    return MISCREG_DTLBIASID;
-                }
-                break;
-              case 7:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_TLBIALL;
-                  case 1:
-                    return MISCREG_TLBIMVA;
-                  case 2:
-                    return MISCREG_TLBIASID;
-                  case 3:
-                    return MISCREG_TLBIMVAA;
-                  case 5:
-                    return MISCREG_TLBIMVAL;
-                  case 7:
-                    return MISCREG_TLBIMVAAL;
-                }
-                break;
-            }
-        } else if (opc1 == 4) {
-            if (crm == 0) {
-                switch (opc2) {
-                  case 1:
-                    return MISCREG_TLBIIPAS2IS;
-                  case 5:
-                    return MISCREG_TLBIIPAS2LIS;
-                }
-            } else if (crm == 3) {
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_TLBIALLHIS;
-                  case 1:
-                    return MISCREG_TLBIMVAHIS;
-                  case 4:
-                    return MISCREG_TLBIALLNSNHIS;
-                  case 5:
-                    return MISCREG_TLBIMVALHIS;
-                }
-            } else if (crm == 4) {
-                switch (opc2) {
-                  case 1:
-                    return MISCREG_TLBIIPAS2;
-                  case 5:
-                    return MISCREG_TLBIIPAS2L;
-                }
-            } else if (crm == 7) {
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_TLBIALLH;
-                  case 1:
-                    return MISCREG_TLBIMVAH;
-                  case 4:
-                    return MISCREG_TLBIALLNSNH;
-                  case 5:
-                    return MISCREG_TLBIMVALH;
-                }
-            }
-        }
-        break;
-      case 9:
-        // Every cop register with CRn = 9 and CRm in
-        // {0-2}, {5-8} is implementation defined regardless
-        // of opc1 and opc2.
-        switch (crm) {
-          case 0:
-          case 1:
-          case 2:
-          case 5:
-          case 6:
-          case 7:
-          case 8:
+    MiscRegNum32 cop_reg(15, opc1, crn, crm, opc2);
+    auto it = miscRegNum32ToIdx.find(cop_reg);
+    if (it != miscRegNum32ToIdx.end()) {
+        return it->second;
+    } else {
+        if ((crn == 15) ||
+            (crn == 9 && (crm <= 2 || crm >= 5)) ||
+            (crn == 10 && opc1 == 0 && crm <= 1) ||
+            (crn == 11 && opc1 <= 7 && (crm <= 8 || crm ==15))) {
             return MISCREG_IMPDEF_UNIMPL;
+        } else {
+            return MISCREG_UNKNOWN;
         }
-        if (opc1 == 0) {
-            switch (crm) {
-              case 12:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_PMCR;
-                  case 1:
-                    return MISCREG_PMCNTENSET;
-                  case 2:
-                    return MISCREG_PMCNTENCLR;
-                  case 3:
-                    return MISCREG_PMOVSR;
-                  case 4:
-                    return MISCREG_PMSWINC;
-                  case 5:
-                    return MISCREG_PMSELR;
-                  case 6:
-                    return MISCREG_PMCEID0;
-                  case 7:
-                    return MISCREG_PMCEID1;
-                }
-                break;
-              case 13:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_PMCCNTR;
-                  case 1:
-                    // Selector is PMSELR.SEL
-                    return MISCREG_PMXEVTYPER_PMCCFILTR;
-                  case 2:
-                    return MISCREG_PMXEVCNTR;
-                }
-                break;
-              case 14:
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_PMUSERENR;
-                  case 1:
-                    return MISCREG_PMINTENSET;
-                  case 2:
-                    return MISCREG_PMINTENCLR;
-                  case 3:
-                    return MISCREG_PMOVSSET;
-                }
-                break;
-            }
-        } else if (opc1 == 1) {
-            switch (crm) {
-              case 0:
-                switch (opc2) {
-                  case 2: // L2CTLR, L2 Control Register
-                    return MISCREG_L2CTLR;
-                  case 3:
-                    return MISCREG_L2ECTLR;
-                }
-                break;
-                break;
-            }
-        }
-        break;
-      case 10:
-        if (opc1 == 0) {
-            // crm 0, 1, 4, and 8, with op2 0 - 7, reserved for TLB lockdown
-            if (crm < 2) {
-                return MISCREG_IMPDEF_UNIMPL;
-            } else if (crm == 2) { // TEX Remap Registers
-                if (opc2 == 0) {
-                    // Selector is TTBCR.EAE
-                    return MISCREG_PRRR_MAIR0;
-                } else if (opc2 == 1) {
-                    // Selector is TTBCR.EAE
-                    return MISCREG_NMRR_MAIR1;
-                }
-            } else if (crm == 3) {
-                if (opc2 == 0) {
-                    return MISCREG_AMAIR0;
-                } else if (opc2 == 1) {
-                    return MISCREG_AMAIR1;
-                }
-            }
-        } else if (opc1 == 4) {
-            // crm 0, 1, 4, and 8, with op2 0 - 7, reserved for TLB lockdown
-            if (crm == 2) {
-                if (opc2 == 0)
-                    return MISCREG_HMAIR0;
-                else if (opc2 == 1)
-                    return MISCREG_HMAIR1;
-            } else if (crm == 3) {
-                if (opc2 == 0)
-                    return MISCREG_HAMAIR0;
-                else if (opc2 == 1)
-                    return MISCREG_HAMAIR1;
-            }
-        }
-        break;
-      case 11:
-        if (opc1 <=7) {
-            switch (crm) {
-              case 0:
-              case 1:
-              case 2:
-              case 3:
-              case 4:
-              case 5:
-              case 6:
-              case 7:
-              case 8:
-              case 15:
-                // Reserved for DMA operations for TCM access
-                return MISCREG_IMPDEF_UNIMPL;
-              default:
-                break;
-            }
-        }
-        break;
-      case 12:
-        if (opc1 == 0) {
-            if (crm == 0) {
-                if (opc2 == 0) {
-                    return MISCREG_VBAR;
-                } else if (opc2 == 1) {
-                    return MISCREG_MVBAR;
-                }
-            } else if (crm == 1) {
-                if (opc2 == 0) {
-                    return MISCREG_ISR;
-                }
-            } else if (crm == 8) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICC_IAR0;
-                    case 1:
-                        return MISCREG_ICC_EOIR0;
-                    case 2:
-                        return MISCREG_ICC_HPPIR0;
-                    case 3:
-                        return MISCREG_ICC_BPR0;
-                    case 4:
-                        return MISCREG_ICC_AP0R0;
-                    case 5:
-                        return MISCREG_ICC_AP0R1;
-                    case 6:
-                        return MISCREG_ICC_AP0R2;
-                    case 7:
-                        return MISCREG_ICC_AP0R3;
-                }
-            } else if (crm == 9) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICC_AP1R0;
-                    case 1:
-                        return MISCREG_ICC_AP1R1;
-                    case 2:
-                        return MISCREG_ICC_AP1R2;
-                    case 3:
-                        return MISCREG_ICC_AP1R3;
-                }
-            } else if (crm == 11) {
-                switch (opc2) {
-                    case 1:
-                        return MISCREG_ICC_DIR;
-                    case 3:
-                        return MISCREG_ICC_RPR;
-                }
-            } else if (crm == 12) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICC_IAR1;
-                    case 1:
-                        return MISCREG_ICC_EOIR1;
-                    case 2:
-                        return MISCREG_ICC_HPPIR1;
-                    case 3:
-                        return MISCREG_ICC_BPR1;
-                    case 4:
-                        return MISCREG_ICC_CTLR;
-                    case 5:
-                        return MISCREG_ICC_SRE;
-                    case 6:
-                        return MISCREG_ICC_IGRPEN0;
-                    case 7:
-                        return MISCREG_ICC_IGRPEN1;
-                }
-            }
-        } else if (opc1 == 4) {
-            if (crm == 0 && opc2 == 0) {
-                return MISCREG_HVBAR;
-            } else if (crm == 8) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICH_AP0R0;
-                    case 1:
-                        return MISCREG_ICH_AP0R1;
-                    case 2:
-                        return MISCREG_ICH_AP0R2;
-                    case 3:
-                        return MISCREG_ICH_AP0R3;
-                }
-            } else if (crm == 9) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICH_AP1R0;
-                    case 1:
-                        return MISCREG_ICH_AP1R1;
-                    case 2:
-                        return MISCREG_ICH_AP1R2;
-                    case 3:
-                        return MISCREG_ICH_AP1R3;
-                    case 5:
-                        return MISCREG_ICC_HSRE;
-                }
-            } else if (crm == 11) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICH_HCR;
-                    case 1:
-                        return MISCREG_ICH_VTR;
-                    case 2:
-                        return MISCREG_ICH_MISR;
-                    case 3:
-                        return MISCREG_ICH_EISR;
-                    case 5:
-                        return MISCREG_ICH_ELRSR;
-                    case 7:
-                        return MISCREG_ICH_VMCR;
-                }
-            } else if (crm == 12) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICH_LR0;
-                    case 1:
-                        return MISCREG_ICH_LR1;
-                    case 2:
-                        return MISCREG_ICH_LR2;
-                    case 3:
-                        return MISCREG_ICH_LR3;
-                    case 4:
-                        return MISCREG_ICH_LR4;
-                    case 5:
-                        return MISCREG_ICH_LR5;
-                    case 6:
-                        return MISCREG_ICH_LR6;
-                    case 7:
-                        return MISCREG_ICH_LR7;
-                }
-            } else if (crm == 13) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICH_LR8;
-                    case 1:
-                        return MISCREG_ICH_LR9;
-                    case 2:
-                        return MISCREG_ICH_LR10;
-                    case 3:
-                        return MISCREG_ICH_LR11;
-                    case 4:
-                        return MISCREG_ICH_LR12;
-                    case 5:
-                        return MISCREG_ICH_LR13;
-                    case 6:
-                        return MISCREG_ICH_LR14;
-                    case 7:
-                        return MISCREG_ICH_LR15;
-                }
-            } else if (crm == 14) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICH_LRC0;
-                    case 1:
-                        return MISCREG_ICH_LRC1;
-                    case 2:
-                        return MISCREG_ICH_LRC2;
-                    case 3:
-                        return MISCREG_ICH_LRC3;
-                    case 4:
-                        return MISCREG_ICH_LRC4;
-                    case 5:
-                        return MISCREG_ICH_LRC5;
-                    case 6:
-                        return MISCREG_ICH_LRC6;
-                    case 7:
-                        return MISCREG_ICH_LRC7;
-                }
-            } else if (crm == 15) {
-                switch (opc2) {
-                    case 0:
-                        return MISCREG_ICH_LRC8;
-                    case 1:
-                        return MISCREG_ICH_LRC9;
-                    case 2:
-                        return MISCREG_ICH_LRC10;
-                    case 3:
-                        return MISCREG_ICH_LRC11;
-                    case 4:
-                        return MISCREG_ICH_LRC12;
-                    case 5:
-                        return MISCREG_ICH_LRC13;
-                    case 6:
-                        return MISCREG_ICH_LRC14;
-                    case 7:
-                        return MISCREG_ICH_LRC15;
-                }
-            }
-        } else if (opc1 == 6) {
-            if (crm == 12) {
-                switch (opc2) {
-                    case 4:
-                        return MISCREG_ICC_MCTLR;
-                    case 5:
-                        return MISCREG_ICC_MSRE;
-                    case 7:
-                        return MISCREG_ICC_MGRPEN1;
-                }
-            }
-        }
-        break;
-      case 13:
-        if (opc1 == 0) {
-            if (crm == 0) {
-                switch (opc2) {
-                  case 0:
-                    return MISCREG_FCSEIDR;
-                  case 1:
-                    return MISCREG_CONTEXTIDR;
-                  case 2:
-                    return MISCREG_TPIDRURW;
-                  case 3:
-                    return MISCREG_TPIDRURO;
-                  case 4:
-                    return MISCREG_TPIDRPRW;
-                }
-            }
-        } else if (opc1 == 4) {
-            if (crm == 0 && opc2 == 2)
-                return MISCREG_HTPIDR;
-        }
-        break;
-      case 14:
-        if (opc1 == 0) {
-            switch (crm) {
-              case 0:
-                if (opc2 == 0)
-                    return MISCREG_CNTFRQ;
-                break;
-              case 1:
-                if (opc2 == 0)
-                    return MISCREG_CNTKCTL;
-                break;
-              case 2:
-                if (opc2 == 0)
-                    return MISCREG_CNTP_TVAL;
-                else if (opc2 == 1)
-                    return MISCREG_CNTP_CTL;
-                break;
-              case 3:
-                if (opc2 == 0)
-                    return MISCREG_CNTV_TVAL;
-                else if (opc2 == 1)
-                    return MISCREG_CNTV_CTL;
-                break;
-            }
-        } else if (opc1 == 4) {
-            if (crm == 1 && opc2 == 0) {
-                return MISCREG_CNTHCTL;
-            } else if (crm == 2) {
-                if (opc2 == 0)
-                    return MISCREG_CNTHP_TVAL;
-                else if (opc2 == 1)
-                    return MISCREG_CNTHP_CTL;
-            }
-        }
-        break;
-      case 15:
-        // Implementation defined
-        return MISCREG_IMPDEF_UNIMPL;
     }
-    // Unrecognized register
-    return MISCREG_CP15_UNIMPL;
 }
 
 MiscRegIndex
 decodeCP15Reg64(unsigned crm, unsigned opc1)
 {
-    switch (crm) {
-      case 2:
-        switch (opc1) {
-          case 0:
-            return MISCREG_TTBR0;
-          case 1:
-            return MISCREG_TTBR1;
-          case 4:
-            return MISCREG_HTTBR;
-          case 6:
-            return MISCREG_VTTBR;
-        }
-        break;
-      case 7:
-        if (opc1 == 0)
-            return MISCREG_PAR;
-        break;
-      case 14:
-        switch (opc1) {
-          case 0:
-            return MISCREG_CNTPCT;
-          case 1:
-            return MISCREG_CNTVCT;
-          case 2:
-            return MISCREG_CNTP_CVAL;
-          case 3:
-            return MISCREG_CNTV_CVAL;
-          case 4:
-            return MISCREG_CNTVOFF;
-          case 6:
-            return MISCREG_CNTHP_CVAL;
-        }
-        break;
-      case 12:
-        switch (opc1) {
-          case 0:
-            return MISCREG_ICC_SGI1R;
-          case 1:
-            return MISCREG_ICC_ASGI1R;
-          case 2:
-            return MISCREG_ICC_SGI0R;
-          default:
-            break;
-        }
-        break;
-      case 15:
-        if (opc1 == 0)
-            return MISCREG_CPUMERRSR;
-        else if (opc1 == 1)
-            return MISCREG_L2MERRSR;
-        break;
+    MiscRegNum32 cop_reg(15, opc1, crm);
+    auto it = miscRegNum32ToIdx.find(cop_reg);
+    if (it != miscRegNum32ToIdx.end()) {
+        return it->second;
+    } else {
+        return MISCREG_UNKNOWN;
     }
-    // Unrecognized register
-    return MISCREG_CP15_UNIMPL;
 }
 
 std::tuple<bool, bool>
@@ -1450,1953 +803,523 @@
     }
 }
 
+std::bitset<NUM_MISCREG_INFOS> miscRegInfo[NUM_MISCREGS]; // initialized below
+
+namespace {
+
+// The map is translating a MiscRegIndex into AArch64 system register
+// numbers (op0, op1, crn, crm, op2)
+std::unordered_map<MiscRegIndex, MiscRegNum64> idxToMiscRegNum;
+
+// The map is translating AArch64 system register numbers
+// (op0, op1, crn, crm, op2) into a MiscRegIndex
+std::unordered_map<MiscRegNum64, MiscRegIndex> miscRegNumToIdx{
+    { MiscRegNum64(1, 0, 7, 1, 0), MISCREG_IC_IALLUIS },
+    { MiscRegNum64(1, 0, 7, 5, 0), MISCREG_IC_IALLU },
+    { MiscRegNum64(1, 0, 7, 6, 1), MISCREG_DC_IVAC_Xt },
+    { MiscRegNum64(1, 0, 7, 6, 2), MISCREG_DC_ISW_Xt },
+    { MiscRegNum64(1, 0, 7, 8, 0), MISCREG_AT_S1E1R_Xt },
+    { MiscRegNum64(1, 0, 7, 8, 1), MISCREG_AT_S1E1W_Xt },
+    { MiscRegNum64(1, 0, 7, 8, 2), MISCREG_AT_S1E0R_Xt },
+    { MiscRegNum64(1, 0, 7, 8, 3), MISCREG_AT_S1E0W_Xt },
+    { MiscRegNum64(1, 0, 7, 10, 2), MISCREG_DC_CSW_Xt },
+    { MiscRegNum64(1, 0, 7, 14, 2), MISCREG_DC_CISW_Xt },
+    { MiscRegNum64(1, 0, 8, 3, 0), MISCREG_TLBI_VMALLE1IS },
+    { MiscRegNum64(1, 0, 8, 3, 1), MISCREG_TLBI_VAE1IS_Xt },
+    { MiscRegNum64(1, 0, 8, 3, 2), MISCREG_TLBI_ASIDE1IS_Xt },
+    { MiscRegNum64(1, 0, 8, 3, 3), MISCREG_TLBI_VAAE1IS_Xt },
+    { MiscRegNum64(1, 0, 8, 3, 5), MISCREG_TLBI_VALE1IS_Xt },
+    { MiscRegNum64(1, 0, 8, 3, 7), MISCREG_TLBI_VAALE1IS_Xt },
+    { MiscRegNum64(1, 0, 8, 7, 0), MISCREG_TLBI_VMALLE1 },
+    { MiscRegNum64(1, 0, 8, 7, 1), MISCREG_TLBI_VAE1_Xt },
+    { MiscRegNum64(1, 0, 8, 7, 2), MISCREG_TLBI_ASIDE1_Xt },
+    { MiscRegNum64(1, 0, 8, 7, 3), MISCREG_TLBI_VAAE1_Xt },
+    { MiscRegNum64(1, 0, 8, 7, 5), MISCREG_TLBI_VALE1_Xt },
+    { MiscRegNum64(1, 0, 8, 7, 7), MISCREG_TLBI_VAALE1_Xt },
+    { MiscRegNum64(1, 3, 7, 4, 1), MISCREG_DC_ZVA_Xt },
+    { MiscRegNum64(1, 3, 7, 5, 1), MISCREG_IC_IVAU_Xt },
+    { MiscRegNum64(1, 3, 7, 10, 1), MISCREG_DC_CVAC_Xt },
+    { MiscRegNum64(1, 3, 7, 11, 1), MISCREG_DC_CVAU_Xt },
+    { MiscRegNum64(1, 3, 7, 14, 1), MISCREG_DC_CIVAC_Xt },
+    { MiscRegNum64(1, 4, 7, 8, 0), MISCREG_AT_S1E2R_Xt },
+    { MiscRegNum64(1, 4, 7, 8, 1), MISCREG_AT_S1E2W_Xt },
+    { MiscRegNum64(1, 4, 7, 8, 4), MISCREG_AT_S12E1R_Xt },
+    { MiscRegNum64(1, 4, 7, 8, 5), MISCREG_AT_S12E1W_Xt },
+    { MiscRegNum64(1, 4, 7, 8, 6), MISCREG_AT_S12E0R_Xt },
+    { MiscRegNum64(1, 4, 7, 8, 7), MISCREG_AT_S12E0W_Xt },
+    { MiscRegNum64(1, 4, 8, 0, 1), MISCREG_TLBI_IPAS2E1IS_Xt },
+    { MiscRegNum64(1, 4, 8, 0, 5), MISCREG_TLBI_IPAS2LE1IS_Xt },
+    { MiscRegNum64(1, 4, 8, 3, 0), MISCREG_TLBI_ALLE2IS },
+    { MiscRegNum64(1, 4, 8, 3, 1), MISCREG_TLBI_VAE2IS_Xt },
+    { MiscRegNum64(1, 4, 8, 3, 4), MISCREG_TLBI_ALLE1IS },
+    { MiscRegNum64(1, 4, 8, 3, 5), MISCREG_TLBI_VALE2IS_Xt },
+    { MiscRegNum64(1, 4, 8, 3, 6), MISCREG_TLBI_VMALLS12E1IS },
+    { MiscRegNum64(1, 4, 8, 4, 1), MISCREG_TLBI_IPAS2E1_Xt },
+    { MiscRegNum64(1, 4, 8, 4, 5), MISCREG_TLBI_IPAS2LE1_Xt },
+    { MiscRegNum64(1, 4, 8, 7, 0), MISCREG_TLBI_ALLE2 },
+    { MiscRegNum64(1, 4, 8, 7, 1), MISCREG_TLBI_VAE2_Xt },
+    { MiscRegNum64(1, 4, 8, 7, 4), MISCREG_TLBI_ALLE1 },
+    { MiscRegNum64(1, 4, 8, 7, 5), MISCREG_TLBI_VALE2_Xt },
+    { MiscRegNum64(1, 4, 8, 7, 6), MISCREG_TLBI_VMALLS12E1 },
+    { MiscRegNum64(1, 6, 7, 8, 0), MISCREG_AT_S1E3R_Xt },
+    { MiscRegNum64(1, 6, 7, 8, 1), MISCREG_AT_S1E3W_Xt },
+    { MiscRegNum64(1, 6, 8, 3, 0), MISCREG_TLBI_ALLE3IS },
+    { MiscRegNum64(1, 6, 8, 3, 1), MISCREG_TLBI_VAE3IS_Xt },
+    { MiscRegNum64(1, 6, 8, 3, 5), MISCREG_TLBI_VALE3IS_Xt },
+    { MiscRegNum64(1, 6, 8, 7, 0), MISCREG_TLBI_ALLE3 },
+    { MiscRegNum64(1, 6, 8, 7, 1), MISCREG_TLBI_VAE3_Xt },
+    { MiscRegNum64(1, 6, 8, 7, 5), MISCREG_TLBI_VALE3_Xt },
+    { MiscRegNum64(2, 0, 0, 0, 2), MISCREG_OSDTRRX_EL1 },
+    { MiscRegNum64(2, 0, 0, 0, 4), MISCREG_DBGBVR0_EL1 },
+    { MiscRegNum64(2, 0, 0, 0, 5), MISCREG_DBGBCR0_EL1 },
+    { MiscRegNum64(2, 0, 0, 0, 6), MISCREG_DBGWVR0_EL1 },
+    { MiscRegNum64(2, 0, 0, 0, 7), MISCREG_DBGWCR0_EL1 },
+    { MiscRegNum64(2, 0, 0, 1, 4), MISCREG_DBGBVR1_EL1 },
+    { MiscRegNum64(2, 0, 0, 1, 5), MISCREG_DBGBCR1_EL1 },
+    { MiscRegNum64(2, 0, 0, 1, 6), MISCREG_DBGWVR1_EL1 },
+    { MiscRegNum64(2, 0, 0, 1, 7), MISCREG_DBGWCR1_EL1 },
+    { MiscRegNum64(2, 0, 0, 2, 0), MISCREG_MDCCINT_EL1 },
+    { MiscRegNum64(2, 0, 0, 2, 2), MISCREG_MDSCR_EL1 },
+    { MiscRegNum64(2, 0, 0, 2, 4), MISCREG_DBGBVR2_EL1 },
+    { MiscRegNum64(2, 0, 0, 2, 5), MISCREG_DBGBCR2_EL1 },
+    { MiscRegNum64(2, 0, 0, 2, 6), MISCREG_DBGWVR2_EL1 },
+    { MiscRegNum64(2, 0, 0, 2, 7), MISCREG_DBGWCR2_EL1 },
+    { MiscRegNum64(2, 0, 0, 3, 2), MISCREG_OSDTRTX_EL1 },
+    { MiscRegNum64(2, 0, 0, 3, 4), MISCREG_DBGBVR3_EL1 },
+    { MiscRegNum64(2, 0, 0, 3, 5), MISCREG_DBGBCR3_EL1 },
+    { MiscRegNum64(2, 0, 0, 3, 6), MISCREG_DBGWVR3_EL1 },
+    { MiscRegNum64(2, 0, 0, 3, 7), MISCREG_DBGWCR3_EL1 },
+    { MiscRegNum64(2, 0, 0, 4, 4), MISCREG_DBGBVR4_EL1 },
+    { MiscRegNum64(2, 0, 0, 4, 5), MISCREG_DBGBCR4_EL1 },
+    { MiscRegNum64(2, 0, 0, 4, 6), MISCREG_DBGWVR4_EL1 },
+    { MiscRegNum64(2, 0, 0, 4, 7), MISCREG_DBGWCR4_EL1 },
+    { MiscRegNum64(2, 0, 0, 5, 4), MISCREG_DBGBVR5_EL1 },
+    { MiscRegNum64(2, 0, 0, 5, 5), MISCREG_DBGBCR5_EL1 },
+    { MiscRegNum64(2, 0, 0, 5, 6), MISCREG_DBGWVR5_EL1 },
+    { MiscRegNum64(2, 0, 0, 5, 7), MISCREG_DBGWCR5_EL1 },
+    { MiscRegNum64(2, 0, 0, 6, 2), MISCREG_OSECCR_EL1 },
+    { MiscRegNum64(2, 0, 0, 6, 4), MISCREG_DBGBVR6_EL1 },
+    { MiscRegNum64(2, 0, 0, 6, 5), MISCREG_DBGBCR6_EL1 },
+    { MiscRegNum64(2, 0, 0, 6, 6), MISCREG_DBGWVR6_EL1 },
+    { MiscRegNum64(2, 0, 0, 6, 7), MISCREG_DBGWCR6_EL1 },
+    { MiscRegNum64(2, 0, 0, 7, 4), MISCREG_DBGBVR7_EL1 },
+    { MiscRegNum64(2, 0, 0, 7, 5), MISCREG_DBGBCR7_EL1 },
+    { MiscRegNum64(2, 0, 0, 7, 6), MISCREG_DBGWVR7_EL1 },
+    { MiscRegNum64(2, 0, 0, 7, 7), MISCREG_DBGWCR7_EL1 },
+    { MiscRegNum64(2, 0, 0, 8, 4), MISCREG_DBGBVR8_EL1 },
+    { MiscRegNum64(2, 0, 0, 8, 5), MISCREG_DBGBCR8_EL1 },
+    { MiscRegNum64(2, 0, 0, 8, 6), MISCREG_DBGWVR8_EL1 },
+    { MiscRegNum64(2, 0, 0, 8, 7), MISCREG_DBGWCR8_EL1 },
+    { MiscRegNum64(2, 0, 0, 9, 4), MISCREG_DBGBVR9_EL1 },
+    { MiscRegNum64(2, 0, 0, 9, 5), MISCREG_DBGBCR9_EL1 },
+    { MiscRegNum64(2, 0, 0, 9, 6), MISCREG_DBGWVR9_EL1 },
+    { MiscRegNum64(2, 0, 0, 9, 7), MISCREG_DBGWCR9_EL1 },
+    { MiscRegNum64(2, 0, 0, 10, 4), MISCREG_DBGBVR10_EL1 },
+    { MiscRegNum64(2, 0, 0, 10, 5), MISCREG_DBGBCR10_EL1 },
+    { MiscRegNum64(2, 0, 0, 10, 6), MISCREG_DBGWVR10_EL1 },
+    { MiscRegNum64(2, 0, 0, 10, 7), MISCREG_DBGWCR10_EL1 },
+    { MiscRegNum64(2, 0, 0, 11, 4), MISCREG_DBGBVR11_EL1 },
+    { MiscRegNum64(2, 0, 0, 11, 5), MISCREG_DBGBCR11_EL1 },
+    { MiscRegNum64(2, 0, 0, 11, 6), MISCREG_DBGWVR11_EL1 },
+    { MiscRegNum64(2, 0, 0, 11, 7), MISCREG_DBGWCR11_EL1 },
+    { MiscRegNum64(2, 0, 0, 12, 4), MISCREG_DBGBVR12_EL1 },
+    { MiscRegNum64(2, 0, 0, 12, 5), MISCREG_DBGBCR12_EL1 },
+    { MiscRegNum64(2, 0, 0, 12, 6), MISCREG_DBGWVR12_EL1 },
+    { MiscRegNum64(2, 0, 0, 12, 7), MISCREG_DBGWCR12_EL1 },
+    { MiscRegNum64(2, 0, 0, 13, 4), MISCREG_DBGBVR13_EL1 },
+    { MiscRegNum64(2, 0, 0, 13, 5), MISCREG_DBGBCR13_EL1 },
+    { MiscRegNum64(2, 0, 0, 13, 6), MISCREG_DBGWVR13_EL1 },
+    { MiscRegNum64(2, 0, 0, 13, 7), MISCREG_DBGWCR13_EL1 },
+    { MiscRegNum64(2, 0, 0, 14, 4), MISCREG_DBGBVR14_EL1 },
+    { MiscRegNum64(2, 0, 0, 14, 5), MISCREG_DBGBCR14_EL1 },
+    { MiscRegNum64(2, 0, 0, 14, 6), MISCREG_DBGWVR14_EL1 },
+    { MiscRegNum64(2, 0, 0, 14, 7), MISCREG_DBGWCR14_EL1 },
+    { MiscRegNum64(2, 0, 0, 15, 4), MISCREG_DBGBVR15_EL1 },
+    { MiscRegNum64(2, 0, 0, 15, 5), MISCREG_DBGBCR15_EL1 },
+    { MiscRegNum64(2, 0, 0, 15, 6), MISCREG_DBGWVR15_EL1 },
+    { MiscRegNum64(2, 0, 0, 15, 7), MISCREG_DBGWCR15_EL1 },
+    { MiscRegNum64(2, 0, 1, 0, 0), MISCREG_MDRAR_EL1 },
+    { MiscRegNum64(2, 0, 1, 0, 4), MISCREG_OSLAR_EL1 },
+    { MiscRegNum64(2, 0, 1, 1, 4), MISCREG_OSLSR_EL1 },
+    { MiscRegNum64(2, 0, 1, 3, 4), MISCREG_OSDLR_EL1 },
+    { MiscRegNum64(2, 0, 1, 4, 4), MISCREG_DBGPRCR_EL1 },
+    { MiscRegNum64(2, 0, 7, 8, 6), MISCREG_DBGCLAIMSET_EL1 },
+    { MiscRegNum64(2, 0, 7, 9, 6), MISCREG_DBGCLAIMCLR_EL1 },
+    { MiscRegNum64(2, 0, 7, 14, 6), MISCREG_DBGAUTHSTATUS_EL1 },
+    { MiscRegNum64(2, 2, 0, 0, 0), MISCREG_TEECR32_EL1 },
+    { MiscRegNum64(2, 2, 1, 0, 0), MISCREG_TEEHBR32_EL1 },
+    { MiscRegNum64(2, 3, 0, 1, 0), MISCREG_MDCCSR_EL0 },
+    { MiscRegNum64(2, 3, 0, 4, 0), MISCREG_MDDTR_EL0 },
+    { MiscRegNum64(2, 3, 0, 5, 0), MISCREG_MDDTRRX_EL0 },
+    { MiscRegNum64(2, 4, 0, 7, 0), MISCREG_DBGVCR32_EL2 },
+    { MiscRegNum64(3, 0, 0, 0, 0), MISCREG_MIDR_EL1 },
+    { MiscRegNum64(3, 0, 0, 0, 5), MISCREG_MPIDR_EL1 },
+    { MiscRegNum64(3, 0, 0, 0, 6), MISCREG_REVIDR_EL1 },
+    { MiscRegNum64(3, 0, 0, 1, 0), MISCREG_ID_PFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 1, 1), MISCREG_ID_PFR1_EL1 },
+    { MiscRegNum64(3, 0, 0, 1, 2), MISCREG_ID_DFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 1, 3), MISCREG_ID_AFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 1, 4), MISCREG_ID_MMFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 1, 5), MISCREG_ID_MMFR1_EL1 },
+    { MiscRegNum64(3, 0, 0, 1, 6), MISCREG_ID_MMFR2_EL1 },
+    { MiscRegNum64(3, 0, 0, 1, 7), MISCREG_ID_MMFR3_EL1 },
+    { MiscRegNum64(3, 0, 0, 2, 0), MISCREG_ID_ISAR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 2, 1), MISCREG_ID_ISAR1_EL1 },
+    { MiscRegNum64(3, 0, 0, 2, 2), MISCREG_ID_ISAR2_EL1 },
+    { MiscRegNum64(3, 0, 0, 2, 3), MISCREG_ID_ISAR3_EL1 },
+    { MiscRegNum64(3, 0, 0, 2, 4), MISCREG_ID_ISAR4_EL1 },
+    { MiscRegNum64(3, 0, 0, 2, 5), MISCREG_ID_ISAR5_EL1 },
+    { MiscRegNum64(3, 0, 0, 2, 6), MISCREG_ID_MMFR4_EL1 },
+    { MiscRegNum64(3, 0, 0, 2, 7), MISCREG_ID_ISAR6_EL1 },
+    { MiscRegNum64(3, 0, 0, 3, 0), MISCREG_MVFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 3, 1), MISCREG_MVFR1_EL1 },
+    { MiscRegNum64(3, 0, 0, 3, 2), MISCREG_MVFR2_EL1 },
+    { MiscRegNum64(3, 0, 0, 3, 3), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 3, 4), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 3, 5), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 3, 6), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 3, 7), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 4, 0), MISCREG_ID_AA64PFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 4, 1), MISCREG_ID_AA64PFR1_EL1 },
+    { MiscRegNum64(3, 0, 0, 4, 2), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 4, 3), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 4, 4), MISCREG_ID_AA64ZFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 4, 5), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 4, 6), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 4, 7), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 5, 0), MISCREG_ID_AA64DFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 5, 1), MISCREG_ID_AA64DFR1_EL1 },
+    { MiscRegNum64(3, 0, 0, 5, 2), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 5, 3), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 5, 4), MISCREG_ID_AA64AFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 5, 5), MISCREG_ID_AA64AFR1_EL1 },
+    { MiscRegNum64(3, 0, 0, 5, 6), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 5, 7), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 6, 0), MISCREG_ID_AA64ISAR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 6, 1), MISCREG_ID_AA64ISAR1_EL1 },
+    { MiscRegNum64(3, 0, 0, 6, 2), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 6, 3), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 6, 4), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 6, 5), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 6, 6), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 6, 7), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 7, 0), MISCREG_ID_AA64MMFR0_EL1 },
+    { MiscRegNum64(3, 0, 0, 7, 1), MISCREG_ID_AA64MMFR1_EL1 },
+    { MiscRegNum64(3, 0, 0, 7, 2), MISCREG_ID_AA64MMFR2_EL1 },
+    { MiscRegNum64(3, 0, 0, 7, 3), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 7, 4), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 7, 5), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 7, 6), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 0, 7, 7), MISCREG_RAZ },
+    { MiscRegNum64(3, 0, 1, 0, 0), MISCREG_SCTLR_EL1 },
+    { MiscRegNum64(3, 0, 1, 0, 1), MISCREG_ACTLR_EL1 },
+    { MiscRegNum64(3, 0, 1, 0, 2), MISCREG_CPACR_EL1 },
+    { MiscRegNum64(3, 0, 1, 2, 0), MISCREG_ZCR_EL1 },
+    { MiscRegNum64(3, 0, 2, 0, 0), MISCREG_TTBR0_EL1 },
+    { MiscRegNum64(3, 0, 2, 0, 1), MISCREG_TTBR1_EL1 },
+    { MiscRegNum64(3, 0, 2, 0, 2), MISCREG_TCR_EL1 },
+    { MiscRegNum64(3, 0, 2, 1, 0), MISCREG_APIAKeyLo_EL1 },
+    { MiscRegNum64(3, 0, 2, 1, 1), MISCREG_APIAKeyHi_EL1 },
+    { MiscRegNum64(3, 0, 2, 1, 2), MISCREG_APIBKeyLo_EL1 },
+    { MiscRegNum64(3, 0, 2, 1, 3), MISCREG_APIBKeyHi_EL1 },
+    { MiscRegNum64(3, 0, 2, 2, 0), MISCREG_APDAKeyLo_EL1 },
+    { MiscRegNum64(3, 0, 2, 2, 1), MISCREG_APDAKeyHi_EL1 },
+    { MiscRegNum64(3, 0, 2, 2, 2), MISCREG_APDBKeyLo_EL1 },
+    { MiscRegNum64(3, 0, 2, 2, 3), MISCREG_APDBKeyHi_EL1 },
+    { MiscRegNum64(3, 0, 2, 3, 0), MISCREG_APGAKeyLo_EL1 },
+    { MiscRegNum64(3, 0, 2, 3, 1), MISCREG_APGAKeyHi_EL1 },
+    { MiscRegNum64(3, 0, 4, 0, 0), MISCREG_SPSR_EL1 },
+    { MiscRegNum64(3, 0, 4, 0, 1), MISCREG_ELR_EL1 },
+    { MiscRegNum64(3, 0, 4, 1, 0), MISCREG_SP_EL0 },
+    { MiscRegNum64(3, 0, 4, 2, 0), MISCREG_SPSEL },
+    { MiscRegNum64(3, 0, 4, 2, 2), MISCREG_CURRENTEL },
+    { MiscRegNum64(3, 0, 4, 2, 3), MISCREG_PAN },
+    { MiscRegNum64(3, 0, 4, 2, 4), MISCREG_UAO },
+    { MiscRegNum64(3, 0, 4, 6, 0), MISCREG_ICC_PMR_EL1 },
+    { MiscRegNum64(3, 0, 5, 1, 0), MISCREG_AFSR0_EL1 },
+    { MiscRegNum64(3, 0, 5, 1, 1), MISCREG_AFSR1_EL1 },
+    { MiscRegNum64(3, 0, 5, 2, 0), MISCREG_ESR_EL1 },
+    { MiscRegNum64(3, 0, 5, 3, 0), MISCREG_ERRIDR_EL1 },
+    { MiscRegNum64(3, 0, 5, 3, 1), MISCREG_ERRSELR_EL1 },
+    { MiscRegNum64(3, 0, 5, 4, 0), MISCREG_ERXFR_EL1 },
+    { MiscRegNum64(3, 0, 5, 4, 1), MISCREG_ERXCTLR_EL1 },
+    { MiscRegNum64(3, 0, 5, 4, 2), MISCREG_ERXSTATUS_EL1 },
+    { MiscRegNum64(3, 0, 5, 4, 3), MISCREG_ERXADDR_EL1 },
+    { MiscRegNum64(3, 0, 5, 5, 0), MISCREG_ERXMISC0_EL1 },
+    { MiscRegNum64(3, 0, 5, 5, 1), MISCREG_ERXMISC1_EL1 },
+    { MiscRegNum64(3, 0, 6, 0, 0), MISCREG_FAR_EL1 },
+    { MiscRegNum64(3, 0, 7, 4, 0), MISCREG_PAR_EL1 },
+    { MiscRegNum64(3, 0, 9, 14, 1), MISCREG_PMINTENSET_EL1 },
+    { MiscRegNum64(3, 0, 9, 14, 2), MISCREG_PMINTENCLR_EL1 },
+    { MiscRegNum64(3, 0, 10, 2, 0), MISCREG_MAIR_EL1 },
+    { MiscRegNum64(3, 0, 10, 3, 0), MISCREG_AMAIR_EL1 },
+    { MiscRegNum64(3, 0, 12, 0, 0), MISCREG_VBAR_EL1 },
+    { MiscRegNum64(3, 0, 12, 0, 1), MISCREG_RVBAR_EL1 },
+    { MiscRegNum64(3, 0, 12, 1, 0), MISCREG_ISR_EL1 },
+    { MiscRegNum64(3, 0, 12, 1, 1), MISCREG_DISR_EL1 },
+    { MiscRegNum64(3, 0, 12, 8, 0), MISCREG_ICC_IAR0_EL1 },
+    { MiscRegNum64(3, 0, 12, 8, 1), MISCREG_ICC_EOIR0_EL1 },
+    { MiscRegNum64(3, 0, 12, 8, 2), MISCREG_ICC_HPPIR0_EL1 },
+    { MiscRegNum64(3, 0, 12, 8, 3), MISCREG_ICC_BPR0_EL1 },
+    { MiscRegNum64(3, 0, 12, 8, 4), MISCREG_ICC_AP0R0_EL1 },
+    { MiscRegNum64(3, 0, 12, 8, 5), MISCREG_ICC_AP0R1_EL1 },
+    { MiscRegNum64(3, 0, 12, 8, 6), MISCREG_ICC_AP0R2_EL1 },
+    { MiscRegNum64(3, 0, 12, 8, 7), MISCREG_ICC_AP0R3_EL1 },
+    { MiscRegNum64(3, 0, 12, 9, 0), MISCREG_ICC_AP1R0_EL1 },
+    { MiscRegNum64(3, 0, 12, 9, 1), MISCREG_ICC_AP1R1_EL1 },
+    { MiscRegNum64(3, 0, 12, 9, 2), MISCREG_ICC_AP1R2_EL1 },
+    { MiscRegNum64(3, 0, 12, 9, 3), MISCREG_ICC_AP1R3_EL1 },
+    { MiscRegNum64(3, 0, 12, 11, 1), MISCREG_ICC_DIR_EL1 },
+    { MiscRegNum64(3, 0, 12, 11, 3), MISCREG_ICC_RPR_EL1 },
+    { MiscRegNum64(3, 0, 12, 11, 5), MISCREG_ICC_SGI1R_EL1 },
+    { MiscRegNum64(3, 0, 12, 11, 6), MISCREG_ICC_ASGI1R_EL1 },
+    { MiscRegNum64(3, 0, 12, 11, 7), MISCREG_ICC_SGI0R_EL1 },
+    { MiscRegNum64(3, 0, 12, 12, 0), MISCREG_ICC_IAR1_EL1 },
+    { MiscRegNum64(3, 0, 12, 12, 1), MISCREG_ICC_EOIR1_EL1 },
+    { MiscRegNum64(3, 0, 12, 12, 2), MISCREG_ICC_HPPIR1_EL1 },
+    { MiscRegNum64(3, 0, 12, 12, 3), MISCREG_ICC_BPR1_EL1 },
+    { MiscRegNum64(3, 0, 12, 12, 4), MISCREG_ICC_CTLR_EL1 },
+    { MiscRegNum64(3, 0, 12, 12, 5), MISCREG_ICC_SRE_EL1 },
+    { MiscRegNum64(3, 0, 12, 12, 6), MISCREG_ICC_IGRPEN0_EL1 },
+    { MiscRegNum64(3, 0, 12, 12, 7), MISCREG_ICC_IGRPEN1_EL1 },
+    { MiscRegNum64(3, 0, 13, 0, 1), MISCREG_CONTEXTIDR_EL1 },
+    { MiscRegNum64(3, 0, 13, 0, 4), MISCREG_TPIDR_EL1 },
+    { MiscRegNum64(3, 0, 14, 1, 0), MISCREG_CNTKCTL_EL1 },
+    { MiscRegNum64(3, 0, 15, 0, 0), MISCREG_IL1DATA0_EL1 },
+    { MiscRegNum64(3, 0, 15, 0, 1), MISCREG_IL1DATA1_EL1 },
+    { MiscRegNum64(3, 0, 15, 0, 2), MISCREG_IL1DATA2_EL1 },
+    { MiscRegNum64(3, 0, 15, 0, 3), MISCREG_IL1DATA3_EL1 },
+    { MiscRegNum64(3, 0, 15, 1, 0), MISCREG_DL1DATA0_EL1 },
+    { MiscRegNum64(3, 0, 15, 1, 1), MISCREG_DL1DATA1_EL1 },
+    { MiscRegNum64(3, 0, 15, 1, 2), MISCREG_DL1DATA2_EL1 },
+    { MiscRegNum64(3, 0, 15, 1, 3), MISCREG_DL1DATA3_EL1 },
+    { MiscRegNum64(3, 0, 15, 1, 4), MISCREG_DL1DATA4_EL1 },
+    { MiscRegNum64(3, 1, 0, 0, 0), MISCREG_CCSIDR_EL1 },
+    { MiscRegNum64(3, 1, 0, 0, 1), MISCREG_CLIDR_EL1 },
+    { MiscRegNum64(3, 1, 0, 0, 7), MISCREG_AIDR_EL1 },
+    { MiscRegNum64(3, 1, 11, 0, 2), MISCREG_L2CTLR_EL1 },
+    { MiscRegNum64(3, 1, 11, 0, 3), MISCREG_L2ECTLR_EL1 },
+    { MiscRegNum64(3, 1, 15, 0, 0), MISCREG_L2ACTLR_EL1 },
+    { MiscRegNum64(3, 1, 15, 2, 0), MISCREG_CPUACTLR_EL1 },
+    { MiscRegNum64(3, 1, 15, 2, 1), MISCREG_CPUECTLR_EL1 },
+    { MiscRegNum64(3, 1, 15, 2, 2), MISCREG_CPUMERRSR_EL1 },
+    { MiscRegNum64(3, 1, 15, 2, 3), MISCREG_L2MERRSR_EL1 },
+    { MiscRegNum64(3, 1, 15, 3, 0), MISCREG_CBAR_EL1 },
+    { MiscRegNum64(3, 2, 0, 0, 0), MISCREG_CSSELR_EL1 },
+    { MiscRegNum64(3, 3, 0, 0, 1), MISCREG_CTR_EL0 },
+    { MiscRegNum64(3, 3, 0, 0, 7), MISCREG_DCZID_EL0 },
+    { MiscRegNum64(3, 3, 4, 2, 0), MISCREG_NZCV },
+    { MiscRegNum64(3, 3, 4, 2, 1), MISCREG_DAIF },
+    { MiscRegNum64(3, 3, 4, 4, 0), MISCREG_FPCR },
+    { MiscRegNum64(3, 3, 4, 4, 1), MISCREG_FPSR },
+    { MiscRegNum64(3, 3, 4, 5, 0), MISCREG_DSPSR_EL0 },
+    { MiscRegNum64(3, 3, 4, 5, 1), MISCREG_DLR_EL0 },
+    { MiscRegNum64(3, 3, 9, 12, 0), MISCREG_PMCR_EL0 },
+    { MiscRegNum64(3, 3, 9, 12, 1), MISCREG_PMCNTENSET_EL0 },
+    { MiscRegNum64(3, 3, 9, 12, 2), MISCREG_PMCNTENCLR_EL0 },
+    { MiscRegNum64(3, 3, 9, 12, 3), MISCREG_PMOVSCLR_EL0 },
+    { MiscRegNum64(3, 3, 9, 12, 4), MISCREG_PMSWINC_EL0 },
+    { MiscRegNum64(3, 3, 9, 12, 5), MISCREG_PMSELR_EL0 },
+    { MiscRegNum64(3, 3, 9, 12, 6), MISCREG_PMCEID0_EL0 },
+    { MiscRegNum64(3, 3, 9, 12, 7), MISCREG_PMCEID1_EL0 },
+    { MiscRegNum64(3, 3, 9, 13, 0), MISCREG_PMCCNTR_EL0 },
+    { MiscRegNum64(3, 3, 9, 13, 1), MISCREG_PMXEVTYPER_EL0 },
+    { MiscRegNum64(3, 3, 9, 13, 2), MISCREG_PMXEVCNTR_EL0 },
+    { MiscRegNum64(3, 3, 9, 14, 0), MISCREG_PMUSERENR_EL0 },
+    { MiscRegNum64(3, 3, 9, 14, 3), MISCREG_PMOVSSET_EL0 },
+    { MiscRegNum64(3, 3, 13, 0, 2), MISCREG_TPIDR_EL0 },
+    { MiscRegNum64(3, 3, 13, 0, 3), MISCREG_TPIDRRO_EL0 },
+    { MiscRegNum64(3, 3, 14, 0, 0), MISCREG_CNTFRQ_EL0 },
+    { MiscRegNum64(3, 3, 14, 0, 1), MISCREG_CNTPCT_EL0 },
+    { MiscRegNum64(3, 3, 14, 0, 2), MISCREG_CNTVCT_EL0 },
+    { MiscRegNum64(3, 3, 14, 2, 0), MISCREG_CNTP_TVAL_EL0 },
+    { MiscRegNum64(3, 3, 14, 2, 1), MISCREG_CNTP_CTL_EL0 },
+    { MiscRegNum64(3, 3, 14, 2, 2), MISCREG_CNTP_CVAL_EL0 },
+    { MiscRegNum64(3, 3, 14, 3, 0), MISCREG_CNTV_TVAL_EL0 },
+    { MiscRegNum64(3, 3, 14, 3, 1), MISCREG_CNTV_CTL_EL0 },
+    { MiscRegNum64(3, 3, 14, 3, 2), MISCREG_CNTV_CVAL_EL0 },
+    { MiscRegNum64(3, 3, 14, 8, 0), MISCREG_PMEVCNTR0_EL0 },
+    { MiscRegNum64(3, 3, 14, 8, 1), MISCREG_PMEVCNTR1_EL0 },
+    { MiscRegNum64(3, 3, 14, 8, 2), MISCREG_PMEVCNTR2_EL0 },
+    { MiscRegNum64(3, 3, 14, 8, 3), MISCREG_PMEVCNTR3_EL0 },
+    { MiscRegNum64(3, 3, 14, 8, 4), MISCREG_PMEVCNTR4_EL0 },
+    { MiscRegNum64(3, 3, 14, 8, 5), MISCREG_PMEVCNTR5_EL0 },
+    { MiscRegNum64(3, 3, 14, 12, 0), MISCREG_PMEVTYPER0_EL0 },
+    { MiscRegNum64(3, 3, 14, 12, 1), MISCREG_PMEVTYPER1_EL0 },
+    { MiscRegNum64(3, 3, 14, 12, 2), MISCREG_PMEVTYPER2_EL0 },
+    { MiscRegNum64(3, 3, 14, 12, 3), MISCREG_PMEVTYPER3_EL0 },
+    { MiscRegNum64(3, 3, 14, 12, 4), MISCREG_PMEVTYPER4_EL0 },
+    { MiscRegNum64(3, 3, 14, 12, 5), MISCREG_PMEVTYPER5_EL0 },
+    { MiscRegNum64(3, 3, 14, 15, 7), MISCREG_PMCCFILTR_EL0 },
+    { MiscRegNum64(3, 4, 0, 0, 0), MISCREG_VPIDR_EL2 },
+    { MiscRegNum64(3, 4, 0, 0, 5), MISCREG_VMPIDR_EL2 },
+    { MiscRegNum64(3, 4, 1, 0, 0), MISCREG_SCTLR_EL2 },
+    { MiscRegNum64(3, 4, 1, 0, 1), MISCREG_ACTLR_EL2 },
+    { MiscRegNum64(3, 4, 1, 1, 0), MISCREG_HCR_EL2 },
+    { MiscRegNum64(3, 4, 1, 1, 1), MISCREG_MDCR_EL2 },
+    { MiscRegNum64(3, 4, 1, 1, 2), MISCREG_CPTR_EL2 },
+    { MiscRegNum64(3, 4, 1, 1, 3), MISCREG_HSTR_EL2 },
+    { MiscRegNum64(3, 4, 1, 1, 7), MISCREG_HACR_EL2 },
+    { MiscRegNum64(3, 4, 1, 2, 0), MISCREG_ZCR_EL2 },
+    { MiscRegNum64(3, 4, 2, 0, 0), MISCREG_TTBR0_EL2 },
+    { MiscRegNum64(3, 4, 2, 0, 1), MISCREG_TTBR1_EL2 },
+    { MiscRegNum64(3, 4, 2, 0, 2), MISCREG_TCR_EL2 },
+    { MiscRegNum64(3, 4, 2, 1, 0), MISCREG_VTTBR_EL2 },
+    { MiscRegNum64(3, 4, 2, 1, 2), MISCREG_VTCR_EL2 },
+    { MiscRegNum64(3, 4, 2, 6, 0), MISCREG_VSTTBR_EL2 },
+    { MiscRegNum64(3, 4, 2, 6, 2), MISCREG_VSTCR_EL2 },
+    { MiscRegNum64(3, 4, 3, 0, 0), MISCREG_DACR32_EL2 },
+    { MiscRegNum64(3, 4, 4, 0, 0), MISCREG_SPSR_EL2 },
+    { MiscRegNum64(3, 4, 4, 0, 1), MISCREG_ELR_EL2 },
+    { MiscRegNum64(3, 4, 4, 1, 0), MISCREG_SP_EL1 },
+    { MiscRegNum64(3, 4, 4, 3, 0), MISCREG_SPSR_IRQ_AA64 },
+    { MiscRegNum64(3, 4, 4, 3, 1), MISCREG_SPSR_ABT_AA64 },
+    { MiscRegNum64(3, 4, 4, 3, 2), MISCREG_SPSR_UND_AA64 },
+    { MiscRegNum64(3, 4, 4, 3, 3), MISCREG_SPSR_FIQ_AA64 },
+    { MiscRegNum64(3, 4, 5, 0, 1), MISCREG_IFSR32_EL2 },
+    { MiscRegNum64(3, 4, 5, 1, 0), MISCREG_AFSR0_EL2 },
+    { MiscRegNum64(3, 4, 5, 1, 1), MISCREG_AFSR1_EL2 },
+    { MiscRegNum64(3, 4, 5, 2, 0), MISCREG_ESR_EL2 },
+    { MiscRegNum64(3, 4, 5, 2, 3), MISCREG_VSESR_EL2 },
+    { MiscRegNum64(3, 4, 5, 3, 0), MISCREG_FPEXC32_EL2 },
+    { MiscRegNum64(3, 4, 6, 0, 0), MISCREG_FAR_EL2 },
+    { MiscRegNum64(3, 4, 6, 0, 4), MISCREG_HPFAR_EL2 },
+    { MiscRegNum64(3, 4, 10, 2, 0), MISCREG_MAIR_EL2 },
+    { MiscRegNum64(3, 4, 10, 3, 0), MISCREG_AMAIR_EL2 },
+    { MiscRegNum64(3, 4, 12, 0, 0), MISCREG_VBAR_EL2 },
+    { MiscRegNum64(3, 4, 12, 0, 1), MISCREG_RVBAR_EL2 },
+    { MiscRegNum64(3, 4, 12, 1, 1), MISCREG_VDISR_EL2 },
+    { MiscRegNum64(3, 4, 12, 8, 0), MISCREG_ICH_AP0R0_EL2 },
+    { MiscRegNum64(3, 4, 12, 8, 1), MISCREG_ICH_AP0R1_EL2 },
+    { MiscRegNum64(3, 4, 12, 8, 2), MISCREG_ICH_AP0R2_EL2 },
+    { MiscRegNum64(3, 4, 12, 8, 3), MISCREG_ICH_AP0R3_EL2 },
+    { MiscRegNum64(3, 4, 12, 9, 0), MISCREG_ICH_AP1R0_EL2 },
+    { MiscRegNum64(3, 4, 12, 9, 1), MISCREG_ICH_AP1R1_EL2 },
+    { MiscRegNum64(3, 4, 12, 9, 2), MISCREG_ICH_AP1R2_EL2 },
+    { MiscRegNum64(3, 4, 12, 9, 3), MISCREG_ICH_AP1R3_EL2 },
+    { MiscRegNum64(3, 4, 12, 9, 5), MISCREG_ICC_SRE_EL2 },
+    { MiscRegNum64(3, 4, 12, 11, 0), MISCREG_ICH_HCR_EL2 },
+    { MiscRegNum64(3, 4, 12, 11, 1), MISCREG_ICH_VTR_EL2 },
+    { MiscRegNum64(3, 4, 12, 11, 2), MISCREG_ICH_MISR_EL2 },
+    { MiscRegNum64(3, 4, 12, 11, 3), MISCREG_ICH_EISR_EL2 },
+    { MiscRegNum64(3, 4, 12, 11, 5), MISCREG_ICH_ELRSR_EL2 },
+    { MiscRegNum64(3, 4, 12, 11, 7), MISCREG_ICH_VMCR_EL2 },
+    { MiscRegNum64(3, 4, 12, 12, 0), MISCREG_ICH_LR0_EL2 },
+    { MiscRegNum64(3, 4, 12, 12, 1), MISCREG_ICH_LR1_EL2 },
+    { MiscRegNum64(3, 4, 12, 12, 2), MISCREG_ICH_LR2_EL2 },
+    { MiscRegNum64(3, 4, 12, 12, 3), MISCREG_ICH_LR3_EL2 },
+    { MiscRegNum64(3, 4, 12, 12, 4), MISCREG_ICH_LR4_EL2 },
+    { MiscRegNum64(3, 4, 12, 12, 5), MISCREG_ICH_LR5_EL2 },
+    { MiscRegNum64(3, 4, 12, 12, 6), MISCREG_ICH_LR6_EL2 },
+    { MiscRegNum64(3, 4, 12, 12, 7), MISCREG_ICH_LR7_EL2 },
+    { MiscRegNum64(3, 4, 12, 13, 0), MISCREG_ICH_LR8_EL2 },
+    { MiscRegNum64(3, 4, 12, 13, 1), MISCREG_ICH_LR9_EL2 },
+    { MiscRegNum64(3, 4, 12, 13, 2), MISCREG_ICH_LR10_EL2 },
+    { MiscRegNum64(3, 4, 12, 13, 3), MISCREG_ICH_LR11_EL2 },
+    { MiscRegNum64(3, 4, 12, 13, 4), MISCREG_ICH_LR12_EL2 },
+    { MiscRegNum64(3, 4, 12, 13, 5), MISCREG_ICH_LR13_EL2 },
+    { MiscRegNum64(3, 4, 12, 13, 6), MISCREG_ICH_LR14_EL2 },
+    { MiscRegNum64(3, 4, 12, 13, 7), MISCREG_ICH_LR15_EL2 },
+    { MiscRegNum64(3, 4, 13, 0, 1), MISCREG_CONTEXTIDR_EL2 },
+    { MiscRegNum64(3, 4, 13, 0, 2), MISCREG_TPIDR_EL2 },
+    { MiscRegNum64(3, 4, 14, 0, 3), MISCREG_CNTVOFF_EL2 },
+    { MiscRegNum64(3, 4, 14, 1, 0), MISCREG_CNTHCTL_EL2 },
+    { MiscRegNum64(3, 4, 14, 2, 0), MISCREG_CNTHP_TVAL_EL2 },
+    { MiscRegNum64(3, 4, 14, 2, 1), MISCREG_CNTHP_CTL_EL2 },
+    { MiscRegNum64(3, 4, 14, 2, 2), MISCREG_CNTHP_CVAL_EL2 },
+    { MiscRegNum64(3, 4, 14, 3, 0), MISCREG_CNTHV_TVAL_EL2 },
+    { MiscRegNum64(3, 4, 14, 3, 1), MISCREG_CNTHV_CTL_EL2 },
+    { MiscRegNum64(3, 4, 14, 3, 2), MISCREG_CNTHV_CVAL_EL2 },
+    { MiscRegNum64(3, 4, 14, 4, 0), MISCREG_CNTHVS_TVAL_EL2 },
+    { MiscRegNum64(3, 4, 14, 4, 1), MISCREG_CNTHVS_CTL_EL2 },
+    { MiscRegNum64(3, 4, 14, 4, 2), MISCREG_CNTHVS_CVAL_EL2 },
+    { MiscRegNum64(3, 4, 14, 5, 0), MISCREG_CNTHPS_TVAL_EL2 },
+    { MiscRegNum64(3, 4, 14, 5, 1), MISCREG_CNTHPS_CTL_EL2 },
+    { MiscRegNum64(3, 4, 14, 5, 2), MISCREG_CNTHPS_CVAL_EL2 },
+    { MiscRegNum64(3, 5, 1, 0, 0), MISCREG_SCTLR_EL12 },
+    { MiscRegNum64(3, 5, 1, 0, 2), MISCREG_CPACR_EL12 },
+    { MiscRegNum64(3, 5, 1, 2, 0), MISCREG_ZCR_EL12 },
+    { MiscRegNum64(3, 5, 2, 0, 0), MISCREG_TTBR0_EL12 },
+    { MiscRegNum64(3, 5, 2, 0, 1), MISCREG_TTBR1_EL12 },
+    { MiscRegNum64(3, 5, 2, 0, 2), MISCREG_TCR_EL12 },
+    { MiscRegNum64(3, 5, 4, 0, 0), MISCREG_SPSR_EL12 },
+    { MiscRegNum64(3, 5, 4, 0, 1), MISCREG_ELR_EL12 },
+    { MiscRegNum64(3, 5, 5, 1, 0), MISCREG_AFSR0_EL12 },
+    { MiscRegNum64(3, 5, 5, 1, 1), MISCREG_AFSR1_EL12 },
+    { MiscRegNum64(3, 5, 5, 2, 0), MISCREG_ESR_EL12 },
+    { MiscRegNum64(3, 5, 6, 0, 0), MISCREG_FAR_EL12 },
+    { MiscRegNum64(3, 5, 10, 2, 0), MISCREG_MAIR_EL12 },
+    { MiscRegNum64(3, 5, 10, 3, 0), MISCREG_AMAIR_EL12 },
+    { MiscRegNum64(3, 5, 12, 0, 0), MISCREG_VBAR_EL12 },
+    { MiscRegNum64(3, 5, 13, 0, 1), MISCREG_CONTEXTIDR_EL12 },
+    { MiscRegNum64(3, 5, 14, 1, 0), MISCREG_CNTKCTL_EL12 },
+    { MiscRegNum64(3, 5, 14, 2, 0), MISCREG_CNTP_TVAL_EL02 },
+    { MiscRegNum64(3, 5, 14, 2, 1), MISCREG_CNTP_CTL_EL02 },
+    { MiscRegNum64(3, 5, 14, 2, 2), MISCREG_CNTP_CVAL_EL02 },
+    { MiscRegNum64(3, 5, 14, 3, 0), MISCREG_CNTV_TVAL_EL02 },
+    { MiscRegNum64(3, 5, 14, 3, 1), MISCREG_CNTV_CTL_EL02 },
+    { MiscRegNum64(3, 5, 14, 3, 2), MISCREG_CNTV_CVAL_EL02 },
+    { MiscRegNum64(3, 6, 1, 0, 0), MISCREG_SCTLR_EL3 },
+    { MiscRegNum64(3, 6, 1, 0, 1), MISCREG_ACTLR_EL3 },
+    { MiscRegNum64(3, 6, 1, 1, 0), MISCREG_SCR_EL3 },
+    { MiscRegNum64(3, 6, 1, 1, 1), MISCREG_SDER32_EL3 },
+    { MiscRegNum64(3, 6, 1, 1, 2), MISCREG_CPTR_EL3 },
+    { MiscRegNum64(3, 6, 1, 2, 0), MISCREG_ZCR_EL3 },
+    { MiscRegNum64(3, 6, 1, 3, 1), MISCREG_MDCR_EL3 },
+    { MiscRegNum64(3, 6, 2, 0, 0), MISCREG_TTBR0_EL3 },
+    { MiscRegNum64(3, 6, 2, 0, 2), MISCREG_TCR_EL3 },
+    { MiscRegNum64(3, 6, 4, 0, 0), MISCREG_SPSR_EL3 },
+    { MiscRegNum64(3, 6, 4, 0, 1), MISCREG_ELR_EL3 },
+    { MiscRegNum64(3, 6, 4, 1, 0), MISCREG_SP_EL2 },
+    { MiscRegNum64(3, 6, 5, 1, 0), MISCREG_AFSR0_EL3 },
+    { MiscRegNum64(3, 6, 5, 1, 1), MISCREG_AFSR1_EL3 },
+    { MiscRegNum64(3, 6, 5, 2, 0), MISCREG_ESR_EL3 },
+    { MiscRegNum64(3, 6, 6, 0, 0), MISCREG_FAR_EL3 },
+    { MiscRegNum64(3, 6, 10, 2, 0), MISCREG_MAIR_EL3 },
+    { MiscRegNum64(3, 6, 10, 3, 0), MISCREG_AMAIR_EL3 },
+    { MiscRegNum64(3, 6, 12, 0, 0), MISCREG_VBAR_EL3 },
+    { MiscRegNum64(3, 6, 12, 0, 1), MISCREG_RVBAR_EL3 },
+    { MiscRegNum64(3, 6, 12, 0, 2), MISCREG_RMR_EL3 },
+    { MiscRegNum64(3, 6, 12, 12, 4), MISCREG_ICC_CTLR_EL3 },
+    { MiscRegNum64(3, 6, 12, 12, 5), MISCREG_ICC_SRE_EL3 },
+    { MiscRegNum64(3, 6, 12, 12, 7), MISCREG_ICC_IGRPEN1_EL3 },
+    { MiscRegNum64(3, 6, 13, 0, 2), MISCREG_TPIDR_EL3 },
+    { MiscRegNum64(3, 7, 14, 2, 0), MISCREG_CNTPS_TVAL_EL1 },
+    { MiscRegNum64(3, 7, 14, 2, 1), MISCREG_CNTPS_CTL_EL1 },
+    { MiscRegNum64(3, 7, 14, 2, 2), MISCREG_CNTPS_CVAL_EL1 }
+};
+
+}
+
 MiscRegIndex
 decodeAArch64SysReg(unsigned op0, unsigned op1,
                     unsigned crn, unsigned crm,
                     unsigned op2)
 {
-    switch (op0) {
-      case 1:
-        switch (crn) {
-          case 7:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_IC_IALLUIS;
-                    }
-                    break;
-                  case 5:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_IC_IALLU;
-                    }
-                    break;
-                  case 6:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_DC_IVAC_Xt;
-                      case 2:
-                        return MISCREG_DC_ISW_Xt;
-                    }
-                    break;
-                  case 8:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AT_S1E1R_Xt;
-                      case 1:
-                        return MISCREG_AT_S1E1W_Xt;
-                      case 2:
-                        return MISCREG_AT_S1E0R_Xt;
-                      case 3:
-                        return MISCREG_AT_S1E0W_Xt;
-                    }
-                    break;
-                  case 10:
-                    switch (op2) {
-                      case 2:
-                        return MISCREG_DC_CSW_Xt;
-                    }
-                    break;
-                  case 14:
-                    switch (op2) {
-                      case 2:
-                        return MISCREG_DC_CISW_Xt;
-                    }
-                    break;
-                }
-                break;
-              case 3:
-                switch (crm) {
-                  case 4:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_DC_ZVA_Xt;
-                    }
-                    break;
-                  case 5:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_IC_IVAU_Xt;
-                    }
-                    break;
-                  case 10:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_DC_CVAC_Xt;
-                    }
-                    break;
-                  case 11:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_DC_CVAU_Xt;
-                    }
-                    break;
-                  case 14:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_DC_CIVAC_Xt;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 8:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AT_S1E2R_Xt;
-                      case 1:
-                        return MISCREG_AT_S1E2W_Xt;
-                      case 4:
-                        return MISCREG_AT_S12E1R_Xt;
-                      case 5:
-                        return MISCREG_AT_S12E1W_Xt;
-                      case 6:
-                        return MISCREG_AT_S12E0R_Xt;
-                      case 7:
-                        return MISCREG_AT_S12E0W_Xt;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 8:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AT_S1E3R_Xt;
-                      case 1:
-                        return MISCREG_AT_S1E3W_Xt;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 8:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TLBI_VMALLE1IS;
-                      case 1:
-                        return MISCREG_TLBI_VAE1IS_Xt;
-                      case 2:
-                        return MISCREG_TLBI_ASIDE1IS_Xt;
-                      case 3:
-                        return MISCREG_TLBI_VAAE1IS_Xt;
-                      case 5:
-                        return MISCREG_TLBI_VALE1IS_Xt;
-                      case 7:
-                        return MISCREG_TLBI_VAALE1IS_Xt;
-                    }
-                    break;
-                  case 7:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TLBI_VMALLE1;
-                      case 1:
-                        return MISCREG_TLBI_VAE1_Xt;
-                      case 2:
-                        return MISCREG_TLBI_ASIDE1_Xt;
-                      case 3:
-                        return MISCREG_TLBI_VAAE1_Xt;
-                      case 5:
-                        return MISCREG_TLBI_VALE1_Xt;
-                      case 7:
-                        return MISCREG_TLBI_VAALE1_Xt;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_TLBI_IPAS2E1IS_Xt;
-                      case 5:
-                        return MISCREG_TLBI_IPAS2LE1IS_Xt;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TLBI_ALLE2IS;
-                      case 1:
-                        return MISCREG_TLBI_VAE2IS_Xt;
-                      case 4:
-                        return MISCREG_TLBI_ALLE1IS;
-                      case 5:
-                        return MISCREG_TLBI_VALE2IS_Xt;
-                      case 6:
-                        return MISCREG_TLBI_VMALLS12E1IS;
-                    }
-                    break;
-                  case 4:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_TLBI_IPAS2E1_Xt;
-                      case 5:
-                        return MISCREG_TLBI_IPAS2LE1_Xt;
-                    }
-                    break;
-                  case 7:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TLBI_ALLE2;
-                      case 1:
-                        return MISCREG_TLBI_VAE2_Xt;
-                      case 4:
-                        return MISCREG_TLBI_ALLE1;
-                      case 5:
-                        return MISCREG_TLBI_VALE2_Xt;
-                      case 6:
-                        return MISCREG_TLBI_VMALLS12E1;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TLBI_ALLE3IS;
-                      case 1:
-                        return MISCREG_TLBI_VAE3IS_Xt;
-                      case 5:
-                        return MISCREG_TLBI_VALE3IS_Xt;
-                    }
-                    break;
-                  case 7:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TLBI_ALLE3;
-                      case 1:
-                        return MISCREG_TLBI_VAE3_Xt;
-                      case 5:
-                        return MISCREG_TLBI_VALE3_Xt;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 11:
-          case 15:
-            // SYS Instruction with CRn = { 11, 15 }
-            // (Trappable by HCR_EL2.TIDCP)
+    MiscRegNum64 sys_reg(op0, op1, crn, crm, op2);
+    auto it = miscRegNumToIdx.find(sys_reg);
+    if (it != miscRegNumToIdx.end()) {
+        return it->second;
+    } else {
+        // Check for a pseudo register before returning MISCREG_UNKNOWN
+        if ((op0 == 1 || op0 == 3) && (crn == 11 || crn == 15)) {
             return MISCREG_IMPDEF_UNIMPL;
+        } else {
+            return MISCREG_UNKNOWN;
         }
-        break;
-      case 2:
-        switch (crn) {
-          case 0:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 2:
-                        return MISCREG_OSDTRRX_EL1;
-                      case 4:
-                        return MISCREG_DBGBVR0_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR0_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR0_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR0_EL1;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR1_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR1_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR1_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR1_EL1;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MDCCINT_EL1;
-                      case 2:
-                        return MISCREG_MDSCR_EL1;
-                      case 4:
-                        return MISCREG_DBGBVR2_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR2_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR2_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR2_EL1;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 2:
-                        return MISCREG_OSDTRTX_EL1;
-                      case 4:
-                        return MISCREG_DBGBVR3_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR3_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR3_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR3_EL1;
-                    }
-                    break;
-                  case 4:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR4_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR4_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR4_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR4_EL1;
-                    }
-                    break;
-                  case 5:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR5_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR5_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR5_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR5_EL1;
-                    }
-                    break;
-                  case 6:
-                    switch (op2) {
-                      case 2:
-                        return MISCREG_OSECCR_EL1;
-                      case 4:
-                        return MISCREG_DBGBVR6_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR6_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR6_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR6_EL1;
-                    }
-                    break;
-                  case 7:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR7_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR7_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR7_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR7_EL1;
-                    }
-                    break;
-                  case 8:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR8_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR8_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR8_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR8_EL1;
-                    }
-                    break;
-                  case 9:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR9_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR9_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR9_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR9_EL1;
-                    }
-                    break;
-                  case 10:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR10_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR10_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR10_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR10_EL1;
-                    }
-                    break;
-                  case 11:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR11_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR11_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR11_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR11_EL1;
-                    }
-                    break;
-                  case 12:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR12_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR12_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR12_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR12_EL1;
-                    }
-                    break;
-                  case 13:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR13_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR13_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR13_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR13_EL1;
-                    }
-                    break;
-                  case 14:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR14_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR14_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR14_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR14_EL1;
-                    }
-                    break;
-                  case 15:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGBVR15_EL1;
-                      case 5:
-                        return MISCREG_DBGBCR15_EL1;
-                      case 6:
-                        return MISCREG_DBGWVR15_EL1;
-                      case 7:
-                        return MISCREG_DBGWCR15_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 2:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TEECR32_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 3:
-                switch (crm) {
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MDCCSR_EL0;
-                    }
-                    break;
-                  case 4:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MDDTR_EL0;
-                    }
-                    break;
-                  case 5:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MDDTRRX_EL0;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 7:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_DBGVCR32_EL2;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 1:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MDRAR_EL1;
-                      case 4:
-                        return MISCREG_OSLAR_EL1;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_OSLSR_EL1;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_OSDLR_EL1;
-                    }
-                    break;
-                  case 4:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_DBGPRCR_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 2:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TEEHBR32_EL1;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 7:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 8:
-                    switch (op2) {
-                      case 6:
-                        return MISCREG_DBGCLAIMSET_EL1;
-                    }
-                    break;
-                  case 9:
-                    switch (op2) {
-                      case 6:
-                        return MISCREG_DBGCLAIMCLR_EL1;
-                    }
-                    break;
-                  case 14:
-                    switch (op2) {
-                      case 6:
-                        return MISCREG_DBGAUTHSTATUS_EL1;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-        }
-        break;
-      case 3:
-        switch (crn) {
-          case 0:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MIDR_EL1;
-                      case 5:
-                        return MISCREG_MPIDR_EL1;
-                      case 6:
-                        return MISCREG_REVIDR_EL1;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ID_PFR0_EL1;
-                      case 1:
-                        return MISCREG_ID_PFR1_EL1;
-                      case 2:
-                        return MISCREG_ID_DFR0_EL1;
-                      case 3:
-                        return MISCREG_ID_AFR0_EL1;
-                      case 4:
-                        return MISCREG_ID_MMFR0_EL1;
-                      case 5:
-                        return MISCREG_ID_MMFR1_EL1;
-                      case 6:
-                        return MISCREG_ID_MMFR2_EL1;
-                      case 7:
-                        return MISCREG_ID_MMFR3_EL1;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ID_ISAR0_EL1;
-                      case 1:
-                        return MISCREG_ID_ISAR1_EL1;
-                      case 2:
-                        return MISCREG_ID_ISAR2_EL1;
-                      case 3:
-                        return MISCREG_ID_ISAR3_EL1;
-                      case 4:
-                        return MISCREG_ID_ISAR4_EL1;
-                      case 5:
-                        return MISCREG_ID_ISAR5_EL1;
-                      case 6:
-                        return MISCREG_ID_MMFR4_EL1;
-                      case 7:
-                        return MISCREG_ID_ISAR6_EL1;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MVFR0_EL1;
-                      case 1:
-                        return MISCREG_MVFR1_EL1;
-                      case 2:
-                        return MISCREG_MVFR2_EL1;
-                      case 3 ... 7:
-                        return MISCREG_RAZ;
-                    }
-                    break;
-                  case 4:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ID_AA64PFR0_EL1;
-                      case 1:
-                        return MISCREG_ID_AA64PFR1_EL1;
-                      case 2 ... 3:
-                        return MISCREG_RAZ;
-                      case 4:
-                        return MISCREG_ID_AA64ZFR0_EL1;
-                      case 5 ... 7:
-                        return MISCREG_RAZ;
-                    }
-                    break;
-                  case 5:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ID_AA64DFR0_EL1;
-                      case 1:
-                        return MISCREG_ID_AA64DFR1_EL1;
-                      case 4:
-                        return MISCREG_ID_AA64AFR0_EL1;
-                      case 5:
-                        return MISCREG_ID_AA64AFR1_EL1;
-                      case 2:
-                      case 3:
-                      case 6:
-                      case 7:
-                        return MISCREG_RAZ;
-                    }
-                    break;
-                  case 6:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ID_AA64ISAR0_EL1;
-                      case 1:
-                        return MISCREG_ID_AA64ISAR1_EL1;
-                      case 2 ... 7:
-                        return MISCREG_RAZ;
-                    }
-                    break;
-                  case 7:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ID_AA64MMFR0_EL1;
-                      case 1:
-                        return MISCREG_ID_AA64MMFR1_EL1;
-                      case 2:
-                        return MISCREG_ID_AA64MMFR2_EL1;
-                      case 3 ... 7:
-                        return MISCREG_RAZ;
-                    }
-                    break;
-                }
-                break;
-              case 1:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CCSIDR_EL1;
-                      case 1:
-                        return MISCREG_CLIDR_EL1;
-                      case 7:
-                        return MISCREG_AIDR_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 2:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CSSELR_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 3:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_CTR_EL0;
-                      case 7:
-                        return MISCREG_DCZID_EL0;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_VPIDR_EL2;
-                      case 5:
-                        return MISCREG_VMPIDR_EL2;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 1:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SCTLR_EL1;
-                      case 1:
-                        return MISCREG_ACTLR_EL1;
-                      case 2:
-                        return MISCREG_CPACR_EL1;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ZCR_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SCTLR_EL2;
-                      case 1:
-                        return MISCREG_ACTLR_EL2;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_HCR_EL2;
-                      case 1:
-                        return MISCREG_MDCR_EL2;
-                      case 2:
-                        return MISCREG_CPTR_EL2;
-                      case 3:
-                        return MISCREG_HSTR_EL2;
-                      case 7:
-                        return MISCREG_HACR_EL2;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ZCR_EL2;
-                    }
-                    break;
-                }
-                break;
-              case 5:
-                /* op0: 3 Crn:1 op1:5 */
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SCTLR_EL12;
-                      case 2:
-                        return MISCREG_CPACR_EL12;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ZCR_EL12;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SCTLR_EL3;
-                      case 1:
-                        return MISCREG_ACTLR_EL3;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SCR_EL3;
-                      case 1:
-                        return MISCREG_SDER32_EL3;
-                      case 2:
-                        return MISCREG_CPTR_EL3;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ZCR_EL3;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_MDCR_EL3;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 2:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TTBR0_EL1;
-                      case 1:
-                        return MISCREG_TTBR1_EL1;
-                      case 2:
-                        return MISCREG_TCR_EL1;
-                    }
-                    break;
-                  case 0x1:
-                    switch (op2) {
-                      case 0x0:
-                        return MISCREG_APIAKeyLo_EL1;
-                      case 0x1:
-                        return MISCREG_APIAKeyHi_EL1;
-                      case 0x2:
-                        return MISCREG_APIBKeyLo_EL1;
-                      case 0x3:
-                        return MISCREG_APIBKeyHi_EL1;
-                    }
-                    break;
-                  case 0x2:
-                    switch (op2) {
-                      case 0x0:
-                        return MISCREG_APDAKeyLo_EL1;
-                      case 0x1:
-                        return MISCREG_APDAKeyHi_EL1;
-                      case 0x2:
-                        return MISCREG_APDBKeyLo_EL1;
-                      case 0x3:
-                        return MISCREG_APDBKeyHi_EL1;
-                    }
-                    break;
-
-                  case 0x3:
-                    switch (op2) {
-                      case 0x0:
-                        return MISCREG_APGAKeyLo_EL1;
-                      case 0x1:
-                        return MISCREG_APGAKeyHi_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TTBR0_EL2;
-                      case 1:
-                        return MISCREG_TTBR1_EL2;
-                      case 2:
-                        return MISCREG_TCR_EL2;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_VTTBR_EL2;
-                      case 2:
-                        return MISCREG_VTCR_EL2;
-                    }
-                    break;
-                  case 6:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_VSTTBR_EL2;
-                      case 2:
-                        return MISCREG_VSTCR_EL2;
-                    }
-                    break;
-                }
-                break;
-              case 5:
-                /* op0: 3 Crn:2 op1:5 */
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TTBR0_EL12;
-                      case 1:
-                        return MISCREG_TTBR1_EL12;
-                      case 2:
-                        return MISCREG_TCR_EL12;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_TTBR0_EL3;
-                      case 2:
-                        return MISCREG_TCR_EL3;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 3:
-            switch (op1) {
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_DACR32_EL2;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 4:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SPSR_EL1;
-                      case 1:
-                        return MISCREG_ELR_EL1;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SP_EL0;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SPSEL;
-                      case 2:
-                        return MISCREG_CURRENTEL;
-                      case 3:
-                        return MISCREG_PAN;
-                      case 4:
-                        return MISCREG_UAO;
-                    }
-                    break;
-                  case 6:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ICC_PMR_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 3:
-                switch (crm) {
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_NZCV;
-                      case 1:
-                        return MISCREG_DAIF;
-                    }
-                    break;
-                  case 4:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_FPCR;
-                      case 1:
-                        return MISCREG_FPSR;
-                    }
-                    break;
-                  case 5:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_DSPSR_EL0;
-                      case 1:
-                        return MISCREG_DLR_EL0;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SPSR_EL2;
-                      case 1:
-                        return MISCREG_ELR_EL2;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SP_EL1;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SPSR_IRQ_AA64;
-                      case 1:
-                        return MISCREG_SPSR_ABT_AA64;
-                      case 2:
-                        return MISCREG_SPSR_UND_AA64;
-                      case 3:
-                        return MISCREG_SPSR_FIQ_AA64;
-                    }
-                    break;
-                }
-                break;
-              case 5:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SPSR_EL12;
-                      case 1:
-                        return MISCREG_ELR_EL12;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SPSR_EL3;
-                      case 1:
-                        return MISCREG_ELR_EL3;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_SP_EL2;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 5:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AFSR0_EL1;
-                      case 1:
-                        return MISCREG_AFSR1_EL1;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ESR_EL1;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ERRIDR_EL1;
-                      case 1:
-                        return MISCREG_ERRSELR_EL1;
-                    }
-                    break;
-                  case 4:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ERXFR_EL1;
-                      case 1:
-                        return MISCREG_ERXCTLR_EL1;
-                      case 2:
-                        return MISCREG_ERXSTATUS_EL1;
-                      case 3:
-                        return MISCREG_ERXADDR_EL1;
-                    }
-                    break;
-                  case 5:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ERXMISC0_EL1;
-                      case 1:
-                        return MISCREG_ERXMISC1_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_IFSR32_EL2;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AFSR0_EL2;
-                      case 1:
-                        return MISCREG_AFSR1_EL2;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ESR_EL2;
-                      case 3:
-                        return MISCREG_VSESR_EL2;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_FPEXC32_EL2;
-                    }
-                    break;
-                }
-                break;
-              case 5:
-                switch (crm) {
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AFSR0_EL12;
-                      case 1:
-                        return MISCREG_AFSR1_EL12;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ESR_EL12;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AFSR0_EL3;
-                      case 1:
-                        return MISCREG_AFSR1_EL3;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ESR_EL3;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 6:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_FAR_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_FAR_EL2;
-                      case 4:
-                        return MISCREG_HPFAR_EL2;
-                    }
-                    break;
-                }
-                break;
-              case 5:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_FAR_EL12;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_FAR_EL3;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 7:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 4:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_PAR_EL1;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 9:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 14:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_PMINTENSET_EL1;
-                      case 2:
-                        return MISCREG_PMINTENCLR_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 3:
-                switch (crm) {
-                  case 12:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_PMCR_EL0;
-                      case 1:
-                        return MISCREG_PMCNTENSET_EL0;
-                      case 2:
-                        return MISCREG_PMCNTENCLR_EL0;
-                      case 3:
-                        return MISCREG_PMOVSCLR_EL0;
-                      case 4:
-                        return MISCREG_PMSWINC_EL0;
-                      case 5:
-                        return MISCREG_PMSELR_EL0;
-                      case 6:
-                        return MISCREG_PMCEID0_EL0;
-                      case 7:
-                        return MISCREG_PMCEID1_EL0;
-                    }
-                    break;
-                  case 13:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_PMCCNTR_EL0;
-                      case 1:
-                        return MISCREG_PMXEVTYPER_EL0;
-                      case 2:
-                        return MISCREG_PMXEVCNTR_EL0;
-                    }
-                    break;
-                  case 14:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_PMUSERENR_EL0;
-                      case 3:
-                        return MISCREG_PMOVSSET_EL0;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 10:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MAIR_EL1;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AMAIR_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MAIR_EL2;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AMAIR_EL2;
-                    }
-                    break;
-                }
-                break;
-              case 5:
-                switch (crm) {
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MAIR_EL12;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AMAIR_EL12;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_MAIR_EL3;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_AMAIR_EL3;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 11:
-            switch (op1) {
-              case 1:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 2:
-                        return MISCREG_L2CTLR_EL1;
-                      case 3:
-                        return MISCREG_L2ECTLR_EL1;
-                    }
-                    break;
-                }
-                [[fallthrough]];
-              default:
-                // S3_<op1>_11_<Cm>_<op2>
-                return MISCREG_IMPDEF_UNIMPL;
-            }
-            GEM5_UNREACHABLE;
-          case 12:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_VBAR_EL1;
-                      case 1:
-                        return MISCREG_RVBAR_EL1;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ISR_EL1;
-                      case 1:
-                        return MISCREG_DISR_EL1;
-                    }
-                    break;
-                  case 8:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ICC_IAR0_EL1;
-                      case 1:
-                        return MISCREG_ICC_EOIR0_EL1;
-                      case 2:
-                        return MISCREG_ICC_HPPIR0_EL1;
-                      case 3:
-                        return MISCREG_ICC_BPR0_EL1;
-                      case 4:
-                        return MISCREG_ICC_AP0R0_EL1;
-                      case 5:
-                        return MISCREG_ICC_AP0R1_EL1;
-                      case 6:
-                        return MISCREG_ICC_AP0R2_EL1;
-                      case 7:
-                        return MISCREG_ICC_AP0R3_EL1;
-                    }
-                    break;
-                  case 9:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ICC_AP1R0_EL1;
-                      case 1:
-                        return MISCREG_ICC_AP1R1_EL1;
-                      case 2:
-                        return MISCREG_ICC_AP1R2_EL1;
-                      case 3:
-                        return MISCREG_ICC_AP1R3_EL1;
-                    }
-                    break;
-                  case 11:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_ICC_DIR_EL1;
-                      case 3:
-                        return MISCREG_ICC_RPR_EL1;
-                      case 5:
-                        return MISCREG_ICC_SGI1R_EL1;
-                      case 6:
-                        return MISCREG_ICC_ASGI1R_EL1;
-                      case 7:
-                        return MISCREG_ICC_SGI0R_EL1;
-                    }
-                    break;
-                  case 12:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ICC_IAR1_EL1;
-                      case 1:
-                        return MISCREG_ICC_EOIR1_EL1;
-                      case 2:
-                        return MISCREG_ICC_HPPIR1_EL1;
-                      case 3:
-                        return MISCREG_ICC_BPR1_EL1;
-                      case 4:
-                        return MISCREG_ICC_CTLR_EL1;
-                      case 5:
-                        return MISCREG_ICC_SRE_EL1;
-                      case 6:
-                        return MISCREG_ICC_IGRPEN0_EL1;
-                      case 7:
-                        return MISCREG_ICC_IGRPEN1_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_VBAR_EL2;
-                      case 1:
-                        return MISCREG_RVBAR_EL2;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_VDISR_EL2;
-                    }
-                    break;
-                  case 8:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ICH_AP0R0_EL2;
-                      case 1:
-                        return MISCREG_ICH_AP0R1_EL2;
-                      case 2:
-                        return MISCREG_ICH_AP0R2_EL2;
-                      case 3:
-                        return MISCREG_ICH_AP0R3_EL2;
-                    }
-                    break;
-                  case 9:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ICH_AP1R0_EL2;
-                      case 1:
-                        return MISCREG_ICH_AP1R1_EL2;
-                      case 2:
-                        return MISCREG_ICH_AP1R2_EL2;
-                      case 3:
-                        return MISCREG_ICH_AP1R3_EL2;
-                      case 5:
-                        return MISCREG_ICC_SRE_EL2;
-                    }
-                    break;
-                  case 11:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ICH_HCR_EL2;
-                      case 1:
-                        return MISCREG_ICH_VTR_EL2;
-                      case 2:
-                        return MISCREG_ICH_MISR_EL2;
-                      case 3:
-                        return MISCREG_ICH_EISR_EL2;
-                      case 5:
-                        return MISCREG_ICH_ELRSR_EL2;
-                      case 7:
-                        return MISCREG_ICH_VMCR_EL2;
-                    }
-                    break;
-                  case 12:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ICH_LR0_EL2;
-                      case 1:
-                        return MISCREG_ICH_LR1_EL2;
-                      case 2:
-                        return MISCREG_ICH_LR2_EL2;
-                      case 3:
-                        return MISCREG_ICH_LR3_EL2;
-                      case 4:
-                        return MISCREG_ICH_LR4_EL2;
-                      case 5:
-                        return MISCREG_ICH_LR5_EL2;
-                      case 6:
-                        return MISCREG_ICH_LR6_EL2;
-                      case 7:
-                        return MISCREG_ICH_LR7_EL2;
-                    }
-                    break;
-                  case 13:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_ICH_LR8_EL2;
-                      case 1:
-                        return MISCREG_ICH_LR9_EL2;
-                      case 2:
-                        return MISCREG_ICH_LR10_EL2;
-                      case 3:
-                        return MISCREG_ICH_LR11_EL2;
-                      case 4:
-                        return MISCREG_ICH_LR12_EL2;
-                      case 5:
-                        return MISCREG_ICH_LR13_EL2;
-                      case 6:
-                        return MISCREG_ICH_LR14_EL2;
-                      case 7:
-                        return MISCREG_ICH_LR15_EL2;
-                    }
-                    break;
-                }
-                break;
-              case 5:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_VBAR_EL12;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_VBAR_EL3;
-                      case 1:
-                        return MISCREG_RVBAR_EL3;
-                      case 2:
-                        return MISCREG_RMR_EL3;
-                    }
-                    break;
-                  case 12:
-                    switch (op2) {
-                      case 4:
-                        return MISCREG_ICC_CTLR_EL3;
-                      case 5:
-                        return MISCREG_ICC_SRE_EL3;
-                      case 7:
-                        return MISCREG_ICC_IGRPEN1_EL3;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 13:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_CONTEXTIDR_EL1;
-                      case 4:
-                        return MISCREG_TPIDR_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 3:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 2:
-                        return MISCREG_TPIDR_EL0;
-                      case 3:
-                        return MISCREG_TPIDRRO_EL0;
-                    }
-                    break;
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_CONTEXTIDR_EL2;
-                      case 2:
-                        return MISCREG_TPIDR_EL2;
-                    }
-                    break;
-                }
-                break;
-              case 5:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 1:
-                        return MISCREG_CONTEXTIDR_EL12;
-                    }
-                    break;
-                }
-                break;
-              case 6:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 2:
-                        return MISCREG_TPIDR_EL3;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 14:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTKCTL_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 3:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTFRQ_EL0;
-                      case 1:
-                        return MISCREG_CNTPCT_EL0;
-                      case 2:
-                        return MISCREG_CNTVCT_EL0;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTP_TVAL_EL0;
-                      case 1:
-                        return MISCREG_CNTP_CTL_EL0;
-                      case 2:
-                        return MISCREG_CNTP_CVAL_EL0;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTV_TVAL_EL0;
-                      case 1:
-                        return MISCREG_CNTV_CTL_EL0;
-                      case 2:
-                        return MISCREG_CNTV_CVAL_EL0;
-                    }
-                    break;
-                  case 8:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_PMEVCNTR0_EL0;
-                      case 1:
-                        return MISCREG_PMEVCNTR1_EL0;
-                      case 2:
-                        return MISCREG_PMEVCNTR2_EL0;
-                      case 3:
-                        return MISCREG_PMEVCNTR3_EL0;
-                      case 4:
-                        return MISCREG_PMEVCNTR4_EL0;
-                      case 5:
-                        return MISCREG_PMEVCNTR5_EL0;
-                    }
-                    break;
-                  case 12:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_PMEVTYPER0_EL0;
-                      case 1:
-                        return MISCREG_PMEVTYPER1_EL0;
-                      case 2:
-                        return MISCREG_PMEVTYPER2_EL0;
-                      case 3:
-                        return MISCREG_PMEVTYPER3_EL0;
-                      case 4:
-                        return MISCREG_PMEVTYPER4_EL0;
-                      case 5:
-                        return MISCREG_PMEVTYPER5_EL0;
-                    }
-                    break;
-                  case 15:
-                    switch (op2) {
-                      case 7:
-                        return MISCREG_PMCCFILTR_EL0;
-                    }
-                }
-                break;
-              case 4:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 3:
-                        return MISCREG_CNTVOFF_EL2;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTHCTL_EL2;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTHP_TVAL_EL2;
-                      case 1:
-                        return MISCREG_CNTHP_CTL_EL2;
-                      case 2:
-                        return MISCREG_CNTHP_CVAL_EL2;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTHV_TVAL_EL2;
-                      case 1:
-                        return MISCREG_CNTHV_CTL_EL2;
-                      case 2:
-                        return MISCREG_CNTHV_CVAL_EL2;
-                    }
-                    break;
-                }
-                break;
-              case 5:
-                switch (crm) {
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTKCTL_EL12;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTP_TVAL_EL02;
-                      case 1:
-                        return MISCREG_CNTP_CTL_EL02;
-                      case 2:
-                        return MISCREG_CNTP_CVAL_EL02;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTV_TVAL_EL02;
-                      case 1:
-                        return MISCREG_CNTV_CTL_EL02;
-                      case 2:
-                        return MISCREG_CNTV_CVAL_EL02;
-                    }
-                    break;
-                }
-                break;
-              case 7:
-                switch (crm) {
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CNTPS_TVAL_EL1;
-                      case 1:
-                        return MISCREG_CNTPS_CTL_EL1;
-                      case 2:
-                        return MISCREG_CNTPS_CVAL_EL1;
-                    }
-                    break;
-                }
-                break;
-            }
-            break;
-          case 15:
-            switch (op1) {
-              case 0:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_IL1DATA0_EL1;
-                      case 1:
-                        return MISCREG_IL1DATA1_EL1;
-                      case 2:
-                        return MISCREG_IL1DATA2_EL1;
-                      case 3:
-                        return MISCREG_IL1DATA3_EL1;
-                    }
-                    break;
-                  case 1:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_DL1DATA0_EL1;
-                      case 1:
-                        return MISCREG_DL1DATA1_EL1;
-                      case 2:
-                        return MISCREG_DL1DATA2_EL1;
-                      case 3:
-                        return MISCREG_DL1DATA3_EL1;
-                      case 4:
-                        return MISCREG_DL1DATA4_EL1;
-                    }
-                    break;
-                }
-                break;
-              case 1:
-                switch (crm) {
-                  case 0:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_L2ACTLR_EL1;
-                    }
-                    break;
-                  case 2:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CPUACTLR_EL1;
-                      case 1:
-                        return MISCREG_CPUECTLR_EL1;
-                      case 2:
-                        return MISCREG_CPUMERRSR_EL1;
-                      case 3:
-                        return MISCREG_L2MERRSR_EL1;
-                    }
-                    break;
-                  case 3:
-                    switch (op2) {
-                      case 0:
-                        return MISCREG_CBAR_EL1;
-
-                    }
-                    break;
-                }
-                break;
-            }
-            // S3_<op1>_15_<Cm>_<op2>
-            return MISCREG_IMPDEF_UNIMPL;
-        }
-        break;
     }
-
-    return MISCREG_UNKNOWN;
 }
 
-std::bitset<NUM_MISCREG_INFOS> miscRegInfo[NUM_MISCREGS]; // initialized below
+MiscRegNum64
+encodeAArch64SysReg(MiscRegIndex misc_reg)
+{
+    if (auto it = idxToMiscRegNum.find(misc_reg);
+        it != idxToMiscRegNum.end()) {
+        return it->second;
+    } else {
+        panic("Invalid MiscRegIndex: %n\n", misc_reg);
+    }
+}
 
 void
 ISA::initializeMiscRegMetadata()
@@ -3432,6 +1355,8 @@
     bool EnIA = true; // using APIAKey_EL1 key of instr addrs in ELs 0,1
     bool EnIB = true; // using APIBKey_EL1 key of instr addrs in ELs 0,1
 
+    const bool vhe_implemented = release->has(ArmExtension::FEAT_VHE);
+    const bool sel2_implemented = release->has(ArmExtension::FEAT_SEL2);
     /**
      * Some registers alias with others, and therefore need to be translated.
      * When two mapping registers are given, they are the 32b lower and
@@ -3515,6 +1440,8 @@
       .allPrivileges();
     InitReg(MISCREG_SEV_MAILBOX)
       .allPrivileges();
+    InitReg(MISCREG_TLBINEEDSYNC)
+      .allPrivileges().exceptUserMode();
 
     // AArch32 CP14 registers
     InitReg(MISCREG_DBGDIDR)
@@ -4331,6 +2258,8 @@
       .bankedChild()
       .nonSecure()
       .privSecure(!aarch32EL3)
+      .userSecureRead(!aarch32EL3)
+      .userSecureWrite(!aarch32EL3)
       .res0(0xfffffff8);
     InitReg(MISCREG_CNTP_CTL_S)
       .bankedChild()
@@ -4342,7 +2271,9 @@
     InitReg(MISCREG_CNTP_CVAL_NS)
       .bankedChild()
       .nonSecure()
-      .privSecure(!aarch32EL3);
+      .privSecure(!aarch32EL3)
+      .userSecureRead(!aarch32EL3)
+      .userSecureWrite(!aarch32EL3);
     InitReg(MISCREG_CNTP_CVAL_S)
       .bankedChild()
       .secure()
@@ -4352,7 +2283,9 @@
     InitReg(MISCREG_CNTP_TVAL_NS)
       .bankedChild()
       .nonSecure()
-      .privSecure(!aarch32EL3);
+      .privSecure(!aarch32EL3)
+      .userSecureRead(!aarch32EL3)
+      .userSecureWrite(!aarch32EL3);
     InitReg(MISCREG_CNTP_TVAL_S)
       .bankedChild()
       .secure()
@@ -4817,7 +2750,8 @@
                      | (LSMAOE ? 0 : 0x10000000))
       .mapsTo(MISCREG_SCTLR_NS);
     InitReg(MISCREG_SCTLR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .res0( 0x20440 | (EnDB   ? 0 :     0x2000)
                      | (IESB   ? 0 :   0x200000)
                      | (EnDA   ? 0 :  0x8000000)
@@ -4834,7 +2768,8 @@
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_CPACR);
     InitReg(MISCREG_CPACR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_CPACR_EL1);
     InitReg(MISCREG_SCTLR_EL2)
       .hyp().mon()
@@ -4888,19 +2823,22 @@
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_TTBR0_NS);
     InitReg(MISCREG_TTBR0_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_TTBR0_EL1);
     InitReg(MISCREG_TTBR1_EL1)
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_TTBR1_NS);
     InitReg(MISCREG_TTBR1_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_TTBR1_EL1);
     InitReg(MISCREG_TCR_EL1)
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_TTBCR_NS);
     InitReg(MISCREG_TCR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_TTBCR_NS);
     InitReg(MISCREG_TTBR0_EL2)
       .hyp().mon()
@@ -4931,12 +2869,14 @@
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_SPSR_SVC); // NAM C5.2.17 SPSR_EL1
     InitReg(MISCREG_SPSR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_SPSR_SVC);
     InitReg(MISCREG_ELR_EL1)
       .allPrivileges().exceptUserMode();
     InitReg(MISCREG_ELR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_ELR_EL1);
     InitReg(MISCREG_SP_EL0)
       .allPrivileges().exceptUserMode();
@@ -4987,18 +2927,21 @@
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_ADFSR_NS);
     InitReg(MISCREG_AFSR0_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_ADFSR_NS);
     InitReg(MISCREG_AFSR1_EL1)
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_AIFSR_NS);
     InitReg(MISCREG_AFSR1_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_AIFSR_NS);
     InitReg(MISCREG_ESR_EL1)
       .allPrivileges().exceptUserMode();
     InitReg(MISCREG_ESR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_ESR_EL1);
     InitReg(MISCREG_IFSR32_EL2)
       .hyp().mon()
@@ -5024,7 +2967,8 @@
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_DFAR_NS, MISCREG_IFAR_NS);
     InitReg(MISCREG_FAR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_DFAR_NS, MISCREG_IFAR_NS);
     InitReg(MISCREG_FAR_EL2)
       .hyp().mon()
@@ -5208,13 +3152,15 @@
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_PRRR_NS, MISCREG_NMRR_NS);
     InitReg(MISCREG_MAIR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_PRRR_NS, MISCREG_NMRR_NS);
     InitReg(MISCREG_AMAIR_EL1)
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_AMAIR0_NS, MISCREG_AMAIR1_NS);
     InitReg(MISCREG_AMAIR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_AMAIR0_NS, MISCREG_AMAIR1_NS);
     InitReg(MISCREG_MAIR_EL2)
       .hyp().mon()
@@ -5234,7 +3180,8 @@
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_VBAR_NS);
     InitReg(MISCREG_VBAR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_VBAR_NS);
     InitReg(MISCREG_RVBAR_EL1)
       .allPrivileges().exceptUserMode().writes(0);
@@ -5256,7 +3203,8 @@
       .allPrivileges().exceptUserMode()
       .mapsTo(MISCREG_CONTEXTIDR_NS);
     InitReg(MISCREG_CONTEXTIDR_EL12)
-      .allPrivileges().exceptUserMode()
+      .monE2H()
+      .hypE2H()
       .mapsTo(MISCREG_CONTEXTIDR_NS);
     InitReg(MISCREG_TPIDR_EL1)
       .allPrivileges().exceptUserMode()
@@ -5378,45 +3326,46 @@
       .mapsTo(MISCREG_CNTHP_TVAL);
     InitReg(MISCREG_CNTHPS_CTL_EL2)
       .mon()
-      .hyp()
+      .hypSecure()
       .res0(0xfffffffffffffff8)
-      .unimplemented();
+      .implemented(sel2_implemented);
     InitReg(MISCREG_CNTHPS_CVAL_EL2)
       .mon()
-      .hyp()
-      .res0(0xfffffffffffffff8)
-      .unimplemented();
+      .hypSecure()
+      .implemented(sel2_implemented);
     InitReg(MISCREG_CNTHPS_TVAL_EL2)
       .mon()
-      .hyp()
-      .res0(0xfffffffffffffff8)
-      .unimplemented();
+      .hypSecure()
+      .res0(0xffffffff00000000)
+      .implemented(sel2_implemented);
     InitReg(MISCREG_CNTHV_CTL_EL2)
       .mon()
       .hyp()
-      .res0(0xfffffffffffffff8);
+      .res0(0xfffffffffffffff8)
+      .implemented(vhe_implemented);
     InitReg(MISCREG_CNTHV_CVAL_EL2)
       .mon()
-      .hyp();
+      .hyp()
+      .implemented(vhe_implemented);
     InitReg(MISCREG_CNTHV_TVAL_EL2)
       .mon()
       .hyp()
-      .res0(0xffffffff00000000);
+      .res0(0xffffffff00000000)
+      .implemented(vhe_implemented);
     InitReg(MISCREG_CNTHVS_CTL_EL2)
       .mon()
-      .hyp()
+      .hypSecure()
       .res0(0xfffffffffffffff8)
-      .unimplemented();
+      .implemented(vhe_implemented && sel2_implemented);
     InitReg(MISCREG_CNTHVS_CVAL_EL2)
       .mon()
-      .hyp()
-      .res0(0xfffffffffffffff8)
-      .unimplemented();
+      .hypSecure()
+      .implemented(vhe_implemented && sel2_implemented);
     InitReg(MISCREG_CNTHVS_TVAL_EL2)
       .mon()
-      .hyp()
-      .res0(0xfffffffffffffff8)
-      .unimplemented();
+      .hypSecure()
+      .res0(0xffffffff00000000)
+      .implemented(vhe_implemented && sel2_implemented);
     // ENDIF Armv8.1-VHE
     InitReg(MISCREG_CNTVOFF_EL2)
       .mon()
@@ -5971,7 +3920,8 @@
     InitReg(MISCREG_ZCR_EL2)
         .hyp().mon();
     InitReg(MISCREG_ZCR_EL12)
-        .allPrivileges().exceptUserMode()
+        .monE2H()
+        .hypE2H()
         .mapsTo(MISCREG_ZCR_EL1);
     InitReg(MISCREG_ZCR_EL1)
         .allPrivileges().exceptUserMode();
@@ -5981,12 +3931,6 @@
       .allPrivileges();
     InitReg(MISCREG_RAZ)
       .allPrivileges().exceptUserMode().writes(0);
-    InitReg(MISCREG_CP14_UNIMPL)
-      .unimplemented()
-      .warnNotFail();
-    InitReg(MISCREG_CP15_UNIMPL)
-      .unimplemented()
-      .warnNotFail();
     InitReg(MISCREG_UNKNOWN);
     InitReg(MISCREG_IMPDEF_UNIMPL)
       .unimplemented()
@@ -6036,6 +3980,12 @@
     // DBGDTRTX_EL0 -> DBGDTRRXint
     // MDCR_EL3 -> SDCR, NAM D7-2108 (the latter is unimpl. in gem5)
 
+    // Populate the idxToMiscRegNum map
+    assert(idxToMiscRegNum.empty());
+    for (const auto& [key, val] : miscRegNumToIdx) {
+        idxToMiscRegNum.insert({val, key});
+    }
+
     completed = true;
 }
 
diff --git a/src/arch/arm/regs/misc.hh b/src/arch/arm/regs/misc.hh
index 1a2f137..9b517a7 100644
--- a/src/arch/arm/regs/misc.hh
+++ b/src/arch/arm/regs/misc.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2020 ARM Limited
+ * Copyright (c) 2010-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -90,6 +90,7 @@
         MISCREG_PMXEVTYPER_PMCCFILTR,
         MISCREG_SCTLR_RST,
         MISCREG_SEV_MAILBOX,
+        MISCREG_TLBINEEDSYNC,
 
         // AArch32 CP14 registers (debug/trace/ThumbEE/Jazelle control)
         MISCREG_DBGDIDR,
@@ -1059,7 +1060,7 @@
 
         // NUM_PHYS_MISCREGS specifies the number of actual physical
         // registers, not considering the following pseudo-registers
-        // (dummy registers), like UNKNOWN, CP15_UNIMPL, MISCREG_IMPDEF_UNIMPL.
+        // (dummy registers), like MISCREG_UNKNOWN, MISCREG_IMPDEF_UNIMPL.
         // Checkpointing should use this physical index when
         // saving/restoring register values.
         NUM_PHYS_MISCREGS,
@@ -1067,8 +1068,6 @@
         // Dummy registers
         MISCREG_NOP,
         MISCREG_RAZ,
-        MISCREG_CP14_UNIMPL,
-        MISCREG_CP15_UNIMPL,
         MISCREG_UNKNOWN,
 
         // Implementation defined register: this represent
@@ -1153,12 +1152,113 @@
 
     extern std::bitset<NUM_MISCREG_INFOS> miscRegInfo[NUM_MISCREGS];
 
+    struct MiscRegNum32
+    {
+        MiscRegNum32(unsigned _coproc, unsigned _opc1,
+                     unsigned _crn, unsigned _crm,
+                     unsigned _opc2)
+          : reg64(0), coproc(_coproc), opc1(_opc1), crn(_crn),
+            crm(_crm), opc2(_opc2)
+        {
+            // MCR/MRC CP14 or CP15 register
+            assert(coproc == 0b1110 || coproc == 0b1111);
+            assert(opc1 < 8 && crn < 16 && crm < 16 && opc2 < 8);
+        }
+
+        MiscRegNum32(unsigned _coproc, unsigned _opc1,
+                     unsigned _crm)
+          : reg64(1), coproc(_coproc), opc1(_opc1), crn(0),
+            crm(_crm), opc2(0)
+        {
+            // MCRR/MRRC CP14 or CP15 register
+            assert(coproc == 0b1110 || coproc == 0b1111);
+            assert(opc1 < 16 && crm < 16);
+        }
+
+        MiscRegNum32(const MiscRegNum32& rhs) = default;
+
+        bool
+        operator==(const MiscRegNum32 &other) const
+        {
+            return reg64 == other.reg64 &&
+                coproc == other.coproc &&
+                opc1 == other.opc1 &&
+                crn == other.crn &&
+                crm == other.crm &&
+                opc2 == other.opc2;
+        }
+
+        uint32_t
+        packed() const
+        {
+            return reg64 << 19  |
+                   coproc << 15 |
+                   opc1 << 11   |
+                   crn << 7     |
+                   crm << 3     |
+                   opc2;
+        }
+
+        // 1 if the register is 64bit wide (accessed through MCRR/MRCC)
+        // 0 otherwise. We need this when generating the hash as there
+        // might be collisions between 32 and 64 bit registers
+        const unsigned reg64;
+
+        unsigned coproc;
+        unsigned opc1;
+        unsigned crn;
+        unsigned crm;
+        unsigned opc2;
+    };
+
+    struct MiscRegNum64
+    {
+        MiscRegNum64(unsigned _op0, unsigned _op1,
+                     unsigned _crn, unsigned _crm,
+                     unsigned _op2)
+          : op0(_op0), op1(_op1), crn(_crn),
+            crm(_crm), op2(_op2)
+        {
+            assert(op0 < 4 && op1 < 8 && crn < 16 && crm < 16 && op2 < 8);
+        }
+
+        MiscRegNum64(const MiscRegNum64& rhs) = default;
+
+        bool
+        operator==(const MiscRegNum64 &other) const
+        {
+            return op0 == other.op0 &&
+                op1 == other.op1 &&
+                crn == other.crn &&
+                crm == other.crm &&
+                op2 == other.op2;
+        }
+
+        uint32_t
+        packed() const
+        {
+            return op0 << 14 |
+                   op1 << 11 |
+                   crn << 7  |
+                   crm << 3  |
+                   op2;
+        }
+
+        unsigned op0;
+        unsigned op1;
+        unsigned crn;
+        unsigned crm;
+        unsigned op2;
+    };
+
     // Decodes 32-bit CP14 registers accessible through MCR/MRC instructions
     MiscRegIndex decodeCP14Reg(unsigned crn, unsigned opc1,
                                unsigned crm, unsigned opc2);
     MiscRegIndex decodeAArch64SysReg(unsigned op0, unsigned op1,
                                      unsigned crn, unsigned crm,
                                      unsigned op2);
+    MiscRegNum64 encodeAArch64SysReg(MiscRegIndex misc_reg);
+
     // Whether a particular AArch64 system register is -always- read only.
     bool aarch64SysRegReadOnly(MiscRegIndex miscReg);
 
@@ -1203,6 +1303,7 @@
         "pmxevtyper_pmccfiltr",
         "sctlr_rst",
         "sev_mailbox",
+        "tlbi_needsync",
 
         // AArch32 CP14 registers
         "dbgdidr",
@@ -1352,7 +1453,7 @@
         "actlr_ns",
         "actlr_s",
         "cpacr",
-        "sdrc",
+        "sdcr",
         "scr",
         "sder",
         "nsacr",
@@ -2164,8 +2265,6 @@
         // Dummy registers
         "nop",
         "raz",
-        "cp14_unimpl",
-        "cp15_unimpl",
         "unknown",
         "impl_defined",
         "erridr_el1",
@@ -2286,4 +2385,27 @@
 } // namespace ArmISA
 } // namespace gem5
 
+namespace std
+{
+template<>
+struct hash<gem5::ArmISA::MiscRegNum32>
+{
+    size_t
+    operator()(const gem5::ArmISA::MiscRegNum32& reg) const
+    {
+        return reg.packed();
+    }
+};
+
+template<>
+struct hash<gem5::ArmISA::MiscRegNum64>
+{
+    size_t
+    operator()(const gem5::ArmISA::MiscRegNum64& reg) const
+    {
+        return reg.packed();
+    }
+};
+} // namespace std
+
 #endif // __ARCH_ARM_REGS_MISC_HH__
diff --git a/src/arch/arm/regs/misc_types.hh b/src/arch/arm/regs/misc_types.hh
index 05b00e2..d44651c 100644
--- a/src/arch/arm/regs/misc_types.hh
+++ b/src/arch/arm/regs/misc_types.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2021 Arm Limited
+ * Copyright (c) 2010-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -669,7 +669,59 @@
    BitUnion32(ESR)
         Bitfield<31, 26> ec;
         Bitfield<25> il;
-        Bitfield<15, 0> imm16;
+        Bitfield<24, 0> iss;
+
+        // Generic Condition ISS
+        // Used by exception syndromes holding the condition code of
+        // the trapped instruction. TODO: We should really have a
+        // different SubBitUnion per exception type and avoid
+        // such generic sub-fields
+        SubBitUnion(cond_iss, 24, 0)
+            Bitfield<24> cv;
+            Bitfield<23, 20> cond;
+            Bitfield<19, 0> iss;
+        EndSubBitUnion(cond_iss)
+
+        // Data Abort ISS
+        SubBitUnion(data_abort_iss, 24, 0)
+            Bitfield<24> isv;
+            Bitfield<23, 22> sas;
+            Bitfield<21> sse;
+            Bitfield<20, 16> srt;
+            Bitfield<15> sf;
+            Bitfield<14> ar;
+            Bitfield<13> vncr;
+            Bitfield<10> fnv;
+            Bitfield<9> ea;
+            Bitfield<8> cm;
+            Bitfield<7> s1ptw;
+            Bitfield<6> wnr;
+            Bitfield<5, 0> dfsc;
+        EndSubBitUnion(data_abort_iss)
+
+        // Instruction Abort ISS
+        SubBitUnion(instruction_abort_iss, 24, 0)
+            Bitfield<12, 11> set;
+            Bitfield<10> fnv;
+            Bitfield<9> ea;
+            Bitfield<7> s1ptw;
+            Bitfield<5, 0> ifsc;
+        EndSubBitUnion(instruction_abort_iss)
+
+        // Software Step ISS
+        SubBitUnion(software_step_iss, 24, 0)
+            Bitfield<24> isv;
+            Bitfield<6> ex;
+            Bitfield<5, 0> ifsc;
+        EndSubBitUnion(software_step_iss)
+
+        // Watchpoint ISS
+        SubBitUnion(watchpoint_iss, 24, 0)
+            Bitfield<13> vncr;
+            Bitfield<8> cm;
+            Bitfield<6> wnr;
+            Bitfield<5, 0> dfsc;
+        EndSubBitUnion(watchpoint_iss)
    EndBitUnion(ESR)
 
    BitUnion32(CPTR)
diff --git a/src/arch/arm/remote_gdb.cc b/src/arch/arm/remote_gdb.cc
index 2efa82f..7b91fab 100644
--- a/src/arch/arm/remote_gdb.cc
+++ b/src/arch/arm/remote_gdb.cc
@@ -168,6 +168,20 @@
 
 using namespace ArmISA;
 
+namespace
+{
+
+// https://sourceware.org/gdb/current/onlinedocs/gdb/ARM-Breakpoint-Kinds.html
+enum class ArmBpKind
+{
+    THUMB = 2,
+    THUMB_2 = 3,
+    ARM = 4,
+};
+
+} // namespace
+
+
 static bool
 tryTranslate(ThreadContext *tc, Addr addr)
 {
@@ -221,14 +235,16 @@
     DPRINTF(GDBAcc, "getRegs in remotegdb \n");
 
     for (int i = 0; i < 31; ++i)
-        r.x[i] = context->readIntReg(INTREG_X0 + i);
-    r.spx = context->readIntReg(INTREG_SPX);
+        r.x[i] = context->getReg(int_reg::x(i));
+    r.spx = context->getReg(int_reg::Spx);
     r.pc = context->pcState().instAddr();
     r.cpsr = context->readMiscRegNoEffect(MISCREG_CPSR);
 
     size_t base = 0;
     for (int i = 0; i < NumVecV8ArchRegs; i++) {
-        auto v = (context->readVecReg(RegId(VecRegClass, i))).as<VecElem>();
+        ArmISA::VecRegContainer vc;
+        context->getReg(RegId(VecRegClass, i), &vc);
+        auto v = vc.as<VecElem>();
         for (size_t j = 0; j < NumVecElemPerNeonVecReg; j++) {
             r.v[base] = v[j];
             base++;
@@ -244,7 +260,7 @@
     DPRINTF(GDBAcc, "setRegs in remotegdb \n");
 
     for (int i = 0; i < 31; ++i)
-        context->setIntReg(INTREG_X0 + i, r.x[i]);
+        context->setReg(int_reg::x(i), r.x[i]);
     auto pc_state = context->pcState().as<PCState>();
     pc_state.set(r.pc);
     context->pcState(pc_state);
@@ -252,12 +268,13 @@
     // Update the stack pointer. This should be done after
     // updating CPSR/PSTATE since that might affect how SPX gets
     // mapped.
-    context->setIntReg(INTREG_SPX, r.spx);
+    context->setReg(int_reg::Spx, r.spx);
 
     size_t base = 0;
     for (int i = 0; i < NumVecV8ArchRegs; i++) {
-        auto v = (context->getWritableVecReg(
-                RegId(VecRegClass, i))).as<VecElem>();
+        auto *vc = static_cast<ArmISA::VecRegContainer *>(
+                context->getWritableReg(RegId(VecRegClass, i)));
+        auto v = vc->as<VecElem>();
         for (size_t j = 0; j < NumVecElemPerNeonVecReg; j++) {
             v[j] = r.v[base];
             base++;
@@ -272,21 +289,21 @@
 {
     DPRINTF(GDBAcc, "getRegs in remotegdb \n");
 
-    r.gpr[0] = context->readIntReg(INTREG_R0);
-    r.gpr[1] = context->readIntReg(INTREG_R1);
-    r.gpr[2] = context->readIntReg(INTREG_R2);
-    r.gpr[3] = context->readIntReg(INTREG_R3);
-    r.gpr[4] = context->readIntReg(INTREG_R4);
-    r.gpr[5] = context->readIntReg(INTREG_R5);
-    r.gpr[6] = context->readIntReg(INTREG_R6);
-    r.gpr[7] = context->readIntReg(INTREG_R7);
-    r.gpr[8] = context->readIntReg(INTREG_R8);
-    r.gpr[9] = context->readIntReg(INTREG_R9);
-    r.gpr[10] = context->readIntReg(INTREG_R10);
-    r.gpr[11] = context->readIntReg(INTREG_R11);
-    r.gpr[12] = context->readIntReg(INTREG_R12);
-    r.gpr[13] = context->readIntReg(INTREG_SP);
-    r.gpr[14] = context->readIntReg(INTREG_LR);
+    r.gpr[0] = context->getReg(int_reg::R0);
+    r.gpr[1] = context->getReg(int_reg::R1);
+    r.gpr[2] = context->getReg(int_reg::R2);
+    r.gpr[3] = context->getReg(int_reg::R3);
+    r.gpr[4] = context->getReg(int_reg::R4);
+    r.gpr[5] = context->getReg(int_reg::R5);
+    r.gpr[6] = context->getReg(int_reg::R6);
+    r.gpr[7] = context->getReg(int_reg::R7);
+    r.gpr[8] = context->getReg(int_reg::R8);
+    r.gpr[9] = context->getReg(int_reg::R9);
+    r.gpr[10] = context->getReg(int_reg::R10);
+    r.gpr[11] = context->getReg(int_reg::R11);
+    r.gpr[12] = context->getReg(int_reg::R12);
+    r.gpr[13] = context->getReg(int_reg::Sp);
+    r.gpr[14] = context->getReg(int_reg::Lr);
     r.gpr[15] = context->pcState().instAddr();
     r.cpsr = context->readMiscRegNoEffect(MISCREG_CPSR);
 
@@ -302,21 +319,21 @@
 {
     DPRINTF(GDBAcc, "setRegs in remotegdb \n");
 
-    context->setIntReg(INTREG_R0, r.gpr[0]);
-    context->setIntReg(INTREG_R1, r.gpr[1]);
-    context->setIntReg(INTREG_R2, r.gpr[2]);
-    context->setIntReg(INTREG_R3, r.gpr[3]);
-    context->setIntReg(INTREG_R4, r.gpr[4]);
-    context->setIntReg(INTREG_R5, r.gpr[5]);
-    context->setIntReg(INTREG_R6, r.gpr[6]);
-    context->setIntReg(INTREG_R7, r.gpr[7]);
-    context->setIntReg(INTREG_R8, r.gpr[8]);
-    context->setIntReg(INTREG_R9, r.gpr[9]);
-    context->setIntReg(INTREG_R10, r.gpr[10]);
-    context->setIntReg(INTREG_R11, r.gpr[11]);
-    context->setIntReg(INTREG_R12, r.gpr[12]);
-    context->setIntReg(INTREG_SP, r.gpr[13]);
-    context->setIntReg(INTREG_LR, r.gpr[14]);
+    context->setReg(int_reg::R0, r.gpr[0]);
+    context->setReg(int_reg::R1, r.gpr[1]);
+    context->setReg(int_reg::R2, r.gpr[2]);
+    context->setReg(int_reg::R3, r.gpr[3]);
+    context->setReg(int_reg::R4, r.gpr[4]);
+    context->setReg(int_reg::R5, r.gpr[5]);
+    context->setReg(int_reg::R6, r.gpr[6]);
+    context->setReg(int_reg::R7, r.gpr[7]);
+    context->setReg(int_reg::R8, r.gpr[8]);
+    context->setReg(int_reg::R9, r.gpr[9]);
+    context->setReg(int_reg::R10, r.gpr[10]);
+    context->setReg(int_reg::R11, r.gpr[11]);
+    context->setReg(int_reg::R12, r.gpr[12]);
+    context->setReg(int_reg::Sp, r.gpr[13]);
+    context->setReg(int_reg::Lr, r.gpr[14]);
     PCState pc_state = context->pcState().as<PCState>();
     pc_state.set(r.gpr[15]);
     context->pcState(pc_state);
@@ -362,10 +379,16 @@
 }
 
 bool
-RemoteGDB::checkBpLen(size_t len)
+RemoteGDB::checkBpKind(size_t kind)
 {
-    // 2 for Thumb ISA, 4 for ARM ISA.
-    return len == 2 || len == 4;
+    switch (ArmBpKind(kind)) {
+      case ArmBpKind::THUMB:
+      case ArmBpKind::THUMB_2:
+      case ArmBpKind::ARM:
+        return true;
+      default:
+        return false;
+    }
 }
 
 } // namespace gem5
diff --git a/src/arch/arm/remote_gdb.hh b/src/arch/arm/remote_gdb.hh
index cff6d4a..8e512a4 100644
--- a/src/arch/arm/remote_gdb.hh
+++ b/src/arch/arm/remote_gdb.hh
@@ -120,7 +120,7 @@
   public:
     RemoteGDB(System *_system, int _port);
     BaseGdbRegCache *gdbRegs() override;
-    bool checkBpLen(size_t len) override;
+    bool checkBpKind(size_t kind) override;
     std::vector<std::string>
     availableFeatures() const override
     {
diff --git a/src/arch/arm/self_debug.cc b/src/arch/arm/self_debug.cc
index 2029bdc..86da693 100644
--- a/src/arch/arm/self_debug.cc
+++ b/src/arch/arm/self_debug.cc
@@ -165,8 +165,9 @@
 SelfDebug::isDebugEnabledForEL64(ThreadContext *tc, ExceptionLevel el,
                          bool secure, bool mask)
 {
-    bool route_to_el2 =  ArmSystem::haveEL(tc, EL2) &&
-                         (!secure || HaveSecureEL2Ext(tc)) && enableTdeTge;
+    bool route_to_el2 = ArmSystem::haveEL(tc, EL2) &&
+                        (!secure || HaveExt(tc, ArmExtension::FEAT_SEL2)) &&
+                        enableTdeTge;
 
     ExceptionLevel target_el = route_to_el2 ? EL2 : EL1;
     if (oslk || (sdd && secure && ArmSystem::haveEL(tc, EL3))) {
@@ -256,12 +257,13 @@
         break;
 
       case 0x6:
-        if (HaveVirtHostExt(tc) && !ELIsInHost(tc, el))
+        if (HaveExt(tc, ArmExtension::FEAT_VHE) && !ELIsInHost(tc, el))
              v = testContextMatch(tc, true);
         break;
 
       case 0x7:
-        if (HaveVirtHostExt(tc) && !ELIsInHost(tc, el) && from_link)
+        if (HaveExt(tc, ArmExtension::FEAT_VHE) && !ELIsInHost(tc, el) &&
+            from_link)
             v = testContextMatch(tc, true);
         break;
 
@@ -292,27 +294,29 @@
         break;
 
       case 0xc:
-        if (HaveVirtHostExt(tc) && (!isSecure(tc)|| HaveSecureEL2Ext(tc)))
+        if (HaveExt(tc, ArmExtension::FEAT_VHE) &&
+            (!isSecure(tc)|| HaveExt(tc, ArmExtension::FEAT_SEL2)))
             v = testContextMatch(tc, false);
         break;
 
       case 0xd:
-        if (HaveVirtHostExt(tc) && from_link &&
-            (!isSecure(tc)|| HaveSecureEL2Ext(tc))) {
+        if (HaveExt(tc, ArmExtension::FEAT_VHE) && from_link &&
+            (!isSecure(tc)|| HaveExt(tc, ArmExtension::FEAT_SEL2))) {
              v = testContextMatch(tc, false);
         }
         break;
 
       case 0xe:
-        if (HaveVirtHostExt(tc) && !ELIsInHost(tc, el) &&
-            (!isSecure(tc)|| HaveSecureEL2Ext(tc))) {
+        if (HaveExt(tc, ArmExtension::FEAT_VHE) && !ELIsInHost(tc, el) &&
+            (!isSecure(tc)|| HaveExt(tc, ArmExtension::FEAT_SEL2))) {
             v = testContextMatch(tc, true); // CONTEXTIDR_EL1
             v = v && testContextMatch(tc, false); // CONTEXTIDR_EL2
         }
         break;
       case 0xf:
-        if (HaveVirtHostExt(tc) && !ELIsInHost(tc, el) && from_link &&
-            (!isSecure(tc)|| HaveSecureEL2Ext(tc))) {
+        if (HaveExt(tc, ArmExtension::FEAT_VHE) && !ELIsInHost(tc, el) &&
+            from_link &&
+            (!isSecure(tc)|| HaveExt(tc, ArmExtension::FEAT_SEL2))) {
             v = testContextMatch(tc, true); // CONTEXTIDR_EL1
             v = v && testContextMatch(tc, false); // CONTEXTIDR_EL2
         }
diff --git a/src/arch/arm/semihosting.cc b/src/arch/arm/semihosting.cc
index ed6fa29..8efe841 100644
--- a/src/arch/arm/semihosting.cc
+++ b/src/arch/arm/semihosting.cc
@@ -169,7 +169,7 @@
 bool
 ArmSemihosting::call64(ThreadContext *tc, bool gem5_ops)
 {
-    RegVal op = tc->readIntReg(ArmISA::INTREG_X0) & mask(32);
+    RegVal op = tc->getReg(ArmISA::int_reg::X0) & mask(32);
     if (op > MaxStandardOp && !gem5_ops) {
         unrecognizedCall<Abi64>(
                 tc, "Gem5 semihosting op (0x%x) disabled from here.", op);
@@ -195,7 +195,7 @@
 bool
 ArmSemihosting::call32(ThreadContext *tc, bool gem5_ops)
 {
-    RegVal op = tc->readIntReg(ArmISA::INTREG_R0);
+    RegVal op = tc->getReg(ArmISA::int_reg::R0);
     if (op > MaxStandardOp && !gem5_ops) {
         unrecognizedCall<Abi32>(
                 tc, "Gem5 semihosting op (0x%x) disabled from here.", op);
diff --git a/src/arch/arm/semihosting.hh b/src/arch/arm/semihosting.hh
index a9bdbad..fe7819c 100644
--- a/src/arch/arm/semihosting.hh
+++ b/src/arch/arm/semihosting.hh
@@ -144,7 +144,7 @@
           public:
             // For 64 bit semihosting, the params are pointer to by X1.
             explicit State(const ThreadContext *tc) :
-                StateBase<uint64_t>(tc, tc->readIntReg(ArmISA::INTREG_X1))
+                StateBase<uint64_t>(tc, tc->getReg(ArmISA::int_reg::X1))
             {}
         };
     };
@@ -158,7 +158,7 @@
           public:
             // For 32 bit semihosting, the params are pointer to by R1.
             explicit State(const ThreadContext *tc) :
-                StateBase<uint64_t>(tc, tc->readIntReg(ArmISA::INTREG_R1))
+                StateBase<uint64_t>(tc, tc->getReg(ArmISA::int_reg::R1))
             {}
         };
     };
@@ -646,7 +646,7 @@
     static void
     store(ThreadContext *tc, const ArmSemihosting::RetErrno &err)
     {
-        tc->setIntReg(ArmISA::INTREG_R0, err.first);
+        tc->setReg(ArmISA::int_reg::R0, err.first);
     }
 };
 
@@ -656,7 +656,7 @@
     static void
     store(ThreadContext *tc, const ArmSemihosting::RetErrno &err)
     {
-        tc->setIntReg(ArmISA::INTREG_X0, err.first);
+        tc->setReg(ArmISA::int_reg::X0, err.first);
     }
 };
 
diff --git a/src/arch/arm/table_walker.cc b/src/arch/arm/table_walker.cc
index c7b8c3c..96d5036 100644
--- a/src/arch/arm/table_walker.cc
+++ b/src/arch/arm/table_walker.cc
@@ -383,7 +383,7 @@
             }
         } else switch (currState->el) {
           case EL0:
-            if (HaveVirtHostExt(currState->tc) &&
+            if (HaveExt(currState->tc, ArmExtension::FEAT_VHE) &&
                   currState->hcr.tge == 1 && currState->hcr.e2h ==1) {
               currState->sctlr = currState->tc->readMiscReg(MISCREG_SCTLR_EL2);
               currState->tcr = currState->tc->readMiscReg(MISCREG_TCR_EL2);
@@ -870,7 +870,8 @@
     // 16KB in size. When ARMv8.2-LVA is supported, for the 64KB
     // translation granule size only, the effective minimum value of
     // 52.
-    int in_max = (HaveLVA(currState->tc) && tg == Grain64KB) ? 52 : 48;
+    const bool have_lva = HaveExt(currState->tc, ArmExtension::FEAT_LVA);
+    int in_max = (have_lva && tg == Grain64KB) ? 52 : 48;
     int in_min = 64 - (tg == Grain64KB ? 47 : 48);
 
     return tsz > in_max || tsz < in_min || (low_range ?
@@ -913,7 +914,7 @@
         {
             Addr ttbr0;
             Addr ttbr1;
-            if (HaveVirtHostExt(currState->tc) &&
+            if (HaveExt(currState->tc, ArmExtension::FEAT_VHE) &&
                     currState->hcr.tge==1 && currState->hcr.e2h == 1) {
                 // VHE code for EL2&0 regime
                 ttbr0 = currState->tc->readMiscReg(MISCREG_TTBR0_EL2);
diff --git a/src/arch/arm/tracers/tarmac_parser.cc b/src/arch/arm/tracers/tarmac_parser.cc
index 45a6332..34d7ca7 100644
--- a/src/arch/arm/tracers/tarmac_parser.cc
+++ b/src/arch/arm/tracers/tarmac_parser.cc
@@ -755,45 +755,39 @@
         switch (it->type) {
           case REG_R:
           case REG_X:
-            values.push_back(thread->readIntReg(it->index));
+            values.push_back(thread->getReg(RegId(IntRegClass, it->index)));
             break;
           case REG_S:
             if (instRecord.isetstate == ISET_A64) {
-                const ArmISA::VecRegContainer& vc = thread->readVecReg(
-                    RegId(VecRegClass, it->index));
+                ArmISA::VecRegContainer vc;
+                thread->getReg(RegId(VecRegClass, it->index), &vc);
                 auto vv = vc.as<uint32_t>();
                 values.push_back(vv[0]);
             } else {
-                const VecElem elem = thread->readVecElem(
-                    RegId(VecElemClass,
-                        it->index / NumVecElemPerNeonVecReg,
-                        it->index % NumVecElemPerNeonVecReg));
+                const VecElem elem = thread->getReg(
+                    RegId(VecElemClass, it->index));
                 values.push_back(elem);
             }
             break;
           case REG_D:
             if (instRecord.isetstate == ISET_A64) {
-                const ArmISA::VecRegContainer& vc = thread->readVecReg(
-                    RegId(VecRegClass, it->index));
+                ArmISA::VecRegContainer vc;
+                thread->getReg(RegId(VecRegClass, it->index), &vc);
                 auto vv = vc.as<uint64_t>();
                 values.push_back(vv[0]);
             } else {
-                const VecElem w0 = thread->readVecElem(
-                    RegId(VecElemClass,
-                        it->index / NumVecElemPerNeonVecReg,
-                        it->index % NumVecElemPerNeonVecReg));
-                const VecElem w1 = thread->readVecElem(
-                    RegId(VecElemClass,
-                        (it->index + 1) / NumVecElemPerNeonVecReg,
-                        (it->index + 1) % NumVecElemPerNeonVecReg));
+                const VecElem w0 = thread->getReg(
+                    RegId(VecElemClass, it->index));
+                const VecElem w1 = thread->getReg(
+                    RegId(VecElemClass, it->index + 1));
 
                 values.push_back((uint64_t)(w1) << 32 | w0);
             }
             break;
           case REG_P:
             {
-                const ArmISA::VecPredRegContainer& pc =
-                    thread->readVecPredReg(RegId(VecPredRegClass, it->index));
+                ArmISA::VecPredRegContainer pc;
+                thread->getReg(RegId(VecPredRegClass, it->index), &pc);
                 auto pv = pc.as<uint8_t>();
                 uint64_t p = 0;
                 for (int i = maxVectorLength * 8; i > 0; ) {
@@ -804,28 +798,20 @@
             break;
           case REG_Q:
             if (instRecord.isetstate == ISET_A64) {
-                const ArmISA::VecRegContainer& vc = thread->readVecReg(
-                    RegId(VecRegClass, it->index));
+                ArmISA::VecRegContainer vc;
+                thread->getReg(RegId(VecRegClass, it->index), &vc);
                 auto vv = vc.as<uint64_t>();
                 values.push_back(vv[0]);
                 values.push_back(vv[1]);
             } else {
-                const VecElem w0 = thread->readVecElem(
-                    RegId(VecElemClass,
-                        it->index / NumVecElemPerNeonVecReg,
-                        it->index % NumVecElemPerNeonVecReg));
-                const VecElem w1 = thread->readVecElem(
-                    RegId(VecElemClass,
-                        (it->index + 1) / NumVecElemPerNeonVecReg,
-                        (it->index + 1) % NumVecElemPerNeonVecReg));
-                const VecElem w2 = thread->readVecElem(
-                    RegId(VecElemClass,
-                        (it->index + 2) / NumVecElemPerNeonVecReg,
-                        (it->index + 2) % NumVecElemPerNeonVecReg));
-                const VecElem w3 = thread->readVecElem(
-                    RegId(VecElemClass,
-                        (it->index + 3) / NumVecElemPerNeonVecReg,
-                        (it->index + 3) % NumVecElemPerNeonVecReg));
+                const VecElem w0 = thread->getReg(
+                    RegId(VecElemClass, it->index));
+                const VecElem w1 = thread->getReg(
+                    RegId(VecElemClass, it->index + 1));
+                const VecElem w2 = thread->getReg(
+                    RegId(VecElemClass, it->index + 2));
+                const VecElem w3 = thread->getReg(
+                    RegId(VecElemClass, it->index + 3));
 
                 values.push_back((uint64_t)(w1) << 32 | w0);
                 values.push_back((uint64_t)(w3) << 32 | w2);
@@ -834,8 +820,8 @@
           case REG_Z:
             {
                 int8_t i = maxVectorLength;
-                const ArmISA::VecRegContainer& vc = thread->readVecReg(
-                    RegId(VecRegClass, it->index));
+                ArmISA::VecRegContainer vc;
+                thread->getReg(RegId(VecRegClass, it->index), &vc);
                 auto vv = vc.as<uint64_t>();
                 while (i > 0) {
                     values.push_back(vv[--i]);
@@ -846,16 +832,16 @@
             if (it->index == MISCREG_CPSR) {
                 // Read condition codes from aliased integer regs
                 CPSR cpsr = thread->readMiscRegNoEffect(it->index);
-                cpsr.nz = thread->readCCReg(CCREG_NZ);
-                cpsr.c = thread->readCCReg(CCREG_C);
-                cpsr.v = thread->readCCReg(CCREG_V);
-                cpsr.ge = thread->readCCReg(CCREG_GE);
+                cpsr.nz = thread->getReg(cc_reg::Nz);
+                cpsr.c = thread->getReg(cc_reg::C);
+                cpsr.v = thread->getReg(cc_reg::V);
+                cpsr.ge = thread->getReg(cc_reg::Ge);
                 values.push_back(cpsr);
             } else if (it->index == MISCREG_NZCV) {
                 CPSR cpsr = 0;
-                cpsr.nz = thread->readCCReg(CCREG_NZ);
-                cpsr.c = thread->readCCReg(CCREG_C);
-                cpsr.v = thread->readCCReg(CCREG_V);
+                cpsr.nz = thread->getReg(cc_reg::Nz);
+                cpsr.c = thread->getReg(cc_reg::C);
+                cpsr.v = thread->getReg(cc_reg::V);
                 values.push_back(cpsr);
             } else if (it->index == MISCREG_FPCR) {
                 // Read FPSCR and extract FPCR value
@@ -1153,25 +1139,25 @@
             int base_index = atoi(&buf[1]);
             char* pch = strchr(buf, '_');
             if (pch == NULL) {
-                regRecord.index = INTREG_USR(base_index);
+                regRecord.index = int_reg::usr(base_index);
             } else {
                 ++pch;
                 if (strncmp(pch, "usr", 3) == 0)
-                    regRecord.index = INTREG_USR(base_index);
+                    regRecord.index = int_reg::usr(base_index);
                 else if (strncmp(pch, "fiq", 3) == 0)
-                    regRecord.index = INTREG_FIQ(base_index);
+                    regRecord.index = int_reg::fiq(base_index);
                 else if (strncmp(pch, "irq", 3) == 0)
-                    regRecord.index = INTREG_IRQ(base_index);
+                    regRecord.index = int_reg::irq(base_index);
                 else if (strncmp(pch, "svc", 3) == 0)
-                    regRecord.index = INTREG_SVC(base_index);
+                    regRecord.index = int_reg::svc(base_index);
                 else if (strncmp(pch, "mon", 3) == 0)
-                    regRecord.index = INTREG_MON(base_index);
+                    regRecord.index = int_reg::mon(base_index);
                 else if (strncmp(pch, "abt", 3) == 0)
-                    regRecord.index = INTREG_ABT(base_index);
+                    regRecord.index = int_reg::abt(base_index);
                 else if (strncmp(pch, "und", 3) == 0)
-                    regRecord.index = INTREG_UND(base_index);
+                    regRecord.index = int_reg::und(base_index);
                 else if (strncmp(pch, "hyp", 3) == 0)
-                    regRecord.index = INTREG_HYP(base_index);
+                    regRecord.index = int_reg::hyp(base_index);
             }
         } else if (std::tolower(buf[0]) == 'x' && isdigit(buf[1])) {
             // X register (A64)
@@ -1200,7 +1186,7 @@
         } else if (strncmp(buf, "SP_EL", 5) == 0) {
             // A64 stack pointer
             regRecord.type = REG_X;
-            regRecord.index = INTREG_SP0 + atoi(&buf[5]);
+            regRecord.index = int_reg::Sp0 + atoi(&buf[5]);
         } else if (miscRegMap.count(buf)) {
             // Misc. register
             regRecord.type = REG_MISC;
diff --git a/src/arch/arm/tracers/tarmac_record.cc b/src/arch/arm/tracers/tarmac_record.cc
index 8cd1c81..94dbc51 100644
--- a/src/arch/arm/tracers/tarmac_record.cc
+++ b/src/arch/arm/tracers/tarmac_record.cc
@@ -213,10 +213,10 @@
     // the CC flags on top of the value
     if (regRelIdx == MISCREG_CPSR) {
         CPSR cpsr = thread->readMiscRegNoEffect(MISCREG_CPSR);
-        cpsr.nz = thread->readCCReg(CCREG_NZ);
-        cpsr.c = thread->readCCReg(CCREG_C);
-        cpsr.v = thread->readCCReg(CCREG_V);
-        cpsr.ge = thread->readCCReg(CCREG_GE);
+        cpsr.nz = thread->getReg(cc_reg::Nz);
+        cpsr.c = thread->getReg(cc_reg::C);
+        cpsr.v = thread->getReg(cc_reg::V);
+        cpsr.ge = thread->getReg(cc_reg::Ge);
 
         // update the entry value
         values[Lo] = cpsr;
@@ -232,8 +232,8 @@
     auto thread = tarmCtx.thread;
 
     regValid = true;
-    regName = ccRegName[regRelIdx];
-    values[Lo] = thread->readCCReg(regRelIdx);
+    regName = cc_reg::RegName[regRelIdx];
+    values[Lo] = thread->getReg(RegId(CCRegClass, regRelIdx));
 }
 
 void
@@ -246,7 +246,8 @@
 
     regValid = true;
     regName  = "f" + std::to_string(regRelIdx);
-    values[Lo] = bitsToFloat32(thread->readFloatReg(regRelIdx));
+    RegId reg(FloatRegClass, regRelIdx);
+    values[Lo] = bitsToFloat32(thread->getReg(reg));
 }
 
 void
@@ -270,7 +271,7 @@
 
     regValid = true;
     switch (regRelIdx) {
-      case PCReg:
+      case int_reg::Pc:
         regName = "pc";
         break;
       case StackPointerReg:
@@ -286,7 +287,7 @@
         regName  = "r" + std::to_string(regRelIdx);
         break;
     }
-    values[Lo] = thread->readIntReg(regRelIdx);
+    values[Lo] = thread->getReg(RegId(IntRegClass, regRelIdx));
 }
 
 void
diff --git a/src/arch/arm/tracers/tarmac_record_v8.cc b/src/arch/arm/tracers/tarmac_record_v8.cc
index 8dd96d1..3c4a152 100644
--- a/src/arch/arm/tracers/tarmac_record_v8.cc
+++ b/src/arch/arm/tracers/tarmac_record_v8.cc
@@ -97,14 +97,14 @@
 {
     // Do not trace pseudo register accesses: invalid
     // register entry.
-    if (regRelIdx > NUM_ARCH_INTREGS) {
+    if (regRelIdx > int_reg::NumArchRegs) {
         regValid = false;
         return;
     }
 
     TraceRegEntry::updateInt(tarmCtx, regRelIdx);
 
-    if ((regRelIdx != PCReg) || (regRelIdx != StackPointerReg) ||
+    if ((regRelIdx != int_reg::Pc) || (regRelIdx != StackPointerReg) ||
         (regRelIdx != FramePointerReg) || (regRelIdx != ReturnAddressReg)) {
 
         const auto* arm_inst = static_cast<const ArmStaticInst*>(
@@ -138,8 +138,8 @@
 )
 {
     auto thread = tarmCtx.thread;
-    const auto& vec_container = thread->readVecReg(
-        RegId(regClass, regRelIdx));
+    ArmISA::VecRegContainer vec_container;
+    thread->getReg(RegId(regClass, regRelIdx), &vec_container);
     auto vv = vec_container.as<VecElem>();
 
     regWidth = ArmStaticInst::getCurSveVecLenInBits(thread);
@@ -163,8 +163,8 @@
 )
 {
     auto thread = tarmCtx.thread;
-    const auto& pred_container = thread->readVecPredReg(
-        RegId(regClass, regRelIdx));
+    ArmISA::VecPredRegContainer pred_container;
+    thread->getReg(RegId(regClass, regRelIdx), &pred_container);
 
     // Predicate registers are always 1/8 the size of related vector
     // registers. (getCurSveVecLenInBits(thread) / 8)
diff --git a/src/arch/arm/types.hh b/src/arch/arm/types.hh
index a4d1f33..734fe6f 100644
--- a/src/arch/arm/types.hh
+++ b/src/arch/arm/types.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2013, 2017-2018 ARM Limited
+ * Copyright (c) 2010, 2012-2013, 2017-2018, 2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -202,6 +202,13 @@
         Bitfield<11, 8>  ltcoproc;
     EndBitUnion(ExtMachInst)
 
+    BitUnion32(Affinity)
+        Bitfield<31, 24> aff3;
+        Bitfield<23, 16> aff2;
+        Bitfield<15, 8>  aff1;
+        Bitfield<7, 0>   aff0;
+    EndBitUnion(Affinity)
+
     // Shift types for ARM instructions
     enum ArmShiftType
     {
diff --git a/src/arch/arm/utility.cc b/src/arch/arm/utility.cc
index 2a98eea..1c54c88 100644
--- a/src/arch/arm/utility.cc
+++ b/src/arch/arm/utility.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2014, 2016-2020 ARM Limited
+ * Copyright (c) 2009-2014, 2016-2020, 2022 Arm Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -84,15 +84,16 @@
 bool
 isSecureBelowEL3(ThreadContext *tc)
 {
-    SCR scr = tc->readMiscReg(MISCREG_SCR_EL3);
-    return ArmSystem::haveEL(tc, EL3) && scr.ns == 0;
+    return ArmSystem::haveEL(tc, EL3) &&
+        static_cast<SCR>(tc->readMiscRegNoEffect(MISCREG_SCR)).ns == 0;
 }
 
 ExceptionLevel
 debugTargetFrom(ThreadContext *tc, bool secure)
 {
     bool route_to_el2;
-    if (ArmSystem::haveEL(tc, EL2) && (!secure || HaveSecureEL2Ext(tc))) {
+    if (ArmSystem::haveEL(tc, EL2) &&
+        (!secure || HaveExt(tc, ArmExtension::FEAT_SEL2))) {
         if (ELIs32(tc, EL2)) {
             const HCR hcr = tc->readMiscReg(MISCREG_HCR);
             const HDCR hdcr = tc->readMiscRegNoEffect(MISCREG_HDCR);
@@ -199,13 +200,13 @@
 static RegVal
 getAff2(ArmSystem *arm_sys, ThreadContext *tc)
 {
-    return arm_sys->multiThread ? tc->socketId() << 16 : 0;
+    return arm_sys->multiThread ? tc->socketId() : 0;
 }
 
 static RegVal
 getAff1(ArmSystem *arm_sys, ThreadContext *tc)
 {
-    return arm_sys->multiThread ? tc->cpuId() << 8 : tc->socketId() << 8;
+    return arm_sys->multiThread ? tc->cpuId() : tc->socketId();
 }
 
 static RegVal
@@ -214,64 +215,45 @@
     return arm_sys->multiThread ? tc->threadId() : tc->cpuId();
 }
 
-RegVal
+Affinity
 getAffinity(ArmSystem *arm_sys, ThreadContext *tc)
 {
-    return getAff2(arm_sys, tc) | getAff1(arm_sys, tc) | getAff0(arm_sys, tc);
+    Affinity aff = 0;
+    aff.aff0 = getAff0(arm_sys, tc);
+    aff.aff1 = getAff1(arm_sys, tc);
+    aff.aff2 = getAff2(arm_sys, tc);
+    return aff;
 }
 
 bool
-HavePACExt(ThreadContext *tc)
+HaveExt(ThreadContext* tc, ArmExtension ext)
 {
-    AA64ISAR1 id_aa64isar1 = tc->readMiscReg(MISCREG_ID_AA64ISAR1_EL1);
-    return id_aa64isar1.api | id_aa64isar1.apa |
-        id_aa64isar1.gpi | id_aa64isar1.gpa;
-}
-
-bool
-HaveVirtHostExt(ThreadContext *tc)
-{
-    AA64MMFR1 id_aa64mmfr1 = tc->readMiscReg(MISCREG_ID_AA64MMFR1_EL1);
-    return id_aa64mmfr1.vh;
-}
-
-bool
-HaveLVA(ThreadContext *tc)
-{
-    const AA64MMFR2 mm_fr2 = tc->readMiscReg(MISCREG_ID_AA64MMFR2_EL1);
-    return (bool)mm_fr2.varange;
+    auto *isa = static_cast<ArmISA::ISA *>(tc->getIsaPtr());
+    return isa->getRelease()->has(ext);
 }
 
 ExceptionLevel
 s1TranslationRegime(ThreadContext* tc, ExceptionLevel el)
 {
-
-    SCR scr = tc->readMiscReg(MISCREG_SCR);
     if (el != EL0)
         return el;
-    else if (ArmSystem::haveEL(tc, EL3) && ELIs32(tc, EL3) && scr.ns == 0)
+    else if (ArmSystem::haveEL(tc, EL3) && ELIs32(tc, EL3) &&
+             static_cast<SCR>(tc->readMiscRegNoEffect(MISCREG_SCR)).ns == 0)
         return EL3;
-    else if (HaveVirtHostExt(tc) && ELIsInHost(tc, el))
+    else if (HaveExt(tc, ArmExtension::FEAT_VHE) && ELIsInHost(tc, el))
         return EL2;
     else
         return EL1;
 }
 
 bool
-HaveSecureEL2Ext(ThreadContext *tc)
-{
-    AA64PFR0 id_aa64pfr0 = tc->readMiscReg(MISCREG_ID_AA64PFR0_EL1);
-    return id_aa64pfr0.sel2;
-}
-
-bool
 IsSecureEL2Enabled(ThreadContext *tc)
 {
-    SCR scr = tc->readMiscReg(MISCREG_SCR_EL3);
-    if (ArmSystem::haveEL(tc, EL2) && HaveSecureEL2Ext(tc) &&
+    if (ArmSystem::haveEL(tc, EL2) && HaveExt(tc, ArmExtension::FEAT_SEL2) &&
         !ELIs32(tc, EL2)) {
         if (ArmSystem::haveEL(tc, EL3))
-            return !ELIs32(tc, EL3) && scr.eel2;
+            return !ELIs32(tc, EL3) && static_cast<SCR>(
+                tc->readMiscRegNoEffect(MISCREG_SCR_EL3)).eel2;
         else
             return isSecure(tc);
     }
@@ -281,9 +263,10 @@
 bool
 EL2Enabled(ThreadContext *tc)
 {
-    SCR scr = tc->readMiscReg(MISCREG_SCR_EL3);
     return ArmSystem::haveEL(tc, EL2) &&
-           (!ArmSystem::haveEL(tc, EL3) || scr.ns || IsSecureEL2Enabled(tc));
+           (!ArmSystem::haveEL(tc, EL3) || static_cast<SCR>(
+                tc->readMiscRegNoEffect(MISCREG_SCR_EL3)).ns ||
+            IsSecureEL2Enabled(tc));
 }
 
 bool
@@ -306,7 +289,8 @@
     const HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
     return (ArmSystem::haveEL(tc, EL2) &&
             (IsSecureEL2Enabled(tc) || !isSecureBelowEL3(tc)) &&
-            HaveVirtHostExt(tc) && !ELIs32(tc, EL2) && hcr.e2h == 1 &&
+            HaveExt(tc, ArmExtension::FEAT_VHE) &&
+            !ELIs32(tc, EL2) && hcr.e2h == 1 &&
             (el == EL2 || (el == EL0 && hcr.tge == 1)));
 }
 
@@ -357,14 +341,15 @@
     } else {
         SCR scr = tc->readMiscReg(MISCREG_SCR_EL3);
         bool aarch32_below_el3 = have_el3 && scr.rw == 0 &&
-                            (!secure || !HaveSecureEL2Ext(tc) || !scr.eel2);
+            (!secure || !HaveExt(tc, ArmExtension::FEAT_SEL2) || !scr.eel2);
 
         HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
-        bool sec_el2 = HaveSecureEL2Ext(tc) && scr.eel2;
+        bool sec_el2 = HaveExt(tc, ArmExtension::FEAT_SEL2) && scr.eel2;
         bool aarch32_at_el1 = (aarch32_below_el3 ||
                                (have_el2 && (sec_el2 || !secure) &&
-                                hcr.rw == 0 && !(hcr.e2h && hcr.tge &&
-                                                 HaveVirtHostExt(tc))));
+                                hcr.rw == 0 &&
+                                !(hcr.e2h && hcr.tge &&
+                                 HaveExt(tc, ArmExtension::FEAT_VHE))));
 
         // Only know if EL0 using AArch32 from PSTATE
         if (el == EL0 && !aarch32_at_el1) {
@@ -441,7 +426,7 @@
           case EL2:
           {
             TCR tcr = tc->readMiscReg(MISCREG_TCR_EL2);
-            if (HaveVirtHostExt(tc) && ELIsInHost(tc, el)) {
+            if (HaveExt(tc, ArmExtension::FEAT_VHE) && ELIsInHost(tc, el)) {
                 tbi = selbit? tcr.tbi1 : tcr.tbi0;
                 tbid = selbit? tcr.tbid1 : tcr.tbid0;
             } else {
@@ -465,6 +450,22 @@
     int res = (tbi && (!tbid || !is_instr))? 55: 63;
     return res;
 }
+
+Addr
+maskTaggedAddr(Addr addr, ThreadContext *tc, ExceptionLevel el,
+               int topbit)
+{
+    if (topbit == 63) {
+        return addr;
+    } else if (bits(addr,55) && (el <= EL1 || ELIsInHost(tc, el))) {
+        uint64_t mask = ((uint64_t)0x1 << topbit) -1;
+        addr = addr | ~mask;
+    } else {
+        addr = bits(addr, topbit, 0);
+    }
+    return addr;  // Nothing to do if this is not a tagged address
+}
+
 Addr
 purifyTaggedAddr(Addr addr, ThreadContext *tc, ExceptionLevel el,
                  TCR tcr, bool is_instr)
@@ -472,15 +473,7 @@
     bool selbit = bits(addr, 55);
     int topbit = computeAddrTop(tc, selbit, is_instr, tcr, el);
 
-    if (topbit == 63) {
-        return addr;
-    } else if (selbit && (el == EL1 || el == EL0 || ELIsInHost(tc, el))) {
-        uint64_t mask = ((uint64_t)0x1 << topbit) -1;
-        addr = addr | ~mask;
-    } else {
-        addr = bits(addr, topbit, 0);
-    }
-    return addr;  // Nothing to do if this is not a tagged address
+    return maskTaggedAddr(addr, tc, el, topbit);
 }
 
 Addr
@@ -520,7 +513,7 @@
 {
     bool is_read;
     uint32_t crm;
-    IntRegIndex rt;
+    RegIndex rt;
     uint32_t crn;
     uint32_t opc1;
     uint32_t opc2;
@@ -674,7 +667,7 @@
 {
     bool is_read;
     uint32_t crm;
-    IntRegIndex rt;
+    RegIndex rt;
     uint32_t crn;
     uint32_t opc1;
     uint32_t opc2;
@@ -739,7 +732,7 @@
                     uint32_t iss, ExceptionClass *ec)
 {
     uint32_t crm;
-    IntRegIndex rt;
+    RegIndex rt;
     uint32_t crn;
     uint32_t opc1;
     uint32_t opc2;
@@ -1187,18 +1180,18 @@
 
         if (sysM4To3 == 0) {
             mode = MODE_USER;
-            regIdx = intRegInMode(mode, bits(sysM, 2, 0) + 8);
+            regIdx = int_reg::regInMode(mode, bits(sysM, 2, 0) + 8);
         } else if (sysM4To3 == 1) {
             mode = MODE_FIQ;
-            regIdx = intRegInMode(mode, bits(sysM, 2, 0) + 8);
+            regIdx = int_reg::regInMode(mode, bits(sysM, 2, 0) + 8);
         } else if (sysM4To3 == 3) {
             if (bits(sysM, 1) == 0) {
                 mode = MODE_MON;
-                regIdx = intRegInMode(mode, 14 - bits(sysM, 0));
+                regIdx = int_reg::regInMode(mode, 14 - bits(sysM, 0));
             } else {
                 mode = MODE_HYP;
                 if (bits(sysM, 0) == 1) {
-                    regIdx = intRegInMode(mode, 13); // R13 in HYP
+                    regIdx = int_reg::regInMode(mode, 13); // R13 in HYP
                 } else {
                     isIntReg = false;
                     regIdx = MISCREG_ELR_HYP;
@@ -1213,9 +1206,9 @@
                                     ((sysM2 && !sysM1) << 2) |
                                     ((sysM2 && sysM1) << 3) |
                                     (1 << 4));
-            regIdx = intRegInMode(mode, 14 - bits(sysM, 0));
+            regIdx = int_reg::regInMode(mode, 14 - bits(sysM, 0));
             // Don't flatten the register here. This is going to go through
-            // setIntReg() which will do the flattening
+            // setReg() which will do the flattening
             ok &= mode != cpsr.mode;
         }
     }
@@ -1265,7 +1258,8 @@
     bool unpriv_el1 = currEL(tc) == EL1 &&
         !(ArmSystem::haveEL(tc, EL2) &&
             have_nv_ext && hcr.nv == 1 && hcr.nv1 == 1);
-    bool unpriv_el2 = ArmSystem::haveEL(tc, EL2) && HaveVirtHostExt(tc) &&
+    bool unpriv_el2 = ArmSystem::haveEL(tc, EL2) &&
+                      HaveExt(tc, ArmExtension::FEAT_VHE) &&
                       currEL(tc) == EL2 && hcr.e2h == 1 && hcr.tge == 1;
 
     return (unpriv_el1 || unpriv_el2) && !cpsr.uao;
@@ -1344,12 +1338,14 @@
 void
 syncVecRegsToElems(ThreadContext *tc)
 {
+    int ei = 0;
     for (int ri = 0; ri < NumVecRegs; ri++) {
         RegId reg_id(VecRegClass, ri);
-        const VecRegContainer &reg = tc->readVecReg(reg_id);
-        for (int ei = 0; ei < NumVecElemPerVecReg; ei++) {
-            RegId elem_id(VecElemClass, ri, ei);
-            tc->setVecElem(elem_id, reg.as<VecElem>()[ei]);
+        VecRegContainer reg;
+        tc->getReg(reg_id, &reg);
+        for (int j = 0; j < NumVecElemPerVecReg; j++, ei++) {
+            RegId elem_id(VecElemClass, ei);
+            tc->setReg(elem_id, reg.as<VecElem>()[j]);
         }
     }
 }
@@ -1357,14 +1353,15 @@
 void
 syncVecElemsToRegs(ThreadContext *tc)
 {
+    int ei = 0;
     for (int ri = 0; ri < NumVecRegs; ri++) {
         VecRegContainer reg;
-        for (int ei = 0; ei < NumVecElemPerVecReg; ei++) {
-            RegId elem_id(VecElemClass, ri, ei);
-            reg.as<VecElem>()[ei] = tc->readVecElem(elem_id);
+        for (int j = 0; j < NumVecElemPerVecReg; j++, ei++) {
+            RegId elem_id(VecElemClass, ei);
+            reg.as<VecElem>()[j] = tc->getReg(elem_id);
         }
         RegId reg_id(VecRegClass, ri);
-        tc->setVecReg(reg_id, reg);
+        tc->setReg(reg_id, &reg);
     }
 }
 
diff --git a/src/arch/arm/utility.hh b/src/arch/arm/utility.hh
index 0e5f3bb..2064fef 100644
--- a/src/arch/arm/utility.hh
+++ b/src/arch/arm/utility.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2013, 2016-2020 ARM Limited
+ * Copyright (c) 2010, 2012-2013, 2016-2020, 2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -51,6 +51,7 @@
 #include "base/types.hh"
 #include "cpu/static_inst.hh"
 #include "cpu/thread_context.hh"
+#include "enums/ArmExtension.hh"
 
 namespace gem5
 {
@@ -120,10 +121,12 @@
     return opModeToEL((OperatingMode) (uint8_t)cpsr.mode);
 }
 
-bool HavePACExt(ThreadContext *tc);
-bool HaveVirtHostExt(ThreadContext *tc);
-bool HaveLVA(ThreadContext *tc);
-bool HaveSecureEL2Ext(ThreadContext *tc);
+/**
+ * Returns true if the provided ThreadContext supports the ArmExtension
+ * passed as a second argument.
+ */
+bool HaveExt(ThreadContext *tc, ArmExtension ext);
+
 bool IsSecureEL2Enabled(ThreadContext *tc);
 bool EL2Enabled(ThreadContext *tc);
 
@@ -207,6 +210,8 @@
                       TCR tcr, bool isInstr);
 Addr purifyTaggedAddr(Addr addr, ThreadContext *tc, ExceptionLevel el,
                       bool isInstr);
+Addr maskTaggedAddr(Addr addr, ThreadContext *tc, ExceptionLevel el,
+                    int topbit);
 int computeAddrTop(ThreadContext *tc, bool selbit, bool isInstr,
                    TCR tcr, ExceptionLevel el);
 
@@ -223,10 +228,10 @@
 RegVal getMPIDR(ArmSystem *arm_sys, ThreadContext *tc);
 
 /** Retrieves MPIDR_EL1.{Aff2,Aff1,Aff0} affinity numbers */
-RegVal getAffinity(ArmSystem *arm_sys, ThreadContext *tc);
+Affinity getAffinity(ArmSystem *arm_sys, ThreadContext *tc);
 
 static inline uint32_t
-mcrMrcIssBuild(bool isRead, uint32_t crm, IntRegIndex rt, uint32_t crn,
+mcrMrcIssBuild(bool isRead, uint32_t crm, RegIndex rt, uint32_t crn,
                uint32_t opc1, uint32_t opc2)
 {
     return (isRead << 0) |
@@ -238,19 +243,19 @@
 }
 
 static inline void
-mcrMrcIssExtract(uint32_t iss, bool &isRead, uint32_t &crm, IntRegIndex &rt,
+mcrMrcIssExtract(uint32_t iss, bool &isRead, uint32_t &crm, RegIndex &rt,
                  uint32_t &crn, uint32_t &opc1, uint32_t &opc2)
 {
     isRead = (iss >> 0) & 0x1;
     crm = (iss >> 1) & 0xF;
-    rt = (IntRegIndex)((iss >> 5) & 0xF);
+    rt = (RegIndex)((iss >> 5) & 0xF);
     crn = (iss >> 10) & 0xF;
     opc1 = (iss >> 14) & 0x7;
     opc2 = (iss >> 17) & 0x7;
 }
 
 static inline uint32_t
-mcrrMrrcIssBuild(bool isRead, uint32_t crm, IntRegIndex rt, IntRegIndex rt2,
+mcrrMrrcIssBuild(bool isRead, uint32_t crm, RegIndex rt, RegIndex rt2,
                  uint32_t opc1)
 {
     return (isRead << 0) |
@@ -262,7 +267,7 @@
 
 static inline uint32_t
 msrMrs64IssBuild(bool isRead, uint32_t op0, uint32_t op1, uint32_t crn,
-                 uint32_t crm, uint32_t op2, IntRegIndex rt)
+                 uint32_t crm, uint32_t op2, RegIndex rt)
 {
     return isRead |
         (crm << 1) |
@@ -348,7 +353,7 @@
 
     validReg = decodeMrsMsrBankedReg(
             sysM, r, isIntReg, regIdx, 0, 0, 0, false);
-    return (validReg && isIntReg) ? regIdx : INTREG_ZERO;
+    return (validReg && isIntReg) ? regIdx : int_reg::Zero;
 }
 
 /**
diff --git a/src/arch/generic/vec_pred_reg.hh b/src/arch/generic/vec_pred_reg.hh
index ad6bcce..a9b264e 100644
--- a/src/arch/generic/vec_pred_reg.hh
+++ b/src/arch/generic/vec_pred_reg.hh
@@ -44,6 +44,7 @@
 #include <vector>
 
 #include "base/cprintf.hh"
+#include "base/types.hh"
 #include "sim/serialize_handlers.hh"
 
 namespace gem5
@@ -394,7 +395,29 @@
 /// Dummy type aliases and constants for architectures that do not implement
 /// vector predicate registers.
 /// @{
-using DummyVecPredRegContainer = VecPredRegContainer<8, false>;
+struct DummyVecPredRegContainer
+{
+    RegVal filler = 0;
+    bool operator == (const DummyVecPredRegContainer &d) const { return true; }
+    bool operator != (const DummyVecPredRegContainer &d) const { return true; }
+    template <typename VecElem>
+    VecElem *as() { return nullptr; }
+};
+template <>
+struct ParseParam<DummyVecPredRegContainer>
+{
+    static bool
+    parse(const std::string &s, DummyVecPredRegContainer &value)
+    {
+        return false;
+    }
+};
+static_assert(sizeof(DummyVecPredRegContainer) == sizeof(RegVal));
+static inline std::ostream &
+operator<<(std::ostream &os, const DummyVecPredRegContainer &d)
+{
+    return os;
+}
 /// @}
 
 } // namespace gem5
diff --git a/src/arch/generic/vec_reg.hh b/src/arch/generic/vec_reg.hh
index 34ba554..fecd5c6 100644
--- a/src/arch/generic/vec_reg.hh
+++ b/src/arch/generic/vec_reg.hh
@@ -68,16 +68,17 @@
  * ...
  * // Usage example, for a macro op:
  * VecFloat8Add(ExecContext* xd) {
- *    // Request source vector register to the execution context (const as it
- *    // is read only).
- *    const Vec512& vsrc1raw = xc->readVecRegOperand(this, 0);
+ *    // Request source vector register to the execution context.
+ *    Vec512 vsrc1raw;
+ *    xc->getRegOperand(this, 0, &vsrc1raw);
  *    // View it as a vector of floats (we could just specify the first
  *    // template parametre, the second has a default value that works, and the
  *    // last one is derived by the constness of vsrc1raw).
  *    VecRegT<float, 8, true>& vsrc1 = vsrc1raw->as<float, 8>();
  *
  *    // Second source and view
- *    const Vec512& vsrc2raw = xc->readVecRegOperand(this, 1);
+ *    Vec512 vsrc2raw;
+ *    xc->getRegOperand(this, 1, &vsrc2raw);
  *    VecRegT<float, 8, true>& vsrc2 = vsrc2raw->as<float, 8>();
  *
  *    // Destination and view
@@ -103,6 +104,7 @@
 
 #include "base/cprintf.hh"
 #include "base/logging.hh"
+#include "base/types.hh"
 #include "sim/serialize_handlers.hh"
 
 namespace gem5
@@ -263,10 +265,29 @@
  * vector registers.
  */
 /** @{ */
-using DummyVecElem = uint32_t;
-constexpr unsigned DummyNumVecElemPerVecReg = 2;
-using DummyVecRegContainer =
-    VecRegContainer<DummyNumVecElemPerVecReg * sizeof(DummyVecElem)>;
+struct DummyVecRegContainer
+{
+    RegVal filler = 0;
+    bool operator == (const DummyVecRegContainer &d) const { return true; }
+    bool operator != (const DummyVecRegContainer &d) const { return true; }
+    template <typename VecElem>
+    VecElem *as() { return nullptr; }
+};
+template <>
+struct ParseParam<DummyVecRegContainer>
+{
+    static bool
+    parse(const std::string &s, DummyVecRegContainer &value)
+    {
+        return false;
+    }
+};
+static_assert(sizeof(DummyVecRegContainer) == sizeof(RegVal));
+static inline std::ostream &
+operator<<(std::ostream &os, const DummyVecRegContainer &d)
+{
+    return os;
+}
 /** @} */
 
 } // namespace gem5
diff --git a/src/arch/isa_parser/isa_parser.py b/src/arch/isa_parser/isa_parser.py
index 901662f..d33bcc4 100755
--- a/src/arch/isa_parser/isa_parser.py
+++ b/src/arch/isa_parser/isa_parser.py
@@ -127,17 +127,6 @@
             if operands.readPC or operands.setPC:
                 myDict['op_decl'] += pcstate_decl
 
-            # In case there are predicated register reads and write, declare
-            # the variables for register indicies. It is being assumed that
-            # all the operands in the OperandList are also in the
-            # SubOperandList and in the same order. Otherwise, it is
-            # expected that predication would not be used for the operands.
-            if operands.predRead:
-                myDict['op_decl'] += 'uint8_t _sourceIndex = 0;\n'
-            if operands.predWrite:
-                myDict['op_decl'] += \
-                    '[[maybe_unused]] uint8_t _destIndex = 0;\n'
-
             is_src = lambda op: op.is_src
             is_dest = lambda op: op.is_dest
 
@@ -400,14 +389,6 @@
         # The header of the constructor declares the variables to be used
         # in the body of the constructor.
         header = ''
-        header += '\n\t_numSrcRegs = 0;'
-        header += '\n\t_numDestRegs = 0;'
-        header += '\n\t_numFPDestRegs = 0;'
-        header += '\n\t_numVecDestRegs = 0;'
-        header += '\n\t_numVecElemDestRegs = 0;'
-        header += '\n\t_numVecPredDestRegs = 0;'
-        header += '\n\t_numIntDestRegs = 0;'
-        header += '\n\t_numCCDestRegs = 0;'
 
         self.constructor = header + \
                            self.operands.concatAttrStrings('constructor')
@@ -525,6 +506,18 @@
 
         symbols = ('makeList', 're')
         self.exportContext = dict([(s, eval(s)) for s in symbols])
+        self.exportContext.update({
+            'overrideInOperand': overrideInOperand,
+            'IntRegOp': IntRegOperandDesc,
+            'FloatRegOp': FloatRegOperandDesc,
+            'CCRegOp': CCRegOperandDesc,
+            'VecElemOp': VecElemOperandDesc,
+            'VecRegOp': VecRegOperandDesc,
+            'VecPredRegOp': VecPredRegOperandDesc,
+            'ControlRegOp': ControlRegOperandDesc,
+            'MemOp': MemOperandDesc,
+            'PCStateOp': PCStateOperandDesc,
+        })
 
         self.maxMiscDestRegs = 0
 
@@ -1453,75 +1446,19 @@
 
     def buildOperandNameMap(self, user_dict, lineno):
         operand_name = {}
-        for op_name, val in user_dict.items():
+        for op_name, op_desc in user_dict.items():
+            assert(isinstance(op_desc, OperandDesc))
 
-            # Check if extra attributes have been specified.
-            if len(val) > 9:
-                error(lineno, 'error: too many attributes for operand "%s"' %
-                      base_cls_name)
+            base_cls = op_desc.attrs['base_cls']
 
-            # Pad val with None in case optional args are missing
-            val += (None, None, None, None)
-            base_cls_name, dflt_ext, reg_spec, flags, sort_pri, \
-            read_code, write_code, read_predicate, write_predicate = val[:9]
+            op_desc.setName(op_name)
 
-            # Canonical flag structure is a triple of lists, where each list
-            # indicates the set of flags implied by this operand always, when
-            # used as a source, and when used as a dest, respectively.
-            # For simplicity this can be initialized using a variety of fairly
-            # obvious shortcuts; we convert these to canonical form here.
-            if not flags:
-                # no flags specified (e.g., 'None')
-                flags = ( [], [], [] )
-            elif isinstance(flags, str):
-                # a single flag: assumed to be unconditional
-                flags = ( [ flags ], [], [] )
-            elif isinstance(flags, list):
-                # a list of flags: also assumed to be unconditional
-                flags = ( flags, [], [] )
-            elif isinstance(flags, tuple):
-                # it's a tuple: it should be a triple,
-                # but each item could be a single string or a list
-                (uncond_flags, src_flags, dest_flags) = flags
-                flags = (makeList(uncond_flags),
-                         makeList(src_flags), makeList(dest_flags))
-
-            # Accumulate attributes of new operand class in tmp_dict
-            tmp_dict = {}
-            attrList = ['reg_spec', 'flags', 'sort_pri',
-                        'read_code', 'write_code',
-                        'read_predicate', 'write_predicate']
-            if dflt_ext:
-                dflt_ctype = self.operandTypeMap[dflt_ext]
-                attrList.extend(['dflt_ctype', 'dflt_ext'])
-            # reg_spec is either just a string or a dictionary
-            # (for elems of vector)
-            if isinstance(reg_spec, tuple):
-                (reg_spec, elem_spec) = reg_spec
-                if isinstance(elem_spec, str):
-                    attrList.append('elem_spec')
-                else:
-                    assert(isinstance(elem_spec, dict))
-                    elems = elem_spec
-                    attrList.append('elems')
-            for attr in attrList:
-                tmp_dict[attr] = eval(attr)
-            tmp_dict['base_name'] = op_name
-
-            # New class name will be e.g. "IntReg_Ra"
-            cls_name = base_cls_name + '_' + op_name
-            # Evaluate string arg to get class object.  Note that the
-            # actual base class for "IntReg" is "IntRegOperand", i.e. we
-            # have to append "Operand".
-            try:
-                base_cls = eval(base_cls_name + 'Operand')
-            except NameError:
-                error(lineno,
-                      'error: unknown operand base class "%s"' % base_cls_name)
+            # New class name will be e.g. "IntRegOperand_Ra"
+            cls_name = base_cls.__name__ + '_' + op_name
             # The following statement creates a new class called
             # <cls_name> as a subclass of <base_cls> with the attributes
-            # in tmp_dict, just as if we evaluated a class declaration.
-            operand_name[op_name] = type(cls_name, (base_cls,), tmp_dict)
+            # in op_desc.attrs, just as if we evaluated a class declaration.
+            operand_name[op_name] = type(cls_name, (base_cls,), op_desc.attrs)
 
         self.operandNameMap.update(operand_name)
 
@@ -1540,7 +1477,7 @@
         extensions = self.operandTypeMap.keys()
 
         operandsREString = r'''
-        (?<!\w)      # neg. lookbehind assertion: prevent partial matches
+        (?<!\w|:)     # neg. lookbehind assertion: prevent partial matches
         ((%s)(?:_(%s))?)   # match: operand with optional '_' then suffix
         (?!\w)       # neg. lookahead assertion: prevent partial matches
         ''' % ('|'.join(operands), '|'.join(extensions))
diff --git a/src/arch/isa_parser/operand_list.py b/src/arch/isa_parser/operand_list.py
index 3da7386..3438777 100755
--- a/src/arch/isa_parser/operand_list.py
+++ b/src/arch/isa_parser/operand_list.py
@@ -116,27 +116,16 @@
 
         self.numSrcRegs = len(srcs)
         self.numDestRegs = len(dests)
-        self.numFPDestRegs = sum(r.isFloatReg() for r in dests)
-        self.numIntDestRegs = sum(r.isIntReg() for r in dests)
-        self.numVecDestRegs = sum(r.isVecReg() for r in dests)
-        self.numVecPredDestRegs = sum(r.isVecPredReg() for r in dests)
-        self.numCCDestRegs = sum(r.isCCReg() for r in dests)
-        self.numMiscDestRegs = sum(r.isControlReg() for r in dests)
 
         if len(mem) > 1:
             error("Code block has more than one memory operand")
 
         self.memOperand = mem[0] if mem else None
 
-        # Flags to keep track if one or more operands are to be read/written
-        # conditionally.
-        self.predRead = any(i.hasReadPred() for i in self.items)
-        self.predWrite = any(i.hasWritePred() for i in self.items)
-
         # now make a final pass to finalize op_desc fields that may depend
         # on the register enumeration
         for op_desc in self.items:
-            op_desc.finalize(self.predRead, self.predWrite)
+            op_desc.finalize()
 
     def __len__(self):
         return len(self.items)
@@ -241,8 +230,3 @@
         self.pcPart = None
         if part: self.pcPart = True
         if whole: self.pcPart = False
-
-        # Flags to keep track if one or more operands are to be read/written
-        # conditionally.
-        self.predRead = any(i.hasReadPred() for i in self.items)
-        self.predWrite = any(i.hasWritePred() for i in self.items)
diff --git a/src/arch/isa_parser/operand_types.py b/src/arch/isa_parser/operand_types.py
index a029795..b699025 100755
--- a/src/arch/isa_parser/operand_types.py
+++ b/src/arch/isa_parser/operand_types.py
@@ -37,6 +37,72 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+def overrideInOperand(func):
+    func.override_in_operand = True
+    return func
+
+overrideInOperand.overrides = dict()
+
+class OperandDesc(object):
+    def __init__(self, base_cls, dflt_ext, reg_spec, flags=None,
+            sort_pri=None):
+
+        from .isa_parser import makeList
+
+        # Canonical flag structure is a triple of lists, where each list
+        # indicates the set of flags implied by this operand always, when
+        # used as a source, and when used as a dest, respectively.
+        # For simplicity this can be initialized using a variety of fairly
+        # obvious shortcuts; we convert these to canonical form here.
+        if not flags:
+            # no flags specified (e.g., 'None')
+            flags = ( [], [], [] )
+        elif isinstance(flags, str):
+            # a single flag: assumed to be unconditional
+            flags = ( [ flags ], [], [] )
+        elif isinstance(flags, list):
+            # a list of flags: also assumed to be unconditional
+            flags = ( flags, [], [] )
+        elif isinstance(flags, tuple):
+            # it's a tuple: it should be a triple,
+            # but each item could be a single string or a list
+            (uncond_flags, src_flags, dest_flags) = flags
+            flags = (makeList(uncond_flags),
+                     makeList(src_flags), makeList(dest_flags))
+
+        attrs = {}
+        # reg_spec is either just a string or a dictionary
+        # (for elems of vector)
+        if isinstance(reg_spec, tuple):
+            (reg_spec, elem_spec) = reg_spec
+            if isinstance(elem_spec, str):
+                attrs['elem_spec'] = elem_spec
+            else:
+                assert(isinstance(elem_spec, dict))
+                attrs['elems'] = elem_spec
+
+        for key in dir(self):
+            val = getattr(self, key)
+            # If this is a method, extract the function that implements it.
+            if hasattr(val, '__func__'):
+                val = val.__func__
+            # If this should override something in the operand
+            if getattr(val, 'override_in_operand', False):
+                attrs[key] = val
+
+        attrs.update({
+            'base_cls': base_cls,
+            'dflt_ext': dflt_ext,
+            'reg_spec': reg_spec,
+            'flags': flags,
+            'sort_pri': sort_pri,
+        })
+        self.attrs = attrs
+
+    def setName(self, name):
+        self.attrs['base_name'] = name
+
+
 class Operand(object):
     '''Base class for operand descriptors.  An instance of this class
     (or actually a class derived from this one) represents a specific
@@ -44,38 +110,20 @@
     derived classes encapsulates the traits of a particular operand
     type (e.g., "32-bit integer register").'''
 
-    src_reg_constructor = '\n\tsetSrcRegIdx(_numSrcRegs++, RegId(%s, %s));'
-    dst_reg_constructor = '\n\tsetDestRegIdx(_numDestRegs++, RegId(%s, %s));'
+    src_reg_constructor = '\n\tsetSrcRegIdx(_numSrcRegs++, %s);'
+    dst_reg_constructor = '\n\tsetDestRegIdx(_numDestRegs++, %s);'
 
-    def buildReadCode(self, predRead, func=None):
-        subst_dict = {"name": self.base_name,
-                      "func": func,
-                      "reg_idx": self.reg_spec,
-                      "ctype": self.ctype}
-        if hasattr(self, 'src_reg_idx'):
-            subst_dict['op_idx'] = \
-                    '_sourceIndex++' if predRead else str(self.src_reg_idx)
-        code = self.read_code % subst_dict
-        return '%s = %s;\n' % (self.base_name, code)
+    def regId(self):
+        return f'RegId({self.reg_class}, {self.reg_spec})'
 
-    def buildWriteCode(self, predWrite, func=None):
-        subst_dict = {"name": self.base_name,
-                      "func": func,
-                      "reg_idx": self.reg_spec,
-                      "ctype": self.ctype,
-                      "final_val": self.base_name}
-        if hasattr(self, 'dest_reg_idx'):
-            subst_dict['op_idx'] = \
-                    '_destIndex++' if predWrite else str(self.dest_reg_idx)
-        code = self.write_code % subst_dict
-        return '''
-        {
-            %s final_val = %s;
-            %s;
-            if (traceData) { traceData->setData(final_val); }
-        }''' % (self.dflt_ctype, self.base_name, code)
+    def srcRegId(self):
+        return self.regId()
+
+    def destRegId(self):
+        return self.regId()
 
     def __init__(self, parser, full_name, ext, is_src, is_dest):
+        self.parser = parser
         self.full_name = full_name
         self.ext = ext
         self.is_src = is_src
@@ -93,23 +141,21 @@
     # Finalize additional fields (primarily code fields).  This step
     # is done separately since some of these fields may depend on the
     # register index enumeration that hasn't been performed yet at the
-    # time of __init__(). The register index enumeration is affected
-    # by predicated register reads/writes. Hence, we forward the flags
-    # that indicate whether or not predication is in use.
-    def finalize(self, predRead, predWrite):
+    # time of __init__().
+    def finalize(self):
         self.flags = self.getFlags()
-        self.constructor = self.makeConstructor(predRead, predWrite)
+        self.constructor = self.makeConstructor()
         self.op_decl = self.makeDecl()
 
         if self.is_src:
-            self.op_rd = self.makeRead(predRead)
+            self.op_rd = self.makeRead()
             self.op_src_decl = self.makeDecl()
         else:
             self.op_rd = ''
             self.op_src_decl = ''
 
         if self.is_dest:
-            self.op_wb = self.makeWrite(predWrite)
+            self.op_wb = self.makeWrite()
             self.op_dest_decl = self.makeDecl()
         else:
             self.op_wb = ''
@@ -121,39 +167,12 @@
     def isReg(self):
         return 0
 
-    def isFloatReg(self):
-        return 0
-
-    def isIntReg(self):
-        return 0
-
-    def isCCReg(self):
-        return 0
-
-    def isControlReg(self):
-        return 0
-
-    def isVecReg(self):
-        return 0
-
-    def isVecElem(self):
-        return 0
-
-    def isVecPredReg(self):
-        return 0
-
     def isPCState(self):
         return 0
 
     def isPCPart(self):
         return self.isPCState() and self.reg_spec
 
-    def hasReadPred(self):
-        return self.read_predicate != None
-
-    def hasWritePred(self):
-        return self.write_predicate != None
-
     def getFlags(self):
         # note the empty slice '[:]' gives us a copy of self.flags[0]
         # instead of a reference to it
@@ -169,155 +188,78 @@
         # to avoid 'uninitialized variable' errors from the compiler.
         return self.ctype + ' ' + self.base_name + ' = 0;\n';
 
-
-class IntRegOperand(Operand):
-    reg_class = 'IntRegClass'
-
+class RegOperand(Operand):
     def isReg(self):
         return 1
 
-    def isIntReg(self):
-        return 1
-
-    def makeConstructor(self, predRead, predWrite):
+    def makeConstructor(self):
         c_src = ''
         c_dest = ''
 
         if self.is_src:
-            c_src = self.src_reg_constructor % (self.reg_class, self.reg_spec)
-            if self.hasReadPred():
-                c_src = '\n\tif (%s) {%s\n\t}' % \
-                        (self.read_predicate, c_src)
+            c_src = self.src_reg_constructor % self.srcRegId()
 
         if self.is_dest:
-            c_dest = self.dst_reg_constructor % (self.reg_class, self.reg_spec)
-            c_dest += '\n\t_numIntDestRegs++;'
-            if self.hasWritePred():
-                c_dest = '\n\tif (%s) {%s\n\t}' % \
-                         (self.write_predicate, c_dest)
+            c_dest = self.dst_reg_constructor % self.destRegId()
+            c_dest += f'\n\t_numTypedDestRegs[{self.reg_class}]++;'
 
         return c_src + c_dest
 
-    def makeRead(self, predRead):
-        if (self.ctype == 'float' or self.ctype == 'double'):
-            error('Attempt to read integer register as FP')
-        if self.read_code != None:
-            return self.buildReadCode(predRead, 'readIntRegOperand')
+class RegValOperand(RegOperand):
+    def makeRead(self):
+        reg_val = f'xc->getRegOperand(this, {self.src_reg_idx})'
 
-        int_reg_val = ''
-        if predRead:
-            int_reg_val = 'xc->readIntRegOperand(this, _sourceIndex++)'
-            if self.hasReadPred():
-                int_reg_val = '(%s) ? %s : 0' % \
-                              (self.read_predicate, int_reg_val)
-        else:
-            int_reg_val = 'xc->readIntRegOperand(this, %d)' % self.src_reg_idx
-
-        return '%s = %s;\n' % (self.base_name, int_reg_val)
-
-    def makeWrite(self, predWrite):
-        if (self.ctype == 'float' or self.ctype == 'double'):
-            error('Attempt to write integer register as FP')
-        if self.write_code != None:
-            return self.buildWriteCode(predWrite, 'setIntRegOperand')
-
-        if predWrite:
-            wp = 'true'
-            if self.hasWritePred():
-                wp = self.write_predicate
-
-            wcond = 'if (%s)' % (wp)
-            windex = '_destIndex++'
-        else:
-            wcond = ''
-            windex = '%d' % self.dest_reg_idx
-
-        wb = '''
-        %s
-        {
-            %s final_val = %s;
-            xc->setIntRegOperand(this, %s, final_val);\n
-            if (traceData) { traceData->setData(final_val); }
-        }''' % (wcond, self.ctype, self.base_name, windex)
-
-        return wb
-
-class FloatRegOperand(Operand):
-    reg_class = 'FloatRegClass'
-
-    def isReg(self):
-        return 1
-
-    def isFloatReg(self):
-        return 1
-
-    def makeConstructor(self, predRead, predWrite):
-        c_src = ''
-        c_dest = ''
-
-        if self.is_src:
-            c_src = self.src_reg_constructor % (self.reg_class, self.reg_spec)
-
-        if self.is_dest:
-            c_dest = self.dst_reg_constructor % (self.reg_class, self.reg_spec)
-            c_dest += '\n\t_numFPDestRegs++;'
-
-        return c_src + c_dest
-
-    def makeRead(self, predRead):
-        if self.read_code != None:
-            return self.buildReadCode(predRead, 'readFloatRegOperandBits')
-
-        if predRead:
-            rindex = '_sourceIndex++'
-        else:
-            rindex = '%d' % self.src_reg_idx
-
-        code = 'xc->readFloatRegOperandBits(this, %s)' % rindex
         if self.ctype == 'float':
-            code = 'bitsToFloat32(%s)' % code
+            reg_val = f'bitsToFloat32({reg_val})'
         elif self.ctype == 'double':
-            code = 'bitsToFloat64(%s)' % code
-        return '%s = %s;\n' % (self.base_name, code)
+            reg_val = f'bitsToFloat64({reg_val})'
 
-    def makeWrite(self, predWrite):
-        if self.write_code != None:
-            return self.buildWriteCode(predWrite, 'setFloatRegOperandBits')
+        return f'{self.base_name} = {reg_val};\n'
 
-        if predWrite:
-            wp = '_destIndex++'
-        else:
-            wp = '%d' % self.dest_reg_idx
+    def makeWrite(self):
+        reg_val = self.base_name
 
-        val = 'final_val'
         if self.ctype == 'float':
-            val = 'floatToBits32(%s)' % val
+            reg_val = f'floatToBits32({reg_val})'
         elif self.ctype == 'double':
-            val = 'floatToBits64(%s)' % val
+            reg_val = f'floatToBits64({reg_val})'
 
-        wp = 'xc->setFloatRegOperandBits(this, %s, %s);' % (wp, val)
+        return f'''
+        {{
+            RegVal final_val = {reg_val};
+            xc->setRegOperand(this, {self.dest_reg_idx}, final_val);
+            if (traceData) {{
+                traceData->setData(final_val);
+            }}
+        }}'''
 
-        wb = '''
-        {
-            %s final_val = %s;
-            %s\n
-            if (traceData) { traceData->setData(final_val); }
-        }''' % (self.ctype, self.base_name, wp)
-        return wb
+class RegOperandDesc(OperandDesc):
+    def __init__(self, reg_class, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.attrs['reg_class'] = reg_class
 
-class VecRegOperand(Operand):
+class IntRegOperandDesc(RegOperandDesc):
+    def __init__(self, *args, **kwargs):
+        super().__init__('IntRegClass', RegValOperand, *args, **kwargs)
+
+class FloatRegOperandDesc(RegOperandDesc):
+    def __init__(self, *args, **kwargs):
+        super().__init__('FloatRegClass', RegValOperand, *args, **kwargs)
+
+class CCRegOperandDesc(RegOperandDesc):
+    def __init__(self, *args, **kwargs):
+        super().__init__('CCRegClass', RegValOperand, *args, **kwargs)
+
+class VecElemOperandDesc(RegOperandDesc):
+    def __init__(self, *args, **kwargs):
+        super().__init__('VecElemClass', RegValOperand, *args, **kwargs)
+
+class VecRegOperand(RegOperand):
     reg_class = 'VecRegClass'
 
     def __init__(self, parser, full_name, ext, is_src, is_dest):
-        Operand.__init__(self, parser, full_name, ext, is_src, is_dest)
+        super().__init__(parser, full_name, ext, is_src, is_dest)
         self.elemExt = None
-        self.parser = parser
-
-    def isReg(self):
-        return 1
-
-    def isVecReg(self):
-        return 1
 
     def makeDeclElem(self, elem_op):
         (elem_name, elem_ext) = elem_op
@@ -340,21 +282,6 @@
         else:
             return ''
 
-    def makeConstructor(self, predRead, predWrite):
-        c_src = ''
-        c_dest = ''
-
-        numAccessNeeded = 1
-
-        if self.is_src:
-            c_src = self.src_reg_constructor % (self.reg_class, self.reg_spec)
-
-        if self.is_dest:
-            c_dest = self.dst_reg_constructor % (self.reg_class, self.reg_spec)
-            c_dest += '\n\t_numVecDestRegs++;'
-
-        return c_src + c_dest
-
     # Read destination register to write
     def makeReadWElem(self, elem_op):
         (elem_name, elem_ext) = elem_op
@@ -368,24 +295,18 @@
                   (ctype, elem_name, self.base_name, elem_spec)
         return c_read
 
-    def makeReadW(self, predWrite):
-        func = 'getWritableVecRegOperand'
-        if self.read_code != None:
-            return self.buildReadCode(predWrite, func)
-
-        if predWrite:
-            rindex = '_destIndex++'
-        else:
-            rindex = '%d' % self.dest_reg_idx
-
-        c_readw = '\t\t%s& tmp_d%s = xc->%s(this, %s);\n'\
-                % ('TheISA::VecRegContainer', rindex, func, rindex)
+    def makeReadW(self):
+        tmp_name = f'tmp_d{self.dest_reg_idx}'
+        c_readw = f'\t\tauto &{tmp_name} = \n' \
+                  f'\t\t    *({self.parser.namespace}::VecRegContainer *)\n' \
+                  f'\t\t    xc->getWritableRegOperand(\n' \
+                  f'\t\t        this, {self.dest_reg_idx});\n'
         if self.elemExt:
-            c_readw += '\t\tauto %s = tmp_d%s.as<%s>();\n' % (self.base_name,
-                        rindex, self.parser.operandTypeMap[self.elemExt])
+            ext = f'{self.parser.operandTypeMap[self.elemExt]}'
+            c_readw += f'\t\tauto {self.base_name} = {tmp_name}.as<{ext}>();\n'
         if self.ext:
-            c_readw += '\t\tauto %s = tmp_d%s.as<%s>();\n' % (self.base_name,
-                        rindex, self.parser.operandTypeMap[self.ext])
+            ext = f'{self.parser.operandTypeMap[self.ext]}'
+            c_readw += f'\t\tauto {self.base_name} = {tmp_name}.as<{ext}>();\n'
         if hasattr(self, 'active_elems'):
             if self.active_elems:
                 for elem in self.active_elems:
@@ -406,260 +327,89 @@
                   (elem_name, name, elem_spec)
         return c_read
 
-    def makeRead(self, predRead):
-        func = 'readVecRegOperand'
-        if self.read_code != None:
-            return self.buildReadCode(predRead, func)
-
-        if predRead:
-            rindex = '_sourceIndex++'
-        else:
-            rindex = '%d' % self.src_reg_idx
-
+    def makeRead(self):
         name = self.base_name
         if self.is_dest and self.is_src:
             name += '_merger'
 
-        c_read =  '\t\t%s& tmp_s%s = xc->%s(this, %s);\n' \
-                % ('const TheISA::VecRegContainer', rindex, func, rindex)
+        tmp_name = f'tmp_s{self.src_reg_idx}'
+        c_read = f'\t\t{self.parser.namespace}::VecRegContainer ' \
+                 f'{tmp_name};\n' \
+                 f'\t\txc->getRegOperand(this, {self.src_reg_idx},\n' \
+                 f'\t\t    &{tmp_name});\n'
         # If the parser has detected that elements are being access, create
         # the appropriate view
         if self.elemExt:
-            c_read += '\t\tauto %s = tmp_s%s.as<%s>();\n' % \
-                 (name, rindex, self.parser.operandTypeMap[self.elemExt])
+            ext = f'{self.parser.operandTypeMap[self.elemExt]}'
+            c_read += f'\t\tauto {name} = {tmp_name}.as<{ext}>();\n'
         if self.ext:
-            c_read += '\t\tauto %s = tmp_s%s.as<%s>();\n' % \
-                 (name, rindex, self.parser.operandTypeMap[self.ext])
+            ext = f'{self.parser.operandTypeMap[self.ext]}'
+            c_read += f'\t\tauto {name} = {tmp_name}.as<{ext}>();\n'
         if hasattr(self, 'active_elems'):
             if self.active_elems:
                 for elem in self.active_elems:
                     c_read += self.makeReadElem(elem, name)
         return c_read
 
-    def makeWrite(self, predWrite):
-        func = 'setVecRegOperand'
-        if self.write_code != None:
-            return self.buildWriteCode(predWrite, func)
+    def makeWrite(self):
+        return f'''
+        if (traceData) {{
+            traceData->setData(tmp_d{self.dest_reg_idx});
+        }}
+        '''
 
-        wb = '''
-        if (traceData) {
-            traceData->setData(tmp_d%d);
-        }
-        ''' % self.dest_reg_idx
-        return wb
-
-    def finalize(self, predRead, predWrite):
-        super().finalize(predRead, predWrite)
+    def finalize(self):
+        super().finalize()
         if self.is_dest:
-            self.op_rd = self.makeReadW(predWrite) + self.op_rd
+            self.op_rd = self.makeReadW() + self.op_rd
 
-class VecElemOperand(Operand):
-    reg_class = 'VecElemClass'
+class VecRegOperandDesc(RegOperandDesc):
+    def __init__(self, *args, **kwargs):
+        super().__init__('VecRegClass', VecRegOperand, *args, **kwargs)
 
-    def isReg(self):
-        return 1
-
-    def isVecElem(self):
-        return 1
-
-    def makeDecl(self):
-        if self.is_dest and not self.is_src:
-            return '\n\t%s %s;' % (self.ctype, self.base_name)
-        else:
-            return ''
-
-    def makeConstructor(self, predRead, predWrite):
-        c_src = ''
-        c_dest = ''
-
-        numAccessNeeded = 1
-
-        if self.is_src:
-            c_src = ('\n\tsetSrcRegIdx(_numSrcRegs++, RegId(%s, %s, %s));' %
-                    (self.reg_class, self.reg_spec, self.elem_spec))
-
-        if self.is_dest:
-            c_dest = ('\n\tsetDestRegIdx(_numDestRegs++, RegId(%s, %s, %s));' %
-                    (self.reg_class, self.reg_spec, self.elem_spec))
-            c_dest += '\n\t_numVecElemDestRegs++;'
-        return c_src + c_dest
-
-    def makeRead(self, predRead):
-        c_read = 'xc->readVecElemOperand(this, %d)' % self.src_reg_idx
-
-        if self.ctype == 'float':
-            c_read = 'bitsToFloat32(%s)' % c_read
-        elif self.ctype == 'double':
-            c_read = 'bitsToFloat64(%s)' % c_read
-
-        return '\n\t%s %s = %s;\n' % (self.ctype, self.base_name, c_read)
-
-    def makeWrite(self, predWrite):
-        if self.ctype == 'float':
-            c_write = 'floatToBits32(%s)' % self.base_name
-        elif self.ctype == 'double':
-            c_write = 'floatToBits64(%s)' % self.base_name
-        else:
-            c_write = self.base_name
-
-        c_write = ('\n\txc->setVecElemOperand(this, %d, %s);' %
-                  (self.dest_reg_idx, c_write))
-
-        return c_write
-
-class VecPredRegOperand(Operand):
+class VecPredRegOperand(RegOperand):
     reg_class = 'VecPredRegClass'
 
-    def __init__(self, parser, full_name, ext, is_src, is_dest):
-        Operand.__init__(self, parser, full_name, ext, is_src, is_dest)
-        self.parser = parser
-
-    def isReg(self):
-        return 1
-
-    def isVecPredReg(self):
-        return 1
-
     def makeDecl(self):
         return ''
 
-    def makeConstructor(self, predRead, predWrite):
-        c_src = ''
-        c_dest = ''
-
-        if self.is_src:
-            c_src = self.src_reg_constructor % (self.reg_class, self.reg_spec)
-
-        if self.is_dest:
-            c_dest = self.dst_reg_constructor % (self.reg_class, self.reg_spec)
-            c_dest += '\n\t_numVecPredDestRegs++;'
-
-        return c_src + c_dest
-
-    def makeRead(self, predRead):
-        func = 'readVecPredRegOperand'
-        if self.read_code != None:
-            return self.buildReadCode(predRead, func)
-
-        if predRead:
-            rindex = '_sourceIndex++'
-        else:
-            rindex = '%d' % self.src_reg_idx
-
-        c_read =  '\t\t%s& tmp_s%s = xc->%s(this, %s);\n' % (
-                'const TheISA::VecPredRegContainer', rindex, func, rindex)
+    def makeRead(self):
+        tmp_name = f'tmp_s{self.src_reg_idx}'
+        c_read =  f'\t\t{self.parser.namespace}::VecPredRegContainer \n' \
+                  f'\t\t        {tmp_name};\n' \
+                  f'xc->getRegOperand(this, {self.src_reg_idx}, ' \
+                  f'&{tmp_name});\n'
         if self.ext:
-            c_read += '\t\tauto %s = tmp_s%s.as<%s>();\n' % (
-                    self.base_name, rindex,
-                    self.parser.operandTypeMap[self.ext])
+            c_read += f'\t\tauto {self.base_name} = {tmp_name}.as<' \
+                      f'{self.parser.operandTypeMap[self.ext]}>();\n'
         return c_read
 
-    def makeReadW(self, predWrite):
-        func = 'getWritableVecPredRegOperand'
-        if self.read_code != None:
-            return self.buildReadCode(predWrite, func)
-
-        if predWrite:
-            rindex = '_destIndex++'
-        else:
-            rindex = '%d' % self.dest_reg_idx
-
-        c_readw = '\t\t%s& tmp_d%s = xc->%s(this, %s);\n' % (
-                'TheISA::VecPredRegContainer', rindex, func, rindex)
+    def makeReadW(self):
+        tmp_name = f'tmp_d{self.dest_reg_idx}'
+        c_readw = f'\t\tauto &{tmp_name} = \n' \
+                  f'\t\t    *({self.parser.namespace}::' \
+                  f'VecPredRegContainer *)xc->getWritableRegOperand(' \
+                  f'this, {self.dest_reg_idx});\n'
         if self.ext:
-            c_readw += '\t\tauto %s = tmp_d%s.as<%s>();\n' % (
-                    self.base_name, rindex,
-                    self.parser.operandTypeMap[self.ext])
+            c_readw += f'\t\tauto {self.base_name} = {tmp_name}.as<' \
+                       f'{self.parser.operandTypeMap[self.ext]}>();\n'
         return c_readw
 
-    def makeWrite(self, predWrite):
-        func = 'setVecPredRegOperand'
-        if self.write_code != None:
-            return self.buildWriteCode(predWrite, func)
+    def makeWrite(self):
+        return f'''
+        if (traceData) {{
+            traceData->setData(tmp_d{self.dest_reg_idx});
+        }}
+        '''
 
-        wb = '''
-        if (traceData) {
-            traceData->setData(tmp_d%d);
-        }
-        ''' % self.dest_reg_idx
-        return wb
-
-    def finalize(self, predRead, predWrite):
-        super().finalize(predRead, predWrite)
+    def finalize(self):
+        super().finalize()
         if self.is_dest:
-            self.op_rd = self.makeReadW(predWrite) + self.op_rd
+            self.op_rd = self.makeReadW() + self.op_rd
 
-class CCRegOperand(Operand):
-    reg_class = 'CCRegClass'
-
-    def isReg(self):
-        return 1
-
-    def isCCReg(self):
-        return 1
-
-    def makeConstructor(self, predRead, predWrite):
-        c_src = ''
-        c_dest = ''
-
-        if self.is_src:
-            c_src = self.src_reg_constructor % (self.reg_class, self.reg_spec)
-            if self.hasReadPred():
-                c_src = '\n\tif (%s) {%s\n\t}' % \
-                        (self.read_predicate, c_src)
-
-        if self.is_dest:
-            c_dest = self.dst_reg_constructor % (self.reg_class, self.reg_spec)
-            c_dest += '\n\t_numCCDestRegs++;'
-            if self.hasWritePred():
-                c_dest = '\n\tif (%s) {%s\n\t}' % \
-                         (self.write_predicate, c_dest)
-
-        return c_src + c_dest
-
-    def makeRead(self, predRead):
-        if (self.ctype == 'float' or self.ctype == 'double'):
-            error('Attempt to read condition-code register as FP')
-        if self.read_code != None:
-            return self.buildReadCode(predRead, 'readCCRegOperand')
-
-        int_reg_val = ''
-        if predRead:
-            int_reg_val = 'xc->readCCRegOperand(this, _sourceIndex++)'
-            if self.hasReadPred():
-                int_reg_val = '(%s) ? %s : 0' % \
-                              (self.read_predicate, int_reg_val)
-        else:
-            int_reg_val = 'xc->readCCRegOperand(this, %d)' % self.src_reg_idx
-
-        return '%s = %s;\n' % (self.base_name, int_reg_val)
-
-    def makeWrite(self, predWrite):
-        if (self.ctype == 'float' or self.ctype == 'double'):
-            error('Attempt to write condition-code register as FP')
-        if self.write_code != None:
-            return self.buildWriteCode(predWrite, 'setCCRegOperand')
-
-        if predWrite:
-            wp = 'true'
-            if self.hasWritePred():
-                wp = self.write_predicate
-
-            wcond = 'if (%s)' % (wp)
-            windex = '_destIndex++'
-        else:
-            wcond = ''
-            windex = '%d' % self.dest_reg_idx
-
-        wb = '''
-        %s
-        {
-            %s final_val = %s;
-            xc->setCCRegOperand(this, %s, final_val);\n
-            if (traceData) { traceData->setData(final_val); }
-        }''' % (wcond, self.ctype, self.base_name, windex)
-
-        return wb
+class VecPredRegOperandDesc(RegOperandDesc):
+    def __init__(self, *args, **kwargs):
+        super().__init__('VecPredRegClass', VecPredRegOperand, *args, **kwargs)
 
 class ControlRegOperand(Operand):
     reg_class = 'MiscRegClass'
@@ -670,91 +420,83 @@
     def isControlReg(self):
         return 1
 
-    def makeConstructor(self, predRead, predWrite):
+    def makeConstructor(self):
         c_src = ''
         c_dest = ''
 
         if self.is_src:
-            c_src = self.src_reg_constructor % (self.reg_class, self.reg_spec)
+            c_src = self.src_reg_constructor % self.srcRegId()
 
         if self.is_dest:
-            c_dest = self.dst_reg_constructor % (self.reg_class, self.reg_spec)
+            c_dest = self.dst_reg_constructor % self.destRegId()
 
         return c_src + c_dest
 
-    def makeRead(self, predRead):
+    def makeRead(self):
         bit_select = 0
         if (self.ctype == 'float' or self.ctype == 'double'):
             error('Attempt to read control register as FP')
-        if self.read_code != None:
-            return self.buildReadCode(predRead, 'readMiscRegOperand')
 
-        if predRead:
-            rindex = '_sourceIndex++'
-        else:
-            rindex = '%d' % self.src_reg_idx
+        return f'{self.base_name} = ' \
+               f'xc->readMiscRegOperand(this, {self.src_reg_idx});\n'
 
-        return '%s = xc->readMiscRegOperand(this, %s);\n' % \
-            (self.base_name, rindex)
-
-    def makeWrite(self, predWrite):
+    def makeWrite(self):
         if (self.ctype == 'float' or self.ctype == 'double'):
             error('Attempt to write control register as FP')
-        if self.write_code != None:
-            return self.buildWriteCode(predWrite, 'setMiscRegOperand')
-
-        if predWrite:
-            windex = '_destIndex++'
-        else:
-            windex = '%d' % self.dest_reg_idx
-
-        wb = 'xc->setMiscRegOperand(this, %s, %s);\n' % \
-             (windex, self.base_name)
-        wb += 'if (traceData) { traceData->setData(%s); }' % \
-              self.base_name
+        wb = f'xc->setMiscRegOperand(this, ' \
+             f'{self.dest_reg_idx}, {self.base_name});\n'
+        wb += f'''
+        if (traceData) {{
+            traceData->setData({self.base_name});
+        }}
+        '''
 
         return wb
 
+class ControlRegOperandDesc(RegOperandDesc):
+    def __init__(self, *args, **kwargs):
+        super().__init__('MiscRegClass', ControlRegOperand, *args, **kwargs)
+
 class MemOperand(Operand):
     def isMem(self):
         return 1
 
-    def makeConstructor(self, predRead, predWrite):
+    def makeConstructor(self):
         return ''
 
     def makeDecl(self):
         # Declare memory data variable.
-        return '%s %s = {};\n' % (self.ctype, self.base_name)
+        return f'{self.ctype} {self.base_name} = {{}};\n'
 
-    def makeRead(self, predRead):
-        if self.read_code != None:
-            return self.buildReadCode(predRead)
+    def makeRead(self):
         return ''
 
-    def makeWrite(self, predWrite):
-        if self.write_code != None:
-            return self.buildWriteCode(predWrite)
+    def makeWrite(self):
         return ''
 
+class MemOperandDesc(OperandDesc):
+    def __init__(self, *args, **kwargs):
+        super().__init__(MemOperand, *args, **kwargs)
+
 class PCStateOperand(Operand):
     def __init__(self, parser, *args, **kwargs):
         super().__init__(parser, *args, **kwargs)
         self.parser = parser
 
-    def makeConstructor(self, predRead, predWrite):
+    def makeConstructor(self):
         return ''
 
-    def makeRead(self, predRead):
+    def makeRead(self):
         if self.reg_spec:
             # A component of the PC state.
-            return '%s = __parserAutoPCState.%s();\n' % \
-                (self.base_name, self.reg_spec)
+            return f'{self.base_name} = ' \
+                    f'__parserAutoPCState.{self.reg_spec}();\n'
         else:
             # The whole PC state itself.
             return f'{self.base_name} = ' \
                     f'xc->pcState().as<{self.parser.namespace}::PCState>();\n'
 
-    def makeWrite(self, predWrite):
+    def makeWrite(self):
         if self.reg_spec:
             # A component of the PC state.
             return '__parserAutoPCState.%s(%s);\n' % \
@@ -773,3 +515,7 @@
 
     def isPCState(self):
         return 1
+
+class PCStateOperandDesc(OperandDesc):
+    def __init__(self, *args, **kwargs):
+        super().__init__(PCStateOperand, *args, **kwargs)
diff --git a/src/arch/micro_asm.py b/src/arch/micro_asm.py
index 5eac33d..54f7b61 100644
--- a/src/arch/micro_asm.py
+++ b/src/arch/micro_asm.py
@@ -40,7 +40,7 @@
 #
 ##########################################################################
 
-class Micro_Container(object):
+class MicroContainer:
     def __init__(self, name):
         self.microops = []
         self.name = name
@@ -49,6 +49,8 @@
         self.labels = {}
 
     def add_microop(self, mnemonic, microop):
+        microop.mnemonic = mnemonic
+        microop.micropc = len(self.microops)
         self.microops.append(microop)
 
     def __str__(self):
@@ -57,10 +59,10 @@
             string += "  %s\n" % microop
         return string
 
-class Combinational_Macroop(Micro_Container):
+class CombinationalMacroop(MicroContainer):
     pass
 
-class Rom_Macroop(object):
+class RomMacroop:
     def __init__(self, name, target):
         self.name = name
         self.target = target
@@ -68,7 +70,7 @@
     def __str__(self):
         return "%s: %s\n" % (self.name, self.target)
 
-class Rom(Micro_Container):
+class Rom(MicroContainer):
     def __init__(self, name):
         super().__init__(name)
         self.externs = {}
@@ -197,6 +199,7 @@
 states = (
     ('asm', 'exclusive'),
     ('params', 'exclusive'),
+    ('header', 'exclusive'),
 )
 
 reserved_map = { }
@@ -215,7 +218,7 @@
 # in the "asm" state since it knows it saw a label and not a mnemonic.
 def t_params_COLON(t):
     r':'
-    t.lexer.begin('asm')
+    t.lexer.pop_state()
     return t
 
 # Parameters are a string of text which don't contain an unescaped statement
@@ -228,7 +231,7 @@
         val = mo.group(0)
         return val[1]
     t.value = unescapeParamsRE.sub(unescapeParams, t.value)
-    t.lexer.begin('asm')
+    t.lexer.pop_state()
     return t
 
 # An "ID" in the micro assembler is either a label, directive, or mnemonic
@@ -241,7 +244,11 @@
     # If the ID is really "extern", we shouldn't start looking for parameters
     # yet. The real ID, the label itself, is coming up.
     if t.type != 'EXTERN':
-        t.lexer.begin('params')
+        t.lexer.push_state('params')
+    return t
+
+def t_header_ID(t):
+    r'[A-Za-z_]\w*'
     return t
 
 # If there is a label and you're -not- in the assembler (which would be caught
@@ -249,24 +256,25 @@
 def t_ANY_ID(t):
     r'[A-Za-z_]\w*'
     t.type = reserved_map.get(t.value, 'ID')
+    if t.type == 'MACROOP':
+        t.lexer.push_state('asm')
+        t.lexer.push_state('header')
+    elif t.type == 'ROM':
+        t.lexer.push_state('asm')
+        t.lexer.push_state('header')
     return t
 
 # Braces enter and exit micro assembly
-def t_INITIAL_LBRACE(t):
+def t_header_LBRACE(t):
     r'\{'
-    t.lexer.begin('asm')
+    t.lexer.pop_state()
     return t
 
 def t_asm_RBRACE(t):
     r'\}'
-    t.lexer.begin('INITIAL')
+    t.lexer.pop_state()
     return t
 
-# At the top level, keep track of newlines only for line counting.
-def t_INITIAL_NEWLINE(t):
-    r'\n+'
-    t.lineno += t.value.count('\n')
-
 # In the micro assembler, do line counting but also return a token. The
 # token is needed by the parser to detect the end of a statement.
 def t_asm_NEWLINE(t):
@@ -279,14 +287,19 @@
 def t_params_NEWLINE(t):
     r'\n+'
     t.lineno += t.value.count('\n')
-    t.lexer.begin('asm')
+    t.lexer.pop_state()
     return t
 
 def t_params_SEMI(t):
     r';'
-    t.lexer.begin('asm')
+    t.lexer.pop_state()
     return t
 
+# Unless handled specially above, track newlines only for line counting.
+def t_ANY_NEWLINE(t):
+    r'\n+'
+    t.lineno += t.value.count('\n')
+
 # Basic regular expressions to pick out simple tokens
 t_ANY_LPAREN = r'\('
 t_ANY_RPAREN = r'\)'
@@ -486,7 +499,7 @@
     def __init__(self, macro_type, microops,
             rom = None, rom_macroop_type = None):
         self.lexer = lex.lex()
-        self.parser = yacc.yacc()
+        self.parser = yacc.yacc(write_tables=False)
         self.parser.macro_type = macro_type
         self.parser.macroops = {}
         self.parser.microops = microops
diff --git a/src/arch/micro_asm_test.py b/src/arch/micro_asm_test.py
index d3ad420..08c2412 100755
--- a/src/arch/micro_asm_test.py
+++ b/src/arch/micro_asm_test.py
@@ -24,7 +24,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from micro_asm import MicroAssembler, Combinational_Macroop, Rom_Macroop, Rom
+from micro_asm import MicroAssembler, CombinationalMacroop, RomMacroop, Rom
 
 class Bah(object):
     def __init__(self):
@@ -50,7 +50,7 @@
     "dah": Dah
 }
 
-class TestMacroop(Combinational_Macroop):
+class TestMacroop(CombinationalMacroop):
     def tweak(self):
         microops["bah"] = Bah_Tweaked
     def untweak(self):
@@ -66,7 +66,7 @@
             "print": self.print_debug
         }
 
-assembler = MicroAssembler(TestMacroop, microops, Rom('main ROM'), Rom_Macroop)
+assembler = MicroAssembler(TestMacroop, microops, Rom('main ROM'), RomMacroop)
 
 testAssembly = '''
 # Single line comment
diff --git a/util/stats/__init__.py b/src/arch/mips/AtomicSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/mips/AtomicSimpleCPU.py
index b3f54da..93c289a 100644
--- a/util/stats/__init__.py
+++ b/src/arch/mips/AtomicSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.MipsCPU import MipsAtomicSimpleCPU
+
+AtomicSimpleCPU = MipsAtomicSimpleCPU
diff --git a/src/cpu/simple/SConsopts b/src/arch/mips/MipsCPU.py
similarity index 62%
copy from src/cpu/simple/SConsopts
copy to src/arch/mips/MipsCPU.py
index f12fee2..61b7fd2 100644
--- a/src/cpu/simple/SConsopts
+++ b/src/arch/mips/MipsCPU.py
@@ -1,7 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -26,6 +23,29 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU
+from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU
+from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU
+from m5.objects.BaseO3CPU import BaseO3CPU
+from m5.objects.MipsDecoder import MipsDecoder
+from m5.objects.MipsMMU import MipsMMU
+from m5.objects.MipsInterrupts import MipsInterrupts
+from m5.objects.MipsISA import MipsISA
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+class MipsCPU:
+    ArchDecoder = MipsDecoder
+    ArchMMU = MipsMMU
+    ArchInterrupts = MipsInterrupts
+    ArchISA = MipsISA
+
+class MipsAtomicSimpleCPU(BaseAtomicSimpleCPU, MipsCPU):
+    mmu = MipsMMU()
+
+class MipsNonCachingSimpleCPU(BaseNonCachingSimpleCPU, MipsCPU):
+    mmu = MipsMMU()
+
+class MipsTimingSimpleCPU(BaseTimingSimpleCPU, MipsCPU):
+    mmu = MipsMMU()
+
+class MipsO3CPU(BaseO3CPU, MipsCPU):
+    mmu = MipsMMU()
diff --git a/util/stats/__init__.py b/src/arch/mips/NonCachingSimpleCPU.py
similarity index 90%
copy from util/stats/__init__.py
copy to src/arch/mips/NonCachingSimpleCPU.py
index b3f54da..b375347 100644
--- a/util/stats/__init__.py
+++ b/src/arch/mips/NonCachingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.MipsCPU import MipsNonCachingSimpleCPU
+
+NonCachingSimpleCPU = MipsNonCachingSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/mips/O3CPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/mips/O3CPU.py
index b3f54da..8f7b14c 100644
--- a/util/stats/__init__.py
+++ b/src/arch/mips/O3CPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.MipsCPU import MipsO3CPU
+
+O3CPU = MipsO3CPU
+
+# Deprecated
+DerivO3CPU = O3CPU
diff --git a/src/arch/mips/SConscript b/src/arch/mips/SConscript
index 2a4110a..c93260a 100644
--- a/src/arch/mips/SConscript
+++ b/src/arch/mips/SConscript
@@ -51,6 +51,12 @@
     tags='mips isa')
 SimObject('MipsTLB.py', sim_objects=['MipsTLB'], tags='mips isa')
 
+SimObject('MipsCPU.py', sim_objects=[], tags='mips isa')
+SimObject('AtomicSimpleCPU.py', sim_objects=[], tags='mips isa')
+SimObject('TimingSimpleCPU.py', sim_objects=[], tags='mips isa')
+SimObject('NonCachingSimpleCPU.py', sim_objects=[], tags='mips isa')
+SimObject('O3CPU.py', sim_objects=[], tags='mips isa')
+
 DebugFlag('MipsPRA', tags='mips isa')
 
 ISADesc('isa/main.isa', tags='mips isa')
diff --git a/util/stats/__init__.py b/src/arch/mips/TimingSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/mips/TimingSimpleCPU.py
index b3f54da..af687a7 100644
--- a/util/stats/__init__.py
+++ b/src/arch/mips/TimingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.MipsCPU import MipsTimingSimpleCPU
+
+TimingSimpleCPU = MipsTimingSimpleCPU
diff --git a/src/arch/mips/isa.cc b/src/arch/mips/isa.cc
index 2a075ec..683ed72 100644
--- a/src/arch/mips/isa.cc
+++ b/src/arch/mips/isa.cc
@@ -38,7 +38,10 @@
 #include "cpu/base.hh"
 #include "cpu/reg_class.hh"
 #include "cpu/thread_context.hh"
+#include "debug/FloatRegs.hh"
+#include "debug/IntRegs.hh"
 #include "debug/MipsPRA.hh"
+#include "debug/MiscRegs.hh"
 #include "params/MipsISA.hh"
 
 namespace gem5
@@ -97,13 +100,13 @@
 ISA::ISA(const Params &p) : BaseISA(p), numThreads(p.num_threads),
     numVpes(p.num_vpes)
 {
-    _regClasses.emplace_back(NumIntRegs, 0);
-    _regClasses.emplace_back(NumFloatRegs);
-    _regClasses.emplace_back(1); // Not applicable to MIPS.
-    _regClasses.emplace_back(2); // Not applicable to MIPS.
-    _regClasses.emplace_back(1); // Not applicable to MIPS.
-    _regClasses.emplace_back(0); // Not applicable to MIPS.
-    _regClasses.emplace_back(MISCREG_NUMREGS);
+    _regClasses.emplace_back(NumIntRegs, debug::IntRegs);
+    _regClasses.emplace_back(NumFloatRegs, debug::FloatRegs);
+    _regClasses.emplace_back(1, debug::IntRegs); // Not applicable to MIPS.
+    _regClasses.emplace_back(2, debug::IntRegs); // Not applicable to MIPS.
+    _regClasses.emplace_back(1, debug::IntRegs); // Not applicable to MIPS.
+    _regClasses.emplace_back(0, debug::IntRegs); // Not applicable to MIPS.
+    _regClasses.emplace_back(MISCREG_NUMREGS, debug::MiscRegs);
 
     miscRegFile.resize(MISCREG_NUMREGS);
     bankType.resize(MISCREG_NUMREGS);
diff --git a/src/arch/mips/isa/formats/fp.isa b/src/arch/mips/isa/formats/fp.isa
index 3f1e37e..bfcdb8c 100644
--- a/src/arch/mips/isa/formats/fp.isa
+++ b/src/arch/mips/isa/formats/fp.isa
@@ -104,11 +104,11 @@
         assert(sizeof(T) == 4);
 
         for (int i = 0; i < inst->numSrcRegs(); i++) {
-            uint64_t src_bits = xc->readFloatRegOperandBits(inst, 0);
+            uint64_t src_bits = xc->getRegOperand(inst, 0);
 
             if (isNan(&src_bits, 32) ) {
                 mips_nan = MIPS32_QNAN;
-                xc->setFloatRegOperandBits(inst, 0, mips_nan);
+                xc->setRegOperand(inst, 0, mips_nan);
                 if (traceData) { traceData->setData(mips_nan); }
                 return true;
             }
@@ -129,7 +129,7 @@
             mips_nan = MIPS32_QNAN;
 
             //Set value to QNAN
-            cpu->setFloatRegOperandBits(inst, 0, mips_nan);
+            cpu->setRegOperand(inst, 0, mips_nan);
 
             //Read FCSR from FloatRegFile
             uint32_t fcsr_bits =
diff --git a/src/arch/mips/isa/operands.isa b/src/arch/mips/isa/operands.isa
index 3cb2d43..16c34a7 100644
--- a/src/arch/mips/isa/operands.isa
+++ b/src/arch/mips/isa/operands.isa
@@ -39,115 +39,123 @@
     'df' : 'double'
 }};
 
+let {{
+    class IntReg(IntRegOp):
+        @overrideInOperand
+        def regId(self):
+            return f'(({self.reg_spec}) == 0) ? RegId() : ' \
+                   f'RegId({self.reg_class}, {self.reg_spec})'
+}};
+
 def operands {{
     #General Purpose Integer Reg Operands
-    'Rd': ('IntReg', 'uw', 'RD', 'IsInteger', 1),
-    'Rs': ('IntReg', 'uw', 'RS', 'IsInteger', 2),
-    'Rt': ('IntReg', 'uw', 'RT', 'IsInteger', 3),
+    'Rd': IntReg('uw', 'RD', 'IsInteger', 1),
+    'Rs': IntReg('uw', 'RS', 'IsInteger', 2),
+    'Rt': IntReg('uw', 'RT', 'IsInteger', 3),
 
     #Immediate Value operand
-    'IntImm': ('IntReg', 'uw', 'INTIMM', 'IsInteger', 3),
+    'IntImm': IntReg('uw', 'INTIMM', 'IsInteger', 3),
 
     #Operands used for Link Insts
-    'R31': ('IntReg', 'uw','31','IsInteger', 4),
+    'R31': IntReg('uw', '31', 'IsInteger', 4),
 
     #Special Integer Reg operands
-    'LO0':  ('IntReg', 'uw','INTREG_LO', 'IsInteger', 6),
-    'HI0':  ('IntReg', 'uw','INTREG_HI', 'IsInteger', 7),
+    'LO0':  IntReg('uw', 'INTREG_LO', 'IsInteger', 6),
+    'HI0':  IntReg('uw', 'INTREG_HI', 'IsInteger', 7),
 
     #Bitfield-dependent HI/LO Register Access
-    'LO_RD_SEL': ('IntReg','uw','INTREG_DSP_LO0 + ACDST*3', None, 6),
-    'HI_RD_SEL': ('IntReg','uw','INTREG_DSP_HI0 + ACDST*3', None, 7),
-    'LO_RS_SEL': ('IntReg','uw','INTREG_DSP_LO0 + ACSRC*3', None, 6),
-    'HI_RS_SEL': ('IntReg','uw','INTREG_DSP_HI0 + ACSRC*3', None, 7),
+    'LO_RD_SEL': IntReg('uw', 'INTREG_DSP_LO0 + ACDST*3', None, 6),
+    'HI_RD_SEL': IntReg('uw', 'INTREG_DSP_HI0 + ACDST*3', None, 7),
+    'LO_RS_SEL': IntReg('uw', 'INTREG_DSP_LO0 + ACSRC*3', None, 6),
+    'HI_RS_SEL': IntReg('uw', 'INTREG_DSP_HI0 + ACSRC*3', None, 7),
 
     #DSP Special Purpose Integer Operands
-    'DSPControl': ('IntReg', 'uw', 'INTREG_DSP_CONTROL', None, 8),
-    'DSPLo0': ('IntReg', 'uw', 'INTREG_LO', None, 1),
-    'DSPHi0': ('IntReg', 'uw', 'INTREG_HI', None, 1),
-    'DSPACX0': ('IntReg', 'uw', 'INTREG_DSP_ACX0', None, 1),
-    'DSPLo1': ('IntReg', 'uw', 'INTREG_DSP_LO1', None, 1),
-    'DSPHi1': ('IntReg', 'uw', 'INTREG_DSP_HI1', None, 1),
-    'DSPACX1': ('IntReg', 'uw', 'INTREG_DSP_ACX1', None, 1),
-    'DSPLo2': ('IntReg', 'uw', 'INTREG_DSP_LO2', None, 1),
-    'DSPHi2': ('IntReg', 'uw', 'INTREG_DSP_HI2', None, 1),
-    'DSPACX2': ('IntReg', 'uw', 'INTREG_DSP_ACX2', None, 1),
-    'DSPLo3': ('IntReg', 'uw', 'INTREG_DSP_LO3', None, 1),
-    'DSPHi3': ('IntReg', 'uw', 'INTREG_DSP_HI3', None, 1),
-    'DSPACX3': ('IntReg', 'uw', 'INTREG_DSP_ACX3', None, 1),
+    'DSPControl': IntReg('uw', 'INTREG_DSP_CONTROL', None, 8),
+    'DSPLo0': IntReg('uw', 'INTREG_LO', None, 1),
+    'DSPHi0': IntReg('uw', 'INTREG_HI', None, 1),
+    'DSPACX0': IntReg('uw', 'INTREG_DSP_ACX0', None, 1),
+    'DSPLo1': IntReg('uw', 'INTREG_DSP_LO1', None, 1),
+    'DSPHi1': IntReg('uw', 'INTREG_DSP_HI1', None, 1),
+    'DSPACX1': IntReg('uw', 'INTREG_DSP_ACX1', None, 1),
+    'DSPLo2': IntReg('uw', 'INTREG_DSP_LO2', None, 1),
+    'DSPHi2': IntReg('uw', 'INTREG_DSP_HI2', None, 1),
+    'DSPACX2': IntReg('uw', 'INTREG_DSP_ACX2', None, 1),
+    'DSPLo3': IntReg('uw', 'INTREG_DSP_LO3', None, 1),
+    'DSPHi3': IntReg('uw', 'INTREG_DSP_HI3', None, 1),
+    'DSPACX3': IntReg('uw', 'INTREG_DSP_ACX3', None, 1),
 
     #Floating Point Reg Operands
-    'Fd': ('FloatReg', 'sf', 'FD', 'IsFloating', 1),
-    'Fs': ('FloatReg', 'sf', 'FS', 'IsFloating', 2),
-    'Ft': ('FloatReg', 'sf', 'FT', 'IsFloating', 3),
-    'Fr': ('FloatReg', 'sf', 'FR', 'IsFloating', 3),
+    'Fd': FloatRegOp('sf', 'FD', 'IsFloating', 1),
+    'Fs': FloatRegOp('sf', 'FS', 'IsFloating', 2),
+    'Ft': FloatRegOp('sf', 'FT', 'IsFloating', 3),
+    'Fr': FloatRegOp('sf', 'FR', 'IsFloating', 3),
 
     #Special Purpose Floating Point Control Reg Operands
-    'FIR':  ('FloatReg', 'uw', 'FLOATREG_FIR', 'IsFloating', 1),
-    'FCCR': ('FloatReg', 'uw', 'FLOATREG_FCCR', 'IsFloating', 2),
-    'FEXR': ('FloatReg', 'uw', 'FLOATREG_FEXR', 'IsFloating', 3),
-    'FENR': ('FloatReg', 'uw', 'FLOATREG_FENR', 'IsFloating', 3),
-    'FCSR': ('FloatReg', 'uw', 'FLOATREG_FCSR', 'IsFloating', 3),
+    'FIR':  FloatRegOp('uw', 'FLOATREG_FIR', 'IsFloating', 1),
+    'FCCR': FloatRegOp('uw', 'FLOATREG_FCCR', 'IsFloating', 2),
+    'FEXR': FloatRegOp('uw', 'FLOATREG_FEXR', 'IsFloating', 3),
+    'FENR': FloatRegOp('uw', 'FLOATREG_FENR', 'IsFloating', 3),
+    'FCSR': FloatRegOp('uw', 'FLOATREG_FCSR', 'IsFloating', 3),
 
     #Operands For Paired Singles FP Operations
-    'Fd1': ('FloatReg', 'sf', 'FD', 'IsFloating', 4),
-    'Fd2': ('FloatReg', 'sf', 'FD+1', 'IsFloating', 4),
-    'Fs1': ('FloatReg', 'sf', 'FS', 'IsFloating', 5),
-    'Fs2': ('FloatReg', 'sf', 'FS+1', 'IsFloating', 5),
-    'Ft1': ('FloatReg', 'sf', 'FT', 'IsFloating', 6),
-    'Ft2': ('FloatReg', 'sf', 'FT+1', 'IsFloating', 6),
-    'Fr1': ('FloatReg', 'sf', 'FR', 'IsFloating', 7),
-    'Fr2': ('FloatReg', 'sf', 'FR+1', 'IsFloating', 7),
+    'Fd1': FloatRegOp('sf', 'FD', 'IsFloating', 4),
+    'Fd2': FloatRegOp('sf', 'FD+1', 'IsFloating', 4),
+    'Fs1': FloatRegOp('sf', 'FS', 'IsFloating', 5),
+    'Fs2': FloatRegOp('sf', 'FS+1', 'IsFloating', 5),
+    'Ft1': FloatRegOp('sf', 'FT', 'IsFloating', 6),
+    'Ft2': FloatRegOp('sf', 'FT+1', 'IsFloating', 6),
+    'Fr1': FloatRegOp('sf', 'FR', 'IsFloating', 7),
+    'Fr2': FloatRegOp('sf', 'FR+1', 'IsFloating', 7),
 
     #Status Control Reg
-    'Status': ('ControlReg', 'uw', 'MISCREG_STATUS', None, 1),
+    'Status': ControlRegOp('uw', 'MISCREG_STATUS', None, 1),
 
     #LL Flag
-    'LLFlag': ('ControlReg', 'uw', 'MISCREG_LLFLAG', None, 1),
+    'LLFlag': ControlRegOp('uw', 'MISCREG_LLFLAG', None, 1),
 
     #Thread pointer value for SE mode
-    'TpValue': ('ControlReg', 'ud', 'MISCREG_TP_VALUE', None, 1),
+    'TpValue': ControlRegOp('ud', 'MISCREG_TP_VALUE', None, 1),
 
     # Index Register
-    'Index': ('ControlReg','uw','MISCREG_INDEX',None,1),
+    'Index': ControlRegOp('uw','MISCREG_INDEX',None,1),
 
 
-    'CP0_RD_SEL': ('ControlReg', 'uw', '(RD << 3 | SEL)', None, 1),
+    'CP0_RD_SEL': ControlRegOp('uw', '(RD << 3 | SEL)', None, 1),
 
     #MT Control Regs
-    'MVPConf0': ('ControlReg', 'uw', 'MISCREG_MVP_CONF0', None, 1),
-    'MVPControl': ('ControlReg', 'uw', 'MISCREG_MVP_CONTROL', None, 1),
-    'TCBind': ('ControlReg', 'uw', 'MISCREG_TC_BIND', None, 1),
-    'TCStatus': ('ControlReg', 'uw', 'MISCREG_TC_STATUS', None, 1),
-    'TCRestart': ('ControlReg', 'uw', 'MISCREG_TC_RESTART', None, 1),
-    'VPEConf0': ('ControlReg', 'uw', 'MISCREG_VPE_CONF0', None, 1),
-    'VPEControl': ('ControlReg', 'uw', 'MISCREG_VPE_CONTROL', None, 1),
-    'YQMask': ('ControlReg', 'uw', 'MISCREG_YQMASK', None, 1),
+    'MVPConf0': ControlRegOp('uw', 'MISCREG_MVP_CONF0', None, 1),
+    'MVPControl': ControlRegOp('uw', 'MISCREG_MVP_CONTROL', None, 1),
+    'TCBind': ControlRegOp('uw', 'MISCREG_TC_BIND', None, 1),
+    'TCStatus': ControlRegOp('uw', 'MISCREG_TC_STATUS', None, 1),
+    'TCRestart': ControlRegOp('uw', 'MISCREG_TC_RESTART', None, 1),
+    'VPEConf0': ControlRegOp('uw', 'MISCREG_VPE_CONF0', None, 1),
+    'VPEControl': ControlRegOp('uw', 'MISCREG_VPE_CONTROL', None, 1),
+    'YQMask': ControlRegOp('uw', 'MISCREG_YQMASK', None, 1),
 
     #CP0 Control Regs
-    'EntryHi': ('ControlReg','uw', 'MISCREG_ENTRYHI',None,1),
-    'EntryLo0': ('ControlReg','uw', 'MISCREG_ENTRYLO0',None,1),
-    'EntryLo1': ('ControlReg','uw', 'MISCREG_ENTRYLO1',None,1),
-    'PageMask': ('ControlReg','uw', 'MISCREG_PAGEMASK',None,1),
-    'Random': ('ControlReg','uw', 'MISCREG_CP0_RANDOM',None,1),
-    'ErrorEPC': ('ControlReg','uw', 'MISCREG_ERROR_EPC',None,1),
-    'EPC': ('ControlReg','uw', 'MISCREG_EPC',None,1),
-    'DEPC': ('ControlReg','uw', 'MISCREG_DEPC',None,1),
-    'IntCtl': ('ControlReg','uw', 'MISCREG_INTCTL',None,1),
-    'SRSCtl': ('ControlReg','uw', 'MISCREG_SRSCTL',None,1),
-    'Config': ('ControlReg','uw', 'MISCREG_CONFIG',None,1),
-    'Config3': ('ControlReg','uw', 'MISCREG_CONFIG3',None,1),
-    'Config1': ('ControlReg','uw', 'MISCREG_CONFIG1',None,1),
-    'Config2': ('ControlReg','uw', 'MISCREG_CONFIG2',None,1),
-    'PageGrain': ('ControlReg','uw', 'MISCREG_PAGEGRAIN',None,1),
-    'Debug': ('ControlReg','uw', 'MISCREG_DEBUG',None,1),
-    'Cause': ('ControlReg','uw', 'MISCREG_CAUSE',None,1),
+    'EntryHi': ControlRegOp('uw', 'MISCREG_ENTRYHI',None,1),
+    'EntryLo0': ControlRegOp('uw', 'MISCREG_ENTRYLO0',None,1),
+    'EntryLo1': ControlRegOp('uw', 'MISCREG_ENTRYLO1',None,1),
+    'PageMask': ControlRegOp('uw', 'MISCREG_PAGEMASK',None,1),
+    'Random': ControlRegOp('uw', 'MISCREG_CP0_RANDOM',None,1),
+    'ErrorEPC': ControlRegOp('uw', 'MISCREG_ERROR_EPC',None,1),
+    'EPC': ControlRegOp('uw', 'MISCREG_EPC',None,1),
+    'DEPC': ControlRegOp('uw', 'MISCREG_DEPC',None,1),
+    'IntCtl': ControlRegOp('uw', 'MISCREG_INTCTL',None,1),
+    'SRSCtl': ControlRegOp('uw', 'MISCREG_SRSCTL',None,1),
+    'Config': ControlRegOp('uw', 'MISCREG_CONFIG',None,1),
+    'Config3': ControlRegOp('uw', 'MISCREG_CONFIG3',None,1),
+    'Config1': ControlRegOp('uw', 'MISCREG_CONFIG1',None,1),
+    'Config2': ControlRegOp('uw', 'MISCREG_CONFIG2',None,1),
+    'PageGrain': ControlRegOp('uw', 'MISCREG_PAGEGRAIN',None,1),
+    'Debug': ControlRegOp('uw', 'MISCREG_DEBUG',None,1),
+    'Cause': ControlRegOp('uw', 'MISCREG_CAUSE',None,1),
 
     #Memory Operand
-    'Mem': ('Mem', 'uw', None, (None, 'IsLoad', 'IsStore'), 4),
+    'Mem': MemOp('uw', None, (None, 'IsLoad', 'IsStore'), 4),
 
     #Program Counter Operands
-    'PC': ('PCState', 'uw', 'pc', (None, None, 'IsControl'), 4),
-    'NPC': ('PCState', 'uw', 'npc', (None, None, 'IsControl'), 4),
-    'NNPC': ('PCState', 'uw', 'nnpc', (None, None, 'IsControl'), 4)
+    'PC': PCStateOp('uw', 'pc', (None, None, 'IsControl'), 4),
+    'NPC': PCStateOp('uw', 'npc', (None, None, 'IsControl'), 4),
+    'NNPC': PCStateOp('uw', 'nnpc', (None, None, 'IsControl'), 4)
 }};
diff --git a/src/arch/mips/vecregs.hh b/src/arch/mips/vecregs.hh
index 0c8b86a..546e4cf 100644
--- a/src/arch/mips/vecregs.hh
+++ b/src/arch/mips/vecregs.hh
@@ -40,11 +40,7 @@
 {
 
 // Not applicable to MIPS
-using VecElem = ::gem5::DummyVecElem;
 using VecRegContainer = ::gem5::DummyVecRegContainer;
-constexpr unsigned NumVecElemPerVecReg = ::gem5::DummyNumVecElemPerVecReg;
-
-// Not applicable to MIPS
 using VecPredRegContainer = ::gem5::DummyVecPredRegContainer;
 
 } // namespace MipsISA
diff --git a/src/arch/null/vecregs.hh b/src/arch/null/vecregs.hh
index 81c2f0d..4ca2d2b 100644
--- a/src/arch/null/vecregs.hh
+++ b/src/arch/null/vecregs.hh
@@ -38,8 +38,6 @@
 #ifndef __ARCH_NULL_VECREGS_HH__
 #define __ARCH_NULL_VECREGS_HH__
 
-#include <cstdint>
-
 #include "arch/generic/vec_pred_reg.hh"
 #include "arch/generic/vec_reg.hh"
 
@@ -50,11 +48,7 @@
 {
 
 // Not applicable to null
-using VecElem = ::gem5::DummyVecElem;
 using VecRegContainer = ::gem5::DummyVecRegContainer;
-constexpr unsigned NumVecElemPerVecReg = ::gem5::DummyNumVecElemPerVecReg;
-
-// Not applicable to null
 using VecPredRegContainer = ::gem5::DummyVecPredRegContainer;
 
 } // namespace NullISA
diff --git a/util/stats/__init__.py b/src/arch/power/AtomicSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/power/AtomicSimpleCPU.py
index b3f54da..55b6b96 100644
--- a/util/stats/__init__.py
+++ b/src/arch/power/AtomicSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.PowerCPU import PowerAtomicSimpleCPU
+
+AtomicSimpleCPU = PowerAtomicSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/power/NonCachingSimpleCPU.py
similarity index 90%
copy from util/stats/__init__.py
copy to src/arch/power/NonCachingSimpleCPU.py
index b3f54da..171a90d 100644
--- a/util/stats/__init__.py
+++ b/src/arch/power/NonCachingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.PowerCPU import PowerNonCachingSimpleCPU
+
+NonCachingSimpleCPU = PowerNonCachingSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/power/O3CPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/power/O3CPU.py
index b3f54da..fdb63ed 100644
--- a/util/stats/__init__.py
+++ b/src/arch/power/O3CPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.PowerCPU import PowerO3CPU
+
+O3CPU = PowerO3CPU
+
+# Deprecated
+DerivO3CPU = O3CPU
diff --git a/src/cpu/simple/SConsopts b/src/arch/power/PowerCPU.py
similarity index 61%
copy from src/cpu/simple/SConsopts
copy to src/arch/power/PowerCPU.py
index f12fee2..bf7dc91 100644
--- a/src/cpu/simple/SConsopts
+++ b/src/arch/power/PowerCPU.py
@@ -1,7 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -26,6 +23,29 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU
+from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU
+from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU
+from m5.objects.BaseO3CPU import BaseO3CPU
+from m5.objects.PowerDecoder import PowerDecoder
+from m5.objects.PowerMMU import PowerMMU
+from m5.objects.PowerInterrupts import PowerInterrupts
+from m5.objects.PowerISA import PowerISA
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+class PowerCPU:
+    ArchDecoder = PowerDecoder
+    ArchMMU = PowerMMU
+    ArchInterrupts = PowerInterrupts
+    ArchISA = PowerISA
+
+class PowerAtomicSimpleCPU(BaseAtomicSimpleCPU, PowerCPU):
+    mmu = PowerMMU()
+
+class PowerNonCachingSimpleCPU(BaseNonCachingSimpleCPU, PowerCPU):
+    mmu = PowerMMU()
+
+class PowerTimingSimpleCPU(BaseTimingSimpleCPU, PowerCPU):
+    mmu = PowerMMU()
+
+class PowerO3CPU(BaseO3CPU, PowerCPU):
+    mmu = PowerMMU()
diff --git a/src/arch/power/SConscript b/src/arch/power/SConscript
index 7dd4089..ab96d49 100644
--- a/src/arch/power/SConscript
+++ b/src/arch/power/SConscript
@@ -55,6 +55,12 @@
     'PowerSEWorkload', 'PowerEmuLinux'], tags='power isa')
 SimObject('PowerTLB.py', sim_objects=['PowerTLB'], tags='power isa')
 
+SimObject('PowerCPU.py', sim_objects=[], tags='power isa')
+SimObject('AtomicSimpleCPU.py', sim_objects=[], tags='power isa')
+SimObject('TimingSimpleCPU.py', sim_objects=[], tags='power isa')
+SimObject('NonCachingSimpleCPU.py', sim_objects=[], tags='power isa')
+SimObject('O3CPU.py', sim_objects=[], tags='power isa')
+
 DebugFlag('Power', tags='power isa')
 
 ISADesc('isa/main.isa', tags='power isa')
diff --git a/util/stats/__init__.py b/src/arch/power/TimingSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/power/TimingSimpleCPU.py
index b3f54da..5a9cfa7 100644
--- a/util/stats/__init__.py
+++ b/src/arch/power/TimingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.PowerCPU import PowerTimingSimpleCPU
+
+TimingSimpleCPU = PowerTimingSimpleCPU
diff --git a/src/arch/power/insts/branch.cc b/src/arch/power/insts/branch.cc
index 8540cef..b68f89b 100644
--- a/src/arch/power/insts/branch.cc
+++ b/src/arch/power/insts/branch.cc
@@ -59,7 +59,7 @@
 std::unique_ptr<PCStateBase>
 BranchOp::branchTarget(ThreadContext *tc) const
 {
-    Msr msr = tc->readIntReg(INTREG_MSR);
+    Msr msr = tc->getReg(int_reg::Msr);
     Addr addr;
 
     if (aa)
@@ -108,7 +108,7 @@
 std::unique_ptr<PCStateBase>
 BranchDispCondOp::branchTarget(ThreadContext *tc) const
 {
-    Msr msr = tc->readIntReg(INTREG_MSR);
+    Msr msr = tc->getReg(int_reg::Msr);
     Addr addr;
 
     if (aa)
@@ -160,8 +160,8 @@
 std::unique_ptr<PCStateBase>
 BranchRegCondOp::branchTarget(ThreadContext *tc) const
 {
-    Msr msr = tc->readIntReg(INTREG_MSR);
-    Addr addr = tc->readIntReg(srcRegIdx(_numSrcRegs - 1).index()) & -4ULL;
+    Msr msr = tc->getReg(int_reg::Msr);
+    Addr addr = tc->getReg(srcRegIdx(_numSrcRegs - 1)) & -4ULL;
     return std::make_unique<PowerISA::PCState>(
             msr.sf ? addr : addr & UINT32_MAX);
 }
diff --git a/src/arch/power/isa.cc b/src/arch/power/isa.cc
index 086369f..ff9f9b6 100644
--- a/src/arch/power/isa.cc
+++ b/src/arch/power/isa.cc
@@ -41,6 +41,9 @@
 #include "arch/power/regs/int.hh"
 #include "arch/power/regs/misc.hh"
 #include "cpu/thread_context.hh"
+#include "debug/FloatRegs.hh"
+#include "debug/IntRegs.hh"
+#include "debug/MiscRegs.hh"
 #include "params/PowerISA.hh"
 
 namespace gem5
@@ -51,13 +54,13 @@
 
 ISA::ISA(const Params &p) : BaseISA(p)
 {
-    _regClasses.emplace_back(NumIntRegs, NumIntRegs - 1);
-    _regClasses.emplace_back(NumFloatRegs);
-    _regClasses.emplace_back(1);
-    _regClasses.emplace_back(2);
-    _regClasses.emplace_back(1);
-    _regClasses.emplace_back(0);
-    _regClasses.emplace_back(NUM_MISCREGS);
+    _regClasses.emplace_back(int_reg::NumRegs, debug::IntRegs);
+    _regClasses.emplace_back(float_reg::NumRegs, debug::FloatRegs);
+    _regClasses.emplace_back(1, debug::IntRegs);
+    _regClasses.emplace_back(2, debug::IntRegs);
+    _regClasses.emplace_back(1, debug::IntRegs);
+    _regClasses.emplace_back(0, debug::IntRegs);
+    _regClasses.emplace_back(NUM_MISCREGS, debug::MiscRegs);
     clear();
 }
 
@@ -65,12 +68,16 @@
 ISA::copyRegsFrom(ThreadContext *src)
 {
     // First loop through the integer registers.
-    for (int i = 0; i < NumIntRegs; ++i)
-        tc->setIntReg(i, src->readIntReg(i));
+    for (int i = 0; i < int_reg::NumRegs; ++i) {
+        RegId reg(IntRegClass, i);
+        tc->setReg(reg, src->getReg(reg));
+    }
 
     // Then loop through the floating point registers.
-    for (int i = 0; i < NumFloatRegs; ++i)
-        tc->setFloatReg(i, src->readFloatReg(i));
+    for (int i = 0; i < float_reg::NumRegs; ++i) {
+        RegId reg(FloatRegClass, i);
+        tc->setReg(reg, src->getReg(reg));
+    }
 
     //TODO Copy misc. registers
 
diff --git a/src/arch/power/isa/formats/mem.isa b/src/arch/power/isa/formats/mem.isa
index c4c9968..97b4f81 100644
--- a/src/arch/power/isa/formats/mem.isa
+++ b/src/arch/power/isa/formats/mem.isa
@@ -69,7 +69,7 @@
     {
         Addr EA;
         Fault fault = NoFault;
-        Msr msr = xc->tcBase()->readIntReg(INTREG_MSR);
+        Msr msr = xc->tcBase()->getReg(int_reg::Msr);
 
         %(op_decl)s;
         %(op_rd)s;
@@ -119,7 +119,7 @@
     {
         [[maybe_unused]] Addr EA;
         Fault fault = NoFault;
-        Msr msr = xc->tcBase()->readIntReg(INTREG_MSR);
+        Msr msr = xc->tcBase()->getReg(int_reg::Msr);
 
         %(op_decl)s;
         EA = pkt->req->getVaddr();
@@ -150,7 +150,7 @@
     {
         Addr EA;
         Fault fault = NoFault;
-        Msr msr = xc->tcBase()->readIntReg(INTREG_MSR);
+        Msr msr = xc->tcBase()->getReg(int_reg::Msr);
 
         %(op_decl)s;
         %(op_rd)s;
@@ -184,7 +184,7 @@
     {
         Addr EA;
         Fault fault = NoFault;
-        Msr msr = xc->tcBase()->readIntReg(INTREG_MSR);
+        Msr msr = xc->tcBase()->getReg(int_reg::Msr);
 
         %(op_decl)s;
         %(op_rd)s;
diff --git a/src/arch/power/isa/operands.isa b/src/arch/power/isa/operands.isa
index 765391e..a9bc185 100644
--- a/src/arch/power/isa/operands.isa
+++ b/src/arch/power/isa/operands.isa
@@ -42,39 +42,39 @@
 
 def operands {{
     # General Purpose Integer Reg Operands
-    'Rs': ('IntReg', 'ud', 'RS', 'IsInteger', 1),
-    'Ra': ('IntReg', 'ud', 'RA', 'IsInteger', 2),
-    'Rb': ('IntReg', 'ud', 'RB', 'IsInteger', 3),
-    'Rc': ('IntReg', 'ud', 'RC', 'IsInteger', 4),
-    'Rt': ('IntReg', 'ud', 'RT', 'IsInteger', 5),
+    'Rs': IntRegOp('ud', 'RS', 'IsInteger', 1),
+    'Ra': IntRegOp('ud', 'RA', 'IsInteger', 2),
+    'Rb': IntRegOp('ud', 'RB', 'IsInteger', 3),
+    'Rc': IntRegOp('ud', 'RC', 'IsInteger', 4),
+    'Rt': IntRegOp('ud', 'RT', 'IsInteger', 5),
 
     # General Purpose Floating Point Reg Operands
-    'Fa': ('FloatReg', 'df', 'FRA', 'IsFloating', 1),
-    'Fb': ('FloatReg', 'df', 'FRB', 'IsFloating', 2),
-    'Fc': ('FloatReg', 'df', 'FRC', 'IsFloating', 3),
-    'Fs': ('FloatReg', 'df', 'FRS', 'IsFloating', 4),
-    'Ft': ('FloatReg', 'df', 'FRT', 'IsFloating', 5),
+    'Fa': FloatRegOp('df', 'FRA', 'IsFloating', 1),
+    'Fb': FloatRegOp('df', 'FRB', 'IsFloating', 2),
+    'Fc': FloatRegOp('df', 'FRC', 'IsFloating', 3),
+    'Fs': FloatRegOp('df', 'FRS', 'IsFloating', 4),
+    'Ft': FloatRegOp('df', 'FRT', 'IsFloating', 5),
 
     # Memory Operand
-    'Mem': ('Mem', 'ud', None, (None, 'IsLoad', 'IsStore'), 8),
+    'Mem': MemOp('ud', None, (None, 'IsLoad', 'IsStore'), 8),
 
     # Program counter and next
-    'CIA': ('PCState', 'ud', 'pc', (None, None, 'IsControl'), 9),
-    'NIA': ('PCState', 'ud', 'npc', (None, None, 'IsControl'), 9),
+    'CIA': PCStateOp('ud', 'pc', (None, None, 'IsControl'), 9),
+    'NIA': PCStateOp('ud', 'npc', (None, None, 'IsControl'), 9),
 
     # Control registers
-    'CR': ('IntReg', 'uw', 'INTREG_CR', 'IsInteger', 9),
-    'LR': ('IntReg', 'ud', 'INTREG_LR', 'IsInteger', 9),
-    'CTR': ('IntReg', 'ud', 'INTREG_CTR', 'IsInteger', 9),
-    'TAR': ('IntReg', 'ud', 'INTREG_TAR', 'IsInteger', 9),
-    'XER': ('IntReg', 'uw', 'INTREG_XER', 'IsInteger', 9),
-    'MSR': ('IntReg', 'ud', 'INTREG_MSR', 'IsInteger', 9),
+    'CR': IntRegOp('uw', 'int_reg::Cr', 'IsInteger', 9),
+    'LR': IntRegOp('ud', 'int_reg::Lr', 'IsInteger', 9),
+    'CTR': IntRegOp('ud', 'int_reg::Ctr', 'IsInteger', 9),
+    'TAR': IntRegOp('ud', 'int_reg::Tar', 'IsInteger', 9),
+    'XER': IntRegOp('uw', 'int_reg::Xer', 'IsInteger', 9),
+    'MSR': IntRegOp('ud', 'int_reg::Msr', 'IsInteger', 9),
 
     # Setting as IntReg so things are stored as an integer, not double
-    'FPSCR': ('IntReg', 'uw', 'INTREG_FPSCR', 'IsFloating', 9),
+    'FPSCR': IntRegOp('uw', 'int_reg::Fpscr', 'IsFloating', 9),
 
     # Registers for linked loads and stores
-    'Rsv': ('IntReg', 'uw', 'INTREG_RSV', 'IsInteger', 9),
-    'RsvLen': ('IntReg', 'uw', 'INTREG_RSV_LEN', 'IsInteger', 9),
-    'RsvAddr': ('IntReg', 'ud', 'INTREG_RSV_ADDR', 'IsInteger', 9),
+    'Rsv': IntRegOp('uw', 'int_reg::Rsv', 'IsInteger', 9),
+    'RsvLen': IntRegOp('uw', 'int_reg::RsvLen', 'IsInteger', 9),
+    'RsvAddr': IntRegOp('ud', 'int_reg::RsvAddr', 'IsInteger', 9),
 }};
diff --git a/src/arch/power/linux/linux.hh b/src/arch/power/linux/linux.hh
index 02a45ef..dfe618e 100644
--- a/src/arch/power/linux/linux.hh
+++ b/src/arch/power/linux/linux.hh
@@ -224,10 +224,10 @@
         ctc->getIsaPtr()->copyRegsFrom(ptc);
 
         if (flags & TGT_CLONE_SETTLS)
-            ctc->setIntReg(PowerISA::ThreadPointerReg, tls);
+            ctc->setReg(PowerISA::ThreadPointerReg, tls);
 
         if (stack)
-            ctc->setIntReg(PowerISA::StackPointerReg, stack);
+            ctc->setReg(PowerISA::StackPointerReg, stack);
     }
 };
 
diff --git a/src/arch/power/linux/se_workload.cc b/src/arch/power/linux/se_workload.cc
index 9b54fd8..c376b5f 100644
--- a/src/arch/power/linux/se_workload.cc
+++ b/src/arch/power/linux/se_workload.cc
@@ -88,7 +88,7 @@
     // This will move into the base SEWorkload function at some point.
     process->Process::syscall(tc);
 
-    syscallDescs.get(tc->readIntReg(0))->doSyscall(tc);
+    syscallDescs.get(tc->getReg(int_reg::R0))->doSyscall(tc);
 }
 
 /// Target uname() handler.
diff --git a/src/arch/power/process.cc b/src/arch/power/process.cc
index 8ed4b75..6b147ef 100644
--- a/src/arch/power/process.cc
+++ b/src/arch/power/process.cc
@@ -111,7 +111,7 @@
     // The second doubleword of the descriptor contains the TOC base
     // address for the function
     initVirtMem->readBlob(getStartPC() + 8, &tocBase, sizeof(Addr));
-    tc->setIntReg(TOCPointerReg, gtoh(tocBase, byteOrder));
+    tc->setReg(TOCPointerReg, gtoh(tocBase, byteOrder));
 
     // Fix symbol table entries as they would otherwise point to the
     // function descriptor rather than the actual entry point address
@@ -337,11 +337,11 @@
     ThreadContext *tc = system->threads[contextIds[0]];
 
     //Set the stack pointer register
-    tc->setIntReg(StackPointerReg, stack_min);
+    tc->setReg(StackPointerReg, stack_min);
 
     //Reset the special-purpose registers
-    for (int i = 0; i < NumIntSpecialRegs; i++)
-        tc->setIntReg(NumIntArchRegs + i, 0);
+    for (int i = int_reg::NumArchRegs; i < int_reg::NumRegs; i++)
+        tc->setReg(RegId(IntRegClass, i), (RegVal)0);
 
     //Set the machine status for a typical userspace
     Msr msr = 0;
@@ -354,7 +354,7 @@
     msr.dr = 1;
     msr.ri = 1;
     msr.le = isLittleEndian;
-    tc->setIntReg(INTREG_MSR, msr);
+    tc->setReg(int_reg::Msr, msr);
 
     auto pc = tc->pcState().as<PowerISA::PCState>();
     pc.set(getStartPC());
diff --git a/src/arch/power/regs/float.hh b/src/arch/power/regs/float.hh
index 9464101..ce31c3a 100644
--- a/src/arch/power/regs/float.hh
+++ b/src/arch/power/regs/float.hh
@@ -35,9 +35,13 @@
 namespace PowerISA
 {
 
-const int NumFloatArchRegs = 32;
-const int NumFloatRegs = NumFloatArchRegs;
+namespace float_reg
+{
 
+const int NumArchRegs = 32;
+const int NumRegs = NumArchRegs;
+
+} // namespace float_reg
 } // namespace PowerISA
 } // namespace gem5
 
diff --git a/src/arch/power/regs/int.hh b/src/arch/power/regs/int.hh
index 95fbb8a..324062d 100644
--- a/src/arch/power/regs/int.hh
+++ b/src/arch/power/regs/int.hh
@@ -30,46 +30,127 @@
 #ifndef __ARCH_POWER_REGS_INT_HH__
 #define __ARCH_POWER_REGS_INT_HH__
 
+#include "cpu/reg_class.hh"
+
 namespace gem5
 {
 
 namespace PowerISA
 {
 
-// Constants Related to the number of registers
-const int NumIntArchRegs = 32;
+namespace int_reg
+{
 
-// CR, XER, LR, CTR, TAR, FPSCR, MSR, RSV, RSV-LEN, RSV-ADDR
-// and zero register, which doesn't actually exist but needs a number
-const int NumIntSpecialRegs = 11;
+enum : RegIndex
+{
+    _R0Idx,
+    _R1Idx,
+    _R2Idx,
+    _R3Idx,
+    _R4Idx,
+    _R5Idx,
+    _R6Idx,
+    _R7Idx,
+    _R8Idx,
+    _R9Idx,
+    _R10Idx,
+    _R11Idx,
+    _R12Idx,
+    _R13Idx,
+    _R14Idx,
+    _R15Idx,
+    _R16Idx,
+    _R17Idx,
+    _R18Idx,
+    _R19Idx,
+    _R20Idx,
+    _R21Idx,
+    _R22Idx,
+    _R23Idx,
+    _R24Idx,
+    _R25Idx,
+    _R26Idx,
+    _R27Idx,
+    _R28Idx,
+    _R29Idx,
+    _R30Idx,
+    _R31Idx,
 
-const int NumIntRegs = NumIntArchRegs + NumIntSpecialRegs;
+    NumArchRegs,
+
+    _CrIdx = NumArchRegs,
+    _XerIdx,
+    _LrIdx,
+    _CtrIdx,
+    _TarIdx,
+    _FpscrIdx,
+    _MsrIdx,
+    _RsvIdx,
+    _RsvLenIdx,
+    _RsvAddrIdx,
+
+    NumRegs
+};
+
+inline constexpr RegId
+    R0(IntRegClass, _R0Idx),
+    R1(IntRegClass, _R1Idx),
+    R2(IntRegClass, _R2Idx),
+    R3(IntRegClass, _R3Idx),
+    R4(IntRegClass, _R4Idx),
+    R5(IntRegClass, _R5Idx),
+    R6(IntRegClass, _R6Idx),
+    R7(IntRegClass, _R7Idx),
+    R8(IntRegClass, _R8Idx),
+    R9(IntRegClass, _R9Idx),
+    R10(IntRegClass, _R10Idx),
+    R11(IntRegClass, _R11Idx),
+    R12(IntRegClass, _R12Idx),
+    R13(IntRegClass, _R13Idx),
+    R14(IntRegClass, _R14Idx),
+    R15(IntRegClass, _R15Idx),
+    R16(IntRegClass, _R16Idx),
+    R17(IntRegClass, _R17Idx),
+    R18(IntRegClass, _R18Idx),
+    R19(IntRegClass, _R19Idx),
+    R20(IntRegClass, _R20Idx),
+    R21(IntRegClass, _R21Idx),
+    R22(IntRegClass, _R22Idx),
+    R23(IntRegClass, _R23Idx),
+    R24(IntRegClass, _R24Idx),
+    R25(IntRegClass, _R25Idx),
+    R26(IntRegClass, _R26Idx),
+    R27(IntRegClass, _R27Idx),
+    R28(IntRegClass, _R28Idx),
+    R29(IntRegClass, _R29Idx),
+    R30(IntRegClass, _R30Idx),
+    R31(IntRegClass, _R31Idx),
+
+    Cr(IntRegClass, _CrIdx),
+    Xer(IntRegClass, _XerIdx),
+    Lr(IntRegClass, _LrIdx),
+    Ctr(IntRegClass, _CtrIdx),
+    Tar(IntRegClass, _TarIdx),
+    Fpscr(IntRegClass, _FpscrIdx),
+    Msr(IntRegClass, _MsrIdx),
+    Rsv(IntRegClass, _RsvIdx),
+    RsvLen(IntRegClass, _RsvLenIdx),
+    RsvAddr(IntRegClass, _RsvAddrIdx);
+
+} // namespace int_reg
 
 // Semantically meaningful register indices
-const int ReturnValueReg = 3;
-const int ArgumentReg0 = 3;
-const int ArgumentReg1 = 4;
-const int ArgumentReg2 = 5;
-const int ArgumentReg3 = 6;
-const int ArgumentReg4 = 7;
-const int ArgumentReg5 = 8;
-const int StackPointerReg = 1;
-const int TOCPointerReg = 2;
-const int ThreadPointerReg = 13;
-
-enum MiscIntRegNums
-{
-    INTREG_CR = NumIntArchRegs,
-    INTREG_XER,
-    INTREG_LR,
-    INTREG_CTR,
-    INTREG_TAR,
-    INTREG_FPSCR,
-    INTREG_MSR,
-    INTREG_RSV,
-    INTREG_RSV_LEN,
-    INTREG_RSV_ADDR
-};
+inline constexpr auto
+    &ReturnValueReg = int_reg::R3,
+    &ArgumentReg0 = int_reg::R3,
+    &ArgumentReg1 = int_reg::R4,
+    &ArgumentReg2 = int_reg::R5,
+    &ArgumentReg3 = int_reg::R6,
+    &ArgumentReg4 = int_reg::R7,
+    &ArgumentReg5 = int_reg::R8,
+    &StackPointerReg = int_reg::R1,
+    &TOCPointerReg = int_reg::R2,
+    &ThreadPointerReg = int_reg::R13;
 
 } // namespace PowerISA
 } // namespace gem5
diff --git a/src/arch/power/remote_gdb.cc b/src/arch/power/remote_gdb.cc
index 5955253..bd2e087 100644
--- a/src/arch/power/remote_gdb.cc
+++ b/src/arch/power/remote_gdb.cc
@@ -180,26 +180,28 @@
 {
     DPRINTF(GDBAcc, "getRegs in remotegdb \n");
 
-    Msr msr = context->readIntReg(INTREG_MSR);
+    Msr msr = context->getReg(int_reg::Msr);
     ByteOrder order = (msr.le ? ByteOrder::little : ByteOrder::big);
 
     // Default order on 32-bit PowerPC:
     // R0-R31 (32-bit each), F0-F31 (64-bit IEEE754 double),
     // PC, MSR, CR, LR, CTR, XER, FPSCR (32-bit each)
 
-    for (int i = 0; i < NumIntArchRegs; i++)
-        r.gpr[i] = htog((uint32_t)context->readIntReg(i), order);
+    for (int i = 0; i < int_reg::NumArchRegs; i++) {
+        RegId reg(IntRegClass, i);
+        r.gpr[i] = htog((uint32_t)context->getReg(reg), order);
+    }
 
-    for (int i = 0; i < NumFloatArchRegs; i++)
-        r.fpr[i] = context->readFloatReg(i);
+    for (int i = 0; i < float_reg::NumArchRegs; i++)
+        r.fpr[i] = context->getReg(RegId(FloatRegClass, i));
 
     r.pc = htog((uint32_t)context->pcState().instAddr(), order);
     r.msr = 0; // MSR is privileged, hence not exposed here
-    r.cr = htog((uint32_t)context->readIntReg(INTREG_CR), order);
-    r.lr = htog((uint32_t)context->readIntReg(INTREG_LR), order);
-    r.ctr = htog((uint32_t)context->readIntReg(INTREG_CTR), order);
-    r.xer = htog((uint32_t)context->readIntReg(INTREG_XER), order);
-    r.fpscr = htog((uint32_t)context->readIntReg(INTREG_FPSCR), order);
+    r.cr = htog((uint32_t)context->getReg(int_reg::Cr), order);
+    r.lr = htog((uint32_t)context->getReg(int_reg::Lr), order);
+    r.ctr = htog((uint32_t)context->getReg(int_reg::Ctr), order);
+    r.xer = htog((uint32_t)context->getReg(int_reg::Xer), order);
+    r.fpscr = htog((uint32_t)context->getReg(int_reg::Fpscr), order);
 }
 
 void
@@ -207,25 +209,25 @@
 {
     DPRINTF(GDBAcc, "setRegs in remotegdb \n");
 
-    Msr msr = context->readIntReg(INTREG_MSR);
+    Msr msr = context->getReg(int_reg::Msr);
     ByteOrder order = (msr.le ? ByteOrder::little : ByteOrder::big);
 
-    for (int i = 0; i < NumIntArchRegs; i++)
-        context->setIntReg(i, gtoh(r.gpr[i], order));
+    for (int i = 0; i < int_reg::NumArchRegs; i++)
+        context->setReg(RegId(IntRegClass, i), gtoh(r.gpr[i], order));
 
-    for (int i = 0; i < NumFloatArchRegs; i++)
-        context->setFloatReg(i, r.fpr[i]);
+    for (int i = 0; i < float_reg::NumArchRegs; i++)
+        context->setReg(RegId(FloatRegClass, i), r.fpr[i]);
 
     auto pc = context->pcState().as<PowerISA::PCState>();
     pc.byteOrder(order);
     pc.set(gtoh(r.pc, order));
     context->pcState(pc);
     // MSR is privileged, hence not modified here
-    context->setIntReg(INTREG_CR, gtoh(r.cr, order));
-    context->setIntReg(INTREG_LR, gtoh(r.lr, order));
-    context->setIntReg(INTREG_CTR, gtoh(r.ctr, order));
-    context->setIntReg(INTREG_XER, gtoh(r.xer, order));
-    context->setIntReg(INTREG_FPSCR, gtoh(r.fpscr, order));
+    context->setReg(int_reg::Cr, gtoh(r.cr, order));
+    context->setReg(int_reg::Lr, gtoh(r.lr, order));
+    context->setReg(int_reg::Ctr, gtoh(r.ctr, order));
+    context->setReg(int_reg::Xer, gtoh(r.xer, order));
+    context->setReg(int_reg::Fpscr, gtoh(r.fpscr, order));
 }
 
 void
@@ -233,7 +235,7 @@
 {
     DPRINTF(GDBAcc, "getRegs in remotegdb \n");
 
-    Msr msr = context->readIntReg(INTREG_MSR);
+    Msr msr = context->getReg(int_reg::Msr);
     ByteOrder order = (msr.le ? ByteOrder::little : ByteOrder::big);
 
     // Default order on 64-bit PowerPC:
@@ -241,19 +243,19 @@
     // CIA, MSR, CR, LR, CTR, XER, FPSCR (only CR, XER, FPSCR are 32-bit
     // each and the rest are 64-bit)
 
-    for (int i = 0; i < NumIntArchRegs; i++)
-        r.gpr[i] = htog(context->readIntReg(i), order);
+    for (int i = 0; i < int_reg::NumArchRegs; i++)
+        r.gpr[i] = htog(context->getReg(RegId(IntRegClass, i)), order);
 
-    for (int i = 0; i < NumFloatArchRegs; i++)
-        r.fpr[i] = context->readFloatReg(i);
+    for (int i = 0; i < float_reg::NumArchRegs; i++)
+        r.fpr[i] = context->getReg(RegId(FloatRegClass, i));
 
     r.pc = htog(context->pcState().instAddr(), order);
     r.msr = 0; // MSR is privileged, hence not exposed here
-    r.cr = htog((uint32_t)context->readIntReg(INTREG_CR), order);
-    r.lr = htog(context->readIntReg(INTREG_LR), order);
-    r.ctr = htog(context->readIntReg(INTREG_CTR), order);
-    r.xer = htog((uint32_t)context->readIntReg(INTREG_XER), order);
-    r.fpscr = htog((uint32_t)context->readIntReg(INTREG_FPSCR), order);
+    r.cr = htog((uint32_t)context->getReg(int_reg::Cr), order);
+    r.lr = htog(context->getReg(int_reg::Lr), order);
+    r.ctr = htog(context->getReg(int_reg::Ctr), order);
+    r.xer = htog((uint32_t)context->getReg(int_reg::Xer), order);
+    r.fpscr = htog((uint32_t)context->getReg(int_reg::Fpscr), order);
 }
 
 void
@@ -261,31 +263,31 @@
 {
     DPRINTF(GDBAcc, "setRegs in remotegdb \n");
 
-    Msr msr = context->readIntReg(INTREG_MSR);
+    Msr msr = context->getReg(int_reg::Msr);
     ByteOrder order = (msr.le ? ByteOrder::little : ByteOrder::big);
 
-    for (int i = 0; i < NumIntArchRegs; i++)
-        context->setIntReg(i, gtoh(r.gpr[i], order));
+    for (int i = 0; i < int_reg::NumArchRegs; i++)
+        context->setReg(RegId(IntRegClass, i), gtoh(r.gpr[i], order));
 
-    for (int i = 0; i < NumFloatArchRegs; i++)
-        context->setFloatReg(i, r.fpr[i]);
+    for (int i = 0; i < float_reg::NumArchRegs; i++)
+        context->setReg(RegId(FloatRegClass, i), r.fpr[i]);
 
     auto pc = context->pcState().as<PowerISA::PCState>();
     pc.byteOrder(order);
     pc.set(gtoh(r.pc, order));
     context->pcState(pc);
     // MSR is privileged, hence not modified here
-    context->setIntReg(INTREG_CR, gtoh(r.cr, order));
-    context->setIntReg(INTREG_LR, gtoh(r.lr, order));
-    context->setIntReg(INTREG_CTR, gtoh(r.ctr, order));
-    context->setIntReg(INTREG_XER, gtoh(r.xer, order));
-    context->setIntReg(INTREG_FPSCR, gtoh(r.fpscr, order));
+    context->setReg(int_reg::Cr, gtoh(r.cr, order));
+    context->setReg(int_reg::Lr, gtoh(r.lr, order));
+    context->setReg(int_reg::Ctr, gtoh(r.ctr, order));
+    context->setReg(int_reg::Xer, gtoh(r.xer, order));
+    context->setReg(int_reg::Fpscr, gtoh(r.fpscr, order));
 }
 
 BaseGdbRegCache*
 RemoteGDB::gdbRegs()
 {
-    Msr msr = context()->readIntReg(INTREG_MSR);
+    Msr msr = context()->getReg(int_reg::Msr);
     if (msr.sf)
         return &regCache64;
     else
@@ -310,7 +312,7 @@
     };
 #undef GDB_XML
 
-    Msr msr = context()->readIntReg(INTREG_MSR);
+    Msr msr = context()->getReg(int_reg::Msr);
     auto& annexMap = msr.sf ? annexMap64 : annexMap32;
     auto it = annexMap.find(annex);
     if (it == annexMap.end())
diff --git a/src/arch/power/remote_gdb.hh b/src/arch/power/remote_gdb.hh
index 75838bd..138913e 100644
--- a/src/arch/power/remote_gdb.hh
+++ b/src/arch/power/remote_gdb.hh
@@ -56,8 +56,8 @@
       private:
         struct GEM5_PACKED
         {
-            uint32_t gpr[NumIntArchRegs];
-            uint64_t fpr[NumFloatArchRegs];
+            uint32_t gpr[int_reg::NumArchRegs];
+            uint64_t fpr[float_reg::NumArchRegs];
             uint32_t pc;
             uint32_t msr;
             uint32_t cr;
@@ -85,8 +85,8 @@
       private:
         struct GEM5_PACKED
         {
-            uint64_t gpr[NumIntArchRegs];
-            uint64_t fpr[NumFloatArchRegs];
+            uint64_t gpr[int_reg::NumArchRegs];
+            uint64_t fpr[float_reg::NumArchRegs];
             uint64_t pc;
             uint64_t msr;
             uint32_t cr;
diff --git a/src/arch/power/se_workload.hh b/src/arch/power/se_workload.hh
index c351d65..fdbc08e 100644
--- a/src/arch/power/se_workload.hh
+++ b/src/arch/power/se_workload.hh
@@ -77,14 +77,14 @@
     static void
     store(ThreadContext *tc, const SyscallReturn &ret)
     {
-        PowerISA::Cr cr = tc->readIntReg(PowerISA::INTREG_CR);
+        PowerISA::Cr cr = tc->getReg(PowerISA::int_reg::Cr);
         if (ret.successful()) {
             cr.cr0.so = 0;
         } else {
             cr.cr0.so = 1;
         }
-        tc->setIntReg(PowerISA::INTREG_CR, cr);
-        tc->setIntReg(PowerISA::ReturnValueReg, ret.encodedValue());
+        tc->setReg(PowerISA::int_reg::Cr, cr);
+        tc->setReg(PowerISA::ReturnValueReg, ret.encodedValue());
     }
 };
 
diff --git a/src/arch/power/vecregs.hh b/src/arch/power/vecregs.hh
index 9f557ce..33ac377 100644
--- a/src/arch/power/vecregs.hh
+++ b/src/arch/power/vecregs.hh
@@ -30,8 +30,6 @@
 #ifndef __ARCH_POWER_VECREGS_HH__
 #define __ARCH_POWER_VECREGS_HH__
 
-#include <cstdint>
-
 #include "arch/generic/vec_pred_reg.hh"
 #include "arch/generic/vec_reg.hh"
 
@@ -42,11 +40,7 @@
 {
 
 // Not applicable to Power
-using VecElem = ::gem5::DummyVecElem;
 using VecRegContainer = ::gem5::DummyVecRegContainer;
-constexpr unsigned NumVecElemPerVecReg = ::gem5::DummyNumVecElemPerVecReg;
-
-// Not applicable to Power
 using VecPredRegContainer = ::gem5::DummyVecPredRegContainer;
 
 } // namespace PowerISA
diff --git a/util/stats/__init__.py b/src/arch/riscv/AtomicSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/riscv/AtomicSimpleCPU.py
index b3f54da..f471b64 100644
--- a/util/stats/__init__.py
+++ b/src/arch/riscv/AtomicSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.RiscvCPU import RiscvAtomicSimpleCPU
+
+AtomicSimpleCPU = RiscvAtomicSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/riscv/MinorCPU.py
similarity index 92%
copy from util/stats/__init__.py
copy to src/arch/riscv/MinorCPU.py
index b3f54da..5254bad 100644
--- a/util/stats/__init__.py
+++ b/src/arch/riscv/MinorCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.RiscvCPU import RiscvMinorCPU
+
+MinorCPU = RiscvMinorCPU
diff --git a/util/stats/__init__.py b/src/arch/riscv/NonCachingSimpleCPU.py
similarity index 90%
copy from util/stats/__init__.py
copy to src/arch/riscv/NonCachingSimpleCPU.py
index b3f54da..f7dcebf 100644
--- a/util/stats/__init__.py
+++ b/src/arch/riscv/NonCachingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.RiscvCPU import RiscvNonCachingSimpleCPU
+
+NonCachingSimpleCPU = RiscvNonCachingSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/riscv/O3CPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/riscv/O3CPU.py
index b3f54da..74e658b 100644
--- a/util/stats/__init__.py
+++ b/src/arch/riscv/O3CPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.RiscvCPU import RiscvO3CPU
+
+O3CPU = RiscvO3CPU
+
+# Deprecated
+DerivO3CPU = O3CPU
diff --git a/src/arch/riscv/RiscvCPU.py b/src/arch/riscv/RiscvCPU.py
new file mode 100644
index 0000000..36c2920
--- /dev/null
+++ b/src/arch/riscv/RiscvCPU.py
@@ -0,0 +1,55 @@
+# Copyright 2021 Google, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU
+from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU
+from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU
+from m5.objects.BaseO3CPU import BaseO3CPU
+from m5.objects.BaseMinorCPU import BaseMinorCPU
+from m5.objects.RiscvDecoder import RiscvDecoder
+from m5.objects.RiscvMMU import RiscvMMU
+from m5.objects.RiscvInterrupts import RiscvInterrupts
+from m5.objects.RiscvISA import RiscvISA
+
+class RiscvCPU:
+    ArchDecoder = RiscvDecoder
+    ArchMMU = RiscvMMU
+    ArchInterrupts = RiscvInterrupts
+    ArchISA = RiscvISA
+
+class RiscvAtomicSimpleCPU(BaseAtomicSimpleCPU, RiscvCPU):
+    mmu = RiscvMMU()
+
+class RiscvNonCachingSimpleCPU(BaseNonCachingSimpleCPU, RiscvCPU):
+    mmu = RiscvMMU()
+
+class RiscvTimingSimpleCPU(BaseTimingSimpleCPU, RiscvCPU):
+    mmu = RiscvMMU()
+
+class RiscvO3CPU(BaseO3CPU, RiscvCPU):
+    mmu = RiscvMMU()
+
+class RiscvMinorCPU(BaseMinorCPU, RiscvCPU):
+    mmu = RiscvMMU()
diff --git a/src/arch/riscv/SConscript b/src/arch/riscv/SConscript
index 8b7942e..a9664f4 100644
--- a/src/arch/riscv/SConscript
+++ b/src/arch/riscv/SConscript
@@ -74,6 +74,13 @@
 SimObject('RiscvTLB.py', sim_objects=['RiscvPagetableWalker', 'RiscvTLB'],
     tags='riscv isa')
 
+SimObject('RiscvCPU.py', sim_objects=[], tags='riscv isa')
+SimObject('AtomicSimpleCPU.py', sim_objects=[], tags='riscv isa')
+SimObject('TimingSimpleCPU.py', sim_objects=[], tags='riscv isa')
+SimObject('NonCachingSimpleCPU.py', sim_objects=[], tags='riscv isa')
+SimObject('O3CPU.py', sim_objects=[], tags='riscv isa')
+SimObject('MinorCPU.py', sim_objects=[], tags='riscv isa')
+
 DebugFlag('RiscvMisc', tags='riscv isa')
 DebugFlag('PMP', tags='riscv isa')
 
diff --git a/util/stats/__init__.py b/src/arch/riscv/TimingSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/riscv/TimingSimpleCPU.py
index b3f54da..03d530f 100644
--- a/util/stats/__init__.py
+++ b/src/arch/riscv/TimingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.RiscvCPU import RiscvTimingSimpleCPU
+
+TimingSimpleCPU = RiscvTimingSimpleCPU
diff --git a/src/arch/riscv/faults.cc b/src/arch/riscv/faults.cc
index eea42fe..e609222 100644
--- a/src/arch/riscv/faults.cc
+++ b/src/arch/riscv/faults.cc
@@ -37,7 +37,7 @@
 #include "arch/riscv/utility.hh"
 #include "cpu/base.hh"
 #include "cpu/thread_context.hh"
-#include "debug/Fault.hh"
+#include "debug/Faults.hh"
 #include "sim/debug.hh"
 #include "sim/full_system.hh"
 #include "sim/workload.hh"
@@ -59,7 +59,7 @@
 {
     auto pc_state = tc->pcState().as<PCState>();
 
-    DPRINTFS(Fault, tc->getCpuPtr(), "Fault (%s) at PC: %s\n",
+    DPRINTFS(Faults, tc->getCpuPtr(), "Fault (%s) at PC: %s\n",
              name(), pc_state);
 
     if (FullSystem) {
diff --git a/src/arch/riscv/insts/static_inst.hh b/src/arch/riscv/insts/static_inst.hh
index ef8032d..eec9c88 100644
--- a/src/arch/riscv/insts/static_inst.hh
+++ b/src/arch/riscv/insts/static_inst.hh
@@ -80,7 +80,6 @@
         PCStateBase *ret_pc_ptr = call_pc.clone();
         auto &ret_pc = ret_pc_ptr->as<PCState>();
         ret_pc.advance();
-        ret_pc.pc(cur_pc.as<PCState>().npc());
         return std::unique_ptr<PCStateBase>{ret_pc_ptr};
     }
 
diff --git a/src/arch/riscv/isa.cc b/src/arch/riscv/isa.cc
index c8b752c..6bf34af 100644
--- a/src/arch/riscv/isa.cc
+++ b/src/arch/riscv/isa.cc
@@ -47,7 +47,10 @@
 #include "base/trace.hh"
 #include "cpu/base.hh"
 #include "debug/Checkpoint.hh"
+#include "debug/FloatRegs.hh"
+#include "debug/IntRegs.hh"
 #include "debug/LLSC.hh"
+#include "debug/MiscRegs.hh"
 #include "debug/RiscvMisc.hh"
 #include "mem/packet.hh"
 #include "mem/request.hh"
@@ -193,13 +196,13 @@
 
 ISA::ISA(const Params &p) : BaseISA(p)
 {
-    _regClasses.emplace_back(NumIntRegs, 0);
-    _regClasses.emplace_back(NumFloatRegs);
-    _regClasses.emplace_back(1); // Not applicable to RISCV
-    _regClasses.emplace_back(2); // Not applicable to RISCV
-    _regClasses.emplace_back(1); // Not applicable to RISCV
-    _regClasses.emplace_back(0); // Not applicable to RISCV
-    _regClasses.emplace_back(NUM_MISCREGS);
+    _regClasses.emplace_back(NumIntRegs, debug::IntRegs);
+    _regClasses.emplace_back(NumFloatRegs, debug::FloatRegs);
+    _regClasses.emplace_back(1, debug::IntRegs); // Not applicable to RISCV
+    _regClasses.emplace_back(2, debug::IntRegs); // Not applicable to RISCV
+    _regClasses.emplace_back(1, debug::IntRegs); // Not applicable to RISCV
+    _regClasses.emplace_back(0, debug::IntRegs); // Not applicable to RISCV
+    _regClasses.emplace_back(NUM_MISCREGS, debug::MiscRegs);
 
     miscRegFile.resize(NUM_MISCREGS);
     clear();
@@ -504,30 +507,29 @@
 
 const int WARN_FAILURE = 10000;
 
-// RISC-V allows multiple locks per hart, but each SC has to unlock the most
-// recent one, so we use a stack here.
-std::unordered_map<int, std::stack<Addr>> locked_addrs;
+const Addr INVALID_RESERVATION_ADDR = (Addr) -1;
+std::unordered_map<int, Addr> load_reservation_addrs;
 
 void
 ISA::handleLockedSnoop(PacketPtr pkt, Addr cacheBlockMask)
 {
-    std::stack<Addr>& locked_addr_stack = locked_addrs[tc->contextId()];
+    Addr& load_reservation_addr = load_reservation_addrs[tc->contextId()];
 
-    if (locked_addr_stack.empty())
+    if (load_reservation_addr == INVALID_RESERVATION_ADDR)
         return;
     Addr snoop_addr = pkt->getAddr() & cacheBlockMask;
     DPRINTF(LLSC, "Locked snoop on address %x.\n", snoop_addr);
-    if ((locked_addr_stack.top() & cacheBlockMask) == snoop_addr)
-        locked_addr_stack.pop();
+    if ((load_reservation_addr & cacheBlockMask) == snoop_addr)
+        load_reservation_addr = INVALID_RESERVATION_ADDR;
 }
 
 
 void
 ISA::handleLockedRead(const RequestPtr &req)
 {
-    std::stack<Addr>& locked_addr_stack = locked_addrs[tc->contextId()];
+    Addr& load_reservation_addr = load_reservation_addrs[tc->contextId()];
 
-    locked_addr_stack.push(req->getPaddr() & ~0xF);
+    load_reservation_addr = req->getPaddr() & ~0xF;
     DPRINTF(LLSC, "[cid:%d]: Reserved address %x.\n",
             req->contextId(), req->getPaddr() & ~0xF);
 }
@@ -535,23 +537,25 @@
 bool
 ISA::handleLockedWrite(const RequestPtr &req, Addr cacheBlockMask)
 {
-    std::stack<Addr>& locked_addr_stack = locked_addrs[tc->contextId()];
+    Addr& load_reservation_addr = load_reservation_addrs[tc->contextId()];
+    bool lr_addr_empty = (load_reservation_addr == INVALID_RESERVATION_ADDR);
 
     // Normally RISC-V uses zero to indicate success and nonzero to indicate
     // failure (right now only 1 is reserved), but in gem5 zero indicates
     // failure and one indicates success, so here we conform to that (it should
     // be switched in the instruction's implementation)
 
-    DPRINTF(LLSC, "[cid:%d]: locked_addrs empty? %s.\n", req->contextId(),
-            locked_addr_stack.empty() ? "yes" : "no");
-    if (!locked_addr_stack.empty()) {
+    DPRINTF(LLSC, "[cid:%d]: load_reservation_addrs empty? %s.\n",
+            req->contextId(),
+            lr_addr_empty ? "yes" : "no");
+    if (!lr_addr_empty) {
         DPRINTF(LLSC, "[cid:%d]: addr = %x.\n", req->contextId(),
                 req->getPaddr() & ~0xF);
         DPRINTF(LLSC, "[cid:%d]: last locked addr = %x.\n", req->contextId(),
-                locked_addr_stack.top());
+                load_reservation_addr);
     }
-    if (locked_addr_stack.empty()
-            || locked_addr_stack.top() != ((req->getPaddr() & ~0xF))) {
+    if (lr_addr_empty
+            || load_reservation_addr != ((req->getPaddr() & ~0xF))) {
         req->setExtraData(0);
         int stCondFailures = tc->readStCondFailures();
         tc->setStCondFailures(++stCondFailures);
@@ -564,6 +568,7 @@
     if (req->isUncacheable()) {
         req->setExtraData(2);
     }
+
     return true;
 }
 
@@ -575,3 +580,17 @@
 
 } // namespace RiscvISA
 } // namespace gem5
+
+std::ostream &
+operator<<(std::ostream &os, gem5::RiscvISA::PrivilegeMode pm)
+{
+    switch (pm) {
+    case gem5::RiscvISA::PRV_U:
+        return os << "PRV_U";
+    case gem5::RiscvISA::PRV_S:
+        return os << "PRV_S";
+    case gem5::RiscvISA::PRV_M:
+        return os << "PRV_M";
+    }
+    return os << "PRV_<invalid>";
+}
diff --git a/src/arch/riscv/isa.hh b/src/arch/riscv/isa.hh
index 143cc69..81923b5 100644
--- a/src/arch/riscv/isa.hh
+++ b/src/arch/riscv/isa.hh
@@ -119,4 +119,6 @@
 } // namespace RiscvISA
 } // namespace gem5
 
+std::ostream &operator<<(std::ostream &os, gem5::RiscvISA::PrivilegeMode pm);
+
 #endif // __ARCH_RISCV_ISA_HH__
diff --git a/src/arch/riscv/isa/decoder.isa b/src/arch/riscv/isa/decoder.isa
index 3cbba5a..6cd7d95 100644
--- a/src/arch/riscv/isa/decoder.isa
+++ b/src/arch/riscv/isa/decoder.isa
@@ -135,7 +135,7 @@
                     return std::make_shared<IllegalInstFault>(
                             "source reg x0", machInst);
                 }
-                Rc1_sd = (int32_t)Rc1_sd + imm;
+                Rc1_sw = (int32_t)(Rc1_sw + imm);
             }});
             0x2: c_li({{
                 imm = CIMM5;
@@ -312,7 +312,7 @@
                                 "source reg x0", machInst);
                     }
                     NPC = Rc1;
-                }}, IsIndirectControl, IsUncondControl, IsCall);
+                }}, IsIndirectControl, IsUncondControl);
                 default: CROp::c_mv({{
                     if (RC1 == 0) {
                         return std::make_shared<IllegalInstFault>(
@@ -400,6 +400,15 @@
 
         0x01: decode FUNCT3 {
             format Load {
+                0x1: flh({{
+                    STATUS status = xc->readMiscReg(MISCREG_STATUS);
+                    if (status.fs == FPUStatus::OFF)
+                        return std::make_shared<IllegalInstFault>(
+                                    "FPU is off", machInst);
+                    freg_t fd;
+                    fd = freg(f16(Mem_uh));
+                    Fd_bits = fd.v;
+                }}, inst_flags=FloatMemReadOp);
                 0x2: flw({{
                     STATUS status = xc->readMiscReg(MISCREG_STATUS);
                     if (status.fs == FPUStatus::OFF)
@@ -431,13 +440,49 @@
         }
 
         0x04: decode FUNCT3 {
+            0x1: decode FS3 {
+                format IOp {
+                    0x00: slli({{
+                        Rd = Rs1 << imm;
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
+                    0x05: bseti({{
+                        uint64_t index = imm & (64 - 1);
+                        Rd = Rs1 | (UINT64_C(1) << index);
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
+                    0x09: bclri({{
+                        uint64_t index = imm & (64 - 1);
+                        Rd = Rs1 & (~(UINT64_C(1) << index));
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
+                    0x0d: binvi({{
+                        uint64_t index = imm & (64 - 1);
+                        Rd = Rs1 ^ (UINT64_C(1) << index);
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
+                }
+                format ROp {
+                    0x0c: decode RS2 {
+                        0x00: clz({{
+                            Rd = clz64(Rs1);
+                        }});
+                        0x01: ctz({{
+                            Rd = ctz64(Rs1);
+                        }});
+                        0x02: cpop({{
+                            Rd = popCount(Rs1);
+                        }});
+                        0x04: sextb({{
+                            Rd = sext<8>(Rs1_ub);
+                        }});
+                        0x05: sexth({{
+                            Rd = sext<16>(Rs1_uh);
+                        }});
+                    }
+                }
+            }
+
             format IOp {
                 0x0: addi({{
                     Rd_sd = Rs1_sd + imm;
                 }});
-                0x1: slli({{
-                    Rd = Rs1 << imm;
-                }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
                 0x2: slti({{
                     Rd = (Rs1_sd < imm) ? 1 : 0;
                 }});
@@ -447,13 +492,40 @@
                 0x4: xori({{
                     Rd = Rs1 ^ imm;
                 }}, uint64_t);
-                0x5: decode SRTYPE {
+                0x5: decode FS3 {
                     0x0: srli({{
                         Rd = Rs1 >> imm;
                     }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
-                    0x1: srai({{
+                    0x5: orcb({{
+                        Rd = 0;
+                        Rd |= (Rs1<7:0> ? UINT64_C(0xff) : 0x0);
+                        Rd |= (Rs1<15:8> ? UINT64_C(0xff) : 0x0) << 8;
+                        Rd |= (Rs1<23:16> ? UINT64_C(0xff) : 0x0) << 16;
+                        Rd |= (Rs1<31:24> ? UINT64_C(0xff) : 0x0) << 24;
+                        Rd |= (Rs1<39:32> ? UINT64_C(0xff) : 0x0) << 32;
+                        Rd |= (Rs1<47:40> ? UINT64_C(0xff) : 0x0) << 40;
+                        Rd |= (Rs1<55:48> ? UINT64_C(0xff) : 0x0) << 48;
+                        Rd |= (Rs1<63:56> ? UINT64_C(0xff) : 0x0) << 56;
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
+                    0x8: srai({{
                         Rd_sd = Rs1_sd >> imm;
                     }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
+                    0x9: bexti({{
+                        uint64_t index = imm & (64 - 1);
+                        Rd = (Rs1 >> index) & 0x1;
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
+                    0xc: rori({{
+                        Rd = (Rs1 >> imm) | (Rs1 << ((64 - imm) & (64 - 1)));
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
+                    0xd: decode RS2 {
+                        0x18: rev8({{
+                            Rd = 0;
+                            Rd |= ((Rs1 & 0xffULL) << 56) | (((Rs1 >> 56) & 0xffULL));
+                            Rd |= (((Rs1 >> 8) & 0xffULL) << 48) | (((Rs1 >> 48) & 0xffULL) << 8);
+                            Rd |= (((Rs1 >> 16) & 0xffULL) << 40) | (((Rs1 >> 40) & 0xffULL) << 16);
+                            Rd |= (((Rs1 >> 24) & 0xffULL) << 32) | (((Rs1 >> 32) & 0xffULL) << 24);
+                        }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT6; }});
+                    }
                 }
                 0x6: ori({{
                     Rd = Rs1 | imm;
@@ -471,18 +543,37 @@
         0x06: decode FUNCT3 {
             format IOp {
                 0x0: addiw({{
-                    Rd_sd = Rs1_sw + imm;
+                    Rd_sw = (int32_t)(Rs1_sw + imm);
                 }}, int32_t);
-                0x1: slliw({{
-                    Rd_sd = Rs1_sw << imm;
-                }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT5; }});
-                0x5: decode SRTYPE {
+                0x1: decode FS3 {
+                    0x0: slliw({{
+                        Rd_sd = Rs1_sw << imm;
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT5; }});
+                    0x1: slliuw({{
+                        Rd = ((uint64_t)(Rs1_uw)) << imm;
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT5; }});
+                    0xc: decode FS2 {
+                        0x0: clzw({{
+                            Rd = clz32(Rs1);
+                        }});
+                        0x1: ctzw({{
+                            Rd = ctz32(Rs1);
+                        }});
+                        0x2: cpopw({{
+                            Rd = popCount(Rs1<31:0>);
+                        }});
+                    }
+                }
+                0x5: decode FS3 {
                     0x0: srliw({{
                         Rd_sd = (int32_t)(Rs1_uw >> imm);
                     }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT5; }});
-                    0x1: sraiw({{
+                    0x8: sraiw({{
                         Rd_sd = Rs1_sw >> imm;
                     }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT5; }});
+                    0xc: roriw({{
+                        Rd = (int32_t) ((Rs1_uw >> imm) | (Rs1_uw << ((32 - imm) & (32 - 1))));
+                    }}, imm_type = uint64_t, imm_code = {{ imm = SHAMT5; }});
                 }
             }
         }
@@ -506,6 +597,14 @@
 
         0x09: decode FUNCT3 {
             format Store {
+                0x1: fsh({{
+                    STATUS status = xc->readMiscReg(MISCREG_STATUS);
+                    if (status.fs == FPUStatus::OFF)
+                        return std::make_shared<IllegalInstFault>(
+                                "FPU is off", machInst);
+
+                    Mem_uh = (uint16_t)Fs2_bits;
+                }}, inst_flags=FloatMemWriteOp);
                 0x2: fsw({{
                     STATUS status = xc->readMiscReg(MISCREG_STATUS);
                     if (status.fs == FPUStatus::OFF)
@@ -712,6 +811,30 @@
                         Rd = negate ? ~res + (Rs1_sd*Rs2_sd == 0 ? 1 : 0)
                                     : res;
                     }}, IntMultOp);
+                    0x5: clmul({{
+                        Rd = 0;
+                        for (int i = 0; i < 64; i++) {
+                            if ((Rs2 >> i) & 1) {
+                                Rd ^= Rs1 << i;
+                            }
+                        }
+                    }});
+                    0x14: bset({{
+                        Rs2 &= (64 - 1);
+                        Rd = Rs1 | (UINT64_C(1) << Rs2);
+                    }});
+                    0x24: bclr({{
+                        Rs2 &= (64 - 1);
+                        Rd = Rs1 & (~(UINT64_C(1) << Rs2));
+                    }});
+                    0x30: rol({{
+                        int shamt = Rs2 & (64 - 1);
+                        Rd = (Rs1 << shamt) | (Rs1 >> ((64 - shamt) & (64 - 1)));
+                    }});
+                    0x34: binv({{
+                        Rs2 &= (64 - 1);
+                        Rd = Rs1 ^ (UINT64_C(1) << Rs2);
+                    }});
                 }
                 0x2: decode FUNCT7 {
                     0x0: slt({{
@@ -737,6 +860,17 @@
                                        carry;
                         Rd = negate ? ~res + (Rs1_sd*Rs2 == 0 ? 1 : 0) : res;
                     }}, IntMultOp);
+                    0x5: clmulr({{
+                        Rd = 0;
+                        for (int i = 0; i < 64; i++) {
+                            if ((Rs2 >> i) & 1) {
+                                Rd ^= Rs1 >> (64-i-1);
+                            }
+                        }
+                    }});
+                    0x10: sh1add({{
+                        Rd = (Rs1 << 1) + Rs2;
+                    }});
                 }
                 0x3: decode FUNCT7 {
                     0x0: sltu({{
@@ -757,6 +891,14 @@
 
                         Rd = hi + (mid1 >> 32) + (mid2 >> 32) + carry;
                     }}, IntMultOp);
+                    0x5: clmulh({{
+                        Rd = 0;
+                        for (int i = 1; i < 64; i++) {
+                            if ((Rs2 >> i) & 1) {
+                                Rd ^= (Rs1 >> (64-i));
+                            }
+                        }
+                    }});
                 }
                 0x4: decode FUNCT7 {
                     0x0: xor({{
@@ -773,6 +915,15 @@
                             Rd_sd = Rs1_sd/Rs2_sd;
                         }
                     }}, IntDivOp);
+                    0x5: min({{
+                        Rd = (((int64_t) Rs1) < ((int64_t) Rs2)) ? Rs1 : Rs2;
+                    }});
+                    0x10: sh2add({{
+                        Rd = (Rs1 << 2) + Rs2;
+                    }});
+                    0x20: xnor({{
+                        Rd = ~(Rs1 ^ Rs2);
+                    }});
                 }
                 0x5: decode FUNCT7 {
                     0x0: srl({{
@@ -788,6 +939,17 @@
                     0x20: sra({{
                         Rd_sd = Rs1_sd >> Rs2<5:0>;
                     }});
+                    0x5: minu({{
+                        Rd = Rs1 < Rs2 ? Rs1 : Rs2;
+                    }});
+                    0x24: bext({{
+                        Rs2 &= (64 - 1);
+                        Rd = (Rs1 >> Rs2) & 0x1;
+                    }});
+                    0x30: ror({{
+                        int shamt = Rs2 & (64 - 1);
+                        Rd = (Rs1 >> shamt) | (Rs1 << ((64 - shamt) & (64 - 1)));
+                    }});
                 }
                 0x6: decode FUNCT7 {
                     0x0: or({{
@@ -804,6 +966,15 @@
                             Rd = Rs1_sd%Rs2_sd;
                         }
                     }}, IntDivOp);
+                    0x5: max({{
+                        Rd = (((int64_t) Rs1) > ((int64_t) Rs2)) ? Rs1 : Rs2;
+                    }});
+                    0x10: sh3add({{
+                        Rd = (Rs1 << 3) + Rs2;
+                    }});
+                    0x20: orn({{
+                        Rd = Rs1 | (~Rs2);
+                    }});
                 }
                 0x7: decode FUNCT7 {
                     0x0: and({{
@@ -816,6 +987,12 @@
                             Rd = Rs1%Rs2;
                         }
                     }}, IntDivOp);
+                    0x5: maxu({{
+                        Rd = Rs1 > Rs2 ? Rs1 : Rs2;
+                    }});
+                    0x20: andn({{
+                        Rd = Rs1 & (~Rs2);
+                    }});
                 }
             }
         }
@@ -833,23 +1010,45 @@
                     0x1: mulw({{
                         Rd_sd = (int32_t)(Rs1_sw*Rs2_sw);
                     }}, IntMultOp);
+                    0x4: adduw({{
+                        Rd = Rs1_uw + Rs2;
+                    }});
                     0x20: subw({{
                         Rd_sd = Rs1_sw - Rs2_sw;
                     }});
                 }
-                0x1: sllw({{
-                    Rd_sd = Rs1_sw << Rs2<4:0>;
-                }});
-                0x4: divw({{
-                    if (Rs2_sw == 0) {
-                        Rd_sd = -1;
-                    } else if (Rs1_sw == std::numeric_limits<int32_t>::min()
-                            && Rs2_sw == -1) {
-                        Rd_sd = std::numeric_limits<int32_t>::min();
-                    } else {
-                        Rd_sd = Rs1_sw/Rs2_sw;
-                    }
-                }}, IntDivOp);
+                0x1: decode FUNCT7 {
+                    0x0: sllw({{
+                        Rd_sd = Rs1_sw << Rs2<4:0>;
+                    }});
+                    0x30: rolw({{
+                        int shamt = Rs2 & (32 - 1);
+                        Rd = (int32_t) ((Rs1_uw << shamt) | (Rs1_uw >> ((32 - shamt) & (32 - 1))));
+                    }});
+                }
+                0x2: decode FUNCT7 {
+                    0x10: sh1adduw({{
+                        Rd = (((uint64_t)Rs1_uw) << 1) + Rs2;
+                    }});
+                }
+                0x4: decode FUNCT7 {
+                    0x1: divw({{
+                        if (Rs2_sw == 0) {
+                            Rd_sd = -1;
+                        } else if (Rs1_sw == std::numeric_limits<int32_t>::min()
+                                && Rs2_sw == -1) {
+                            Rd_sd = std::numeric_limits<int32_t>::min();
+                        } else {
+                            Rd_sd = Rs1_sw/Rs2_sw;
+                        }
+                    }}, IntDivOp);
+                    0x4: zexth ({{
+                        Rd = Rs1_uh;
+                    }});
+                    0x10: sh2adduw({{
+                        Rd = (((uint64_t)Rs1_uw) << 2) + Rs2;
+                    }});
+                }
                 0x5: decode FUNCT7 {
                     0x0: srlw({{
                         Rd_sd = (int32_t)(Rs1_uw >> Rs2<4:0>);
@@ -864,17 +1063,26 @@
                     0x20: sraw({{
                         Rd_sd = Rs1_sw >> Rs2<4:0>;
                     }});
+                    0x30: rorw({{
+                        int shamt = Rs2 & (32 - 1);
+                        Rd = (int32_t) ((Rs1_uw >> shamt) | (Rs1_uw << ((32 - shamt) & (32 - 1))));
+                    }});
                 }
-                0x6: remw({{
-                    if (Rs2_sw == 0) {
-                        Rd_sd = Rs1_sw;
-                    } else if (Rs1_sw == std::numeric_limits<int32_t>::min()
-                            && Rs2_sw == -1) {
-                        Rd_sd = 0;
-                    } else {
-                        Rd_sd = Rs1_sw%Rs2_sw;
-                    }
-                }}, IntDivOp);
+                0x6:  decode FUNCT7 {
+                    0x1: remw({{
+                        if (Rs2_sw == 0) {
+                            Rd_sd = Rs1_sw;
+                        } else if (Rs1_sw == std::numeric_limits<int32_t>::min()
+                                && Rs2_sw == -1) {
+                            Rd_sd = 0;
+                        } else {
+                            Rd_sd = Rs1_sw%Rs2_sw;
+                        }
+                    }}, IntDivOp);
+                    0x10: sh3adduw({{
+                        Rd = (((uint64_t)Rs1_uw) << 3) + Rs2;
+                    }});
+                }
                 0x7: remuw({{
                     if (Rs2_uw == 0) {
                         Rd_sd = (int32_t)Rs1_uw;
@@ -903,6 +1111,14 @@
                                          f64(freg(Fs3_bits))));
                     Fd_bits = fd.v;
                 }}, FloatMultAccOp);
+                0x2: fmadd_h({{
+                    RM_REQUIRED;
+                    freg_t fd;
+                    fd = freg(f16_mulAdd(f16(freg(Fs1_bits)),
+                                         f16(freg(Fs2_bits)),
+                                         f16(freg(Fs3_bits))));
+                    Fd_bits = fd.v;
+                }}, FloatMultAccOp);
             }
             0x11: decode FUNCT2 {
                 0x0: fmsub_s({{
@@ -923,6 +1139,15 @@
                                         mask(63, 63))));
                     Fd_bits = fd.v;
                 }}, FloatMultAccOp);
+                0x2: fmsub_h({{
+                    RM_REQUIRED;
+                    freg_t fd;
+                    fd = freg(f16_mulAdd(f16(freg(Fs1_bits)),
+                                    f16(freg(Fs2_bits)),
+                                    f16(f16(freg(Fs3_bits)).v ^
+                                        mask(15, 15))));
+                    Fd_bits = fd.v;
+                }}, FloatMultAccOp);
             }
             0x12: decode FUNCT2 {
                 0x0: fnmsub_s({{
@@ -943,6 +1168,15 @@
                                          f64(freg(Fs3_bits))));
                     Fd_bits = fd.v;
                 }}, FloatMultAccOp);
+                0x2: fnmsub_h({{
+                    RM_REQUIRED;
+                    freg_t fd;
+                    fd = freg(f16_mulAdd(f16(f16(freg(Fs1_bits)).v ^
+                                             mask(15, 15)),
+                                         f16(freg(Fs2_bits)),
+                                         f16(freg(Fs3_bits))));
+                    Fd_bits = fd.v;
+                }}, FloatMultAccOp);
             }
             0x13: decode FUNCT2 {
                 0x0: fnmadd_s({{
@@ -965,6 +1199,16 @@
                                         mask(63, 63))));
                     Fd_bits = fd.v;
                 }}, FloatMultAccOp);
+                0x2: fnmadd_h({{
+                    RM_REQUIRED;
+                    freg_t fd;
+                    fd = freg(f16_mulAdd(f16(f16(freg(Fs1_bits)).v ^
+                                             mask(15, 15)),
+                                    f16(freg(Fs2_bits)),
+                                    f16(f16(freg(Fs3_bits)).v ^
+                                        mask(15, 15))));
+                    Fd_bits = fd.v;
+                }}, FloatMultAccOp);
             }
             0x14: decode FUNCT7 {
                 0x0: fadd_s({{
@@ -981,6 +1225,13 @@
                                       f64(freg(Fs2_bits))));
                     Fd_bits = fd.v;
                 }}, FloatAddOp);
+                0x2: fadd_h({{
+                    RM_REQUIRED;
+                    freg_t fd;
+                    fd = freg(f16_add(f16(freg(Fs1_bits)),
+                                      f16(freg(Fs2_bits))));
+                    Fd_bits = fd.v;
+                }}, FloatAddOp);
                 0x4: fsub_s({{
                     RM_REQUIRED;
                     freg_t fd;
@@ -995,6 +1246,13 @@
                                       f64(freg(Fs2_bits))));
                     Fd_bits = fd.v;
                 }}, FloatAddOp);
+                0x6: fsub_h({{
+                    RM_REQUIRED;
+                    freg_t fd;
+                    fd = freg(f16_sub(f16(freg(Fs1_bits)),
+                                      f16(freg(Fs2_bits))));
+                    Fd_bits = fd.v;
+                }}, FloatAddOp);
                 0x8: fmul_s({{
                     RM_REQUIRED;
                     freg_t fd;
@@ -1009,6 +1267,13 @@
                                       f64(freg(Fs2_bits))));
                     Fd_bits = fd.v;
                 }}, FloatMultOp);
+                0xa: fmul_h({{
+                    RM_REQUIRED;
+                    freg_t fd;
+                    fd = freg(f16_mul(f16(freg(Fs1_bits)),
+                                      f16(freg(Fs2_bits))));
+                    Fd_bits = fd.v;
+                }}, FloatMultOp);
                 0xc: fdiv_s({{
                     RM_REQUIRED;
                     freg_t fd;
@@ -1023,19 +1288,29 @@
                                       f64(freg(Fs2_bits))));
                     Fd_bits = fd.v;
                 }}, FloatDivOp);
+                0xe: fdiv_h({{
+                    RM_REQUIRED;
+                    freg_t fd;
+                    fd = freg(f16_div(f16(freg(Fs1_bits)),
+                                      f16(freg(Fs2_bits))));
+                    Fd_bits = fd.v;
+                }}, FloatDivOp);
                 0x10: decode ROUND_MODE {
                     0x0: fsgnj_s({{
-                        Fd_bits = boxF32(insertBits(unboxF32(Fs2_bits), 30, 0,
-                                                    unboxF32(Fs1_bits)));
+                        auto sign = bits(unboxF32(Fs2_bits), 31);
+                        Fd_bits = boxF32(insertBits(unboxF32(Fs1_bits), 31,
+                                                    sign));
                         }}, FloatMiscOp);
                     0x1: fsgnjn_s({{
-                        Fd_bits = boxF32(insertBits(unboxF32(~Fs2_bits), 30, 0,
-                                                    unboxF32(Fs1_bits)));
+                        auto sign = ~bits(unboxF32(Fs2_bits), 31);
+                        Fd_bits = boxF32(insertBits(unboxF32(Fs1_bits), 31,
+                                                    sign));
                         }}, FloatMiscOp);
                     0x2: fsgnjx_s({{
-                        Fd_bits = boxF32(insertBits(
-                                    unboxF32(Fs1_bits) ^ unboxF32(Fs2_bits),
-                                    30, 0, unboxF32(Fs1_bits)));
+                        auto sign = bits(
+                            unboxF32(Fs1_bits) ^ unboxF32(Fs2_bits), 31);
+                        Fd_bits = boxF32(insertBits(unboxF32(Fs1_bits), 31,
+                                                    sign));
                         }}, FloatMiscOp);
                 }
                 0x11: decode ROUND_MODE {
@@ -1050,6 +1325,24 @@
                                 Fs1_bits ^ Fs2_bits, 62, 0, Fs1_bits);
                     }}, FloatMiscOp);
                 }
+                0x12: decode ROUND_MODE {
+                    0x0: fsgnj_h({{
+                        auto sign = bits(unboxF16(Fs2_bits), 15);
+                        Fd_bits = boxF16(insertBits(unboxF16(Fs1_bits), 15,
+                                                    sign));
+                        }}, FloatMiscOp);
+                    0x1: fsgnjn_h({{
+                        auto sign = ~bits(unboxF16(Fs2_bits), 15);
+                        Fd_bits = boxF16(insertBits(unboxF16(Fs1_bits), 15,
+                                                    sign));
+                        }}, FloatMiscOp);
+                    0x2: fsgnjx_h({{
+                        auto sign = bits(
+                            unboxF16(Fs1_bits) ^ unboxF16(Fs2_bits), 15);
+                        Fd_bits = boxF16(insertBits(unboxF16(Fs1_bits), 15,
+                                                    sign));
+                        }}, FloatMiscOp);
+                }
                 0x14: decode ROUND_MODE {
                     0x0: fmin_s({{
                         bool less = f32_lt_quiet(f32(freg(Fs1_bits)),
@@ -1111,26 +1404,78 @@
                             Fd_bits = f64(defaultNaNF64UI).v;
                     }}, FloatCmpOp);
                 }
-                0x20: fcvt_s_d({{
-                    if (CONV_SGN != 1) {
-                        return std::make_shared<IllegalInstFault>(
-                                "CONV_SGN != 1", machInst);
-                    }
-                    RM_REQUIRED;
-                    freg_t fd;
-                    fd = freg(f64_to_f32(f64(freg(Fs1_bits))));
-                    Fd_bits = fd.v;
-                }}, FloatCvtOp);
-                0x21: fcvt_d_s({{
-                    if (CONV_SGN != 0) {
-                        return std::make_shared<IllegalInstFault>(
-                                "CONV_SGN != 0", machInst);
-                    }
-                    RM_REQUIRED;
-                    freg_t fd;
-                    fd = freg(f32_to_f64(f32(freg(Fs1_bits))));
-                    Fd_bits = fd.v;
-                }}, FloatCvtOp);
+                0x16: decode ROUND_MODE {
+                    0x0: fmin_h({{
+                        bool less = f16_lt_quiet(f16(freg(Fs1_bits)),
+                            f16(freg(Fs2_bits))) ||
+                            (f16_eq(f16(freg(Fs1_bits)),
+                            f16(freg(Fs2_bits))) &&
+                            bits(f16(freg(Fs1_bits)).v, 15));
+
+                        Fd_bits = less ||
+                            isNaNF16UI(f16(freg(Fs2_bits)).v) ?
+                            freg(Fs1_bits).v : freg(Fs2_bits).v;
+                        if (isNaNF16UI(f16(freg(Fs1_bits)).v) &&
+                            isNaNF16UI(f16(freg(Fs2_bits)).v))
+                            Fd_bits = f16(defaultNaNF16UI).v;
+                        }}, FloatCmpOp);
+                    0x1: fmax_h({{
+                        bool greater = f16_lt_quiet(f16(freg(Fs2_bits)),
+                            f16(freg(Fs1_bits))) ||
+                            (f16_eq(f16(freg(Fs2_bits)),
+                            f16(freg(Fs1_bits))) &&
+                            bits(f16(freg(Fs2_bits)).v, 15));
+
+                        Fd_bits = greater ||
+                            isNaNF16UI(f16(freg(Fs2_bits)).v) ?
+                            freg(Fs1_bits).v : freg(Fs2_bits).v;
+                        if (isNaNF16UI(f16(freg(Fs1_bits)).v) &&
+                            isNaNF16UI(f16(freg(Fs2_bits)).v))
+                            Fd_bits = f16(defaultNaNF16UI).v;
+                        }}, FloatCmpOp);
+                }
+                0x20: decode CONV_SGN {
+                    0x1: fcvt_s_d({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(f64_to_f32(f64(freg(Fs1_bits))));
+                        Fd_bits = fd.v;
+                    }}, FloatCvtOp);
+                    0x2: fcvt_s_h({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(f16_to_f32(f16(freg(Fs1_bits))));
+                        Fd_bits = fd.v;
+                    }}, FloatCvtOp);
+                }
+                0x21: decode CONV_SGN {
+                    0x0: fcvt_d_s({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(f32_to_f64(f32(freg(Fs1_bits))));
+                        Fd_bits = fd.v;
+                    }}, FloatCvtOp);
+                    0x2: fcvt_d_h({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(f16_to_f64(f16(freg(Fs1_bits))));
+                        Fd_bits = fd.v;
+                    }}, FloatCvtOp);
+                }
+                0x22: decode CONV_SGN {
+                    0x0: fcvt_h_s({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(f32_to_f16(f32(freg(Fs1_bits))));
+                        Fd_bits = fd.v;
+                    }}, FloatCvtOp);
+                    0x1: fcvt_h_d({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(f64_to_f16(f64(freg(Fs1_bits))));
+                        Fd_bits = fd.v;
+                    }}, FloatCvtOp);
+                }
                 0x2c: fsqrt_s({{
                     if (RS2 != 0) {
                         return std::make_shared<IllegalInstFault>(
@@ -1151,6 +1496,16 @@
                     fd = freg(f64_sqrt(f64(freg(Fs1_bits))));
                     Fd_bits = fd.v;
                 }}, FloatSqrtOp);
+                0x2e: fsqrt_h({{
+                    if (RS2 != 0) {
+                        return std::make_shared<IllegalInstFault>(
+                                "source reg x1", machInst);
+                    }
+                    freg_t fd;
+                    RM_REQUIRED;
+                    fd = freg(f16_sqrt(f16(freg(Fs1_bits))));
+                    Fd_bits = fd.v;
+                }}, FloatSqrtOp);
                 0x50: decode ROUND_MODE {
                     0x0: fle_s({{
                         Rd = f32_le(f32(freg(Fs1_bits)), f32(freg(Fs2_bits)));
@@ -1173,6 +1528,17 @@
                         Rd = f64_eq(f64(freg(Fs1_bits)), f64(freg(Fs2_bits)));
                     }}, FloatCmpOp);
                 }
+                0x52: decode ROUND_MODE {
+                    0x0: fle_h({{
+                        Rd = f16_le(f16(freg(Fs1_bits)), f16(freg(Fs2_bits)));
+                    }}, FloatCmpOp);
+                    0x1: flt_h({{
+                        Rd = f16_lt(f16(freg(Fs1_bits)), f16(freg(Fs2_bits)));
+                    }}, FloatCmpOp);
+                    0x2: feq_h({{
+                        Rd = f16_eq(f16(freg(Fs1_bits)), f16(freg(Fs2_bits)));
+                    }}, FloatCmpOp);
+                }
                 0x60: decode CONV_SGN {
                     0x0: fcvt_w_s({{
                         RM_REQUIRED;
@@ -1213,6 +1579,26 @@
                         Rd = f64_to_ui64(f64(freg(Fs1_bits)), rm, true);
                     }}, FloatCvtOp);
                 }
+                0x62: decode CONV_SGN {
+                    0x0: fcvt_w_h({{
+                        RM_REQUIRED;
+                        Rd_sd = sext<32>(f16_to_i32(f16(freg(Fs1_bits)), rm,
+                                                    true));
+                    }}, FloatCvtOp);
+                    0x1: fcvt_wu_h({{
+                        RM_REQUIRED;
+                        Rd = sext<32>(f16_to_ui32(f16(freg(Fs1_bits)), rm,
+                                                  true));
+                    }}, FloatCvtOp);
+                    0x2: fcvt_l_h({{
+                        RM_REQUIRED;
+                        Rd_sd = f16_to_i64(f16(freg(Fs1_bits)), rm, true);
+                    }}, FloatCvtOp);
+                    0x3: fcvt_lu_h({{
+                        RM_REQUIRED;
+                        Rd = f16_to_ui64(f16(freg(Fs1_bits)), rm, true);
+                    }}, FloatCvtOp);
+                }
                 0x68: decode CONV_SGN {
                     0x0: fcvt_s_w({{
                         RM_REQUIRED;
@@ -1223,7 +1609,7 @@
                     0x1: fcvt_s_wu({{
                         RM_REQUIRED;
                         freg_t fd;
-                        fd = freg(ui32_to_f32((int32_t)Rs1_uw));
+                        fd = freg(ui32_to_f32((uint32_t)Rs1_uw));
                         Fd_bits = fd.v;
                         }}, FloatCvtOp);
                     0x2: fcvt_s_l({{
@@ -1257,8 +1643,34 @@
                         Fd = (double)Rs1;
                     }}, FloatCvtOp);
                 }
+                0x6a: decode CONV_SGN {
+                    0x0: fcvt_h_w({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(i32_to_f16((int32_t)Rs1_sw));
+                        Fd_bits = fd.v;
+                        }}, FloatCvtOp);
+                    0x1: fcvt_h_wu({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(ui32_to_f16((uint32_t)Rs1_uw));
+                        Fd_bits = fd.v;
+                        }}, FloatCvtOp);
+                    0x2: fcvt_h_l({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(i64_to_f16(Rs1_ud));
+                        Fd_bits = fd.v;
+                        }}, FloatCvtOp);
+                    0x3: fcvt_h_lu({{
+                        RM_REQUIRED;
+                        freg_t fd;
+                        fd = freg(ui64_to_f16(Rs1));
+                        Fd_bits = fd.v;
+                        }}, FloatCvtOp);
+                }
                 0x70: decode ROUND_MODE {
-                    0x0: fmv_x_s({{
+                    0x0: fmv_x_w({{
                         Rd = (uint32_t)Fs1_bits;
                         if ((Rd&0x80000000) != 0) {
                             Rd |= (0xFFFFFFFFULL << 32);
@@ -1276,7 +1688,18 @@
                         Rd = f64_classify(f64(freg(Fs1_bits)));
                     }}, FloatMiscOp);
                 }
-                0x78: fmv_s_x({{
+                0x72: decode ROUND_MODE {
+                    0x0: fmv_x_h({{
+                        Rd = (uint16_t)Fs1_bits;
+                        if ((Rd&0x8000) != 0) {
+                            Rd |= (0xFFFFFFFFFFFFULL << 16);
+                        }
+                    }}, FloatCvtOp);
+                    0x1: fclass_h({{
+                        Rd = f16_classify(f16(freg(Fs1_bits)));
+                    }}, FloatMiscOp);
+                }
+                0x78: fmv_w_x({{
                     freg_t fd;
                     fd = freg(f32(Rs1_uw));
                     Fd_bits = fd.v;
@@ -1286,6 +1709,11 @@
                     fd = freg(f64(Rs1));
                     Fd_bits = fd.v;
                 }}, FloatCvtOp);
+                0x7a: fmv_h_x({{
+                    freg_t fd;
+                    fd = freg(f16(Rs1_uh));
+                    Fd_bits = fd.v;
+                }}, FloatCvtOp);
             }
         }
 
@@ -1340,13 +1768,13 @@
             0x0: Jump::jalr({{
                 Rd = NPC;
                 NPC = (imm + Rs1) & (~0x1);
-            }}, IsIndirectControl, IsUncondControl, IsCall);
+            }}, IsIndirectControl, IsUncondControl);
         }
 
         0x1b: JOp::jal({{
             Rd = NPC;
             NPC = PC + imm;
-        }}, IsDirectControl, IsUncondControl, IsCall);
+        }}, IsDirectControl, IsUncondControl);
 
         0x1c: decode FUNCT3 {
             format SystemOp {
diff --git a/src/arch/riscv/isa/formats/standard.isa b/src/arch/riscv/isa/formats/standard.isa
index 9345c1f..e40f097 100644
--- a/src/arch/riscv/isa/formats/standard.isa
+++ b/src/arch/riscv/isa/formats/standard.isa
@@ -240,6 +240,49 @@
     };
 }};
 
+def template JumpConstructor {{
+    %(class_name)s::%(class_name)s(MachInst machInst)
+        : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
+    {
+        %(set_reg_idx_arr)s;
+        %(constructor)s;
+        %(imm_code)s;
+        if (QUADRANT != 0x3) {
+            // Handle "c_jr" instruction, set "IsReturn" flag if RC1 is 1 or 5
+            if (CFUNCT1 == 0 && (RC1 == 1 || RC1 == 5))
+                flags[IsReturn] = true;
+        } else {
+            bool rd_link = (RD == 1 || RD == 5);
+            bool rs1_link = (RS1 == 1 || RS1 == 5);
+            // Handle "jalr" and "jal" instruction,
+            // set "IsCall" if RD is link register
+            if (rd_link)
+                flags[IsCall] = true;
+
+            // Handle "Jalr" instruction
+            if (FUNCT3 == 0x0) {
+                // If RD is not link and RS1 is link, then pop RAS
+                if (!rd_link && rs1_link) flags[IsReturn] = true;
+                else if (rd_link) {
+                    // If RD is link and RS1 is not link, push RAS
+                    if (!rs1_link) flags[IsCall] = true;
+                    // If RD is link and RS1 is link and rd != rs1
+                    else if (rs1_link) {
+                        if (RS1 != RD) {
+                            // Both are link and RS1 == RD, pop then push
+                            flags[IsReturn] = true;
+                            flags[IsCall] = true;
+                        } else {
+                            // Both are link and RS1 != RD, push RAS
+                            flags[IsCall] = true;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}};
+
 def template JumpExecute {{
     Fault
     %(class_name)s::execute(
@@ -290,23 +333,19 @@
         %(op_rd)s;
 
         RegVal data, olddata;
-
+        auto lowestAllowedMode = (PrivilegeMode)bits(csr, 9, 8);
+        auto pm = (PrivilegeMode)xc->readMiscReg(MISCREG_PRV);
+        if (pm < lowestAllowedMode) {
+            return std::make_shared<IllegalInstFault>(
+                    csprintf("%s is not accessible in %s\n", csrName, pm),
+                    machInst);
+        }
         switch (csr) {
           case CSR_SATP: {
-            auto pm = (PrivilegeMode)xc->readMiscReg(MISCREG_PRV);
             STATUS status = xc->readMiscReg(MISCREG_STATUS);
-            if (pm == PRV_U || (pm == PRV_S && status.tvm == 1)) {
+            if (pm == PRV_S && status.tvm == 1) {
                 return std::make_shared<IllegalInstFault>(
-                        "SATP access in user mode or with TVM enabled\n",
-                        machInst);
-            }
-            break;
-          }
-          case CSR_MSTATUS: {
-            auto pm = (PrivilegeMode)xc->readMiscReg(MISCREG_PRV);
-            if (pm != PrivilegeMode::PRV_M) {
-                return std::make_shared<IllegalInstFault>(
-                        "MSTATUS is only accessibly in machine mode\n",
+                        "SATP access with TVM enabled\n",
                         machInst);
             }
             break;
@@ -424,7 +463,7 @@
         {'code': code, 'imm_code': 'imm = sext<12>(IMM12);',
          'regs': ','.join(regs)}, opt_flags)
     header_output = JumpDeclare.subst(iop)
-    decoder_output = ImmConstructor.subst(iop)
+    decoder_output = JumpConstructor.subst(iop)
     decode_block = BasicDecode.subst(iop)
     exec_output = JumpExecute.subst(iop)
 }};
@@ -454,7 +493,7 @@
         {'code': code, 'imm_code': imm_code,
          'regs': ','.join(regs)}, opt_flags)
     header_output = BranchDeclare.subst(iop)
-    decoder_output = ImmConstructor.subst(iop)
+    decoder_output = JumpConstructor.subst(iop)
     decode_block = BasicDecode.subst(iop)
     exec_output = BranchExecute.subst(iop)
 }};
diff --git a/src/arch/riscv/isa/operands.isa b/src/arch/riscv/isa/operands.isa
index 78cd5f9..4f0c3ed 100644
--- a/src/arch/riscv/isa/operands.isa
+++ b/src/arch/riscv/isa/operands.isa
@@ -41,40 +41,48 @@
     'df' : 'double'
 }};
 
+let {{
+    class IntReg(IntRegOp):
+        @overrideInOperand
+        def regId(self):
+            return f'(({self.reg_spec}) == 0) ? RegId() : ' \
+                   f'RegId({self.reg_class}, {self.reg_spec})'
+}};
+
 def operands {{
 #General Purpose Integer Reg Operands
-    'Rd': ('IntReg', 'ud', 'RD', 'IsInteger', 1),
-    'Rs1': ('IntReg', 'ud', 'RS1', 'IsInteger', 2),
-    'Rs2': ('IntReg', 'ud', 'RS2', 'IsInteger', 3),
-    'Rt': ('IntReg', 'ud', 'AMOTempReg', 'IsInteger', 4),
-    'Rc1': ('IntReg', 'ud', 'RC1', 'IsInteger', 2),
-    'Rc2': ('IntReg', 'ud', 'RC2', 'IsInteger', 3),
-    'Rp1': ('IntReg', 'ud', 'RP1 + 8', 'IsInteger', 2),
-    'Rp2': ('IntReg', 'ud', 'RP2 + 8', 'IsInteger', 3),
-    'ra': ('IntReg', 'ud', 'ReturnAddrReg', 'IsInteger', 1),
-    'sp': ('IntReg', 'ud', 'StackPointerReg', 'IsInteger', 2),
+    'Rd': IntReg('ud', 'RD', 'IsInteger', 1),
+    'Rs1': IntReg('ud', 'RS1', 'IsInteger', 2),
+    'Rs2': IntReg('ud', 'RS2', 'IsInteger', 3),
+    'Rt': IntReg('ud', 'AMOTempReg', 'IsInteger', 4),
+    'Rc1': IntReg('ud', 'RC1', 'IsInteger', 2),
+    'Rc2': IntReg('ud', 'RC2', 'IsInteger', 3),
+    'Rp1': IntReg('ud', 'RP1 + 8', 'IsInteger', 2),
+    'Rp2': IntReg('ud', 'RP2 + 8', 'IsInteger', 3),
+    'ra': IntReg('ud', 'ReturnAddrReg', 'IsInteger', 1),
+    'sp': IntReg('ud', 'StackPointerReg', 'IsInteger', 2),
 
-    'a0': ('IntReg', 'ud', '10', 'IsInteger', 1),
+    'a0': IntReg('ud', '10', 'IsInteger', 1),
 
-    'Fd': ('FloatReg', 'df', 'FD', 'IsFloating', 1),
-    'Fd_bits': ('FloatReg', 'ud', 'FD', 'IsFloating', 1),
-    'Fs1': ('FloatReg', 'df', 'FS1', 'IsFloating', 2),
-    'Fs1_bits': ('FloatReg', 'ud', 'FS1', 'IsFloating', 2),
-    'Fs2': ('FloatReg', 'df', 'FS2', 'IsFloating', 3),
-    'Fs2_bits': ('FloatReg', 'ud', 'FS2', 'IsFloating', 3),
-    'Fs3': ('FloatReg', 'df', 'FS3', 'IsFloating', 4),
-    'Fs3_bits': ('FloatReg', 'ud', 'FS3', 'IsFloating', 4),
-    'Fc1': ('FloatReg', 'df', 'FC1', 'IsFloating', 1),
-    'Fc1_bits': ('FloatReg', 'ud', 'FC1', 'IsFloating', 1),
-    'Fc2': ('FloatReg', 'df', 'FC2', 'IsFloatReg', 2),
-    'Fc2_bits': ('FloatReg', 'ud', 'FC2', 'IsFloating', 2),
-    'Fp2': ('FloatReg', 'df', 'FP2 + 8', 'IsFloating', 2),
-    'Fp2_bits': ('FloatReg', 'ud', 'FP2 + 8', 'IsFloating', 2),
+    'Fd': FloatRegOp('df', 'FD', 'IsFloating', 1),
+    'Fd_bits': FloatRegOp('ud', 'FD', 'IsFloating', 1),
+    'Fs1': FloatRegOp('df', 'FS1', 'IsFloating', 2),
+    'Fs1_bits': FloatRegOp('ud', 'FS1', 'IsFloating', 2),
+    'Fs2': FloatRegOp('df', 'FS2', 'IsFloating', 3),
+    'Fs2_bits': FloatRegOp('ud', 'FS2', 'IsFloating', 3),
+    'Fs3': FloatRegOp('df', 'FS3', 'IsFloating', 4),
+    'Fs3_bits': FloatRegOp('ud', 'FS3', 'IsFloating', 4),
+    'Fc1': FloatRegOp('df', 'FC1', 'IsFloating', 1),
+    'Fc1_bits': FloatRegOp('ud', 'FC1', 'IsFloating', 1),
+    'Fc2': FloatRegOp('df', 'FC2', 'IsFloatReg', 2),
+    'Fc2_bits': FloatRegOp('ud', 'FC2', 'IsFloating', 2),
+    'Fp2': FloatRegOp('df', 'FP2 + 8', 'IsFloating', 2),
+    'Fp2_bits': FloatRegOp('ud', 'FP2 + 8', 'IsFloating', 2),
 
 #Memory Operand
-    'Mem': ('Mem', 'ud', None, (None, 'IsLoad', 'IsStore'), 5),
+    'Mem': MemOp('ud', None, (None, 'IsLoad', 'IsStore'), 5),
 
 #Program Counter Operands
-    'PC': ('PCState', 'ud', 'pc', (None, None, 'IsControl'), 7),
-    'NPC': ('PCState', 'ud', 'npc', (None, None, 'IsControl'), 8),
+    'PC': PCStateOp('ud', 'pc', (None, None, 'IsControl'), 7),
+    'NPC': PCStateOp('ud', 'npc', (None, None, 'IsControl'), 8),
 }};
diff --git a/src/arch/riscv/linux/se_workload.cc b/src/arch/riscv/linux/se_workload.cc
index 2e2a7d2..dd9a8a2 100644
--- a/src/arch/riscv/linux/se_workload.cc
+++ b/src/arch/riscv/linux/se_workload.cc
@@ -157,8 +157,8 @@
     { 30,   "ioprio_get" },
     { 31,   "ioprio_set" },
     { 32,   "flock" },
-    { 33,   "mknodat" },
-    { 34,   "mkdirat" },
+    { 33,   "mknodat", mknodatFunc<RiscvLinux64> },
+    { 34,   "mkdirat", mkdiratFunc<RiscvLinux64> },
     { 35,   "unlinkat", unlinkatFunc<RiscvLinux64> },
     { 36,   "symlinkat" },
     { 37,   "linkat" },
@@ -173,7 +173,7 @@
     { 46,   "ftruncate", ftruncate64Func },
     { 47,   "fallocate", fallocateFunc<RiscvLinux64> },
     { 48,   "faccessat", faccessatFunc<RiscvLinux64> },
-    { 49,   "chdir" },
+    { 49,   "chdir", chdirFunc },
     { 50,   "fchdir" },
     { 51,   "chroot" },
     { 52,   "fchmod", fchmodFunc<RiscvLinux64> },
@@ -183,14 +183,18 @@
     { 56,   "openat", openatFunc<RiscvLinux64> },
     { 57,   "close", closeFunc },
     { 58,   "vhangup" },
-    { 59,   "pipe2" },
+    { 59,   "pipe2", pipe2Func },
     { 60,   "quotactl" },
+#if defined(SYS_getdents64)
+    { 61,   "getdents64", getdents64Func },
+#else
     { 61,   "getdents64" },
+#endif
     { 62,   "lseek", lseekFunc },
     { 63,   "read", readFunc<RiscvLinux64> },
     { 64,   "write", writeFunc<RiscvLinux64> },
     { 66,   "writev", writevFunc<RiscvLinux64> },
-    { 67,   "pread64" },
+    { 67,   "pread64", pread64Func<RiscvLinux64> },
     { 68,   "pwrite64", pwrite64Func<RiscvLinux64> },
     { 69,   "preadv" },
     { 70,   "pwritev" },
@@ -246,7 +250,7 @@
     { 120,  "sched_getscheduler" },
     { 121,  "sched_getparam" },
     { 122,  "sched_setaffinity" },
-    { 123,  "sched_getaffinity" },
+    { 123,  "sched_getaffinity", schedGetaffinityFunc<RiscvLinux64> },
     { 124,  "sched_yield", ignoreWarnOnceFunc },
     { 125,  "sched_get_priority_max" },
     { 126,  "sched_get_priority_min" },
@@ -291,7 +295,7 @@
     { 165,  "getrusage", getrusageFunc<RiscvLinux64> },
     { 166,  "umask", umaskFunc },
     { 167,  "prctl" },
-    { 168,  "getcpu" },
+    { 168,  "getcpu", getcpuFunc },
     { 169,  "gettimeofday", gettimeofdayFunc<RiscvLinux64> },
     { 170,  "settimeofday" },
     { 171,  "adjtimex" },
@@ -321,21 +325,21 @@
     { 195,  "shmctl" },
     { 196,  "shmat" },
     { 197,  "shmdt" },
-    { 198,  "socket" },
-    { 199,  "socketpair" },
-    { 200,  "bind" },
-    { 201,  "listen" },
-    { 202,  "accept" },
-    { 203,  "connect" },
-    { 204,  "getsockname" },
-    { 205,  "getpeername" },
-    { 206,  "sendo" },
-    { 207,  "recvfrom" },
-    { 208,  "setsockopt" },
-    { 209,  "getsockopt" },
-    { 210,  "shutdown" },
-    { 211,  "sendmsg" },
-    { 212,  "recvmsg" },
+    { 198,  "socket", socketFunc<RiscvLinux64> },
+    { 199,  "socketpair", socketpairFunc<RiscvLinux64> },
+    { 200,  "bind", bindFunc },
+    { 201,  "listen", listenFunc },
+    { 202,  "accept", acceptFunc<RiscvLinux64> },
+    { 203,  "connect", connectFunc },
+    { 204,  "getsockname", getsocknameFunc },
+    { 205,  "getpeername", getpeernameFunc },
+    { 206,  "sendto", sendtoFunc<RiscvLinux64> },
+    { 207,  "recvfrom", recvfromFunc<RiscvLinux64> },
+    { 208,  "setsockopt", setsockoptFunc },
+    { 209,  "getsockopt", getsockoptFunc },
+    { 210,  "shutdown", shutdownFunc },
+    { 211,  "sendmsg", sendmsgFunc },
+    { 212,  "recvmsg", recvmsgFunc },
     { 213,  "readahead" },
     { 214,  "brk", brkFunc },
     { 215,  "munmap", munmapFunc<RiscvLinux64> },
@@ -367,7 +371,7 @@
     { 241,  "perf_event_open" },
     { 242,  "accept4" },
     { 243,  "recvmmsg" },
-    { 260,  "wait4" },
+    { 260,  "wait4", wait4Func<RiscvLinux64> },
     { 261,  "prlimit64", prlimitFunc<RiscvLinux64> },
     { 262,  "fanotify_init" },
     { 263,  "fanotify_mark" },
@@ -385,7 +389,7 @@
     { 275,  "sched_getattr" },
     { 276,  "renameat2" },
     { 277,  "seccomp" },
-    { 278,  "getrandom" },
+    { 278,  "getrandom", getrandomFunc<RiscvLinux64> },
     { 279,  "memfd_create" },
     { 280,  "bpf" },
     { 281,  "execveat" },
@@ -396,18 +400,18 @@
     { 286,  "preadv2" },
     { 287,  "pwritev2" },
     { 1024, "open", openFunc<RiscvLinux64> },
-    { 1025, "link" },
+    { 1025, "link", linkFunc },
     { 1026, "unlink", unlinkFunc },
-    { 1027, "mknod" },
+    { 1027, "mknod", mknodFunc },
     { 1028, "chmod", chmodFunc<RiscvLinux64> },
     { 1029, "chown", chownFunc },
     { 1030, "mkdir", mkdirFunc },
-    { 1031, "rmdir" },
+    { 1031, "rmdir", rmdirFunc },
     { 1032, "lchown" },
     { 1033, "access", accessFunc },
     { 1034, "rename", renameFunc },
     { 1035, "readlink", readlinkFunc<RiscvLinux64> },
-    { 1036, "symlink" },
+    { 1036, "symlink", symlinkFunc },
     { 1037, "utimes", utimesFunc<RiscvLinux64> },
     { 1038, "stat", stat64Func<RiscvLinux64> },
     { 1039, "lstat", lstat64Func<RiscvLinux64> },
@@ -415,7 +419,7 @@
     { 1041, "dup2", dup2Func },
     { 1042, "epoll_create" },
     { 1043, "inotifiy_init" },
-    { 1044, "eventfd" },
+    { 1044, "eventfd", eventfdFunc<RiscvLinux64> },
     { 1045, "signalfd" },
     { 1046, "sendfile" },
     { 1047, "ftruncate", ftruncate64Func },
@@ -425,21 +429,25 @@
     { 1051, "fstat", fstat64Func<RiscvLinux64> },
     { 1052, "fcntl", fcntl64Func },
     { 1053, "fadvise64" },
-    { 1054, "newfstatat" },
+    { 1054, "newfstatat", newfstatatFunc<RiscvLinux64> },
     { 1055, "fstatfs", fstatfsFunc<RiscvLinux64> },
     { 1056, "statfs", statfsFunc<RiscvLinux64> },
     { 1057, "lseek", lseekFunc },
     { 1058, "mmap", mmapFunc<RiscvLinux64> },
     { 1059, "alarm" },
-    { 1060, "getpgrp" },
+    { 1060, "getpgrp", getpgrpFunc },
     { 1061, "pause" },
     { 1062, "time", timeFunc<RiscvLinux64> },
     { 1063, "utime" },
     { 1064, "creat" },
+#if defined(SYS_getdents)
+    { 1065, "getdents", getdentsFunc },
+#else
     { 1065, "getdents" },
+#endif
     { 1066, "futimesat" },
-    { 1067, "select" },
-    { 1068, "poll" },
+    { 1067, "select", selectFunc<RiscvLinux64> },
+    { 1068, "poll", pollFunc<RiscvLinux64> },
     { 1069, "epoll_wait" },
     { 1070, "ustat" },
     { 1071, "vfork" },
@@ -488,8 +496,8 @@
     { 30,   "ioprio_get" },
     { 31,   "ioprio_set" },
     { 32,   "flock" },
-    { 33,   "mknodat" },
-    { 34,   "mkdirat" },
+    { 33,   "mknodat", mknodatFunc<RiscvLinux32> },
+    { 34,   "mkdirat", mkdiratFunc<RiscvLinux32> },
     { 35,   "unlinkat", unlinkatFunc<RiscvLinux32> },
     { 36,   "symlinkat" },
     { 37,   "linkat" },
@@ -504,7 +512,7 @@
     { 46,   "ftruncate", ftruncateFunc<RiscvLinux32> },
     { 47,   "fallocate", fallocateFunc<RiscvLinux32> },
     { 48,   "faccessat", faccessatFunc<RiscvLinux32> },
-    { 49,   "chdir" },
+    { 49,   "chdir", chdirFunc },
     { 50,   "fchdir" },
     { 51,   "chroot" },
     { 52,   "fchmod", fchmodFunc<RiscvLinux32> },
@@ -514,14 +522,18 @@
     { 56,   "openat", openatFunc<RiscvLinux32> },
     { 57,   "close", closeFunc },
     { 58,   "vhangup" },
-    { 59,   "pipe2" },
+    { 59,   "pipe2", pipe2Func },
     { 60,   "quotactl" },
+#if defined(SYS_getdents64)
+    { 61,   "getdents64", getdents64Func },
+#else
     { 61,   "getdents64" },
+#endif
     { 62,   "lseek", lseekFunc },
     { 63,   "read", readFunc<RiscvLinux32> },
     { 64,   "write", writeFunc<RiscvLinux32> },
     { 66,   "writev", writevFunc<RiscvLinux32> },
-    { 67,   "pread64" },
+    { 67,   "pread64", pread64Func<RiscvLinux32> },
     { 68,   "pwrite64", pwrite64Func<RiscvLinux32> },
     { 69,   "preadv" },
     { 70,   "pwritev" },
@@ -577,7 +589,7 @@
     { 120,  "sched_getscheduler" },
     { 121,  "sched_getparam" },
     { 122,  "sched_setaffinity" },
-    { 123,  "sched_getaffinity" },
+    { 123,  "sched_getaffinity", schedGetaffinityFunc<RiscvLinux32> },
     { 124,  "sched_yield", ignoreWarnOnceFunc },
     { 125,  "sched_get_priority_max" },
     { 126,  "sched_get_priority_min" },
@@ -622,7 +634,7 @@
     { 165,  "getrusage", getrusageFunc<RiscvLinux32> },
     { 166,  "umask", umaskFunc },
     { 167,  "prctl" },
-    { 168,  "getcpu" },
+    { 168,  "getcpu", getcpuFunc },
     { 169,  "gettimeofday", gettimeofdayFunc<RiscvLinux32> },
     { 170,  "settimeofday" },
     { 171,  "adjtimex" },
@@ -652,21 +664,21 @@
     { 195,  "shmctl" },
     { 196,  "shmat" },
     { 197,  "shmdt" },
-    { 198,  "socket" },
-    { 199,  "socketpair" },
-    { 200,  "bind" },
-    { 201,  "listen" },
-    { 202,  "accept" },
-    { 203,  "connect" },
-    { 204,  "getsockname" },
-    { 205,  "getpeername" },
-    { 206,  "sendo" },
-    { 207,  "recvfrom" },
-    { 208,  "setsockopt" },
-    { 209,  "getsockopt" },
-    { 210,  "shutdown" },
-    { 211,  "sendmsg" },
-    { 212,  "recvmsg" },
+    { 198,  "socket", socketFunc<RiscvLinux32> },
+    { 199,  "socketpair", socketpairFunc<RiscvLinux32> },
+    { 200,  "bind", bindFunc },
+    { 201,  "listen", listenFunc },
+    { 202,  "accept", acceptFunc<RiscvLinux32> },
+    { 203,  "connect", connectFunc },
+    { 204,  "getsockname", getsocknameFunc },
+    { 205,  "getpeername", getpeernameFunc },
+    { 206,  "sendto", sendtoFunc<RiscvLinux32> },
+    { 207,  "recvfrom", recvfromFunc<RiscvLinux32> },
+    { 208,  "setsockopt", setsockoptFunc },
+    { 209,  "getsockopt", getsockoptFunc },
+    { 210,  "shutdown", shutdownFunc },
+    { 211,  "sendmsg", sendmsgFunc },
+    { 212,  "recvmsg", recvmsgFunc },
     { 213,  "readahead" },
     { 214,  "brk", brkFunc },
     { 215,  "munmap", munmapFunc<RiscvLinux32> },
@@ -698,7 +710,7 @@
     { 241,  "perf_event_open" },
     { 242,  "accept4" },
     { 243,  "recvmmsg" },
-    { 260,  "wait4" },
+    { 260,  "wait4", wait4Func<RiscvLinux32> },
     { 261,  "prlimit64", prlimitFunc<RiscvLinux32> },
     { 262,  "fanotify_init" },
     { 263,  "fanotify_mark" },
@@ -716,7 +728,7 @@
     { 275,  "sched_getattr" },
     { 276,  "renameat2" },
     { 277,  "seccomp" },
-    { 278,  "getrandom" },
+    { 278,  "getrandom", getrandomFunc<RiscvLinux32> },
     { 279,  "memfd_create" },
     { 280,  "bpf" },
     { 281,  "execveat" },
@@ -727,18 +739,18 @@
     { 286,  "preadv2" },
     { 287,  "pwritev2" },
     { 1024, "open", openFunc<RiscvLinux32> },
-    { 1025, "link" },
+    { 1025, "link", linkFunc },
     { 1026, "unlink", unlinkFunc },
-    { 1027, "mknod" },
+    { 1027, "mknod", mknodFunc },
     { 1028, "chmod", chmodFunc<RiscvLinux32> },
     { 1029, "chown", chownFunc },
     { 1030, "mkdir", mkdirFunc },
-    { 1031, "rmdir" },
+    { 1031, "rmdir", rmdirFunc },
     { 1032, "lchown" },
     { 1033, "access", accessFunc },
     { 1034, "rename", renameFunc },
     { 1035, "readlink", readlinkFunc<RiscvLinux32> },
-    { 1036, "symlink" },
+    { 1036, "symlink", symlinkFunc },
     { 1037, "utimes", utimesFunc<RiscvLinux32> },
     { 1038, "stat", statFunc<RiscvLinux32> },
     { 1039, "lstat", lstatFunc<RiscvLinux32> },
@@ -746,7 +758,7 @@
     { 1041, "dup2", dup2Func },
     { 1042, "epoll_create" },
     { 1043, "inotifiy_init" },
-    { 1044, "eventfd" },
+    { 1044, "eventfd", eventfdFunc<RiscvLinux32> },
     { 1045, "signalfd" },
     { 1046, "sendfile" },
     { 1047, "ftruncate", ftruncateFunc<RiscvLinux32> },
@@ -756,21 +768,25 @@
     { 1051, "fstat", fstatFunc<RiscvLinux32> },
     { 1052, "fcntl", fcntlFunc },
     { 1053, "fadvise64" },
-    { 1054, "newfstatat" },
+    { 1054, "newfstatat", newfstatatFunc<RiscvLinux32> },
     { 1055, "fstatfs", fstatfsFunc<RiscvLinux32> },
     { 1056, "statfs", statfsFunc<RiscvLinux32> },
     { 1057, "lseek", lseekFunc },
     { 1058, "mmap", mmapFunc<RiscvLinux32> },
     { 1059, "alarm" },
-    { 1060, "getpgrp" },
+    { 1060, "getpgrp", getpgrpFunc },
     { 1061, "pause" },
     { 1062, "time", timeFunc<RiscvLinux32> },
     { 1063, "utime" },
     { 1064, "creat" },
+#if defined(SYS_getdents)
+    { 1065, "getdents", getdentsFunc },
+#else
     { 1065, "getdents" },
+#endif
     { 1066, "futimesat" },
-    { 1067, "select" },
-    { 1068, "poll" },
+    { 1067, "select", selectFunc<RiscvLinux32> },
+    { 1068, "poll", pollFunc<RiscvLinux32> },
     { 1069, "epoll_wait" },
     { 1070, "ustat" },
     { 1071, "vfork" },
diff --git a/src/arch/riscv/pagetable_walker.cc b/src/arch/riscv/pagetable_walker.cc
index 08767c2..cbd5bd2 100644
--- a/src/arch/riscv/pagetable_walker.cc
+++ b/src/arch/riscv/pagetable_walker.cc
@@ -305,7 +305,7 @@
     walker->pma->check(read->req);
     // Effective privilege mode for pmp checks for page table
     // walks is S mode according to specs
-    fault = walker->pmp->pmpCheck(read->req, mode,
+    fault = walker->pmp->pmpCheck(read->req, BaseMMU::Read,
                     RiscvISA::PrivilegeMode::PRV_S, tc, entry.vaddr);
 
     if (fault == NoFault) {
@@ -357,7 +357,7 @@
                         walker->pma->check(read->req);
 
                         fault = walker->pmp->pmpCheck(read->req,
-                                            mode, pmode, tc, entry.vaddr);
+                                            BaseMMU::Write, pmode, tc, entry.vaddr);
 
                     }
                     // perform step 8 only if pmp checks pass
@@ -426,6 +426,10 @@
         //If we didn't return, we're setting up another read.
         RequestPtr request = std::make_shared<Request>(
             nextRead, oldRead->getSize(), flags, walker->requestorId);
+
+        delete oldRead;
+        oldRead = nullptr;
+
         read = new Packet(request, MemCmd::ReadReq);
         read->allocate();
 
@@ -501,6 +505,8 @@
         }
         sendPackets();
     } else {
+        delete pkt;
+
         sendPackets();
     }
     if (inflight == 0 && read == NULL && writes.size() == 0) {
diff --git a/src/arch/riscv/regs/float.hh b/src/arch/riscv/regs/float.hh
index 37b94e2..78b4ca3 100644
--- a/src/arch/riscv/regs/float.hh
+++ b/src/arch/riscv/regs/float.hh
@@ -66,6 +66,17 @@
 // Generic floating point value type.
 using freg_t = float64_t;
 
+// Extract a 16 bit float packed into a 64 bit value.
+static constexpr uint16_t
+unboxF16(uint64_t v)
+{
+    // The upper 48 bits should all be ones.
+    if (bits(v, 63, 16) == mask(48))
+        return bits(v, 15, 0);
+    else
+        return defaultNaNF16UI;
+}
+
 // Extract a 32 bit float packed into a 64 bit value.
 static constexpr uint32_t
 unboxF32(uint64_t v)
@@ -77,15 +88,19 @@
         return defaultNaNF32UI;
 }
 
+static constexpr uint64_t boxF16(uint16_t v) { return mask(63, 16) | v; }
 static constexpr uint64_t boxF32(uint32_t v) { return mask(63, 32) | v; }
 
 // Create fixed size floats from raw bytes or generic floating point values.
+static constexpr float16_t f16(uint16_t v) { return {v}; }
 static constexpr float32_t f32(uint32_t v) { return {v}; }
 static constexpr float64_t f64(uint64_t v) { return {v}; }
+static constexpr float16_t f16(freg_t r) { return {unboxF16(r.v)}; }
 static constexpr float32_t f32(freg_t r) { return {unboxF32(r.v)}; }
 static constexpr float64_t f64(freg_t r) { return r; }
 
 // Create generic floating point values from fixed size floats.
+static constexpr freg_t freg(float16_t f) { return {boxF16(f.v)}; }
 static constexpr freg_t freg(float32_t f) { return {boxF32(f.v)}; }
 static constexpr freg_t freg(float64_t f) { return f; }
 static constexpr freg_t freg(uint_fast16_t f) { return {f}; }
diff --git a/src/arch/riscv/remote_gdb.hh b/src/arch/riscv/remote_gdb.hh
index 40fe821..753859f 100644
--- a/src/arch/riscv/remote_gdb.hh
+++ b/src/arch/riscv/remote_gdb.hh
@@ -56,7 +56,7 @@
 
     bool acc(Addr addr, size_t len) override;
     // A breakpoint will be 2 bytes if it is compressed and 4 if not
-    bool checkBpLen(size_t len) override { return len == 2 || len == 4; }
+    bool checkBpKind(size_t kind) override { return kind == 2 || kind == 4; }
 
     class RiscvGdbRegCache : public BaseGdbRegCache
     {
diff --git a/src/arch/riscv/vecregs.hh b/src/arch/riscv/vecregs.hh
index 51fd244..a6c11e1 100644
--- a/src/arch/riscv/vecregs.hh
+++ b/src/arch/riscv/vecregs.hh
@@ -46,8 +46,6 @@
 #ifndef __ARCH_RISCV_VECREGS_HH__
 #define __ARCH_RISCV_VECREGS_HH__
 
-#include <cstdint>
-
 #include "arch/generic/vec_pred_reg.hh"
 #include "arch/generic/vec_reg.hh"
 
@@ -58,11 +56,7 @@
 {
 
 // Not applicable to RISC-V
-using VecElem = ::gem5::DummyVecElem;
 using VecRegContainer = ::gem5::DummyVecRegContainer;
-constexpr unsigned NumVecElemPerVecReg = ::gem5::DummyNumVecElemPerVecReg;
-
-// Not applicable to RISC-V
 using VecPredRegContainer = ::gem5::DummyVecPredRegContainer;
 
 } // namespace RiscvISA
diff --git a/util/stats/__init__.py b/src/arch/sparc/AtomicSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/sparc/AtomicSimpleCPU.py
index b3f54da..6f57f88 100644
--- a/util/stats/__init__.py
+++ b/src/arch/sparc/AtomicSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.SparcCPU import SparcAtomicSimpleCPU
+
+AtomicSimpleCPU = SparcAtomicSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/sparc/NonCachingSimpleCPU.py
similarity index 90%
copy from util/stats/__init__.py
copy to src/arch/sparc/NonCachingSimpleCPU.py
index b3f54da..5d8b5ff 100644
--- a/util/stats/__init__.py
+++ b/src/arch/sparc/NonCachingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.SparcCPU import SparcNonCachingSimpleCPU
+
+NonCachingSimpleCPU = SparcNonCachingSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/sparc/O3CPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/sparc/O3CPU.py
index b3f54da..486c6c8 100644
--- a/util/stats/__init__.py
+++ b/src/arch/sparc/O3CPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.SparcCPU import SparcO3CPU
+
+O3CPU = SparcO3CPU
+
+# Deprecated
+DerivO3CPU = O3CPU
diff --git a/src/arch/sparc/SConscript b/src/arch/sparc/SConscript
index c25d33f..71770b3 100644
--- a/src/arch/sparc/SConscript
+++ b/src/arch/sparc/SConscript
@@ -56,6 +56,12 @@
     'SparcSEWorkload', 'SparcEmuLinux'], tags='sparc isa')
 SimObject('SparcTLB.py', sim_objects=['SparcTLB'], tags='sparc isa')
 
+SimObject('SparcCPU.py', sim_objects=[], tags='sparc isa')
+SimObject('AtomicSimpleCPU.py', sim_objects=[], tags='sparc isa')
+SimObject('TimingSimpleCPU.py', sim_objects=[], tags='sparc isa')
+SimObject('NonCachingSimpleCPU.py', sim_objects=[], tags='sparc isa')
+SimObject('O3CPU.py', sim_objects=[], tags='sparc isa')
+
 DebugFlag('Sparc', "Generic SPARC ISA stuff", tags='sparc isa')
 DebugFlag('RegisterWindows', "Register window manipulation", tags='sparc isa')
 
diff --git a/src/cpu/simple/SConsopts b/src/arch/sparc/SparcCPU.py
similarity index 61%
copy from src/cpu/simple/SConsopts
copy to src/arch/sparc/SparcCPU.py
index f12fee2..b6c3305 100644
--- a/src/cpu/simple/SConsopts
+++ b/src/arch/sparc/SparcCPU.py
@@ -1,7 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -26,6 +23,29 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU
+from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU
+from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU
+from m5.objects.BaseO3CPU import BaseO3CPU
+from m5.objects.SparcDecoder import SparcDecoder
+from m5.objects.SparcMMU import SparcMMU
+from m5.objects.SparcInterrupts import SparcInterrupts
+from m5.objects.SparcISA import SparcISA
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+class SparcCPU:
+    ArchDecoder = SparcDecoder
+    ArchMMU = SparcMMU
+    ArchInterrupts = SparcInterrupts
+    ArchISA = SparcISA
+
+class SparcAtomicSimpleCPU(BaseAtomicSimpleCPU, SparcCPU):
+    mmu = SparcMMU()
+
+class SparcNonCachingSimpleCPU(BaseNonCachingSimpleCPU, SparcCPU):
+    mmu = SparcMMU()
+
+class SparcTimingSimpleCPU(BaseTimingSimpleCPU, SparcCPU):
+    mmu = SparcMMU()
+
+class SparcO3CPU(BaseO3CPU, SparcCPU):
+    mmu = SparcMMU()
diff --git a/util/stats/__init__.py b/src/arch/sparc/TimingSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/sparc/TimingSimpleCPU.py
index b3f54da..0471c18 100644
--- a/util/stats/__init__.py
+++ b/src/arch/sparc/TimingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.SparcCPU import SparcTimingSimpleCPU
+
+TimingSimpleCPU = SparcTimingSimpleCPU
diff --git a/src/arch/sparc/isa.cc b/src/arch/sparc/isa.cc
index 2debbc5..cec483f 100644
--- a/src/arch/sparc/isa.cc
+++ b/src/arch/sparc/isa.cc
@@ -39,6 +39,8 @@
 #include "base/trace.hh"
 #include "cpu/base.hh"
 #include "cpu/thread_context.hh"
+#include "debug/FloatRegs.hh"
+#include "debug/IntRegs.hh"
 #include "debug/MiscRegs.hh"
 #include "debug/Timer.hh"
 #include "params/SparcISA.hh"
@@ -68,13 +70,13 @@
 
 ISA::ISA(const Params &p) : BaseISA(p)
 {
-    _regClasses.emplace_back(NumIntRegs, 0);
-    _regClasses.emplace_back(NumFloatRegs);
-    _regClasses.emplace_back(1); // Not applicable for SPARC
-    _regClasses.emplace_back(2); // Not applicable for SPARC
-    _regClasses.emplace_back(1); // Not applicable for SPARC
-    _regClasses.emplace_back(0); // Not applicable for SPARC
-    _regClasses.emplace_back(NumMiscRegs);
+    _regClasses.emplace_back(NumIntRegs, debug::IntRegs);
+    _regClasses.emplace_back(NumFloatRegs, debug::FloatRegs);
+    _regClasses.emplace_back(1, debug::IntRegs); // Not applicable for SPARC
+    _regClasses.emplace_back(2, debug::IntRegs); // Not applicable for SPARC
+    _regClasses.emplace_back(1, debug::IntRegs); // Not applicable for SPARC
+    _regClasses.emplace_back(0, debug::IntRegs); // Not applicable for SPARC
+    _regClasses.emplace_back(NumMiscRegs, debug::MiscRegs);
     clear();
 }
 
diff --git a/src/arch/sparc/isa/operands.isa b/src/arch/sparc/isa/operands.isa
index 7a2da13..5e10017 100644
--- a/src/arch/sparc/isa/operands.isa
+++ b/src/arch/sparc/isa/operands.isa
@@ -65,128 +65,143 @@
     }
 }};
 
+let {{
+    class IntReg(IntRegOp):
+        @overrideInOperand
+        def regId(self):
+            return f'(({self.reg_spec}) == 0) ? RegId() : ' \
+                   f'RegId({self.reg_class}, {self.reg_spec})'
+}};
+
 def operands {{
     # Int regs default to unsigned, but code should not count on this.
     # For clarity, descriptions that depend on unsigned behavior should
     # explicitly specify '.uq'.
 
-    'Rd':               ('IntReg', 'udw', 'RD', 'IsInteger', 1),
+    'Rd':               IntReg('udw', 'RD', 'IsInteger', 1),
     # The Rd from the previous window
-    'Rd_prev':          ('IntReg', 'udw', 'RD + NumIntArchRegs + NumMicroIntRegs', 'IsInteger', 2),
+    'Rd_prev':          IntReg('udw',
+            'RD + NumIntArchRegs + NumMicroIntRegs', 'IsInteger', 2),
     # The Rd from the next window
-    'Rd_next':          ('IntReg', 'udw', 'RD + 2 * NumIntArchRegs + NumMicroIntRegs', 'IsInteger', 3),
+    'Rd_next':          IntReg('udw',
+            'RD + 2 * NumIntArchRegs + NumMicroIntRegs', 'IsInteger', 3),
     # For microcoded twin load instructions, RdTwin appears in the "code"
     # for the instruction is replaced by RdLow or RdHigh by the format
     # before it's processed by the iop.
     # The low (even) register of a two register pair
-    'RdLow':            ('IntReg', 'udw', 'RD & (~1)', 'IsInteger', 4),
+    'RdLow':            IntReg('udw', 'RD & (~1)', 'IsInteger', 4),
     # The high (odd) register of a two register pair
-    'RdHigh':           ('IntReg', 'udw', 'RD | 1', 'IsInteger', 5),
-    'Rs1':              ('IntReg', 'udw', 'RS1', 'IsInteger', 6),
-    'Rs2':              ('IntReg', 'udw', 'RS2', 'IsInteger', 7),
+    'RdHigh':           IntRegOp('udw', 'RD | 1', 'IsInteger', 5),
+    'Rs1':              IntReg('udw', 'RS1', 'IsInteger', 6),
+    'Rs2':              IntReg('udw', 'RS2', 'IsInteger', 7),
     # A microcode register. Right now, this is the only one.
-    'uReg0':            ('IntReg', 'udw', 'INTREG_UREG0', 'IsInteger', 8),
+    'uReg0':            IntReg('udw', 'INTREG_UREG0', 'IsInteger', 8),
     # Because double and quad precision register numbers are decoded
     # differently, they get different operands. The single precision versions
     # have an s post pended to their name.
-    'Frds':             ('FloatReg', 'sf', 'RD', 'IsFloating', 10),
-    #'Frd':             ('FloatReg', 'df', 'dfpr(RD)', 'IsFloating', 10),
-    'Frd_low':          ('FloatReg', 'uw', 'dfprl(RD)', 'IsFloating', 10),
-    'Frd_high':         ('FloatReg', 'uw', 'dfprh(RD)', 'IsFloating', 10),
+    'Frds':             FloatRegOp('sf', 'RD', 'IsFloating', 10),
+    #'Frd':             FloatRegOp('df', 'dfpr(RD)', 'IsFloating', 10),
+    'Frd_low':          FloatRegOp('uw', 'dfprl(RD)', 'IsFloating', 10),
+    'Frd_high':         FloatRegOp('uw', 'dfprh(RD)', 'IsFloating', 10),
     # Each Frd_N refers to the Nth double precision register from Frd.
     # Note that this adds twice N to the register number.
-    #'Frd_0':           ('FloatReg', 'df', 'dfpr(RD)', 'IsFloating', 10),
-    'Frd_0_low':        ('FloatReg', 'uw', 'dfprl(RD)', 'IsFloating', 10),
-    'Frd_0_high':       ('FloatReg', 'uw', 'dfprh(RD)', 'IsFloating', 10),
-    #'Frd_1':           ('FloatReg', 'df', 'dfpr(RD) + 2', 'IsFloating', 10),
-    'Frd_1_low':        ('FloatReg', 'uw', 'dfprl(RD) + 2', 'IsFloating', 10),
-    'Frd_1_high':       ('FloatReg', 'uw', 'dfprh(RD) + 2', 'IsFloating', 10),
-    #'Frd_2':           ('FloatReg', 'df', 'dfpr(RD) + 4', 'IsFloating', 10),
-    'Frd_2_low':        ('FloatReg', 'uw', 'dfprl(RD) + 4', 'IsFloating', 10),
-    'Frd_2_high':       ('FloatReg', 'uw', 'dfprh(RD) + 4', 'IsFloating', 10),
-    #'Frd_3':           ('FloatReg', 'df', 'dfpr(RD) + 6', 'IsFloating', 10),
-    'Frd_3_low':        ('FloatReg', 'uw', 'dfprl(RD) + 6', 'IsFloating', 10),
-    'Frd_3_high':       ('FloatReg', 'uw', 'dfprh(RD) + 6', 'IsFloating', 10),
-    #'Frd_4':           ('FloatReg', 'df', 'dfpr(RD) + 8', 'IsFloating', 10),
-    'Frd_4_low':        ('FloatReg', 'uw', 'dfprl(RD) + 8', 'IsFloating', 10),
-    'Frd_4_high':       ('FloatReg', 'uw', 'dfprh(RD) + 8', 'IsFloating', 10),
-    #'Frd_5':           ('FloatReg', 'df', 'dfpr(RD) + 10', 'IsFloating', 10),
-    'Frd_5_low':        ('FloatReg', 'uw', 'dfprl(RD) + 10', 'IsFloating', 10),
-    'Frd_5_high':       ('FloatReg', 'uw', 'dfprh(RD) + 10', 'IsFloating', 10),
-    #'Frd_6':           ('FloatReg', 'df', 'dfpr(RD) + 12', 'IsFloating', 10),
-    'Frd_6_low':        ('FloatReg', 'uw', 'dfprl(RD) + 12', 'IsFloating', 10),
-    'Frd_6_high':       ('FloatReg', 'uw', 'dfprh(RD) + 12', 'IsFloating', 10),
-    #'Frd_7':           ('FloatReg', 'df', 'dfpr(RD) + 14', 'IsFloating', 10),
-    'Frd_7_low':        ('FloatReg', 'uw', 'dfprl(RD) + 14', 'IsFloating', 10),
-    'Frd_7_high':       ('FloatReg', 'uw', 'dfprh(RD) + 14', 'IsFloating', 10),
-    'Frs1s':            ('FloatReg', 'sf', 'RS1', 'IsFloating', 11),
-    #'Frs1':            ('FloatReg', 'df', 'dfpr(RS1)', 'IsFloating', 11),
-    'Frs1_low':         ('FloatReg', 'uw', 'dfprl(RS1)', 'IsFloating', 11),
-    'Frs1_high':        ('FloatReg', 'uw', 'dfprh(RS1)', 'IsFloating', 11),
-    'Frs2s':            ('FloatReg', 'sf', 'RS2', 'IsFloating', 12),
-    #'Frs2':            ('FloatReg', 'df', 'dfpr(RS2)', 'IsFloating', 12),
-    'Frs2_low':         ('FloatReg', 'uw', 'dfprl(RS2)', 'IsFloating', 12),
-    'Frs2_high':        ('FloatReg', 'uw', 'dfprh(RS2)', 'IsFloating', 12),
-    'PC':               ('PCState', 'udw', 'pc', (None, None, 'IsControl'), 30),
-    'NPC':              ('PCState', 'udw', 'npc', (None, None, 'IsControl'), 30),
-    'NNPC':             ('PCState', 'udw', 'nnpc', (None, None, 'IsControl'), 30),
+    #'Frd_0':           FloatRegOp('df', 'dfpr(RD)', 'IsFloating', 10),
+    'Frd_0_low':        FloatRegOp('uw', 'dfprl(RD)', 'IsFloating', 10),
+    'Frd_0_high':       FloatRegOp('uw', 'dfprh(RD)', 'IsFloating', 10),
+    #'Frd_1':           FloatRegOp('df', 'dfpr(RD) + 2', 'IsFloating', 10),
+    'Frd_1_low':        FloatRegOp('uw', 'dfprl(RD) + 2', 'IsFloating', 10),
+    'Frd_1_high':       FloatRegOp('uw', 'dfprh(RD) + 2', 'IsFloating', 10),
+    #'Frd_2':           FloatRegOp('df', 'dfpr(RD) + 4', 'IsFloating', 10),
+    'Frd_2_low':        FloatRegOp('uw', 'dfprl(RD) + 4', 'IsFloating', 10),
+    'Frd_2_high':       FloatRegOp('uw', 'dfprh(RD) + 4', 'IsFloating', 10),
+    #'Frd_3':           FloatRegOp('df', 'dfpr(RD) + 6', 'IsFloating', 10),
+    'Frd_3_low':        FloatRegOp('uw', 'dfprl(RD) + 6', 'IsFloating', 10),
+    'Frd_3_high':       FloatRegOp('uw', 'dfprh(RD) + 6', 'IsFloating', 10),
+    #'Frd_4':           FloatRegOp('df', 'dfpr(RD) + 8', 'IsFloating', 10),
+    'Frd_4_low':        FloatRegOp('uw', 'dfprl(RD) + 8', 'IsFloating', 10),
+    'Frd_4_high':       FloatRegOp('uw', 'dfprh(RD) + 8', 'IsFloating', 10),
+    #'Frd_5':           FloatRegOp('df', 'dfpr(RD) + 10', 'IsFloating', 10),
+    'Frd_5_low':        FloatRegOp('uw', 'dfprl(RD) + 10', 'IsFloating', 10),
+    'Frd_5_high':       FloatRegOp('uw', 'dfprh(RD) + 10', 'IsFloating', 10),
+    #'Frd_6':           FloatRegOp('df', 'dfpr(RD) + 12', 'IsFloating', 10),
+    'Frd_6_low':        FloatRegOp('uw', 'dfprl(RD) + 12', 'IsFloating', 10),
+    'Frd_6_high':       FloatRegOp('uw', 'dfprh(RD) + 12', 'IsFloating', 10),
+    #'Frd_7':           FloatRegOp('df', 'dfpr(RD) + 14', 'IsFloating', 10),
+    'Frd_7_low':        FloatRegOp('uw', 'dfprl(RD) + 14', 'IsFloating', 10),
+    'Frd_7_high':       FloatRegOp('uw', 'dfprh(RD) + 14', 'IsFloating', 10),
+    'Frs1s':            FloatRegOp('sf', 'RS1', 'IsFloating', 11),
+    #'Frs1':            FloatRegOp('df', 'dfpr(RS1)', 'IsFloating', 11),
+    'Frs1_low':         FloatRegOp('uw', 'dfprl(RS1)', 'IsFloating', 11),
+    'Frs1_high':        FloatRegOp('uw', 'dfprh(RS1)', 'IsFloating', 11),
+    'Frs2s':            FloatRegOp('sf', 'RS2', 'IsFloating', 12),
+    #'Frs2':            FloatRegOp('df', 'dfpr(RS2)', 'IsFloating', 12),
+    'Frs2_low':         FloatRegOp('uw', 'dfprl(RS2)', 'IsFloating', 12),
+    'Frs2_high':        FloatRegOp('uw', 'dfprh(RS2)', 'IsFloating', 12),
+    'PC':               PCStateOp('udw', 'pc', (None, None, 'IsControl'), 30),
+    'NPC':              PCStateOp('udw', 'npc', (None, None, 'IsControl'), 30),
+    'NNPC':             PCStateOp('udw', 'nnpc',
+                                  (None, None, 'IsControl'), 30),
     # Registers which are used explicitly in instructions
-    'R0':               ('IntReg', 'udw', '0', None, 6),
-    'R1':               ('IntReg', 'udw', '1', None, 7),
-    'R15':              ('IntReg', 'udw', '15', 'IsInteger', 8),
-    'R16':              ('IntReg', 'udw', '16', None, 9),
-    'O0':               ('IntReg', 'udw', 'INTREG_O0', 'IsInteger', 10),
-    'O1':               ('IntReg', 'udw', 'INTREG_O1', 'IsInteger', 11),
-    'O2':               ('IntReg', 'udw', 'INTREG_O2', 'IsInteger', 12),
-    'O3':               ('IntReg', 'udw', 'INTREG_O3', 'IsInteger', 13),
-    'O4':               ('IntReg', 'udw', 'INTREG_O4', 'IsInteger', 14),
-    'O5':               ('IntReg', 'udw', 'INTREG_O5', 'IsInteger', 15),
+    'R0':               IntReg('udw', '0', None, 6),
+    'R1':               IntReg('udw', '1', None, 7),
+    'R15':              IntReg('udw', '15', 'IsInteger', 8),
+    'R16':              IntReg('udw', '16', None, 9),
+    'O0':               IntReg('udw', 'INTREG_O0', 'IsInteger', 10),
+    'O1':               IntReg('udw', 'INTREG_O1', 'IsInteger', 11),
+    'O2':               IntReg('udw', 'INTREG_O2', 'IsInteger', 12),
+    'O3':               IntReg('udw', 'INTREG_O3', 'IsInteger', 13),
+    'O4':               IntReg('udw', 'INTREG_O4', 'IsInteger', 14),
+    'O5':               IntReg('udw', 'INTREG_O5', 'IsInteger', 15),
 
     # Control registers
-    'Y':                ('IntReg', 'udw', 'INTREG_Y', None, 40),
-    'Ccr':              ('IntReg', 'udw', 'INTREG_CCR', None, 41),
-    'Asi':              ('ControlReg', 'udw', 'MISCREG_ASI', None, 42),
-    'Fprs':             ('ControlReg', 'udw', 'MISCREG_FPRS', None, 43),
-    'Pcr':              ('ControlReg', 'udw', 'MISCREG_PCR', None, 44),
-    'Pic':              ('ControlReg', 'udw', 'MISCREG_PIC', None, 45),
-    'Gsr':              ('IntReg', 'udw', 'INTREG_GSR', None, 46),
-    'Softint':          ('ControlReg', 'udw', 'MISCREG_SOFTINT', None, 47),
-    'SoftintSet':       ('ControlReg', 'udw', 'MISCREG_SOFTINT_SET', None, 48),
-    'SoftintClr':       ('ControlReg', 'udw', 'MISCREG_SOFTINT_CLR', None, 49),
-    'TickCmpr':         ('ControlReg', 'udw', 'MISCREG_TICK_CMPR', None, 50),
-    'Stick':            ('ControlReg', 'udw', 'MISCREG_STICK', None, 51),
-    'StickCmpr':        ('ControlReg', 'udw', 'MISCREG_STICK_CMPR', None, 52),
+    'Y':                IntReg('udw', 'INTREG_Y', None, 40),
+    'Ccr':              IntReg('udw', 'INTREG_CCR', None, 41),
+    'Asi':              ControlRegOp('udw', 'MISCREG_ASI', None, 42),
+    'Fprs':             ControlRegOp('udw', 'MISCREG_FPRS', None, 43),
+    'Pcr':              ControlRegOp('udw', 'MISCREG_PCR', None, 44),
+    'Pic':              ControlRegOp('udw', 'MISCREG_PIC', None, 45),
+    'Gsr':              IntReg('udw', 'INTREG_GSR', None, 46),
+    'Softint':          ControlRegOp('udw', 'MISCREG_SOFTINT', None, 47),
+    'SoftintSet':       ControlRegOp('udw', 'MISCREG_SOFTINT_SET', None, 48),
+    'SoftintClr':       ControlRegOp('udw', 'MISCREG_SOFTINT_CLR', None, 49),
+    'TickCmpr':         ControlRegOp('udw', 'MISCREG_TICK_CMPR', None, 50),
+    'Stick':            ControlRegOp('udw', 'MISCREG_STICK', None, 51),
+    'StickCmpr':        ControlRegOp('udw', 'MISCREG_STICK_CMPR', None, 52),
 
-    'Tpc':              ('ControlReg', 'udw', 'MISCREG_TPC', None, 53),
-    'Tnpc':             ('ControlReg', 'udw', 'MISCREG_TNPC', None, 54),
-    'Tstate':           ('ControlReg', 'udw', 'MISCREG_TSTATE', None, 55),
-    'Tt':               ('ControlReg', 'udw', 'MISCREG_TT', None, 56),
-    'Tick':             ('ControlReg', 'udw', 'MISCREG_TICK', None, 57),
-    'Tba':              ('ControlReg', 'udw', 'MISCREG_TBA', None, 58),
-    'Pstate':           ('ControlReg', 'pstate', 'MISCREG_PSTATE', None, 59),
-    'Tl':               ('ControlReg', 'udw', 'MISCREG_TL', None, 60),
-    'Pil':              ('ControlReg', 'udw', 'MISCREG_PIL', None, 61),
-    'Cwp':              ('ControlReg', 'udw', 'MISCREG_CWP',
+    'Tpc':              ControlRegOp('udw', 'MISCREG_TPC', None, 53),
+    'Tnpc':             ControlRegOp('udw', 'MISCREG_TNPC', None, 54),
+    'Tstate':           ControlRegOp('udw', 'MISCREG_TSTATE', None, 55),
+    'Tt':               ControlRegOp('udw', 'MISCREG_TT', None, 56),
+    'Tick':             ControlRegOp('udw', 'MISCREG_TICK', None, 57),
+    'Tba':              ControlRegOp('udw', 'MISCREG_TBA', None, 58),
+    'Pstate':           ControlRegOp('pstate', 'MISCREG_PSTATE', None, 59),
+    'Tl':               ControlRegOp('udw', 'MISCREG_TL', None, 60),
+    'Pil':              ControlRegOp('udw', 'MISCREG_PIL', None, 61),
+    'Cwp':              ControlRegOp('udw', 'MISCREG_CWP',
             (None, None, ['IsSerializeAfter',
                           'IsSerializing',
                           'IsNonSpeculative']), 62),
-    'Cansave':          ('IntReg', 'udw', 'INTREG_CANSAVE', None, 63),
-    'Canrestore':       ('IntReg', 'udw', 'INTREG_CANRESTORE', None, 64),
-    'Cleanwin':         ('IntReg', 'udw', 'INTREG_CLEANWIN', None, 65),
-    'Otherwin':         ('IntReg', 'udw', 'INTREG_OTHERWIN', None, 66),
-    'Wstate':           ('IntReg', 'udw', 'INTREG_WSTATE', None, 67),
-    'Gl':               ('ControlReg', 'udw', 'MISCREG_GL', None, 68),
+    'Cansave':          IntReg('udw', 'INTREG_CANSAVE', None, 63),
+    'Canrestore':       IntReg('udw', 'INTREG_CANRESTORE', None, 64),
+    'Cleanwin':         IntReg('udw', 'INTREG_CLEANWIN', None, 65),
+    'Otherwin':         IntReg('udw', 'INTREG_OTHERWIN', None, 66),
+    'Wstate':           IntReg('udw', 'INTREG_WSTATE', None, 67),
+    'Gl':               ControlRegOp('udw', 'MISCREG_GL', None, 68),
 
-    'Hpstate':          ('ControlReg', 'hpstate', 'MISCREG_HPSTATE', None, 69),
-    'Htstate':          ('ControlReg', 'udw', 'MISCREG_HTSTATE', None, 70),
-    'Hintp':            ('ControlReg', 'udw', 'MISCREG_HINTP', None, 71),
-    'Htba':             ('ControlReg', 'udw', 'MISCREG_HTBA', None, 72),
-    'HstickCmpr':       ('ControlReg', 'udw', 'MISCREG_HSTICK_CMPR', None, 73),
-    'Hver':             ('ControlReg', 'udw', 'MISCREG_HVER', None, 74),
-    'StrandStsReg':     ('ControlReg', 'udw', 'MISCREG_STRAND_STS_REG', None, 75),
+    'Hpstate':          ControlRegOp('hpstate', 'MISCREG_HPSTATE', None, 69),
+    'Htstate':          ControlRegOp('udw', 'MISCREG_HTSTATE', None, 70),
+    'Hintp':            ControlRegOp('udw', 'MISCREG_HINTP', None, 71),
+    'Htba':             ControlRegOp('udw', 'MISCREG_HTBA', None, 72),
+    'HstickCmpr':       ControlRegOp('udw', 'MISCREG_HSTICK_CMPR', None, 73),
+    'Hver':             ControlRegOp('udw', 'MISCREG_HVER', None, 74),
+    'StrandStsReg':     ControlRegOp('udw', 'MISCREG_STRAND_STS_REG',
+                                     None, 75),
 
-    'Fsr':              ('ControlReg', 'udw', 'MISCREG_FSR', (None, None, ['IsSerializeAfter','IsSerializing','IsNonSpeculative']), 80),
+    'Fsr':              ControlRegOp('udw', 'MISCREG_FSR',
+                                     (None, None, ['IsSerializeAfter',
+                                                   'IsSerializing',
+                                                   'IsNonSpeculative']), 80),
     # Mem gets a large number so it's always last
-    'Mem':              ('Mem', 'udw', None, (None, 'IsLoad', 'IsStore'), 100)
+    'Mem':              MemOp('udw', None, (None, 'IsLoad', 'IsStore'), 100)
 
 }};
diff --git a/src/arch/sparc/vecregs.hh b/src/arch/sparc/vecregs.hh
index bb5c038..d1d9dfd 100644
--- a/src/arch/sparc/vecregs.hh
+++ b/src/arch/sparc/vecregs.hh
@@ -39,11 +39,7 @@
 {
 
 // Not applicable to SPARC
-using VecElem = ::gem5::DummyVecElem;
 using VecRegContainer = ::gem5::DummyVecRegContainer;
-constexpr unsigned NumVecElemPerVecReg = ::gem5::DummyNumVecElemPerVecReg;
-
-// Not applicable to SPARC
 using VecPredRegContainer = ::gem5::DummyVecPredRegContainer;
 
 } // namespace SparcISA
diff --git a/util/stats/__init__.py b/src/arch/x86/AtomicSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/x86/AtomicSimpleCPU.py
index b3f54da..4323461 100644
--- a/util/stats/__init__.py
+++ b/src/arch/x86/AtomicSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.X86CPU import X86AtomicSimpleCPU
+
+AtomicSimpleCPU = X86AtomicSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/x86/NonCachingSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/x86/NonCachingSimpleCPU.py
index b3f54da..0559bf6 100644
--- a/util/stats/__init__.py
+++ b/src/arch/x86/NonCachingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.X86CPU import X86NonCachingSimpleCPU
+
+NonCachingSimpleCPU = X86NonCachingSimpleCPU
diff --git a/util/stats/__init__.py b/src/arch/x86/O3CPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/x86/O3CPU.py
index b3f54da..a81acf1 100644
--- a/util/stats/__init__.py
+++ b/src/arch/x86/O3CPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,9 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.X86CPU import X86O3CPU
+
+O3CPU = X86O3CPU
+
+# Deprecated
+DerivO3CPU = O3CPU
diff --git a/src/arch/x86/SConscript b/src/arch/x86/SConscript
index d1ec549..9eeefd7 100644
--- a/src/arch/x86/SConscript
+++ b/src/arch/x86/SConscript
@@ -62,7 +62,8 @@
 Source('utility.cc', tags='x86 isa')
 
 SimObject('X86SeWorkload.py', sim_objects=['X86EmuLinux'], tags='x86 isa')
-SimObject('X86FsWorkload.py', sim_objects=['X86FsWorkload', 'X86FsLinux'],
+SimObject('X86FsWorkload.py',
+    sim_objects=['X86BareMetalWorkload', 'X86FsWorkload', 'X86FsLinux'],
     tags='x86 isa')
 SimObject('X86Decoder.py', sim_objects=['X86Decoder'], tags='x86 isa')
 SimObject('X86ISA.py', sim_objects=['X86ISA'], tags='x86 isa')
@@ -72,6 +73,12 @@
 SimObject('X86TLB.py', sim_objects=['X86PagetableWalker', 'X86TLB'],
     tags='x86 isa')
 
+SimObject('X86CPU.py', sim_objects=[], tags='x86 isa')
+SimObject('AtomicSimpleCPU.py', sim_objects=[], tags='x86 isa')
+SimObject('TimingSimpleCPU.py', sim_objects=[], tags='x86 isa')
+SimObject('NonCachingSimpleCPU.py', sim_objects=[], tags='x86 isa')
+SimObject('O3CPU.py', sim_objects=[], tags='x86 isa')
+
 DebugFlag('LocalApic', "Local APIC debugging", tags='x86 isa')
 DebugFlag('X86', "Generic X86 ISA debugging", tags='x86 isa')
 DebugFlag('ACPI', "ACPI debugging", tags='x86 isa')
diff --git a/util/stats/__init__.py b/src/arch/x86/TimingSimpleCPU.py
similarity index 91%
copy from util/stats/__init__.py
copy to src/arch/x86/TimingSimpleCPU.py
index b3f54da..cf6c529 100644
--- a/util/stats/__init__.py
+++ b/src/arch/x86/TimingSimpleCPU.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.objects.X86CPU import X86TimingSimpleCPU
+
+TimingSimpleCPU = X86TimingSimpleCPU
diff --git a/src/arch/x86/X86CPU.py b/src/arch/x86/X86CPU.py
new file mode 100644
index 0000000..0b46c94
--- /dev/null
+++ b/src/arch/x86/X86CPU.py
@@ -0,0 +1,62 @@
+# Copyright 2021 Google, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from m5.proxy import Self
+
+from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU
+from m5.objects.BaseNonCachingSimpleCPU import BaseNonCachingSimpleCPU
+from m5.objects.BaseTimingSimpleCPU import BaseTimingSimpleCPU
+from m5.objects.BaseO3CPU import BaseO3CPU
+from m5.objects.X86Decoder import X86Decoder
+from m5.objects.X86MMU import X86MMU
+from m5.objects.X86LocalApic import X86LocalApic
+from m5.objects.X86ISA import X86ISA
+
+class X86CPU:
+    ArchDecoder = X86Decoder
+    ArchMMU = X86MMU
+    ArchInterrupts = X86LocalApic
+    ArchISA = X86ISA
+
+class X86AtomicSimpleCPU(BaseAtomicSimpleCPU, X86CPU):
+    mmu = X86MMU()
+
+class X86NonCachingSimpleCPU(BaseNonCachingSimpleCPU, X86CPU):
+    mmu = X86MMU()
+
+class X86TimingSimpleCPU(BaseTimingSimpleCPU, X86CPU):
+    mmu = X86MMU()
+
+class X86O3CPU(BaseO3CPU, X86CPU):
+    mmu = X86MMU()
+    needsTSO = True
+
+    # For x86, each CC reg is used to hold only a subset of the
+    # flags, so we need 4-5 times the number of CC regs as
+    # physical integer regs to be sure we don't run out.  In
+    # typical real machines, CC regs are not explicitly renamed
+    # (it's a side effect of int reg renaming), so they should
+    # never be the bottleneck here.
+    numPhysCCRegs = Self.numPhysIntRegs * 5
diff --git a/src/arch/x86/X86FsWorkload.py b/src/arch/x86/X86FsWorkload.py
index a049203..52dbadf 100644
--- a/src/arch/x86/X86FsWorkload.py
+++ b/src/arch/x86/X86FsWorkload.py
@@ -39,7 +39,12 @@
 from m5.objects.SMBios import X86SMBiosSMBiosTable
 from m5.objects.IntelMP import X86IntelMPFloatingPointer, X86IntelMPConfigTable
 from m5.objects.ACPI import X86ACPIRSDP
-from m5.objects.Workload import KernelWorkload
+from m5.objects.Workload import KernelWorkload, Workload
+
+class X86BareMetalWorkload(Workload):
+    type = 'X86BareMetalWorkload'
+    cxx_header = 'arch/x86/bare_metal/workload.hh'
+    cxx_class = 'gem5::X86ISA::BareMetalWorkload'
 
 class X86FsWorkload(KernelWorkload):
     type = 'X86FsWorkload'
diff --git a/src/arch/x86/X86LocalApic.py b/src/arch/x86/X86LocalApic.py
index baf4216..e9a31aa 100644
--- a/src/arch/x86/X86LocalApic.py
+++ b/src/arch/x86/X86LocalApic.py
@@ -42,7 +42,7 @@
 
 from m5.objects.BaseInterrupts import BaseInterrupts
 from m5.objects.ClockDomain import DerivedClockDomain
-from m5.SimObject import SimObject
+from m5.objects.IntPin import IntSinkPin
 
 class X86LocalApic(BaseInterrupts):
     type = 'X86LocalApic'
@@ -57,6 +57,9 @@
     int_slave     = DeprecatedParam(int_responder,
                         '`int_slave` is now called `int_responder`')
 
+    lint0 = IntSinkPin('Local interrupt pin 0')
+    lint1 = IntSinkPin('Local interrupt pin 1')
+
     int_latency = Param.Latency('1ns', \
             "Latency for an interrupt to propagate through this device.")
     pio = ResponsePort("Programmed I/O port")
diff --git a/util/stats/__init__.py b/src/arch/x86/bare_metal/SConscript
similarity index 93%
rename from util/stats/__init__.py
rename to src/arch/x86/bare_metal/SConscript
index b3f54da..d271c15 100644
--- a/util/stats/__init__.py
+++ b/src/arch/x86/bare_metal/SConscript
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2022 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+Import('*')
+
+Source('workload.cc', tags='x86 isa')
diff --git a/src/arch/x86/bare_metal/workload.cc b/src/arch/x86/bare_metal/workload.cc
new file mode 100644
index 0000000..068c7e8
--- /dev/null
+++ b/src/arch/x86/bare_metal/workload.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2022 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "arch/x86/bare_metal/workload.hh"
+
+#include "arch/x86/faults.hh"
+#include "arch/x86/pcstate.hh"
+#include "cpu/thread_context.hh"
+#include "params/X86BareMetalWorkload.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+
+namespace X86ISA
+{
+
+BareMetalWorkload::BareMetalWorkload(const Params &p) : Workload(p)
+{}
+
+void
+BareMetalWorkload::initState()
+{
+    Workload::initState();
+
+    for (auto *tc: system->threads) {
+        X86ISA::InitInterrupt(0).invoke(tc);
+
+        if (tc->contextId() == 0) {
+            PCState pc = tc->pcState().as<PCState>();
+            // Don't start in the microcode ROM which would halt this CPU.
+            pc.upc(0);
+            pc.nupc(1);
+            tc->pcState(pc);
+            tc->activate();
+        } else {
+            // This is an application processor (AP). It should be initialized
+            // to look like only the BIOS POST has run on it and put then put
+            // it into a halted state.
+            tc->suspend();
+        }
+    }
+}
+
+} // namespace X86ISA
+
+} // namespace gem5
diff --git a/src/arch/x86/bare_metal/workload.hh b/src/arch/x86/bare_metal/workload.hh
new file mode 100644
index 0000000..5e4838e
--- /dev/null
+++ b/src/arch/x86/bare_metal/workload.hh
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2022 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARCH_X86_BARE_METAL_WORKLOAD_HH__
+#define __ARCH_X86_BARE_METAL_WORKLOAD_HH__
+
+#include "base/loader/object_file.hh"
+#include "base/loader/symtab.hh"
+#include "base/types.hh"
+#include "cpu/thread_context.hh"
+#include "params/X86BareMetalWorkload.hh"
+#include "sim/workload.hh"
+
+namespace gem5
+{
+
+namespace X86ISA
+{
+
+class BareMetalWorkload : public Workload
+{
+  public:
+    using Params = X86BareMetalWorkloadParams;
+    BareMetalWorkload(const Params &p);
+
+  public:
+    void initState() override;
+
+    Addr getEntry() const override { return 0; }
+    ByteOrder byteOrder() const override { return ByteOrder::little; }
+    loader::Arch getArch() const override { return loader::UnknownArch; }
+    const loader::SymbolTable &
+    symtab(ThreadContext *tc) override
+    {
+        static loader::SymbolTable sym_tab;
+        return sym_tab;
+    }
+
+    bool
+    insertSymbol(const loader::Symbol &symbol) override
+    {
+        return false;
+    }
+};
+
+} // namespace X86ISA
+
+} // namespace gem5
+
+#endif // __ARCH_X86_BARE_METAL_WORKLOAD_HH__
diff --git a/src/arch/x86/decoder.cc b/src/arch/x86/decoder.cc
index 842e0ad..ef87ff3 100644
--- a/src/arch/x86/decoder.cc
+++ b/src/arch/x86/decoder.cc
@@ -181,11 +181,11 @@
 Decoder::State
 Decoder::doPrefixState(uint8_t nextByte)
 {
-    uint8_t prefix = Prefixes[nextByte];
+    // The REX and VEX prefixes only exist in 64 bit mode, so we use a
+    // different table for that.
+    const int table_idx = emi.mode.submode == SixtyFourBitMode ? 1 : 0;
+    const uint8_t prefix = Prefixes[table_idx][nextByte];
     State nextState = PrefixState;
-    // REX prefixes are only recognized in 64 bit mode.
-    if (prefix == RexPrefix && emi.mode.submode != SixtyFourBitMode)
-        prefix = 0;
     if (prefix)
         consumeByte();
     switch(prefix) {
@@ -515,7 +515,7 @@
     State nextState = ErrorState;
     ModRM modRM = nextByte;
     DPRINTF(Decoder, "Found modrm byte %#x.\n", nextByte);
-    if (defOp == 1) {
+    if (emi.addrSize == 2) {
         // Figure out 16 bit displacement size.
         if ((modRM.mod == 0 && modRM.rm == 6) || modRM.mod == 2)
             displacementSize = 2;
@@ -544,8 +544,7 @@
 
     // If there's an SIB, get that next.
     // There is no SIB in 16 bit mode.
-    if (modRM.rm == 4 && modRM.mod != 3) {
-            // && in 32/64 bit mode)
+    if (modRM.rm == 4 && modRM.mod != 3 && emi.addrSize != 2) {
         nextState = SIBState;
     } else if (displacementSize) {
         nextState = DisplacementState;
diff --git a/src/arch/x86/decoder.hh b/src/arch/x86/decoder.hh
index 1f7a7e4..29415ef 100644
--- a/src/arch/x86/decoder.hh
+++ b/src/arch/x86/decoder.hh
@@ -59,7 +59,7 @@
     // These are defined and documented in decoder_tables.cc
     static const uint8_t SizeTypeToSize[3][10];
     typedef const uint8_t ByteTable[256];
-    static ByteTable Prefixes;
+    static ByteTable Prefixes[2];
 
     static ByteTable UsesModRMOneByte;
     static ByteTable UsesModRMTwoByte;
@@ -70,7 +70,6 @@
     static ByteTable ImmediateTypeTwoByte;
     static ByteTable ImmediateTypeThreeByte0F38;
     static ByteTable ImmediateTypeThreeByte0F3A;
-    static ByteTable ImmediateTypeVex[10];
 
     static X86ISAInst::MicrocodeRom microcodeRom;
 
@@ -111,6 +110,8 @@
     uint8_t defAddr = 0;
     uint8_t stack = 0;
 
+    uint8_t cpl = 0;
+
     uint8_t
     getNextByte()
     {
@@ -257,6 +258,7 @@
     Decoder(const X86DecoderParams &p) : InstDecoder(p, &fetchChunk)
     {
         emi.reset();
+        emi.mode.cpl = cpl;
         emi.mode.mode = mode;
         emi.mode.submode = submode;
     }
@@ -264,8 +266,10 @@
     void
     setM5Reg(HandyM5Reg m5Reg)
     {
+        cpl = m5Reg.cpl;
         mode = (X86Mode)(uint64_t)m5Reg.mode;
         submode = (X86SubMode)(uint64_t)m5Reg.submode;
+        emi.mode.cpl = cpl;
         emi.mode.mode = mode;
         emi.mode.submode = submode;
         altOp = m5Reg.altOp;
@@ -299,8 +303,10 @@
         Decoder *dec = dynamic_cast<Decoder *>(old);
         assert(dec);
 
+        cpl = dec->cpl;
         mode = dec->mode;
         submode = dec->submode;
+        emi.mode.cpl = cpl;
         emi.mode.mode = mode;
         emi.mode.submode = submode;
         altOp = dec->altOp;
diff --git a/src/arch/x86/decoder_tables.cc b/src/arch/x86/decoder_tables.cc
index b1154e7..2fba889 100644
--- a/src/arch/x86/decoder_tables.cc
+++ b/src/arch/x86/decoder_tables.cc
@@ -59,9 +59,30 @@
     const uint8_t V2 = Vex2Prefix;
     const uint8_t V3 = Vex3Prefix;
 
-    //This table identifies whether a byte is a prefix, and if it is,
+    //These table identifies whether a byte is a prefix, and if it is,
     //which prefix it is.
-    const Decoder::ByteTable Decoder::Prefixes =
+    const Decoder::ByteTable Decoder::Prefixes[] =
+    {{    //LSB
+// MSB   0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F
+/*   0*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   1*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   2*/ 0 , 0 , 0 , 0 , 0 , 0 , ES, 0 , 0 , 0 , 0 , 0 , 0 , 0 , CS, 0,
+/*   3*/ 0 , 0 , 0 , 0 , 0 , 0 , SS, 0 , 0 , 0 , 0 , 0 , 0 , 0 , DS, 0,
+/*   4*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
+/*   5*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   6*/ 0 , 0 , 0 , 0 , FS, GS, OO, AO, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   7*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   8*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   9*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   A*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   B*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   C*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   D*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   E*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
+/*   F*/ LO, 0 , RN, RE, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
+    },
+
+    //This table is the same as the above, except used in 64 bit mode.
     {    //LSB
 // MSB   0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F
 /*   0*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
@@ -80,7 +101,7 @@
 /*   D*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
 /*   E*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0,
 /*   F*/ LO, 0 , RN, RE, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
-    };
+    }};
 
     // These tables identify whether a particular opcode uses the ModRM byte.
     const Decoder::ByteTable Decoder::UsesModRMOneByte =
@@ -215,7 +236,7 @@
 /*  6 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , ZW, ZW, BY, BY, 0 , 0 , 0 , 0 ,
 /*  7 */ BY, BY, BY, BY, BY, BY, BY, BY, BY, BY, BY, BY, BY, BY, BY, BY,
 /*  8 */ BY, ZW, BY, BY, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
-/*  9 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
+/*  9 */ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , PO, 0 , 0 , 0 , 0 , 0 ,
 /*  A */ VW, VW, VW, VW, 0 , 0 , 0 , 0 , BY, ZW, 0 , 0 , 0 , 0 , 0 , 0 ,
 /*  B */ BY, BY, BY, BY, BY, BY, BY, BY, VW, VW, VW, VW, VW, VW, VW, VW,
 /*  C */ BY, BY, WO, 0 , 0 , 0 , BY, ZW, EN, 0 , WO, 0 , 0 , BY, 0 , 0 ,
diff --git a/src/arch/x86/emulenv.cc b/src/arch/x86/emulenv.cc
index bfca1e5..1f9c5f6 100644
--- a/src/arch/x86/emulenv.cc
+++ b/src/arch/x86/emulenv.cc
@@ -52,74 +52,77 @@
     //Use the SIB byte for addressing if the modrm byte calls for it.
     if (machInst.modRM.rm == 4 && machInst.addrSize != 2) {
         scale = 1 << machInst.sib.scale;
-        index = machInst.sib.index | (machInst.rex.x << 3);
-        base = machInst.sib.base | (machInst.rex.b << 3);
+        index = RegId(IntRegClass, machInst.sib.index | (machInst.rex.x << 3));
+        base = RegId(IntRegClass, machInst.sib.base | (machInst.rex.b << 3));
         //In this special case, we don't use a base. The displacement also
         //changes, but that's managed by the decoder.
-        if (machInst.sib.base == INTREG_RBP && machInst.modRM.mod == 0)
-            base = INTREG_T0;
+        if (machInst.sib.base == (RegIndex)int_reg::Rbp &&
+                machInst.modRM.mod == 0)
+            base = int_reg::T0;
         //In -this- special case, we don't use an index.
-        if (index == INTREG_RSP)
-            index = INTREG_T0;
+        if (index == int_reg::Rsp)
+            index = int_reg::T0;
     } else {
         if (machInst.addrSize == 2) {
             unsigned rm = machInst.modRM.rm;
             if (rm <= 3) {
                 scale = 1;
                 if (rm < 2) {
-                    base = INTREG_RBX;
+                    base = int_reg::Rbx;
                 } else {
-                    base = INTREG_RBP;
+                    base = int_reg::Rbp;
                 }
-                index = (rm % 2) ? INTREG_RDI : INTREG_RSI;
+                index = RegId(IntRegClass,
+                        (rm % 2) ? int_reg::Rdi : int_reg::Rsi);
             } else {
                 scale = 0;
                 switch (rm) {
                   case 4:
-                    base = INTREG_RSI;
+                    base = int_reg::Rsi;
                     break;
                   case 5:
-                    base = INTREG_RDI;
+                    base = int_reg::Rdi;
                     break;
                   case 6:
-                    base = INTREG_RBP;
+                    // There is a special case when mod is 0 and rm is 6.
+                    base = machInst.modRM.mod == 0 ? int_reg::T0 :
+                        int_reg::Rbp;
                     break;
                   case 7:
-                    base = INTREG_RBX;
+                    base = int_reg::Rbx;
                     break;
                 }
             }
         } else {
             scale = 0;
-            base = machInst.modRM.rm | (machInst.rex.b << 3);
+            base = RegId(IntRegClass,
+                    machInst.modRM.rm | (machInst.rex.b << 3));
             if (machInst.modRM.mod == 0 && machInst.modRM.rm == 5) {
                 //Since we need to use a different encoding of this
                 //instruction anyway, just ignore the base in those cases
-                base = INTREG_T0;
+                base = int_reg::T0;
             }
         }
     }
-    //Figure out what segment to use. This won't be entirely accurate since
-    //the presence of a displacement is supposed to make the instruction
-    //default to the data segment.
-    if ((base != INTREG_RBP && base != INTREG_RSP) || machInst.dispSize) {
-        seg = SEGMENT_REG_DS;
-        //Handle any segment override that might have been in the instruction
-        int segFromInst = machInst.legacy.seg;
-        if (segFromInst)
-            seg = (SegmentRegIndex)(segFromInst - 1);
+    //Figure out what segment to use.
+    if (base != int_reg::Rbp && base != int_reg::Rsp) {
+        seg = segment_idx::Ds;
     } else {
-        seg = SEGMENT_REG_SS;
+        seg = segment_idx::Ss;
     }
+    //Handle any segment override that might have been in the instruction
+    int segFromInst = machInst.legacy.seg;
+    if (segFromInst)
+        seg = segFromInst - 1;
 }
 
 void EmulEnv::setSeg(const ExtMachInst & machInst)
 {
-    seg = SEGMENT_REG_DS;
+    seg = segment_idx::Ds;
     //Handle any segment override that might have been in the instruction
     int segFromInst = machInst.legacy.seg;
     if (segFromInst)
-        seg = (SegmentRegIndex)(segFromInst - 1);
+        seg = segFromInst - 1;
 }
 
 } // namespace gem5
diff --git a/src/arch/x86/emulenv.hh b/src/arch/x86/emulenv.hh
index ad14747..366ffe5 100644
--- a/src/arch/x86/emulenv.hh
+++ b/src/arch/x86/emulenv.hh
@@ -51,19 +51,19 @@
     {
         RegIndex reg;
         RegIndex regm;
-        SegmentRegIndex seg;
+        int seg;
         uint8_t scale;
-        RegIndex index;
-        RegIndex base;
+        RegId index;
+        RegId base;
         int dataSize;
         int addressSize;
         int stackSize;
 
         EmulEnv(RegIndex _reg, RegIndex _regm,
                 int _dataSize, int _addressSize, int _stackSize) :
-            reg(_reg), regm(_regm), seg(SEGMENT_REG_DS),
-            scale(0), index(INTREG_T0),
-            base(INTREG_T0),
+            reg(_reg), regm(_regm), seg(segment_idx::Ds),
+            scale(0), index(int_reg::T0),
+            base(int_reg::T0),
             dataSize(_dataSize), addressSize(_addressSize),
             stackSize(_stackSize)
         {;}
diff --git a/src/arch/x86/faults.cc b/src/arch/x86/faults.cc
index ae9586b..3ef886e 100644
--- a/src/arch/x86/faults.cc
+++ b/src/arch/x86/faults.cc
@@ -43,6 +43,7 @@
 #include "arch/x86/generated/decoder.hh"
 #include "arch/x86/insts/static_inst.hh"
 #include "arch/x86/mmu.hh"
+#include "arch/x86/regs/misc.hh"
 #include "base/loader/symtab.hh"
 #include "base/trace.hh"
 #include "cpu/thread_context.hh"
@@ -67,16 +68,19 @@
     PCState pc = tc->pcState().as<PCState>();
     DPRINTF(Faults, "RIP %#x: vector %d: %s\n", pc.pc(), vector, describe());
     using namespace X86ISAInst::rom_labels;
-    HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
+    HandyM5Reg m5reg = tc->readMiscRegNoEffect(misc_reg::M5Reg);
     MicroPC entry;
     if (m5reg.mode == LongMode) {
-        entry = isSoft() ? extern_label_longModeSoftInterrupt :
-                           extern_label_longModeInterrupt;
+        entry = extern_label_longModeInterrupt;
     } else {
-        entry = extern_label_legacyModeInterrupt;
+        if (m5reg.submode == RealMode)
+            entry = extern_label_realModeInterrupt;
+        else
+            entry = extern_label_legacyModeInterrupt;
     }
-    tc->setIntReg(INTREG_MICRO(1), vector);
-    tc->setIntReg(INTREG_MICRO(7), pc.pc());
+    tc->setReg(intRegMicro(1), vector);
+    Addr cs_base = tc->readMiscRegNoEffect(misc_reg::CsEffBase);
+    tc->setReg(intRegMicro(7), pc.pc() - cs_base);
     if (errorCode != (uint64_t)(-1)) {
         if (m5reg.mode == LongMode) {
             entry = extern_label_longModeInterruptWithError;
@@ -84,10 +88,7 @@
             panic("Legacy mode interrupts with error codes "
                     "aren't implemented.");
         }
-        // Software interrupts shouldn't have error codes. If one
-        // does, there would need to be microcode to set it up.
-        assert(!isSoft());
-        tc->setIntReg(INTREG_MICRO(15), errorCode);
+        tc->setReg(intRegMicro(15), errorCode);
     }
     pc.upc(romMicroPC(entry));
     pc.nupc(romMicroPC(entry) + 1);
@@ -137,16 +138,16 @@
     if (FullSystem) {
         // Invalidate any matching TLB entries before handling the page fault.
         tc->getMMUPtr()->demapPage(addr, 0);
-        HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
+        HandyM5Reg m5reg = tc->readMiscRegNoEffect(misc_reg::M5Reg);
         X86FaultBase::invoke(tc);
         // If something bad happens while trying to enter the page fault
         // handler, I'm pretty sure that's a double fault and then all
         // bets are off. That means it should be safe to update this
         // state now.
         if (m5reg.mode == LongMode)
-            tc->setMiscReg(MISCREG_CR2, addr);
+            tc->setMiscReg(misc_reg::Cr2, addr);
         else
-            tc->setMiscReg(MISCREG_CR2, (uint32_t)addr);
+            tc->setMiscReg(misc_reg::Cr2, (uint32_t)addr);
     } else if (!tc->getProcessPtr()->fixupFault(addr)) {
         PageFaultErrorCode code = errorCode;
         const char *modeStr = "";
@@ -182,22 +183,22 @@
 {
     DPRINTF(Faults, "Init interrupt.\n");
     // The otherwise unmodified integer registers should be set to 0.
-    for (int index = 0; index < NUM_ARCH_INTREGS; index++) {
-        tc->setIntReg(index, 0);
+    for (int index = 0; index < int_reg::NumArchRegs; index++) {
+        tc->setReg(RegId(IntRegClass, index), (RegVal)0);
     }
 
-    CR0 cr0 = tc->readMiscReg(MISCREG_CR0);
+    CR0 cr0 = tc->readMiscReg(misc_reg::Cr0);
     CR0 newCR0 = 1 << 4;
     newCR0.cd = cr0.cd;
     newCR0.nw = cr0.nw;
-    tc->setMiscReg(MISCREG_CR0, newCR0);
-    tc->setMiscReg(MISCREG_CR2, 0);
-    tc->setMiscReg(MISCREG_CR3, 0);
-    tc->setMiscReg(MISCREG_CR4, 0);
+    tc->setMiscReg(misc_reg::Cr0, newCR0);
+    tc->setMiscReg(misc_reg::Cr2, 0);
+    tc->setMiscReg(misc_reg::Cr3, 0);
+    tc->setMiscReg(misc_reg::Cr4, 0);
 
-    tc->setMiscReg(MISCREG_RFLAGS, 0x0000000000000002ULL);
+    tc->setMiscReg(misc_reg::Rflags, 0x0000000000000002ULL);
 
-    tc->setMiscReg(MISCREG_EFER, 0);
+    tc->setMiscReg(misc_reg::Efer, 0);
 
     SegAttr dataAttr = 0;
     dataAttr.dpl = 0;
@@ -213,12 +214,12 @@
     dataAttr.expandDown = 0;
     dataAttr.system = 1;
 
-    for (int seg = 0; seg != NUM_SEGMENTREGS; seg++) {
-        tc->setMiscReg(MISCREG_SEG_SEL(seg), 0);
-        tc->setMiscReg(MISCREG_SEG_BASE(seg), 0);
-        tc->setMiscReg(MISCREG_SEG_EFF_BASE(seg), 0);
-        tc->setMiscReg(MISCREG_SEG_LIMIT(seg), 0xffff);
-        tc->setMiscReg(MISCREG_SEG_ATTR(seg), dataAttr);
+    for (int seg = 0; seg != segment_idx::NumIdxs; seg++) {
+        tc->setMiscReg(misc_reg::segSel(seg), 0);
+        tc->setMiscReg(misc_reg::segBase(seg), 0);
+        tc->setMiscReg(misc_reg::segEffBase(seg), 0);
+        tc->setMiscReg(misc_reg::segLimit(seg), 0xffff);
+        tc->setMiscReg(misc_reg::segAttr(seg), dataAttr);
     }
 
     SegAttr codeAttr = 0;
@@ -235,60 +236,60 @@
     codeAttr.expandDown = 0;
     codeAttr.system = 1;
 
-    tc->setMiscReg(MISCREG_CS, 0xf000);
-    tc->setMiscReg(MISCREG_CS_BASE,
-            0x00000000ffff0000ULL);
-    tc->setMiscReg(MISCREG_CS_EFF_BASE,
-            0x00000000ffff0000ULL);
+    tc->setMiscReg(misc_reg::Cs, 0xf000);
+    tc->setMiscReg(misc_reg::CsBase, 0x00000000ffff0000ULL);
+    tc->setMiscReg(misc_reg::CsEffBase, 0x00000000ffff0000ULL);
     // This has the base value pre-added.
-    tc->setMiscReg(MISCREG_CS_LIMIT, 0xffffffff);
-    tc->setMiscReg(MISCREG_CS_ATTR, codeAttr);
+    tc->setMiscReg(misc_reg::CsLimit, 0xffffffff);
+    tc->setMiscReg(misc_reg::CsAttr, codeAttr);
 
-    PCState pc(0x000000000000fff0ULL + tc->readMiscReg(MISCREG_CS_BASE));
+    PCState pc(0x000000000000fff0ULL + tc->readMiscReg(misc_reg::CsBase));
     tc->pcState(pc);
 
-    tc->setMiscReg(MISCREG_TSG_BASE, 0);
-    tc->setMiscReg(MISCREG_TSG_LIMIT, 0xffff);
+    tc->setMiscReg(misc_reg::TsgBase, 0);
+    tc->setMiscReg(misc_reg::TsgLimit, 0xffff);
 
-    tc->setMiscReg(MISCREG_IDTR_BASE, 0);
-    tc->setMiscReg(MISCREG_IDTR_LIMIT, 0xffff);
+    tc->setMiscReg(misc_reg::IdtrBase, 0);
+    tc->setMiscReg(misc_reg::IdtrLimit, 0xffff);
 
     SegAttr tslAttr = 0;
+    tslAttr.unusable = 1;
     tslAttr.present = 1;
     tslAttr.type = 2; // LDT
-    tc->setMiscReg(MISCREG_TSL, 0);
-    tc->setMiscReg(MISCREG_TSL_BASE, 0);
-    tc->setMiscReg(MISCREG_TSL_LIMIT, 0xffff);
-    tc->setMiscReg(MISCREG_TSL_ATTR, tslAttr);
+    tc->setMiscReg(misc_reg::Tsl, 0);
+    tc->setMiscReg(misc_reg::TslBase, 0);
+    tc->setMiscReg(misc_reg::TslLimit, 0xffff);
+    tc->setMiscReg(misc_reg::TslAttr, tslAttr);
 
     SegAttr trAttr = 0;
+    trAttr.unusable = 0;
     trAttr.present = 1;
     trAttr.type = 3; // Busy 16-bit TSS
-    tc->setMiscReg(MISCREG_TR, 0);
-    tc->setMiscReg(MISCREG_TR_BASE, 0);
-    tc->setMiscReg(MISCREG_TR_LIMIT, 0xffff);
-    tc->setMiscReg(MISCREG_TR_ATTR, trAttr);
+    tc->setMiscReg(misc_reg::Tr, 0);
+    tc->setMiscReg(misc_reg::TrBase, 0);
+    tc->setMiscReg(misc_reg::TrLimit, 0xffff);
+    tc->setMiscReg(misc_reg::TrAttr, trAttr);
 
     // This value should be the family/model/stepping of the processor.
     // (page 418). It should be consistent with the value from CPUID, but
     // the actual value probably doesn't matter much.
-    tc->setIntReg(INTREG_RDX, 0);
+    tc->setReg(int_reg::Rdx, (RegVal)0);
 
-    tc->setMiscReg(MISCREG_DR0, 0);
-    tc->setMiscReg(MISCREG_DR1, 0);
-    tc->setMiscReg(MISCREG_DR2, 0);
-    tc->setMiscReg(MISCREG_DR3, 0);
+    tc->setMiscReg(misc_reg::Dr0, 0);
+    tc->setMiscReg(misc_reg::Dr1, 0);
+    tc->setMiscReg(misc_reg::Dr2, 0);
+    tc->setMiscReg(misc_reg::Dr3, 0);
 
-    tc->setMiscReg(MISCREG_DR6, 0x00000000ffff0ff0ULL);
-    tc->setMiscReg(MISCREG_DR7, 0x0000000000000400ULL);
+    tc->setMiscReg(misc_reg::Dr6, 0x00000000ffff0ff0ULL);
+    tc->setMiscReg(misc_reg::Dr7, 0x0000000000000400ULL);
 
-    tc->setMiscReg(MISCREG_MXCSR, 0x1f80);
+    tc->setMiscReg(misc_reg::Mxcsr, 0x1f80);
 
     // Flag all elements on the x87 stack as empty.
-    tc->setMiscReg(MISCREG_FTW, 0xFFFF);
+    tc->setMiscReg(misc_reg::Ftw, 0xFFFF);
 
     // Update the handy M5 Reg.
-    tc->setMiscReg(MISCREG_M5_REG, 0);
+    tc->setMiscReg(misc_reg::M5Reg, 0);
     MicroPC entry = X86ISAInst::rom_labels::extern_label_initIntHalt;
     pc.upc(romMicroPC(entry));
     pc.nupc(romMicroPC(entry) + 1);
@@ -299,19 +300,19 @@
 StartupInterrupt::invoke(ThreadContext *tc, const StaticInstPtr &inst)
 {
     DPRINTF(Faults, "Startup interrupt with vector %#x.\n", vector);
-    HandyM5Reg m5Reg = tc->readMiscReg(MISCREG_M5_REG);
+    HandyM5Reg m5Reg = tc->readMiscReg(misc_reg::M5Reg);
     if (m5Reg.mode != LegacyMode || m5Reg.submode != RealMode) {
         panic("Startup IPI recived outside of real mode. "
                 "Don't know what to do. %d, %d", m5Reg.mode, m5Reg.submode);
     }
 
-    tc->setMiscReg(MISCREG_CS, vector << 8);
-    tc->setMiscReg(MISCREG_CS_BASE, vector << 12);
-    tc->setMiscReg(MISCREG_CS_EFF_BASE, vector << 12);
+    tc->setMiscReg(misc_reg::Cs, vector << 8);
+    tc->setMiscReg(misc_reg::CsBase, vector << 12);
+    tc->setMiscReg(misc_reg::CsEffBase, vector << 12);
     // This has the base value pre-added.
-    tc->setMiscReg(MISCREG_CS_LIMIT, 0xffff);
+    tc->setMiscReg(misc_reg::CsLimit, 0xffff);
 
-    tc->pcState(tc->readMiscReg(MISCREG_CS_BASE));
+    tc->pcState(tc->readMiscReg(misc_reg::CsBase));
 }
 
 } // namespace X86ISA
diff --git a/src/arch/x86/faults.hh b/src/arch/x86/faults.hh
index a8d19d9..a1ff1b0 100644
--- a/src/arch/x86/faults.hh
+++ b/src/arch/x86/faults.hh
@@ -70,7 +70,6 @@
     const char *name() const override { return faultName; }
     virtual bool isBenign() { return true; }
     virtual const char *mnemonic() const { return mnem; }
-    virtual bool isSoft() { return false; }
 
     void invoke(ThreadContext *tc, const StaticInstPtr &inst=
                 nullStaticInstPtr) override;
@@ -369,16 +368,6 @@
                 nullStaticInstPtr) override;
 };
 
-class SoftwareInterrupt : public X86Interrupt
-{
-  public:
-    SoftwareInterrupt(uint8_t _vector) :
-        X86Interrupt("Software Interrupt", "#INTR", _vector)
-    {}
-
-    bool isSoft() override { return true; }
-};
-
 } // namespace X86ISA
 } // namespace gem5
 
diff --git a/src/arch/x86/fs_workload.cc b/src/arch/x86/fs_workload.cc
index dd71222..1a41238 100644
--- a/src/arch/x86/fs_workload.cc
+++ b/src/arch/x86/fs_workload.cc
@@ -62,13 +62,10 @@
 {}
 
 void
-installSegDesc(ThreadContext *tc, SegmentRegIndex seg,
-               SegDescriptor desc, bool longmode)
+installSegDesc(ThreadContext *tc, int seg, SegDescriptor desc, bool longmode)
 {
-    bool honorBase = !longmode || seg == SEGMENT_REG_FS ||
-                                  seg == SEGMENT_REG_GS ||
-                                  seg == SEGMENT_REG_TSL ||
-                                  seg == SYS_SEGMENT_REG_TR;
+    bool honorBase = !longmode || seg == segment_idx::Fs ||
+                                  seg == segment_idx::Gs;
 
     SegAttr attr = 0;
 
@@ -99,10 +96,10 @@
         attr.expandDown = 0;
     }
 
-    tc->setMiscReg(MISCREG_SEG_BASE(seg), desc.base);
-    tc->setMiscReg(MISCREG_SEG_EFF_BASE(seg), honorBase ? desc.base : 0);
-    tc->setMiscReg(MISCREG_SEG_LIMIT(seg), desc.limit);
-    tc->setMiscReg(MISCREG_SEG_ATTR(seg), (RegVal)attr);
+    tc->setMiscReg(misc_reg::segBase(seg), desc.base);
+    tc->setMiscReg(misc_reg::segEffBase(seg), honorBase ? desc.base : 0);
+    tc->setMiscReg(misc_reg::segLimit(seg), desc.limit);
+    tc->setMiscReg(misc_reg::segAttr(seg), (RegVal)attr);
 }
 
 void
@@ -189,7 +186,7 @@
     SegSelector cs = 0;
     cs.si = numGDTEntries - 1;
 
-    tc->setMiscReg(MISCREG_CS, (RegVal)cs);
+    tc->setMiscReg(misc_reg::Cs, (RegVal)cs);
 
     // 32 bit data segment
     SegDescriptor dsDesc = initDesc;
@@ -207,18 +204,18 @@
     SegSelector ds = 0;
     ds.si = numGDTEntries - 1;
 
-    tc->setMiscReg(MISCREG_DS, (RegVal)ds);
-    tc->setMiscReg(MISCREG_ES, (RegVal)ds);
-    tc->setMiscReg(MISCREG_FS, (RegVal)ds);
-    tc->setMiscReg(MISCREG_GS, (RegVal)ds);
-    tc->setMiscReg(MISCREG_SS, (RegVal)ds);
+    tc->setMiscReg(misc_reg::Ds, (RegVal)ds);
+    tc->setMiscReg(misc_reg::Es, (RegVal)ds);
+    tc->setMiscReg(misc_reg::Fs, (RegVal)ds);
+    tc->setMiscReg(misc_reg::Gs, (RegVal)ds);
+    tc->setMiscReg(misc_reg::Ss, (RegVal)ds);
 
-    tc->setMiscReg(MISCREG_TSL, 0);
+    tc->setMiscReg(misc_reg::Tsl, 0);
     SegAttr ldtAttr = 0;
     ldtAttr.unusable = 1;
-    tc->setMiscReg(MISCREG_TSL_ATTR, ldtAttr);
-    tc->setMiscReg(MISCREG_TSG_BASE, GDTBase);
-    tc->setMiscReg(MISCREG_TSG_LIMIT, 8 * numGDTEntries - 1);
+    tc->setMiscReg(misc_reg::TslAttr, ldtAttr);
+    tc->setMiscReg(misc_reg::TsgBase, GDTBase);
+    tc->setMiscReg(misc_reg::TsgLimit, 8 * numGDTEntries - 1);
 
     SegDescriptor tssDesc = initDesc;
     tssDesc.type = 0xB;
@@ -232,8 +229,8 @@
     SegSelector tss = 0;
     tss.si = numGDTEntries - 1;
 
-    tc->setMiscReg(MISCREG_TR, (RegVal)tss);
-    installSegDesc(tc, SYS_SEGMENT_REG_TR, tssDesc, true);
+    tc->setMiscReg(misc_reg::Tr, (RegVal)tss);
+    installSegDesc(tc, segment_idx::Tr, tssDesc, true);
 
     /*
      * Identity map the first 4GB of memory. In order to map this region
@@ -287,38 +284,38 @@
     /*
      * Transition from real mode all the way up to Long mode
      */
-    CR0 cr0 = tc->readMiscRegNoEffect(MISCREG_CR0);
+    CR0 cr0 = tc->readMiscRegNoEffect(misc_reg::Cr0);
     // Turn off paging.
     cr0.pg = 0;
-    tc->setMiscReg(MISCREG_CR0, cr0);
+    tc->setMiscReg(misc_reg::Cr0, cr0);
     // Turn on protected mode.
     cr0.pe = 1;
-    tc->setMiscReg(MISCREG_CR0, cr0);
+    tc->setMiscReg(misc_reg::Cr0, cr0);
 
-    CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
+    CR4 cr4 = tc->readMiscRegNoEffect(misc_reg::Cr4);
     // Turn on pae.
     cr4.pae = 1;
-    tc->setMiscReg(MISCREG_CR4, cr4);
+    tc->setMiscReg(misc_reg::Cr4, cr4);
 
     // Point to the page tables.
-    tc->setMiscReg(MISCREG_CR3, PageMapLevel4);
+    tc->setMiscReg(misc_reg::Cr3, PageMapLevel4);
 
-    Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
+    Efer efer = tc->readMiscRegNoEffect(misc_reg::Efer);
     // Enable long mode.
     efer.lme = 1;
-    tc->setMiscReg(MISCREG_EFER, efer);
+    tc->setMiscReg(misc_reg::Efer, efer);
 
     // Start using longmode segments.
-    installSegDesc(tc, SEGMENT_REG_CS, csDesc, true);
-    installSegDesc(tc, SEGMENT_REG_DS, dsDesc, true);
-    installSegDesc(tc, SEGMENT_REG_ES, dsDesc, true);
-    installSegDesc(tc, SEGMENT_REG_FS, dsDesc, true);
-    installSegDesc(tc, SEGMENT_REG_GS, dsDesc, true);
-    installSegDesc(tc, SEGMENT_REG_SS, dsDesc, true);
+    installSegDesc(tc, segment_idx::Cs, csDesc, true);
+    installSegDesc(tc, segment_idx::Ds, dsDesc, true);
+    installSegDesc(tc, segment_idx::Es, dsDesc, true);
+    installSegDesc(tc, segment_idx::Fs, dsDesc, true);
+    installSegDesc(tc, segment_idx::Gs, dsDesc, true);
+    installSegDesc(tc, segment_idx::Ss, dsDesc, true);
 
     // Activate long mode.
     cr0.pg = 1;
-    tc->setMiscReg(MISCREG_CR0, cr0);
+    tc->setMiscReg(misc_reg::Cr0, cr0);
 
     tc->pcState(kernelObj->entryPoint());
 
diff --git a/src/arch/x86/fs_workload.hh b/src/arch/x86/fs_workload.hh
index 779e6ab..5edadae 100644
--- a/src/arch/x86/fs_workload.hh
+++ b/src/arch/x86/fs_workload.hh
@@ -72,8 +72,8 @@
 
 } // namespace intelmp
 
-void installSegDesc(ThreadContext *tc, SegmentRegIndex seg,
-                    SegDescriptor desc, bool longmode);
+void installSegDesc(ThreadContext *tc, int seg, SegDescriptor desc,
+        bool longmode);
 
 class FsWorkload : public KernelWorkload
 {
diff --git a/src/arch/x86/insts/decode_fault.hh b/src/arch/x86/insts/decode_fault.hh
new file mode 100644
index 0000000..ba70b3c
--- /dev/null
+++ b/src/arch/x86/insts/decode_fault.hh
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2007 The Hewlett-Packard Development Company
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARCH_X86_INSTS_DEBUGFAULT_HH__
+#define __ARCH_X86_INSTS_DEBUGFAULT_HH__
+
+#include "arch/x86/insts/static_inst.hh"
+
+namespace gem5
+{
+
+namespace X86ISA
+{
+
+class DecodeFaultInst : public X86StaticInst
+{
+  private:
+    Fault fault;
+
+  public:
+    // Constructor.
+    DecodeFaultInst(const char *mnem, ExtMachInst _machInst, Fault _fault) :
+        X86StaticInst(mnem, _machInst, No_OpClass), fault(_fault)
+    {}
+
+    Fault
+    execute(ExecContext *tc, Trace::InstRecord *traceData) const override
+    {
+        return fault;
+    }
+};
+
+} // namespace X86ISA
+} // namespace gem5
+
+#endif //__ARCH_X86_INSTS_DEBUGFAULT_HH__
diff --git a/src/arch/x86/insts/microop_args.hh b/src/arch/x86/insts/microop_args.hh
index c1e4b12..81ed412 100644
--- a/src/arch/x86/insts/microop_args.hh
+++ b/src/arch/x86/insts/microop_args.hh
@@ -37,6 +37,7 @@
 
 #include "arch/x86/insts/static_inst.hh"
 #include "arch/x86/regs/int.hh"
+#include "arch/x86/regs/segment.hh"
 #include "arch/x86/types.hh"
 #include "base/compiler.hh"
 #include "base/cprintf.hh"
@@ -155,7 +156,7 @@
 
     template <class InstType>
     FoldedOp(InstType *inst, ArgType idx) :
-        Base(INTREG_FOLDED(idx.index, inst->foldOBit), inst->dataSize)
+        Base(intRegFolded(idx.index, inst->foldOBit), inst->dataSize)
     {}
 
     void
@@ -360,12 +361,12 @@
 
     template <class InstType>
     AddrOp(InstType *inst, const ArgType &args) : scale(args.scale),
-        index(INTREG_FOLDED(args.index.index, inst->foldABit)),
-        base(INTREG_FOLDED(args.base.index, inst->foldABit)),
+        index(intRegFolded(args.index.index, inst->foldABit)),
+        base(intRegFolded(args.base.index, inst->foldABit)),
         disp(args.disp), segment(args.segment.index),
         size(inst->addressSize)
     {
-        assert(segment < NUM_SEGMENTREGS);
+        assert(segment < segment_idx::NumIdxs);
     }
 
     void
diff --git a/src/arch/x86/insts/static_inst.cc b/src/arch/x86/insts/static_inst.cc
index 01e62f1..03d844b 100644
--- a/src/arch/x86/insts/static_inst.cc
+++ b/src/arch/x86/insts/static_inst.cc
@@ -64,43 +64,43 @@
 {
     switch (segment)
     {
-      case SEGMENT_REG_ES:
+      case segment_idx::Es:
         ccprintf(os, "ES");
         break;
-      case SEGMENT_REG_CS:
+      case segment_idx::Cs:
         ccprintf(os, "CS");
         break;
-      case SEGMENT_REG_SS:
+      case segment_idx::Ss:
         ccprintf(os, "SS");
         break;
-      case SEGMENT_REG_DS:
+      case segment_idx::Ds:
         ccprintf(os, "DS");
         break;
-      case SEGMENT_REG_FS:
+      case segment_idx::Fs:
         ccprintf(os, "FS");
         break;
-      case SEGMENT_REG_GS:
+      case segment_idx::Gs:
         ccprintf(os, "GS");
         break;
-      case SEGMENT_REG_HS:
+      case segment_idx::Hs:
         ccprintf(os, "HS");
         break;
-      case SEGMENT_REG_TSL:
+      case segment_idx::Tsl:
         ccprintf(os, "TSL");
         break;
-      case SEGMENT_REG_TSG:
+      case segment_idx::Tsg:
         ccprintf(os, "TSG");
         break;
-      case SEGMENT_REG_LS:
+      case segment_idx::Ls:
         ccprintf(os, "LS");
         break;
-      case SEGMENT_REG_MS:
+      case segment_idx::Ms:
         ccprintf(os, "MS");
         break;
-      case SYS_SEGMENT_REG_TR:
+      case segment_idx::Tr:
         ccprintf(os, "TR");
         break;
-      case SYS_SEGMENT_REG_IDTR:
+      case segment_idx::Idtr:
         ccprintf(os, "IDTR");
         break;
       default:
@@ -166,56 +166,57 @@
                 suffix = "l";
 
             switch (reg_idx) {
-              case INTREG_RAX:
+              case int_reg::Rax:
                 ccprintf(os, abcdFormats[size], "a");
                 break;
-              case INTREG_RBX:
+              case int_reg::Rbx:
                 ccprintf(os, abcdFormats[size], "b");
                 break;
-              case INTREG_RCX:
+              case int_reg::Rcx:
                 ccprintf(os, abcdFormats[size], "c");
                 break;
-              case INTREG_RDX:
+              case int_reg::Rdx:
                 ccprintf(os, abcdFormats[size], "d");
                 break;
-              case INTREG_RSP:
+              case int_reg::Rsp:
                 ccprintf(os, piFormats[size], "sp");
                 break;
-              case INTREG_RBP:
+              case int_reg::Rbp:
                 ccprintf(os, piFormats[size], "bp");
                 break;
-              case INTREG_RSI:
+              case int_reg::Rsi:
                 ccprintf(os, piFormats[size], "si");
                 break;
-              case INTREG_RDI:
+              case int_reg::Rdi:
                 ccprintf(os, piFormats[size], "di");
                 break;
-              case INTREG_R8W:
+              case int_reg::R8:
                 ccprintf(os, longFormats[size], "8");
                 break;
-              case INTREG_R9W:
+              case int_reg::R9:
                 ccprintf(os, longFormats[size], "9");
                 break;
-              case INTREG_R10W:
+              case int_reg::R10:
                 ccprintf(os, longFormats[size], "10");
                 break;
-              case INTREG_R11W:
+              case int_reg::R11:
                 ccprintf(os, longFormats[size], "11");
                 break;
-              case INTREG_R12W:
+              case int_reg::R12:
                 ccprintf(os, longFormats[size], "12");
                 break;
-              case INTREG_R13W:
+              case int_reg::R13:
                 ccprintf(os, longFormats[size], "13");
                 break;
-              case INTREG_R14W:
+              case int_reg::R14:
                 ccprintf(os, longFormats[size], "14");
                 break;
-              case INTREG_R15W:
+              case int_reg::R15:
                 ccprintf(os, longFormats[size], "15");
                 break;
               default:
-                ccprintf(os, microFormats[size], reg_idx - INTREG_MICRO_BEGIN);
+                ccprintf(os, microFormats[size],
+                        reg_idx - int_reg::MicroBegin);
             }
             ccprintf(os, suffix);
         }
@@ -265,13 +266,13 @@
         os << "rip";
         someAddr = true;
     } else {
-        if (scale != 0 && index != NUM_INTREGS) {
+        if (scale != 0 && index != int_reg::NumRegs) {
             if (scale != 1)
                 ccprintf(os, "%d*", scale);
             printReg(os, RegId(IntRegClass, index), addressSize);
             someAddr = true;
         }
-        if (base != NUM_INTREGS) {
+        if (base != int_reg::NumRegs) {
             if (someAddr)
                 os << " + ";
             printReg(os, RegId(IntRegClass, base), addressSize);
diff --git a/src/arch/x86/insts/static_inst.hh b/src/arch/x86/insts/static_inst.hh
index 176fc3d..03ee1be 100644
--- a/src/arch/x86/insts/static_inst.hh
+++ b/src/arch/x86/insts/static_inst.hh
@@ -39,6 +39,7 @@
 #define __ARCH_X86_INSTS_STATICINST_HH__
 
 #include "arch/x86/pcstate.hh"
+#include "arch/x86/regs/int.hh"
 #include "arch/x86/types.hh"
 #include "base/trace.hh"
 #include "cpu/static_inst.hh"
diff --git a/src/arch/x86/interrupts.cc b/src/arch/x86/interrupts.cc
index 80ed3a6..74df9ab 100644
--- a/src/arch/x86/interrupts.cc
+++ b/src/arch/x86/interrupts.cc
@@ -54,6 +54,7 @@
 
 #include "arch/x86/intmessage.hh"
 #include "arch/x86/regs/apic.hh"
+#include "arch/x86/regs/misc.hh"
 #include "cpu/base.hh"
 #include "debug/LocalApic.hh"
 #include "dev/x86/i82094aa.hh"
@@ -274,6 +275,40 @@
 
 
 void
+X86ISA::Interrupts::raiseInterruptPin(int number)
+{
+    panic_if(number < 0 || number > 1,
+            "Asked to raise unrecognized int pin %d.", number);
+    DPRINTF(LocalApic, "Raised wired interrupt pin LINT%d.\n", number);
+
+    const LVTEntry entry =
+        regs[(number == 0) ? APIC_LVT_LINT0 : APIC_LVT_LINT1];
+
+    if (entry.masked) {
+        DPRINTF(LocalApic, "The interrupt was masked.\n");
+        return;
+    }
+
+    PacketPtr pkt = buildIntAcknowledgePacket();
+    auto on_completion = [this, dm=entry.deliveryMode, trigger=entry.trigger](
+            PacketPtr pkt) {
+        requestInterrupt(pkt->getLE<uint8_t>(), dm, trigger);
+        delete pkt;
+    };
+    intRequestPort.sendMessage(pkt, sys->isTimingMode(), on_completion);
+}
+
+
+void
+X86ISA::Interrupts::lowerInterruptPin(int number)
+{
+    panic_if(number < 0 || number > 1,
+            "Asked to lower unrecognized int pin %d.", number);
+    DPRINTF(LocalApic, "Lowered wired interrupt pin LINT%d.\n", number);
+}
+
+
+void
 X86ISA::Interrupts::setThreadContext(ThreadContext *_tc)
 {
     assert(_tc);
@@ -599,22 +634,28 @@
 X86ISA::Interrupts::Interrupts(const Params &p)
     : BaseInterrupts(p), sys(p.system), clockDomain(*p.clk_domain),
       apicTimerEvent([this]{ processApicTimerEvent(); }, name()),
-      pendingSmi(false), smiVector(0),
-      pendingNmi(false), nmiVector(0),
-      pendingExtInt(false), extIntVector(0),
-      pendingInit(false), initVector(0),
-      pendingStartup(false), startupVector(0),
-      startedUp(false), pendingUnmaskableInt(false),
-      pendingIPIs(0),
       intResponsePort(name() + ".int_responder", this, this),
       intRequestPort(name() + ".int_requestor", this, this, p.int_latency),
+      lint0Pin(name() + ".lint0", 0, this, 0),
+      lint1Pin(name() + ".lint1", 0, this, 1),
       pioPort(this), pioDelay(p.pio_latency)
 {
     memset(regs, 0, sizeof(regs));
     //Set the local apic DFR to the flat model.
     regs[APIC_DESTINATION_FORMAT] = (uint32_t)(-1);
-    ISRV = 0;
-    IRRV = 0;
+
+    // At reset, all LVT entries start out zeroed, except for their mask bit.
+    LVTEntry masked = 0;
+    masked.masked = 1;
+
+    regs[APIC_LVT_TIMER] = masked;
+    regs[APIC_LVT_THERMAL_SENSOR] = masked;
+    regs[APIC_LVT_PERFORMANCE_MONITORING_COUNTERS] = masked;
+    regs[APIC_LVT_LINT0] = masked;
+    regs[APIC_LVT_LINT1] = masked;
+    regs[APIC_LVT_ERROR] = masked;
+
+    regs[APIC_SPURIOUS_INTERRUPT_VECTOR] = 0xff;
 
     regs[APIC_VERSION] = (5 << 16) | 0x14;
 }
@@ -623,7 +664,7 @@
 bool
 X86ISA::Interrupts::checkInterrupts() const
 {
-    RFLAGS rflags = tc->readMiscRegNoEffect(MISCREG_RFLAGS);
+    RFLAGS rflags = tc->readMiscRegNoEffect(misc_reg::Rflags);
     if (pendingUnmaskableInt) {
         DPRINTF(LocalApic, "Reported pending unmaskable interrupt.\n");
         return true;
diff --git a/src/arch/x86/interrupts.hh b/src/arch/x86/interrupts.hh
index 7557b22..ebe32fa 100644
--- a/src/arch/x86/interrupts.hh
+++ b/src/arch/x86/interrupts.hh
@@ -56,6 +56,7 @@
 #include "arch/x86/regs/apic.hh"
 #include "base/bitfield.hh"
 #include "cpu/thread_context.hh"
+#include "dev/intpin.hh"
 #include "dev/io_device.hh"
 #include "dev/x86/intdev.hh"
 #include "params/X86LocalApic.hh"
@@ -77,11 +78,11 @@
 class Interrupts : public BaseInterrupts
 {
   protected:
-    System *sys;
+    System *sys = nullptr;
     ClockDomain &clockDomain;
 
     // Storage for the APIC registers
-    uint32_t regs[NUM_APIC_REGS];
+    uint32_t regs[NUM_APIC_REGS] = {};
 
     BitUnion32(LVTEntry)
         Bitfield<7, 0> vector;
@@ -104,29 +105,29 @@
      * A set of variables to keep track of interrupts that don't go through
      * the IRR.
      */
-    bool pendingSmi;
-    uint8_t smiVector;
-    bool pendingNmi;
-    uint8_t nmiVector;
-    bool pendingExtInt;
-    uint8_t extIntVector;
-    bool pendingInit;
-    uint8_t initVector;
-    bool pendingStartup;
-    uint8_t startupVector;
-    bool startedUp;
+    bool pendingSmi = false;
+    uint8_t smiVector = 0;
+    bool pendingNmi = false;
+    uint8_t nmiVector = 0;
+    bool pendingExtInt = false;
+    uint8_t extIntVector = 0;
+    bool pendingInit = false;
+    uint8_t initVector = 0;
+    bool pendingStartup = false;
+    uint8_t startupVector = 0;
+    bool startedUp = false;
 
     // This is a quick check whether any of the above (except ExtInt) are set.
-    bool pendingUnmaskableInt;
+    bool pendingUnmaskableInt = false;
 
     // A count of how many IPIs are in flight.
-    int pendingIPIs;
+    int pendingIPIs = 0;
 
     /*
      * IRR and ISR maintenance.
      */
-    uint8_t IRRV;
-    uint8_t ISRV;
+    uint8_t IRRV = 0;
+    uint8_t ISRV = 0;
 
     int
     findRegArrayMSB(ApicRegIndex base)
@@ -174,16 +175,20 @@
 
     void requestInterrupt(uint8_t vector, uint8_t deliveryMode, bool level);
 
-    int initialApicId;
+    int initialApicId = 0;
 
-    // Ports for interrupts.
+    // Ports for interrupt messages.
     IntResponsePort<Interrupts> intResponsePort;
     IntRequestPort<Interrupts> intRequestPort;
 
+    // Pins for wired interrupts.
+    IntSinkPin<Interrupts> lint0Pin;
+    IntSinkPin<Interrupts> lint1Pin;
+
     // Port for memory mapped register accesses.
     PioPort<Interrupts> pioPort;
 
-    Tick pioDelay;
+    Tick pioDelay = 0;
     Addr pioAddr = MaxAddr;
 
   public:
@@ -222,8 +227,12 @@
     AddrRangeList getAddrRanges() const;
     AddrRangeList getIntAddrRange() const;
 
-    Port &getPort(const std::string &if_name,
-                  PortID idx=InvalidPortID) override
+    void raiseInterruptPin(int number);
+    void lowerInterruptPin(int number);
+
+    Port &
+    getPort(const std::string &if_name,
+            PortID idx=InvalidPortID) override
     {
         if (if_name == "int_requestor") {
             return intRequestPort;
@@ -231,8 +240,13 @@
             return intResponsePort;
         } else if (if_name == "pio") {
             return pioPort;
+        } else if (if_name == "lint0") {
+            return lint0Pin;
+        } else if (if_name == "lint1") {
+            return lint1Pin;
+        } else {
+            return SimObject::getPort(if_name, idx);
         }
-        return SimObject::getPort(if_name, idx);
     }
 
     /*
diff --git a/src/arch/x86/intmessage.hh b/src/arch/x86/intmessage.hh
index e775e2a..f7692e2 100644
--- a/src/arch/x86/intmessage.hh
+++ b/src/arch/x86/intmessage.hh
@@ -88,6 +88,17 @@
         return buildIntPacket(addr, message);
     }
 
+    static inline PacketPtr
+    buildIntAcknowledgePacket()
+    {
+        RequestPtr req = std::make_shared<Request>(
+                PhysAddrIntA, 1, Request::UNCACHEABLE,
+                Request::intRequestorId);
+        PacketPtr pkt = new Packet(req, MemCmd::ReadReq);
+        pkt->allocate();
+        return pkt;
+    }
+
 } // namespace X86ISA
 } // namespace gem5
 
diff --git a/src/arch/x86/isa.cc b/src/arch/x86/isa.cc
index 89e0d29..45962c8 100644
--- a/src/arch/x86/isa.cc
+++ b/src/arch/x86/isa.cc
@@ -36,6 +36,10 @@
 #include "base/compiler.hh"
 #include "cpu/base.hh"
 #include "cpu/thread_context.hh"
+#include "debug/CCRegs.hh"
+#include "debug/FloatRegs.hh"
+#include "debug/IntRegs.hh"
+#include "debug/MiscRegs.hh"
 #include "params/X86ISA.hh"
 #include "sim/serialize.hh"
 
@@ -101,7 +105,7 @@
         m5reg.stack = 1;
     }
 
-    regVal[MISCREG_M5_REG] = m5reg;
+    regVal[misc_reg::M5Reg] = m5reg;
     if (tc)
         tc->getDecoderPtr()->as<Decoder>().setM5Reg(m5reg);
 }
@@ -111,30 +115,30 @@
 {
     // Blank everything. 0 might not be an appropriate value for some things,
     // but it is for most.
-    memset(regVal, 0, NUM_MISCREGS * sizeof(RegVal));
+    memset(regVal, 0, misc_reg::NumRegs * sizeof(RegVal));
 
     // If some state should be non-zero after a reset, set those values here.
-    regVal[MISCREG_CR0] = 0x0000000060000010ULL;
+    regVal[misc_reg::Cr0] = 0x0000000060000010ULL;
 
-    regVal[MISCREG_MTRRCAP] = 0x0508;
+    regVal[misc_reg::Mtrrcap] = 0x0508;
 
-    regVal[MISCREG_MCG_CAP] = 0x104;
+    regVal[misc_reg::McgCap] = 0x104;
 
-    regVal[MISCREG_PAT] = 0x0007040600070406ULL;
+    regVal[misc_reg::Pat] = 0x0007040600070406ULL;
 
-    regVal[MISCREG_SYSCFG] = 0x20601;
+    regVal[misc_reg::Syscfg] = 0x20601;
 
-    regVal[MISCREG_TOP_MEM] = 0x4000000;
+    regVal[misc_reg::TopMem] = 0x4000000;
 
-    regVal[MISCREG_DR6] = (mask(8) << 4) | (mask(16) << 16);
-    regVal[MISCREG_DR7] = 1 << 10;
+    regVal[misc_reg::Dr6] = (mask(8) << 4) | (mask(16) << 16);
+    regVal[misc_reg::Dr7] = 1 << 10;
 
     LocalApicBase lApicBase = 0;
     lApicBase.base = 0xFEE00000 >> 12;
     lApicBase.enable = 1;
     // The "bsp" bit will be set when this register is read, since then we'll
     // have a ThreadContext to check the contextId from.
-    regVal[MISCREG_APIC_BASE] = lApicBase;
+    regVal[misc_reg::ApicBase] = lApicBase;
 }
 
 ISA::ISA(const X86ISAParams &p) : BaseISA(p), vendorString(p.vendor_string)
@@ -142,13 +146,13 @@
     fatal_if(vendorString.size() != 12,
              "CPUID vendor string must be 12 characters\n");
 
-    _regClasses.emplace_back(NumIntRegs, INTREG_T0);
-    _regClasses.emplace_back(NumFloatRegs);
-    _regClasses.emplace_back(1); // Not applicable to X86
-    _regClasses.emplace_back(2); // Not applicable to X86
-    _regClasses.emplace_back(1); // Not applicable to X86
-    _regClasses.emplace_back(NUM_CCREGS);
-    _regClasses.emplace_back(NUM_MISCREGS);
+    _regClasses.emplace_back(int_reg::NumRegs, debug::IntRegs);
+    _regClasses.emplace_back(float_reg::NumRegs, debug::FloatRegs);
+    _regClasses.emplace_back(1, debug::IntRegs); // Not applicable to X86
+    _regClasses.emplace_back(2, debug::IntRegs); // Not applicable to X86
+    _regClasses.emplace_back(1, debug::IntRegs); // Not applicable to X86
+    _regClasses.emplace_back(cc_reg::NumRegs, debug::CCRegs);
+    _regClasses.emplace_back(misc_reg::NumRegs, debug::MiscRegs);
 
     clear();
 }
@@ -159,8 +163,8 @@
     // This function assumes no side effects other than TLB invalidation
     // need to be considered while copying state. That will likely not be
     // true in the future.
-    for (int i = 0; i < NUM_MISCREGS; ++i) {
-        if (!isValidMiscReg(i))
+    for (int i = 0; i < misc_reg::NumRegs; ++i) {
+        if (!misc_reg::isValid(i))
              continue;
 
         dest->setMiscRegNoEffect(i, src->readMiscRegNoEffect(i));
@@ -168,7 +172,7 @@
 
     // The TSC has to be updated with side-effects if the CPUs in a
     // CPU switch have different frequencies.
-    dest->setMiscReg(MISCREG_TSC, src->readMiscReg(MISCREG_TSC));
+    dest->setMiscReg(misc_reg::Tsc, src->readMiscReg(misc_reg::Tsc));
 
     dest->getMMUPtr()->flushAll();
 }
@@ -177,14 +181,20 @@
 ISA::copyRegsFrom(ThreadContext *src)
 {
     //copy int regs
-    for (int i = 0; i < NumIntRegs; ++i)
-         tc->setIntRegFlat(i, src->readIntRegFlat(i));
+    for (int i = 0; i < int_reg::NumRegs; ++i) {
+        RegId reg(IntRegClass, i);
+        tc->setRegFlat(reg, src->getRegFlat(reg));
+    }
     //copy float regs
-    for (int i = 0; i < NumFloatRegs; ++i)
-         tc->setFloatRegFlat(i, src->readFloatRegFlat(i));
+    for (int i = 0; i < float_reg::NumRegs; ++i) {
+        RegId reg(FloatRegClass, i);
+        tc->setRegFlat(reg, src->getRegFlat(reg));
+    }
     //copy condition-code regs
-    for (int i = 0; i < NUM_CCREGS; ++i)
-         tc->setCCRegFlat(i, src->readCCRegFlat(i));
+    for (int i = 0; i < cc_reg::NumRegs; ++i) {
+        RegId reg(CCRegClass, i);
+        tc->setRegFlat(reg, src->getRegFlat(reg));
+    }
     copyMiscRegs(src, tc);
     tc->pcState(src->pcState());
 }
@@ -195,7 +205,7 @@
     // Make sure we're not dealing with an illegal control register.
     // Instructions should filter out these indexes, and nothing else should
     // attempt to read them directly.
-    assert(isValidMiscReg(miscReg));
+    assert(misc_reg::isValid(miscReg));
 
     return regVal[miscReg];
 }
@@ -203,18 +213,18 @@
 RegVal
 ISA::readMiscReg(int miscReg)
 {
-    if (miscReg == MISCREG_TSC) {
-        return regVal[MISCREG_TSC] + tc->getCpuPtr()->curCycle();
+    if (miscReg == misc_reg::Tsc) {
+        return regVal[misc_reg::Tsc] + tc->getCpuPtr()->curCycle();
     }
 
-    if (miscReg == MISCREG_FSW) {
-        RegVal fsw = regVal[MISCREG_FSW];
-        RegVal top = regVal[MISCREG_X87_TOP];
+    if (miscReg == misc_reg::Fsw) {
+        RegVal fsw = regVal[misc_reg::Fsw];
+        RegVal top = regVal[misc_reg::X87Top];
         return insertBits(fsw, 13, 11, top);
     }
 
-    if (miscReg == MISCREG_APIC_BASE) {
-        LocalApicBase base = regVal[MISCREG_APIC_BASE];
+    if (miscReg == misc_reg::ApicBase) {
+        LocalApicBase base = regVal[misc_reg::ApicBase];
         base.bsp = (tc->contextId() == 0);
         return base;
     }
@@ -228,32 +238,32 @@
     // Make sure we're not dealing with an illegal control register.
     // Instructions should filter out these indexes, and nothing else should
     // attempt to write to them directly.
-    assert(isValidMiscReg(miscReg));
+    assert(misc_reg::isValid(miscReg));
 
-    HandyM5Reg m5Reg = regVal[MISCREG_M5_REG];
+    HandyM5Reg m5Reg = regVal[misc_reg::M5Reg];
     int reg_width = 64;
     switch (miscReg) {
-      case MISCREG_X87_TOP:
+      case misc_reg::X87Top:
         reg_width = 3;
         break;
-      case MISCREG_FTW:
+      case misc_reg::Ftw:
         reg_width = 8;
         break;
-      case MISCREG_FSW:
-      case MISCREG_FCW:
-      case MISCREG_FOP:
+      case misc_reg::Fsw:
+      case misc_reg::Fcw:
+      case misc_reg::Fop:
         reg_width = 16;
         break;
-      case MISCREG_MXCSR:
+      case misc_reg::Mxcsr:
         reg_width = 32;
         break;
-      case MISCREG_FISEG:
-      case MISCREG_FOSEG:
+      case misc_reg::Fiseg:
+      case misc_reg::Foseg:
         if (m5Reg.submode != SixtyFourBitMode)
             reg_width = 16;
         break;
-      case MISCREG_FIOFF:
-      case MISCREG_FOOFF:
+      case misc_reg::Fioff:
+      case misc_reg::Fooff:
         if (m5Reg.submode != SixtyFourBitMode)
             reg_width = 32;
         break;
@@ -270,20 +280,20 @@
     RegVal newVal = val;
     switch(miscReg)
     {
-      case MISCREG_CR0:
+      case misc_reg::Cr0:
         {
             CR0 toggled = regVal[miscReg] ^ val;
             CR0 newCR0 = val;
-            Efer efer = regVal[MISCREG_EFER];
+            Efer efer = regVal[misc_reg::Efer];
             if (toggled.pg && efer.lme) {
                 if (newCR0.pg) {
                     //Turning on long mode
                     efer.lma = 1;
-                    regVal[MISCREG_EFER] = efer;
+                    regVal[misc_reg::Efer] = efer;
                 } else {
                     //Turning off long mode
                     efer.lma = 0;
-                    regVal[MISCREG_EFER] = efer;
+                    regVal[misc_reg::Efer] = efer;
                 }
             }
             if (toggled.pg) {
@@ -292,19 +302,19 @@
             //This must always be 1.
             newCR0.et = 1;
             newVal = newCR0;
-            updateHandyM5Reg(regVal[MISCREG_EFER],
+            updateHandyM5Reg(regVal[misc_reg::Efer],
                              newCR0,
-                             regVal[MISCREG_CS_ATTR],
-                             regVal[MISCREG_SS_ATTR],
-                             regVal[MISCREG_RFLAGS]);
+                             regVal[misc_reg::CsAttr],
+                             regVal[misc_reg::SsAttr],
+                             regVal[misc_reg::Rflags]);
         }
         break;
-      case MISCREG_CR2:
+      case misc_reg::Cr2:
         break;
-      case MISCREG_CR3:
+      case misc_reg::Cr3:
         static_cast<MMU *>(tc->getMMUPtr())->flushNonGlobal();
         break;
-      case MISCREG_CR4:
+      case misc_reg::Cr4:
         {
             CR4 toggled = regVal[miscReg] ^ val;
             if (toggled.pae || toggled.pse || toggled.pge) {
@@ -312,79 +322,85 @@
             }
         }
         break;
-      case MISCREG_CR8:
+      case misc_reg::Cr8:
         break;
-      case MISCREG_CS_ATTR:
+      case misc_reg::Rflags:
+        {
+            RFLAGS rflags = val;
+            panic_if(rflags.vm, "Virtual 8086 mode is not supported.");
+            break;
+        }
+      case misc_reg::CsAttr:
         {
             SegAttr toggled = regVal[miscReg] ^ val;
             SegAttr newCSAttr = val;
             if (toggled.longMode) {
                 if (newCSAttr.longMode) {
-                    regVal[MISCREG_ES_EFF_BASE] = 0;
-                    regVal[MISCREG_CS_EFF_BASE] = 0;
-                    regVal[MISCREG_SS_EFF_BASE] = 0;
-                    regVal[MISCREG_DS_EFF_BASE] = 0;
+                    regVal[misc_reg::EsEffBase] = 0;
+                    regVal[misc_reg::CsEffBase] = 0;
+                    regVal[misc_reg::SsEffBase] = 0;
+                    regVal[misc_reg::DsEffBase] = 0;
                 } else {
-                    regVal[MISCREG_ES_EFF_BASE] = regVal[MISCREG_ES_BASE];
-                    regVal[MISCREG_CS_EFF_BASE] = regVal[MISCREG_CS_BASE];
-                    regVal[MISCREG_SS_EFF_BASE] = regVal[MISCREG_SS_BASE];
-                    regVal[MISCREG_DS_EFF_BASE] = regVal[MISCREG_DS_BASE];
+                    regVal[misc_reg::EsEffBase] = regVal[misc_reg::EsBase];
+                    regVal[misc_reg::CsEffBase] = regVal[misc_reg::CsBase];
+                    regVal[misc_reg::SsEffBase] = regVal[misc_reg::SsBase];
+                    regVal[misc_reg::DsEffBase] = regVal[misc_reg::DsBase];
                 }
             }
-            updateHandyM5Reg(regVal[MISCREG_EFER],
-                             regVal[MISCREG_CR0],
+            updateHandyM5Reg(regVal[misc_reg::Efer],
+                             regVal[misc_reg::Cr0],
                              newCSAttr,
-                             regVal[MISCREG_SS_ATTR],
-                             regVal[MISCREG_RFLAGS]);
+                             regVal[misc_reg::SsAttr],
+                             regVal[misc_reg::Rflags]);
         }
         break;
-      case MISCREG_SS_ATTR:
-        updateHandyM5Reg(regVal[MISCREG_EFER],
-                         regVal[MISCREG_CR0],
-                         regVal[MISCREG_CS_ATTR],
+      case misc_reg::SsAttr:
+        updateHandyM5Reg(regVal[misc_reg::Efer],
+                         regVal[misc_reg::Cr0],
+                         regVal[misc_reg::CsAttr],
                          val,
-                         regVal[MISCREG_RFLAGS]);
+                         regVal[misc_reg::Rflags]);
         break;
       // These segments always actually use their bases, or in other words
       // their effective bases must stay equal to their actual bases.
-      case MISCREG_FS_BASE:
-      case MISCREG_GS_BASE:
-      case MISCREG_HS_BASE:
-      case MISCREG_TSL_BASE:
-      case MISCREG_TSG_BASE:
-      case MISCREG_TR_BASE:
-      case MISCREG_IDTR_BASE:
-        regVal[MISCREG_SEG_EFF_BASE(miscReg - MISCREG_SEG_BASE_BASE)] = val;
+      case misc_reg::FsBase:
+      case misc_reg::GsBase:
+      case misc_reg::HsBase:
+      case misc_reg::TslBase:
+      case misc_reg::TsgBase:
+      case misc_reg::TrBase:
+      case misc_reg::IdtrBase:
+        regVal[misc_reg::segEffBase(miscReg - misc_reg::SegBaseBase)] = val;
         break;
       // These segments ignore their bases in 64 bit mode.
       // their effective bases must stay equal to their actual bases.
-      case MISCREG_ES_BASE:
-      case MISCREG_CS_BASE:
-      case MISCREG_SS_BASE:
-      case MISCREG_DS_BASE:
+      case misc_reg::EsBase:
+      case misc_reg::CsBase:
+      case misc_reg::SsBase:
+      case misc_reg::DsBase:
         {
-            Efer efer = regVal[MISCREG_EFER];
-            SegAttr csAttr = regVal[MISCREG_CS_ATTR];
+            Efer efer = regVal[misc_reg::Efer];
+            SegAttr csAttr = regVal[misc_reg::CsAttr];
             if (!efer.lma || !csAttr.longMode) // Check for non 64 bit mode.
-                regVal[MISCREG_SEG_EFF_BASE(miscReg -
-                        MISCREG_SEG_BASE_BASE)] = val;
+                regVal[misc_reg::segEffBase(miscReg -
+                        misc_reg::SegBaseBase)] = val;
         }
         break;
-      case MISCREG_TSC:
-        regVal[MISCREG_TSC] = val - tc->getCpuPtr()->curCycle();
+      case misc_reg::Tsc:
+        regVal[misc_reg::Tsc] = val - tc->getCpuPtr()->curCycle();
         return;
-      case MISCREG_DR0:
-      case MISCREG_DR1:
-      case MISCREG_DR2:
-      case MISCREG_DR3:
+      case misc_reg::Dr0:
+      case misc_reg::Dr1:
+      case misc_reg::Dr2:
+      case misc_reg::Dr3:
         /* These should eventually set up breakpoints. */
         break;
-      case MISCREG_DR4:
-        miscReg = MISCREG_DR6;
+      case misc_reg::Dr4:
+        miscReg = misc_reg::Dr6;
         [[fallthrough]];
-      case MISCREG_DR6:
+      case misc_reg::Dr6:
         {
-            DR6 dr6 = regVal[MISCREG_DR6];
+            DR6 dr6 = regVal[misc_reg::Dr6];
             DR6 newDR6 = val;
             dr6.b0 = newDR6.b0;
             dr6.b1 = newDR6.b1;
@@ -396,12 +412,12 @@
             newVal = dr6;
         }
         break;
-      case MISCREG_DR5:
-        miscReg = MISCREG_DR7;
+      case misc_reg::Dr5:
+        miscReg = misc_reg::Dr7;
         [[fallthrough]];
-      case MISCREG_DR7:
+      case misc_reg::Dr7:
         {
-            DR7 dr7 = regVal[MISCREG_DR7];
+            DR7 dr7 = regVal[misc_reg::Dr7];
             DR7 newDR7 = val;
             dr7.l0 = newDR7.l0;
             dr7.g0 = newDR7.g0;
@@ -442,15 +458,15 @@
             dr7.len3 = newDR7.len3;
         }
         break;
-      case MISCREG_M5_REG:
+      case misc_reg::M5Reg:
         // Writing anything to the m5reg with side effects makes it update
         // based on the current values of the relevant registers. The actual
         // value written is discarded.
-        updateHandyM5Reg(regVal[MISCREG_EFER],
-                         regVal[MISCREG_CR0],
-                         regVal[MISCREG_CS_ATTR],
-                         regVal[MISCREG_SS_ATTR],
-                         regVal[MISCREG_RFLAGS]);
+        updateHandyM5Reg(regVal[misc_reg::Efer],
+                         regVal[misc_reg::Cr0],
+                         regVal[misc_reg::CsAttr],
+                         regVal[misc_reg::SsAttr],
+                         regVal[misc_reg::Rflags]);
         return;
       default:
         break;
@@ -461,25 +477,25 @@
 void
 ISA::serialize(CheckpointOut &cp) const
 {
-    SERIALIZE_ARRAY(regVal, NUM_MISCREGS);
+    SERIALIZE_ARRAY(regVal, misc_reg::NumRegs);
 }
 
 void
 ISA::unserialize(CheckpointIn &cp)
 {
-    UNSERIALIZE_ARRAY(regVal, NUM_MISCREGS);
-    updateHandyM5Reg(regVal[MISCREG_EFER],
-                     regVal[MISCREG_CR0],
-                     regVal[MISCREG_CS_ATTR],
-                     regVal[MISCREG_SS_ATTR],
-                     regVal[MISCREG_RFLAGS]);
+    UNSERIALIZE_ARRAY(regVal, misc_reg::NumRegs);
+    updateHandyM5Reg(regVal[misc_reg::Efer],
+                     regVal[misc_reg::Cr0],
+                     regVal[misc_reg::CsAttr],
+                     regVal[misc_reg::SsAttr],
+                     regVal[misc_reg::Rflags]);
 }
 
 void
 ISA::setThreadContext(ThreadContext *_tc)
 {
     BaseISA::setThreadContext(_tc);
-    tc->getDecoderPtr()->as<Decoder>().setM5Reg(regVal[MISCREG_M5_REG]);
+    tc->getDecoderPtr()->as<Decoder>().setM5Reg(regVal[misc_reg::M5Reg]);
 }
 
 std::string
diff --git a/src/arch/x86/isa.hh b/src/arch/x86/isa.hh
index ee5664a..f19ed9f 100644
--- a/src/arch/x86/isa.hh
+++ b/src/arch/x86/isa.hh
@@ -35,6 +35,7 @@
 #include "arch/generic/isa.hh"
 #include "arch/x86/pcstate.hh"
 #include "arch/x86/regs/float.hh"
+#include "arch/x86/regs/int.hh"
 #include "arch/x86/regs/misc.hh"
 #include "base/types.hh"
 #include "cpu/reg_class.hh"
@@ -51,7 +52,7 @@
 class ISA : public BaseISA
 {
   private:
-    RegVal regVal[NUM_MISCREGS];
+    RegVal regVal[misc_reg::NumRegs];
     void updateHandyM5Reg(Efer efer, CR0 cr0,
             SegAttr csAttr, SegAttr ssAttr, RFLAGS rflags);
 
@@ -99,9 +100,9 @@
     int
     flattenFloatIndex(int reg) const
     {
-        if (reg >= NUM_FLOATREGS) {
-            reg = FLOATREG_STACK(reg - NUM_FLOATREGS,
-                                 regVal[MISCREG_X87_TOP]);
+        if (reg >= float_reg::NumRegs) {
+            reg = float_reg::stack(reg - float_reg::NumRegs,
+                                   regVal[misc_reg::X87Top]);
         }
         return reg;
     }
@@ -115,7 +116,7 @@
     bool
     inUserMode() const override
     {
-        HandyM5Reg m5reg = readMiscRegNoEffect(MISCREG_M5_REG);
+        HandyM5Reg m5reg = readMiscRegNoEffect(misc_reg::M5Reg);
         return m5reg.cpl == 3;
     }
 
diff --git a/src/arch/x86/isa/bitfields.isa b/src/arch/x86/isa/bitfields.isa
index 0404afc..dd81712 100644
--- a/src/arch/x86/isa/bitfields.isa
+++ b/src/arch/x86/isa/bitfields.isa
@@ -83,6 +83,7 @@
 def bitfield STACKSIZE stackSize;
 
 def bitfield MODE mode;
+def bitfield CPL mode.cpl;
 def bitfield MODE_MODE mode.mode;
 def bitfield MODE_SUBMODE mode.submode;
 
diff --git a/src/arch/x86/isa/decoder/locked_opcodes.isa b/src/arch/x86/isa/decoder/locked_opcodes.isa
index 4a3a94b..de75479 100644
--- a/src/arch/x86/isa/decoder/locked_opcodes.isa
+++ b/src/arch/x86/isa/decoder/locked_opcodes.isa
@@ -137,8 +137,8 @@
             }
             'X86ISA::TwoByteOpcode': decode OPCODE_OP_TOP5 {
                 0x04: decode OPCODE_OP_BOTTOM3 {
-                    0x0: WarnUnimpl::mov_Rd_CR8D();
-                    0x2: WarnUnimpl::mov_CR8D_Rd();
+                    0x0: WarnUnimpl::mov_Rd_CR8D(); // privileged
+                    0x2: WarnUnimpl::mov_CR8D_Rd(); // privileged
                 }
                 0x15: decode OPCODE_OP_BOTTOM3 {
                     0x3: BTS_LOCKED(Mv,Gv);
diff --git a/src/arch/x86/isa/decoder/one_byte_opcodes.isa b/src/arch/x86/isa/decoder/one_byte_opcodes.isa
index 04b3adc..ef596df 100644
--- a/src/arch/x86/isa/decoder/one_byte_opcodes.isa
+++ b/src/arch/x86/isa/decoder/one_byte_opcodes.isa
@@ -43,10 +43,11 @@
         0x00: decode OPCODE_OP_BOTTOM3 {
             0x6: decode MODE_SUBMODE {
                 0x0: UD2();
-                default: WarnUnimpl::push_ES();
+                default: PUSH(sEv);
             }
             0x7: decode MODE_SUBMODE {
                 0x0: UD2();
+                0x3, 0x4: POP_REAL(sEv);
                 default: WarnUnimpl::pop_ES();
             }
             default: MultiInst::ADD(OPCODE_OP_BOTTOM3,
@@ -57,7 +58,7 @@
         0x01: decode OPCODE_OP_BOTTOM3 {
             0x6: decode MODE_SUBMODE {
                 0x0: UD2();
-                default: WarnUnimpl::push_CS();
+                default: PUSH(sCv);
             }
             //Any time this is seen, it should generate a two byte opcode
             0x7: M5InternalError::error(
@@ -70,10 +71,11 @@
         0x02: decode OPCODE_OP_BOTTOM3 {
             0x6: decode MODE_SUBMODE {
                 0x0: UD2();
-                default: WarnUnimpl::push_SS();
+                default: PUSH(sSv);
             }
             0x7: decode MODE_SUBMODE {
                 0x0: UD2();
+                0x3, 0x4: POP_REAL(sSv);
                 default: WarnUnimpl::pop_SS();
             }
             default: MultiInst::ADC(OPCODE_OP_BOTTOM3,
@@ -84,10 +86,11 @@
         0x03: decode OPCODE_OP_BOTTOM3 {
             0x6: decode MODE_SUBMODE {
                 0x0: UD2();
-                default: WarnUnimpl::push_DS();
+                default: PUSH(sDv);
             }
             0x7: decode MODE_SUBMODE {
                 0x0: UD2();
+                0x3, 0x4: POP_REAL(sDv);
                 default: WarnUnimpl::pop_DS();
             }
             default: MultiInst::SBB(OPCODE_OP_BOTTOM3,
@@ -189,10 +192,22 @@
             0x1: IMUL(Gv,Ev,Iz);
             0x2: PUSH(Ib);
             0x3: IMUL(Gv,Ev,Ib);
-            0x4: StringInst::INS(Yb,rD);
-            0x5: StringInst::INS(Yz,rD);
-            0x6: StringInst::OUTS(rD,Xb);
-            0x7: StringInst::OUTS(rD,Xz);
+            0x4: decode MODE_SUBMODE {
+                0x3: StringInst::INS_VIRT(Yb,rD);
+                default: StringInst::INS(Yb,rD);
+            }
+            0x5: decode MODE_SUBMODE {
+                0x3: StringInst::INS_VIRT(Yz,rD);
+                default: StringInst::INS(Yz,rD);
+            }
+            0x6: decode MODE_SUBMODE {
+                0x3: StringInst::OUTS_VIRT(rD,Xb);
+                default: StringInst::OUTS(rD,Xb);
+            }
+            0x7: decode MODE_SUBMODE {
+                0x3: StringInst::OUTS_VIRT(rD,Xz);
+                default: StringInst::OUTS(rD,Xz);
+            }
         }
         0x0E: decode OPCODE_OP_BOTTOM3 {
             0x0: JO(Jb);
@@ -278,13 +293,18 @@
             }
             0x5: LEA(Gv,M);
             0x6: decode MODE_SUBMODE {
-                0x3, 0x4: MOV_REAL(Sv,Ev);
+                0x3, 0x4: decode MODRM_REG {
+                    // Moving to the CS selector (0x1) is illegal, and 0x6 and
+                    // 0x7 are reserved.
+                    0x1, 0x6, 0x7: UD2();
+                    default: MOV_REAL(Sv,Ev);
+                }
                 default: decode MODRM_REG {
-                    0x1: UD2(); // Moving to the CS selector is illegal.
+                    // Moving to the CS selector (0x1) is illegal, and 0x6 and
+                    // 0x7 are reserved.
+                    0x1, 0x6, 0x7: UD2();
                     0x2: MOVSS(Sv,Ev);
-                    0x0, 0x3,
-                    0x4, 0x5: MOV(Sv,Ev);
-                    default: UD2();
+                    default: MOV(Sv,Ev);
                 }
             }
             //0x7: group10_Ev();
@@ -302,11 +322,18 @@
             0x1: CQO(rAv,rDv);
             0x2: decode MODE_SUBMODE {
                 0x0: UD2();
+                0x3, 0x4: CALL_FAR_REAL(Iz);
                 default: WarnUnimpl::call_far_Ap();
             }
             0x3: WarnUnimpl::fwait(); //aka wait
-            0x4: PUSHF();
-            0x5: POPF();
+            0x4: decode MODE_SUBMODE {
+                0x3: PUSHF_VIRT();
+                default: PUSHF();
+            }
+            0x5: decode MODE_SUBMODE {
+                0x3: POPF_VIRT();
+                default: POPF();
+            }
             //The 64 bit versions of both of these should be illegal only
             //if CPUID says it isn't supported. For now, we'll just assume
             //that it's supported.
@@ -362,10 +389,12 @@
             0x3: RET_NEAR();
             0x4: decode MODE_SUBMODE {
                 0x0: UD2();
+                0x3, 0x4: LES_REAL(Gz,Mz);
                 default: WarnUnimpl::les_Gz_Mp();
             }
             0x5: decode MODE_SUBMODE {
                 0x0: UD2();
+                0x3, 0x4: LDS_REAL(Gz,Mz);
                 default: WarnUnimpl::lds_Gz_Mp();
             }
             //0x6: group12_Eb_Ib();
@@ -386,12 +415,19 @@
                 // confuse the instruction type specialization code.
                 0x0: Inst::ENTER(Iw,Iw);
                 0x1: Inst::LEAVE();
-                0x2: ret_far_Iw();
+                0x2: decode MODE_SUBMODE {
+                    0x3, 0x4: Inst::RET_FAR_REAL(Iw);
+                    default: ret_far_Iw();
+                }
                 0x3: decode MODE_SUBMODE {
-                    0x3, 0x4: ret_far_real();
+                    0x3, 0x4: Inst::RET_FAR_REAL();
                     default: Inst::RET_FAR();
                 }
-                0x4: Inst::INT3();
+                0x4: decode MODE_SUBMODE {
+                    0x4: Inst::INT3_REAL();
+                    0x3: Inst::INT3_VIRT();
+                    default: Inst::INT3();
+                }
                 0x5: decode FullSystemInt default inst_ib() {
                     0: decode IMMEDIATE {
                         // Really only the LSB matters, but the decoder
@@ -403,7 +439,14 @@
                             }});
                     }
 
-                    default: Inst::INT(Ib);
+                    default: decode MODE_MODE {
+                        0x0: Inst::INT_LONG(Ib);
+                        0x1: decode MODE_SUBMODE {
+                            0x4: Inst::INT_REAL(Ib);
+                            0x3: Inst::INT_VIRT(Ib);
+                            default: Inst::INT_PROT(Ib);
+                        }
+                    }
                 }
                 0x6: decode MODE_SUBMODE {
                     0x0: Inst::UD2();
@@ -484,10 +527,22 @@
             0x1: LOOPE(Jb);
             0x2: LOOP(Jb);
             0x3: JRCXZ(Jb);
-            0x4: IN(rAb,Ib);
-            0x5: IN(rAv,Iv);
-            0x6: OUT(Ib,rAb);
-            0x7: OUT(Iv,rAv);
+            0x4: decode MODE_SUBMODE {
+                0x3: IN_VIRT(rAb,Ib);
+                default: IN(rAb,Ib);
+            }
+            0x5: decode MODE_SUBMODE {
+                0x3: IN_VIRT(rAv,Iv);
+                default: IN(rAv,Iv);
+            }
+            0x6: decode MODE_SUBMODE {
+                0x3: OUT_VIRT(Ib,rAb);
+                default: OUT(Ib,rAb);
+            }
+            0x7: decode MODE_SUBMODE {
+                0x3: OUT_VIRT(Iv,rAv);
+                default: OUT(Iv,rAv);
+            }
         }
         0x1D: decode OPCODE_OP_BOTTOM3 {
             0x0: CALL_NEAR(Jz);
@@ -500,10 +555,22 @@
                 0x4: JMP_FAR_REAL(Iz);
             }
             0x3: JMP(Jb);
-            0x4: IN(rAb,rD);
-            0x5: IN(rAv,rD);
-            0x6: OUT(rD,rAb);
-            0x7: OUT(rD,rAv);
+            0x4: decode MODE_SUBMODE {
+                0x3: IN_VIRT(rAb,rD);
+                default: IN(rAb,rD);
+            }
+            0x5: decode MODE_SUBMODE {
+                0x3: IN_VIRT(rAv,rD);
+                default: IN(rAv,rD);
+            }
+            0x6: decode MODE_SUBMODE {
+                0x3: OUT_VIRT(rD,rAb);
+                default: OUT(rD,rAb);
+            }
+            0x7: decode MODE_SUBMODE {
+                0x3: OUT_VIRT(rD,rAv);
+                default: OUT(rD,rAv);
+            }
         }
         0x1E: decode OPCODE_OP_BOTTOM3 {
             0x0: M5InternalError::error(
@@ -513,7 +580,7 @@
                 {{"Tried to execute the repne prefix!"}});
             0x3: M5InternalError::error(
                 {{"Tried to execute the rep/repe prefix!"}});
-            0x4: HLT();
+            0x4: Cpl0Inst::HLT();
             0x5: CMC();
             //0x6: group3_Eb();
             0x6: decode MODRM_REG {
@@ -542,8 +609,16 @@
         0x1F: decode OPCODE_OP_BOTTOM3 {
             0x0: CLC();
             0x1: STC();
-            0x2: CLI();
-            0x3: STI();
+            0x2: decode MODE_SUBMODE {
+                0x3: CLI_VIRT();
+                0x4: CLI_REAL();
+                default: CLI();
+            }
+            0x3: decode MODE_SUBMODE {
+                0x3: STI_VIRT();
+                0x4: STI_REAL();
+                default: STI();
+            }
             0x4: CLD();
             0x5: STD();
             //0x6: group4();
@@ -557,7 +632,10 @@
                 0x0: INC(Ev);
                 0x1: DEC(Ev);
                 0x2: CALL_NEAR(Ev);
-                0x3: WarnUnimpl::call_far_Mp();
+                0x3: decode MODE_SUBMODE {
+                    0x4: CALL_FAR_REAL(Mz);
+                    default: WarnUnimpl::call_far_Mp();
+                }
                 0x4: JMP(Ev);
                 0x5: decode MODE_SUBMODE {
                     0x0: JMP_FAR(Mz);
diff --git a/src/arch/x86/isa/decoder/two_byte_opcodes.isa b/src/arch/x86/isa/decoder/two_byte_opcodes.isa
index 549db47..22c20e5 100644
--- a/src/arch/x86/isa/decoder/two_byte_opcodes.isa
+++ b/src/arch/x86/isa/decoder/two_byte_opcodes.isa
@@ -48,8 +48,14 @@
             0x00: decode MODRM_REG {
                 0x0: sldt_Mw_or_Rv();
                 0x1: str_Mw_or_Rv();
-                0x2: Inst::LLDT(Ew);
-                0x3: Inst::LTR(Ew);
+                0x2: decode MODE_SUBMODE {
+                    0x0: Cpl0Inst::LLDT_64(Ew);
+                    default: Cpl0Inst::LLDT(Ew);
+                }
+                0x3: decode MODE_SUBMODE {
+                    0x0: Cpl0Inst::LTR_64(Ew);
+                    default: Cpl0Inst::LTR(Ew);
+                }
                 0x4: verr_Mw_or_Rv();
                 0x5: verw_Mw_or_Rv();
                 //0x6: jmpe_Ev(); // IA-64
@@ -93,33 +99,33 @@
                         0x1: xsetbv();
                     }
                     default: decode MODE_SUBMODE {
-                        0x0: Inst::LGDT(M);
+                        0x0: Cpl0Inst::LGDT(M);
                         default: decode OPSIZE {
                             // 16 bit operand sizes are special, but only
                             // in legacy and compatability modes.
-                            0x2: Inst::LGDT_16(M);
-                            default: Inst::LGDT(M);
+                            0x2: Cpl0Inst::LGDT_16(M);
+                            default: Cpl0Inst::LGDT(M);
                         }
                     }
                 }
                 0x3: decode MODRM_MOD {
                     0x3: decode MODRM_RM {
-                        0x0: vmrun();
-                        0x1: vmmcall();
-                        0x2: vmload();
-                        0x3: vmsave();
-                        0x4: stgi();
-                        0x5: clgi();
+                        0x0: vmrun(); // privileged
+                        0x1: vmmcall(); // privileged
+                        0x2: vmload(); // privileged
+                        0x3: vmsave(); // privileged
+                        0x4: stgi(); // privileged
+                        0x5: clgi(); // privileged
                         0x6: skinit();
-                        0x7: invlpga();
+                        0x7: invlpga(); // privileged
                     }
                     default: decode MODE_SUBMODE {
-                        0x0: Inst::LIDT(M);
+                        0x0: Cpl0Inst::LIDT(M);
                         default: decode OPSIZE {
                             // 16 bit operand sizes are special, but only
                             // in legacy and compatability modes.
-                            0x2: Inst::LIDT_16(M);
-                            default: Inst::LIDT(M);
+                            0x2: Cpl0Inst::LIDT_16(M);
+                            default: Cpl0Inst::LIDT(M);
                         }
                     }
                 }
@@ -127,14 +133,14 @@
                     0x3: Inst::SMSW(Rv);
                     default: Inst::SMSW(Mw);
                 }
-                0x6: Inst::LMSW(Ew);
+                0x6: Cpl0Inst::LMSW(Ew);
                 0x7: decode MODRM_MOD {
                     0x3: decode MODRM_RM {
-                        0x0: Inst::SWAPGS();
+                        0x0: Cpl0Inst::SWAPGS();
                         0x1: Inst::RDTSCP();
                         default: Inst::UD2();
                     }
-                    default: Inst::INVLPG(M);
+                    default: Cpl0Inst::INVLPG(M);
                 }
             }
             0x02: lar_Gv_Ew();
@@ -162,20 +168,20 @@
                     0x1: Inst::SYSCALL_LEGACY();
                 }
             }
-            0x06: Inst::CLTS();
+            0x06: Cpl0Inst::CLTS();
             0x07: decode MODE_SUBMODE {
                 0x0: decode OPSIZE {
                     // Return to 64 bit mode.
-                    0x8: Inst::SYSRET_TO_64();
+                    0x8: Cpl0Inst::SYSRET_TO_64();
                     // Return to compatibility mode.
-                    default: Inst::SYSRET_TO_COMPAT();
+                    default: Cpl0Inst::SYSRET_TO_COMPAT();
                 }
-                default: Inst::SYSRET_NON_64();
+                default: Cpl0Inst::SYSRET_NON_64();
             }
         }
         0x01: decode OPCODE_OP_BOTTOM3 {
-            0x0: invd();
-            0x1: wbinvd();
+            0x0: invd(); // privileged
+            0x1: wbinvd(); // privileged
             0x2: Inst::UD2();
             0x3: Inst::UD2();
             0x4: Inst::UD2();
@@ -276,18 +282,20 @@
             0x04: decode LEGACY_DECODEVAL {
                 // no prefix
                 0x0: decode OPCODE_OP_BOTTOM3 {
-                    0x0: CondInst::MOV(
-                        {{isValidMiscReg(MISCREG_CR(MODRM_REG))}},Rd,Cd);
-                    0x1: MOV(Rd,Dd);
-                    0x2: CondInst::MOV(
-                        {{isValidMiscReg(MISCREG_CR(MODRM_REG))}},Cd,Rd);
-                    0x3: MOV(Dd,Rd);
+                    0x0: Cpl0CondInst::MOV(
+                        {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Rd,Cd);
+                    0x1: Cpl0CondInst::MOV({{MODRM_REG < 8}},Rd,Dd);
+                    0x2: Cpl0CondInst::MOV(
+                        {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Cd,Rd);
+                    0x3: Cpl0CondInst::MOV({{MODRM_REG < 8}},Dd,Rd);
                     default: UD2();
                 }
                 // operand size (0x66)
                 0x1: decode OPCODE_OP_BOTTOM3 {
-                    0x0: MOV(Rd,Cd);
-                    0x2: MOV(Cd,Rd);
+                    0x0: Cpl0CondInst::MOV(
+                        {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Rd,Cd);
+                    0x2: Cpl0CondInst::MOV(
+                        {{misc_reg::isValid(misc_reg::cr(MODRM_REG))}},Cd,Rd);
                 }
                 default: UD2();
             }
@@ -340,17 +348,17 @@
             }
         }
         0x06: decode OPCODE_OP_BOTTOM3 {
-            0x0: Inst::WRMSR();
+            0x0: Cpl0Inst::WRMSR();
             0x1: Inst::RDTSC();
-            0x2: Inst::RDMSR();
-            0x3: rdpmc();
+            0x2: Cpl0Inst::RDMSR();
+            0x3: rdpmc(); // privileged
             0x4: decode FullSystemInt {
                 0: SyscallInst::sysenter({{
                     return std::make_shared<SESyscallFault>();
                 }});
                 default: sysenter();
             }
-            0x5: sysexit();
+            0x5: sysexit(); // privileged
             0x6: Inst::UD2();
             0x7: getsec();
         }
@@ -667,8 +675,11 @@
             }
         }
         0x14: decode OPCODE_OP_BOTTOM3 {
-            0x0: push_fs();
-            0x1: pop_fs();
+            0x0: Inst::PUSH(sFv);
+            0x1: decode MODE_SUBMODE {
+                0x3, 0x4: Inst::POP_REAL(sFv);
+                default: pop_fs();
+            }
             0x2: CPUIDInst::CPUID({{
                 CpuidResult result;
                 bool success = doCpuid(xc->tcBase(), bits(Rax, 31, 0),
@@ -694,8 +705,11 @@
             default: Inst::UD2();
         }
         0x15: decode OPCODE_OP_BOTTOM3 {
-            0x0: push_gs();
-            0x1: pop_gs();
+            0x0: Inst::PUSH(sGv);
+            0x1: decode MODE_SUBMODE {
+                0x3, 0x4: Inst::POP_REAL(sGv);
+                default: pop_gs();
+            }
             0x2: rsm_smm();
             0x3: Inst::BTS(Ev,Gv);
             0x4: Inst::SHRD(Ev,Gv,Ib);
@@ -744,10 +758,19 @@
             0x16: decode OPCODE_OP_BOTTOM3 {
                 0x0: CMPXCHG(Eb,Gb);
                 0x1: CMPXCHG(Ev,Gv);
-                0x2: WarnUnimpl::lss_Gz_Mp();
+                0x2: decode MODE_SUBMODE {
+                    0x3, 0x4: LSS_REAL(Gz,Mz);
+                    default: WarnUnimpl::lss_Gz_Mp();
+                }
                 0x3: BTR(Ev,Gv);
-                0x4: WarnUnimpl::lfs_Gz_Mp();
-                0x5: WarnUnimpl::lgs_Gz_Mp();
+                0x4: decode MODE_SUBMODE {
+                    0x3, 0x4: LFS_REAL(Gz,Mz);
+                    default: WarnUnimpl::lfs_Gz_Mp();
+                }
+                0x5: decode MODE_SUBMODE {
+                    0x3, 0x4: LGS_REAL(Gz,Mz);
+                    default: WarnUnimpl::lgs_Gz_Mp();
+                }
                 //The size of the second operand in these instructions
                 //should really be "b" or "w", but it's set to v in order
                 //to have a consistent register size. This shouldn't
diff --git a/src/arch/x86/isa/formats/cond.isa b/src/arch/x86/isa/formats/cond.isa
index ec46595..158dff3 100644
--- a/src/arch/x86/isa/formats/cond.isa
+++ b/src/arch/x86/isa/formats/cond.isa
@@ -47,3 +47,33 @@
     decode_block = '\tif (%s) {\n%s\n\t} else {\n%s\n}\n' % \
         (cond, if_blocks.decode_block, else_blocks.decode_block)
 }};
+
+def format Cpl0CondInst(cond, *opTypeSet) {{
+    blocks = OutputBlocks()
+
+    if_blocks = specializeInst(Name, list(opTypeSet), EmulEnv())
+    blocks.append(if_blocks)
+    else_blocks = specializeInst('UD2', [], EmulEnv())
+    blocks.append(else_blocks)
+
+    (header_output, decoder_output,
+     decode_block, exec_output) = blocks.makeList()
+    decode_block = '''
+        if (%(cond)s) {
+            %(if_block)s
+        } else {
+            %(else_block)s
+        }
+    ''' % {'cond': cond,
+        'if_block': if_blocks.decode_block,
+        'else_block': else_blocks.decode_block
+    }
+    decode_block = '''
+        if (emi.mode.cpl != 0) {
+            return new DecodeFaultInst("%(Name)s", emi,
+                    std::make_shared<GeneralProtection>(0));
+        } else {
+            %(decode_block)s
+        }
+    ''' % {'decode_block': decode_block, 'Name': Name}
+}};
diff --git a/src/arch/x86/isa/formats/multi.isa b/src/arch/x86/isa/formats/multi.isa
index 94859e6..0d4e920 100644
--- a/src/arch/x86/isa/formats/multi.isa
+++ b/src/arch/x86/isa/formats/multi.isa
@@ -47,6 +47,20 @@
      decode_block, exec_output) = blocks.makeList()
 }};
 
+def format Cpl0Inst(*opTypeSet) {{
+    blocks = specializeInst(Name, list(opTypeSet), EmulEnv())
+    (header_output, decoder_output,
+     decode_block, exec_output) = blocks.makeList()
+    decode_block = '''
+        if (emi.mode.cpl != 0) {
+            return new DecodeFaultInst("%(Name)s", emi,
+                    std::make_shared<GeneralProtection>(0));
+        } else {
+            %(decode_block)s
+        }
+    ''' % {'decode_block': decode_block, 'Name': Name}
+}};
+
 def format MultiInst(switchVal, *opTypeSets) {{
     switcher = {}
     for (count, opTypeSet) in zip(range(len(opTypeSets)), opTypeSets):
diff --git a/src/arch/x86/isa/includes.isa b/src/arch/x86/isa/includes.isa
index bb07405..6fc5f44 100644
--- a/src/arch/x86/isa/includes.isa
+++ b/src/arch/x86/isa/includes.isa
@@ -54,6 +54,7 @@
 
 #include "arch/generic/debugfaults.hh"
 #include "arch/x86/emulenv.hh"
+#include "arch/x86/insts/decode_fault.hh"
 #include "arch/x86/insts/macroop.hh"
 #include "arch/x86/insts/microdebug.hh"
 #include "arch/x86/insts/microfpop.hh"
@@ -62,6 +63,9 @@
 #include "arch/x86/insts/microregop.hh"
 #include "arch/x86/insts/microspecop.hh"
 #include "arch/x86/insts/static_inst.hh"
+#include "arch/x86/regs/ccr.hh"
+#include "arch/x86/regs/int.hh"
+#include "arch/x86/regs/misc.hh"
 #include "arch/x86/types.hh"
 #include "arch/x86/utility.hh"
 #include "base/logging.hh"
@@ -81,6 +85,7 @@
 #include "arch/x86/decoder.hh"
 #include "arch/x86/faults.hh"
 #include "arch/x86/microcode_rom.hh"
+#include "arch/x86/regs/ccr.hh"
 #include "arch/x86/regs/float.hh"
 #include "arch/x86/regs/int.hh"
 #include "arch/x86/regs/misc.hh"
diff --git a/src/arch/x86/isa/insts/general_purpose/control_transfer/call.py b/src/arch/x86/isa/insts/general_purpose/control_transfer/call.py
index 4204a8c..8d11256 100644
--- a/src/arch/x86/isa/insts/general_purpose/control_transfer/call.py
+++ b/src/arch/x86/isa/insts/general_purpose/control_transfer/call.py
@@ -44,8 +44,8 @@
     limm t1, imm
     rdip t7
     # Check target of call
-    stis t7, ss, [0, t0, rsp], "-env.dataSize"
-    subi rsp, rsp, ssz
+    st t7, ss, [0, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
     wrip t7, t1
 };
 
@@ -58,8 +58,8 @@
 
     rdip t1
     # Check target of call
-    stis t1, ss, [0, t0, rsp], "-env.dataSize"
-    subi rsp, rsp, ssz
+    st t1, ss, [0, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
     wripi reg, 0
 };
 
@@ -73,8 +73,8 @@
     rdip t7
     ld t1, seg, sib, disp
     # Check target of call
-    st t7, ss, [0, t0, rsp], "-env.dataSize"
-    subi rsp, rsp, ssz
+    st t7, ss, [0, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
     wripi t1, 0
 };
 
@@ -88,10 +88,81 @@
     rdip t7
     ld t1, seg, riprel, disp
     # Check target of call
-    st t7, ss, [0, t0, rsp], "-env.dataSize"
-    subi rsp, rsp, ssz
+    st t7, ss, [0, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
     wripi t1, 0
 };
+
+def macroop CALL_FAR_REAL_I {
+    .function_call
+    .control_indirect
+
+    limm t1, imm, dataSize=8
+
+    # Multiply the data size by 8 to get the size of the offset in bits.
+    limm t3, dsz, dataSize=8
+    slli t3, t3, 3, dataSize=8
+
+    # Extract the selector from the far pointer.
+    srl t2, t1, t3, dataSize=8
+    zexti t2, t2, 15, dataSize=8
+    # Extract the offset from the far pointer.
+    zext t1, t1, t3, dataSize=8
+
+    # Compute the base.
+    slli t3, t2, 4, dataSize=8
+
+    # Make sure pushing the RIP will work after we push cs.
+    cda ss, [1, t0, rsp], "-env.dataSize * 2", addressSize=ssz
+
+    # Push CS.
+    rdsel t4, cs, dataSize=8
+    st t4, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    # Push RIP.
+    rdip t7
+    st t7, ss, [1, t0, rsp], "-env.dataSize * 2", addressSize=ssz
+    subi rsp, rsp, "env.dataSize * 2", dataSize=ssz
+
+    # Install the new selector, base and RIP values.
+    wrsel cs, t2, dataSize=2
+    wrbase cs, t3, dataSize=8
+    wrip t1, t0
+};
+
+def macroop CALL_FAR_REAL_M {
+    .function_call
+    .control_indirect
+
+    lea t1, seg, sib, disp, dataSize=asz
+    # Load the selector.
+    ld t2, seg, [1, t0, t1], dsz, dataSize=2
+    # Load the offset.
+    ld t1, seg, [1, t0, t1]
+
+    # Compute the base.
+    zexti t3, t2, 15, dataSize=8
+    slli t3, t3, 4, dataSize=8
+
+    # Make sure pushing the RIP will work after we push cs.
+    cda ss, [1, t0, rsp], "-env.dataSize * 2", addressSize=ssz
+
+    # Push CS.
+    rdsel t4, cs, dataSize=8
+    st t4, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    # Push RIP.
+    rdip t7
+    st t7, ss, [1, t0, rsp], "-env.dataSize * 2", addressSize=ssz
+    subi rsp, rsp, "env.dataSize * 2", dataSize=ssz
+
+    # Install the new selector, base and RIP values.
+    wrsel cs, t2, dataSize=2
+    wrbase cs, t3, dataSize=8
+    wrip t1, t0
+};
+
+def macroop CALL_FAR_REAL_P {
+    panic "Far call in real mode doesn't support RIP addressing."
+};
 '''
 #let {{
 #    class CALL(Inst):
diff --git a/src/arch/x86/isa/insts/general_purpose/control_transfer/interrupts_and_exceptions.py b/src/arch/x86/isa/insts/general_purpose/control_transfer/interrupts_and_exceptions.py
index cc32b78..7184849 100644
--- a/src/arch/x86/isa/insts/general_purpose/control_transfer/interrupts_and_exceptions.py
+++ b/src/arch/x86/isa/insts/general_purpose/control_transfer/interrupts_and_exceptions.py
@@ -36,7 +36,38 @@
 microcode = '''
 def macroop IRET_REAL {
     .serialize_after
-    panic "Real mode iret isn't implemented!"
+
+    # temp_RIP
+    ld t1, ss, [1, t0, rsp], "0 * env.dataSize", addressSize=ssz
+    # temp_CS
+    ld t2, ss, [1, t0, rsp], "1 * env.dataSize", addressSize=ssz
+    # temp_RFLAGS
+    ld t3, ss, [1, t0, rsp], "2 * env.dataSize", addressSize=ssz
+
+    # Update RSP now that all memory accesses have succeeded.
+    addi rsp, rsp, "3 * env.dataSize", dataSize=ssz
+
+    # Update CS.
+    wrsel cs, t2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t4, t0, t2
+    slli t4, t4, 4, dataSize=8
+    wrbase cs, t4, dataSize=8
+
+    # Update RFLAGS
+    # Get the current RFLAGS
+    rflags t4, dataSize=8
+    # Flip flag bits if they should change.
+    mov t5, t4, t3
+    xor t5, t5, t4, dataSize=8
+    # Don't change VIF, VIP, or VM
+    limm t6, "~(VIFBit | VIPBit | VMBit)", dataSize=8
+    and t5, t5, t6, dataSize=8
+    # Write back RFLAGS with flipped bits.
+    wrflags t4, t5, dataSize=8
+
+    # Update RIP
+    wrip t1, t0
 };
 
 def macroop IRET_PROT {
@@ -53,9 +84,9 @@
     #t4 = handy m5 register
 
     # Pop temp_RIP, temp_CS, and temp_RFLAGS
-    ld t1, ss, [1, t0, rsp], "0 * env.stackSize", dataSize=ssz
-    ld t2, ss, [1, t0, rsp], "1 * env.stackSize", dataSize=ssz
-    ld t3, ss, [1, t0, rsp], "2 * env.stackSize", dataSize=ssz
+    ld t1, ss, [1, t0, rsp], "0 * env.dataSize", addressSize=ssz
+    ld t2, ss, [1, t0, rsp], "1 * env.dataSize", addressSize=ssz
+    ld t3, ss, [1, t0, rsp], "2 * env.dataSize", addressSize=ssz
 
     # Read the handy m5 register for use later
     rdm5reg t4
@@ -100,10 +131,10 @@
     andi t6, t2, 0xF8, dataSize=8
     andi t0, t2, 0x4, flags=(EZF,), dataSize=2
     br label("globalCSDescriptor"), flags=(CEZF,)
-    ld t8, tsl, [1, t0, t6], dataSize=8, atCPL0=True
+    ld t8, tsl, [1, t0, t6], dataSize=8, addressSize=8, atCPL0=True
     br label("processCSDescriptor")
 globalCSDescriptor:
-    ld t8, tsg, [1, t0, t6], dataSize=8, atCPL0=True
+    ld t8, tsg, [1, t0, t6], dataSize=8, addressSize=8, atCPL0=True
 processCSDescriptor:
     chks t2, t6, dataSize=8
 
@@ -125,7 +156,7 @@
     # appropriate/other RIP checks.
     # if temp_RIP > CS.limit throw #GP(0)
     rdlimit t6, cs, dataSize=8
-    sub t0, t1, t6, flags=(ECF,)
+    sub t0, t6, t1, flags=(ECF,)
     fault "std::make_shared<GeneralProtection>(0)", flags=(CECF,)
 
     #(temp_CPL!=CPL)
@@ -135,32 +166,32 @@
     br label("doPopStackStuff"), flags=(nCEZF,)
     # We can modify user visible state here because we're know
     # we're done with things that can fault.
-    addi rsp, rsp, "3 * env.stackSize"
+    addi rsp, rsp, "3 * env.dataSize", dataSize=ssz
     br label("fallThroughPopStackStuff")
 
 doPopStackStuffAndCheckRIP:
     # Check if the RIP is canonical.
-    srai t7, t1, 47, flags=(EZF,), dataSize=ssz
+    srai t7, t1, 47, flags=(EZF,), dataSize=8
     # if t7 isn't 0 or -1, it wasn't canonical.
     br label("doPopStackStuff"), flags=(CEZF,)
-    addi t0, t7, 1, flags=(EZF,), dataSize=ssz
+    addi t0, t7, 1, flags=(EZF,), dataSize=8
     fault "std::make_shared<GeneralProtection>(0)", flags=(nCEZF,)
 
 doPopStackStuff:
     #    POP.v temp_RSP
-    ld t6, ss, [1, t0, rsp], "3 * env.dataSize", dataSize=ssz
+    ld t6, ss, [1, t0, rsp], "3 * env.dataSize", addressSize=ssz
     #    POP.v temp_SS
-    ld t9, ss, [1, t0, rsp], "4 * env.dataSize", dataSize=ssz
+    ld t9, ss, [1, t0, rsp], "4 * env.dataSize", addressSize=ssz
     #    SS = READ_DESCRIPTOR (temp_SS, ss_chk)
     andi t0, t9, 0xFC, flags=(EZF,), dataSize=2
     br label("processSSDescriptor"), flags=(CEZF,)
     andi t7, t9, 0xF8, dataSize=8
     andi t0, t9, 0x4, flags=(EZF,), dataSize=2
     br label("globalSSDescriptor"), flags=(CEZF,)
-    ld t7, tsl, [1, t0, t7], dataSize=8, atCPL0=True
+    ld t7, tsl, [1, t0, t7], dataSize=8, addressSize=8, atCPL0=True
     br label("processSSDescriptor")
 globalSSDescriptor:
-    ld t7, tsg, [1, t0, t7], dataSize=8, atCPL0=True
+    ld t7, tsg, [1, t0, t7], dataSize=8, addressSize=8, atCPL0=True
 processSSDescriptor:
     chks t9, t7, dataSize=8
 
@@ -213,7 +244,7 @@
     #  RF cleared
 
     #RIP = temp_RIP
-    wrip t0, t1, dataSize=ssz
+    wrip t0, t1
 };
 
 def macroop IRET_VIRT {
@@ -233,8 +264,15 @@
     br rom_label("legacyModeInterrupt")
 };
 
-def macroop INT_I {
+def macroop INT3_VIRT {
+    panic "Virtual mode int3 isn't implemented!"
+};
 
+def macroop INT3_REAL {
+    panic "Real mode int3 isn't implemented!"
+};
+
+def macroop INT_LONG_I {
     #load the byte-sized interrupt vector specified in the instruction
     .adjust_imm trimImm(8)
     limm t1, imm, dataSize=8
@@ -242,11 +280,67 @@
     rdip t7
 
     # Are we in long mode?
-    rdm5reg t5
-    andi t0, t5, 0x1, flags=(EZF,)
-    br rom_label("longModeSoftInterrupt"), flags=(CEZF,)
+    br rom_label("longModeSoftInterrupt")
+};
+
+def macroop INT_PROT_I {
+    #load the byte-sized interrupt vector specified in the instruction
+    .adjust_imm trimImm(8)
+    limm t1, imm, dataSize=8
+
+    rdip t7
+
+    # Are we in long mode?
     br rom_label("legacyModeInterrupt")
 };
+
+def macroop INT_REAL_I {
+    #load the byte-sized interrupt vector specified in the instruction
+    .adjust_imm trimImm(8)
+    limm t1, imm, dataSize=8
+
+    # temp_RIP
+    ld t2, idtr, [4, t1, t0], dataSize=2, addressSize=8
+    # temp_CS
+    ld t3, idtr, [4, t1, t0], 2, dataSize=2, addressSize=8
+
+    cda ss, [1, t0, rsp], -2, dataSize=2, addressSize=ssz
+    cda ss, [1, t0, rsp], -6, dataSize=2, addressSize=ssz
+
+    rflags t4, dataSize=8
+    rdsel t5, cs, dataSize=8
+    rdip t6
+
+    # Push RFLAGS.
+    st t4, ss, [1, t0, rsp], -2, dataSize=2, addressSize=ssz
+    # Push CS.
+    st t5, ss, [1, t0, rsp], -4, dataSize=2, addressSize=ssz
+    # Push the next RIP.
+    st t6, ss, [1, t0, rsp], -6, dataSize=2, addressSize=ssz
+
+    # Update RSP
+    subi rsp, rsp, 6, dataSize=ssz
+
+    # Set the CS selector.
+    wrsel cs, t3, dataSize=2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t3, t0, t3
+    # Compute and set CS base.
+    slli t3, t3, 4, dataSize=8
+    wrbase cs, t3, dataSize=8
+
+    # If AC, TF, IF or RF are set, we want to flip them.
+    limm t7, "(ACBit | TFBit | IFBit | RFBit)", dataSize=8
+    and t7, t4, t7, dataSize=8
+    wrflags t4, t7, dataSize=8
+
+    # Set the new RIP
+    wrip t2, t0
+};
+
+def macroop INT_VIRT_I {
+    panic "Virtual mode int3 isn't implemented!"
+};
 '''
 #let {{
 #    class INT(Inst):
diff --git a/src/arch/x86/isa/insts/general_purpose/control_transfer/jump.py b/src/arch/x86/isa/insts/general_purpose/control_transfer/jump.py
index f39e799..77c6968 100644
--- a/src/arch/x86/isa/insts/general_purpose/control_transfer/jump.py
+++ b/src/arch/x86/isa/insts/general_purpose/control_transfer/jump.py
@@ -151,13 +151,14 @@
     .control_indirect
 
     lea t1, seg, sib, disp, dataSize=asz
-    ld t2, seg, [1, t0, t1], dsz
+    ld t2, seg, [1, t0, t1], dsz, dataSize=2
     ld t1, seg, [1, t0, t1]
-    zexti t3, t1, 15, dataSize=8
+    zexti t3, t2, 15, dataSize=8
     slli t3, t3, 4, dataSize=8
-    wrsel cs, t1, dataSize=2
+    wrsel cs, t2, dataSize=2
     wrbase cs, t3, dataSize=8
-    wrip t0, t2, dataSize=asz
+    # Put t1 first so it isn't sign extended.
+    wrip t1, t0
 };
 
 def macroop JMP_FAR_REAL_P
@@ -183,6 +184,7 @@
     slli t3, t1, 4, dataSize=8
     wrsel cs, t1, dataSize=2
     wrbase cs, t3, dataSize=8
-    wrip t0, t2, dataSize=asz
+    # Put t2 first so it isn't sign extended.
+    wrip t2, t0
 };
 '''
diff --git a/src/arch/x86/isa/insts/general_purpose/control_transfer/xreturn.py b/src/arch/x86/isa/insts/general_purpose/control_transfer/xreturn.py
index 58b6bfd..e925f7d 100644
--- a/src/arch/x86/isa/insts/general_purpose/control_transfer/xreturn.py
+++ b/src/arch/x86/isa/insts/general_purpose/control_transfer/xreturn.py
@@ -41,9 +41,9 @@
     .function_return
     .control_indirect
 
-    ld t1, ss, [1, t0, rsp]
+    ld t1, ss, [1, t0, rsp], addressSize=ssz
     # Check address of return
-    addi rsp, rsp, dsz
+    addi rsp, rsp, dsz, dataSize=ssz
     wripi t1, 0
 };
 
@@ -55,28 +55,76 @@
     .control_indirect
 
     limm t2, imm
-    ld t1, ss, [1, t0, rsp]
+    ld t1, ss, [1, t0, rsp], addressSize=ssz
     # Check address of return
-    addi rsp, rsp, dsz
-    add rsp, rsp, t2
+    addi rsp, rsp, dsz, dataSize=ssz
+    add rsp, rsp, t2, dataSize=ssz
     wripi t1, 0
 };
 
+def macroop RET_FAR_REAL {
+    .function_return
+    .control_indirect
+
+    # Pop the return RIP.
+    ld t1, ss, [1, t0, rsp], addressSize=ssz
+    # Pop the return CS.
+    ld t2, ss, [1, t0, rsp], dsz, addressSize=ssz
+    # Adjust RSP.
+    addi rsp, rsp, "2 * env.dataSize", dataSize=ssz
+
+    # Set the CS selector.
+    wrsel cs, t2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t2, t0, t2
+    # Compute and set CS base.
+    slli t2, t2, 4, dataSize=8
+    wrbase cs, t2, dataSize=8
+
+    # Set the new RIP.
+    wrip t1, t0
+};
+
+def macroop RET_FAR_REAL_I {
+    .function_return
+    .control_indirect
+
+    # Pop the return RIP.
+    ld t1, ss, [1, t0, rsp], addressSize=ssz
+    # Pop the return CS.
+    ld t2, ss, [1, t0, rsp], dsz, addressSize=ssz
+    # Adjust RSP.
+    addi rsp, rsp, "2 * env.dataSize", dataSize=ssz
+
+    addi rsp, rsp, imm, dataSize=ssz
+
+    # Set the CS selector.
+    wrsel cs, t2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t2, t0, t2
+    # Compute and set CS base.
+    slli t2, t2, 4, dataSize=8
+    wrbase cs, t2, dataSize=8
+
+    # Set the new RIP.
+    wrip t1, t0
+};
+
 def macroop RET_FAR {
     .adjust_env oszIn64Override
     .function_return
     .control_indirect
 
     # Get the return RIP
-    ld t1, ss, [1, t0, rsp]
+    ld t1, ss, [1, t0, rsp], addressSize=ssz
 
     # Get the return CS
-    ld t2, ss, [1, t0, rsp], ssz
+    ld t2, ss, [1, t0, rsp], dsz, addressSize=ssz
 
     # increment the stack pointer to pop the instruction pointer
     # and the code segment from the stack.
-    addi rsp, rsp, dsz
-    addi rsp, rsp, dsz
+    addi rsp, rsp, dsz, dataSize=ssz
+    addi rsp, rsp, dsz, dataSize=ssz
 
     # Get the rpl
     andi t3, t2, 0x3
@@ -92,10 +140,10 @@
     andi t3, t2, 0xF8, dataSize=8
     andi t0, t2, 0x4, flags=(EZF,), dataSize=2
     br label("globalDescriptor"), flags=(CEZF,)
-    ld t3, tsl, [1, t0, t3], dataSize=8
+    ld t3, tsl, [1, t0, t3], dataSize=8, addressSize=8
     br label("processDescriptor")
 globalDescriptor:
-    ld t3, tsg, [1, t0, t3], dataSize=8
+    ld t3, tsg, [1, t0, t3], dataSize=8, addressSize=8
 processDescriptor:
     chks t2, t3, IretCheck, dataSize=8
     # There should be validity checks on the RIP checks here, but I'll do
diff --git a/src/arch/x86/isa/insts/general_purpose/data_transfer/stack_operations.py b/src/arch/x86/isa/insts/general_purpose/data_transfer/stack_operations.py
index c86e0e2..e97d17f 100644
--- a/src/arch/x86/isa/insts/general_purpose/data_transfer/stack_operations.py
+++ b/src/arch/x86/isa/insts/general_purpose/data_transfer/stack_operations.py
@@ -38,8 +38,8 @@
     # Make the default data size of pops 64 bits in 64 bit mode
     .adjust_env oszIn64Override
 
-    ldis t1, ss, [1, t0, rsp], dataSize=ssz
-    addi rsp, rsp, ssz, dataSize=asz
+    ld t1, ss, [1, t0, rsp], addressSize=ssz
+    addi rsp, rsp, dsz, dataSize=ssz
     mov reg, reg, t1
 };
 
@@ -47,10 +47,10 @@
     # Make the default data size of pops 64 bits in 64 bit mode
     .adjust_env oszIn64Override
 
-    ldis t1, ss, [1, t0, rsp], dataSize=ssz
-    cda seg, sib, disp, dataSize=ssz
-    addi rsp, rsp, ssz, dataSize=asz
-    st t1, seg, sib, disp, dataSize=ssz
+    ld t1, ss, [1, t0, rsp], addressSize=ssz
+    cda seg, sib, disp
+    addi rsp, rsp, dsz, dataSize=ssz
+    st t1, seg, sib, disp
 };
 
 def macroop POP_P {
@@ -58,18 +58,27 @@
     .adjust_env oszIn64Override
 
     rdip t7
-    ld t1, ss, [1, t0, rsp], dataSize=ssz
-    cda seg, sib, disp, dataSize=ssz
-    addi rsp, rsp, ssz, dataSize=asz
-    st t1, seg, riprel, disp, dataSize=ssz
+    ld t1, ss, [1, t0, rsp], addressSize=ssz
+    cda seg, sib, disp
+    addi rsp, rsp, dsz, dataSize=ssz
+    st t1, seg, riprel, disp
+};
+
+def macroop POP_REAL_S {
+    ld t1, ss, [1, t0, rsp], addressSize=ssz, dataSize=2
+    addi rsp, rsp, dsz, dataSize=ssz
+    wrsel sr, t1
+    mov t1, t0, t1, dataSize=2
+    slli t1, t1, 4, dataSize=8
+    wrbase sr, t1, dataSize=8
 };
 
 def macroop PUSH_R {
     # Make the default data size of pops 64 bits in 64 bit mode
     .adjust_env oszIn64Override
 
-    stis reg, ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz
-    subi rsp, rsp, ssz
+    st reg, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
 };
 
 def macroop PUSH_I {
@@ -77,17 +86,17 @@
     .adjust_env oszIn64Override
 
     limm t1, imm
-    stis t1, ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz
-    subi rsp, rsp, ssz
+    st t1, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
 };
 
 def macroop PUSH_M {
     # Make the default data size of pops 64 bits in 64 bit mode
     .adjust_env oszIn64Override
 
-    ld t1, seg, sib, disp, dataSize=ssz
-    st t1, ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz
-    subi rsp, rsp, ssz
+    ld t1, seg, sib, disp
+    st t1, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
 };
 
 def macroop PUSH_P {
@@ -95,50 +104,55 @@
     .adjust_env oszIn64Override
 
     rdip t7
-    ld t1, seg, riprel, disp, dataSize=ssz
-    st t1, ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz
-    subi rsp, rsp, ssz
+    ld t1, seg, riprel, disp
+    st t1, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
+};
+
+def macroop PUSH_S {
+    rdsel t1, sr
+    st t1, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
 };
 
 def macroop PUSHA {
     # Check all the stack addresses. We'll assume that if the beginning and
     # end are ok, then the stuff in the middle should be as well.
-    cda ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz
-    cda ss, [1, t0, rsp], "-8 * env.stackSize", dataSize=ssz
-    st rax, ss, [1, t0, rsp], "1 * -env.stackSize", dataSize=ssz
-    st rcx, ss, [1, t0, rsp], "2 * -env.stackSize", dataSize=ssz
-    st rdx, ss, [1, t0, rsp], "3 * -env.stackSize", dataSize=ssz
-    st rbx, ss, [1, t0, rsp], "4 * -env.stackSize", dataSize=ssz
-    st rsp, ss, [1, t0, rsp], "5 * -env.stackSize", dataSize=ssz
-    st rbp, ss, [1, t0, rsp], "6 * -env.stackSize", dataSize=ssz
-    st rsi, ss, [1, t0, rsp], "7 * -env.stackSize", dataSize=ssz
-    st rdi, ss, [1, t0, rsp], "8 * -env.stackSize", dataSize=ssz
-    subi rsp, rsp, "8 * env.stackSize"
+    cda ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    cda ss, [1, t0, rsp], "-8 * env.dataSize", addressSize=ssz
+    st rax, ss, [1, t0, rsp], "1 * -env.dataSize", addressSize=ssz
+    st rcx, ss, [1, t0, rsp], "2 * -env.dataSize", addressSize=ssz
+    st rdx, ss, [1, t0, rsp], "3 * -env.dataSize", addressSize=ssz
+    st rbx, ss, [1, t0, rsp], "4 * -env.dataSize", addressSize=ssz
+    st rsp, ss, [1, t0, rsp], "5 * -env.dataSize", addressSize=ssz
+    st rbp, ss, [1, t0, rsp], "6 * -env.dataSize", addressSize=ssz
+    st rsi, ss, [1, t0, rsp], "7 * -env.dataSize", addressSize=ssz
+    st rdi, ss, [1, t0, rsp], "8 * -env.dataSize", addressSize=ssz
+    subi rsp, rsp, "8 * env.dataSize", dataSize=ssz
 };
 
 def macroop POPA {
     # Check all the stack addresses. We'll assume that if the beginning and
     # end are ok, then the stuff in the middle should be as well.
-    ld t1, ss, [1, t0, rsp], "0 * env.stackSize", dataSize=ssz
-    ld t2, ss, [1, t0, rsp], "7 * env.stackSize", dataSize=ssz
-    mov rdi, rdi, t1, dataSize=ssz
-    ld rsi, ss, [1, t0, rsp], "1 * env.stackSize", dataSize=ssz
-    ld rbp, ss, [1, t0, rsp], "2 * env.stackSize", dataSize=ssz
-    ld rbx, ss, [1, t0, rsp], "4 * env.stackSize", dataSize=ssz
-    ld rdx, ss, [1, t0, rsp], "5 * env.stackSize", dataSize=ssz
-    ld rcx, ss, [1, t0, rsp], "6 * env.stackSize", dataSize=ssz
-    mov rax, rax, t2, dataSize=ssz
-    addi rsp, rsp, "8 * env.stackSize", dataSize=asz
+    ld t1, ss, [1, t0, rsp], "0 * env.dataSize", addressSize=ssz
+    ld rax, ss, [1, t0, rsp], "7 * env.dataSize", addressSize=ssz
+    mov rdi, rdi, t1
+    ld rsi, ss, [1, t0, rsp], "1 * env.dataSize", addressSize=ssz
+    ld rbp, ss, [1, t0, rsp], "2 * env.dataSize", addressSize=ssz
+    ld rbx, ss, [1, t0, rsp], "4 * env.dataSize", addressSize=ssz
+    ld rdx, ss, [1, t0, rsp], "5 * env.dataSize", addressSize=ssz
+    ld rcx, ss, [1, t0, rsp], "6 * env.dataSize", addressSize=ssz
+    addi rsp, rsp, "8 * env.dataSize", dataSize=ssz
 };
 
 def macroop LEAVE {
     # Make the default data size of pops 64 bits in 64 bit mode
     .adjust_env oszIn64Override
 
-    mov t1, t1, rbp, dataSize=ssz
-    ldis rbp, ss, [1, t0, t1], dataSize=ssz
+    mov t1, t1, rbp
+    ld rbp, ss, [1, t0, t1], addressSize=ssz
     mov rsp, rsp, t1, dataSize=ssz
-    addi rsp, rsp, ssz, dataSize=ssz
+    addi rsp, rsp, dsz, dataSize=ssz
 };
 
 def macroop ENTER_I_I {
@@ -154,8 +168,8 @@
     # t1 is now the masked nesting level, and t2 is the amount of storage.
 
     # Push rbp.
-    stis rbp, ss, [1, t0, rsp], "-env.dataSize"
-    subi rsp, rsp, ssz
+    st rbp, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
 
     # Save the stack pointer for later
     mov t6, t6, rsp
@@ -170,9 +184,9 @@
 
     limm t4, "-1ULL", dataSize=8
 topOfLoop:
-    ldis t5, ss, [dsz, t4, rbp]
-    stis t5, ss, [1, t0, rsp], "-env.dataSize"
-    subi rsp, rsp, ssz
+    ld t5, ss, [dsz, t4, rbp], addressSize=ssz
+    st t5, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
 
     # If we're not done yet, loop
     subi t4, t4, 1, dataSize=8
@@ -181,8 +195,8 @@
 
 bottomOfLoop:
     # Push the old rbp onto the stack
-    stis t6, ss, [1, t0, rsp], "-env.dataSize"
-    subi rsp, rsp, ssz
+    st t6, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
 
 skipLoop:
     sub rsp, rsp, t2, dataSize=ssz
diff --git a/src/arch/x86/isa/insts/general_purpose/flags/load_and_store.py b/src/arch/x86/isa/insts/general_purpose/flags/load_and_store.py
index 76f81c1..31723b3 100644
--- a/src/arch/x86/isa/insts/general_purpose/flags/load_and_store.py
+++ b/src/arch/x86/isa/insts/general_purpose/flags/load_and_store.py
@@ -35,10 +35,13 @@
 
 microcode = '''
 def macroop SAHF {
-    ruflags ah, dataSize=1
+    ruflags t1, dataSize=8
+    mov t1, t1, ah, dataSize=1
+    wruflags t1, t0, dataSize=8
 };
 
 def macroop LAHF {
-    wruflags ah, t0, dataSize=1
+    rflags t1, dataSize=8
+    andi ah, t1, "CFBit | PFBit | AFBit | ZFBit | SFBit | (1 << 1)", dataSize=1
 };
 '''
diff --git a/src/arch/x86/isa/insts/general_purpose/flags/push_and_pop.py b/src/arch/x86/isa/insts/general_purpose/flags/push_and_pop.py
index d2e0dc7..cebe65d 100644
--- a/src/arch/x86/isa/insts/general_purpose/flags/push_and_pop.py
+++ b/src/arch/x86/isa/insts/general_purpose/flags/push_and_pop.py
@@ -38,15 +38,23 @@
     .adjust_env oszIn64Override
 
     rflags t1
-    st t1, ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz
-    subi rsp, rsp, ssz
+    st t1, ss, [1, t0, rsp], "-env.dataSize", addressSize=ssz
+    subi rsp, rsp, dsz, dataSize=ssz
+};
+
+def macroop PUSHF_VIRT {
+    panic "Virtual mode pushf isn't implemented!"
 };
 
 def macroop POPF {
     .adjust_env oszIn64Override
 
-    ld t1, ss, [1, t0, rsp], dataSize=ssz
-    addi rsp, rsp, ssz
+    ld t1, ss, [1, t0, rsp], addressSize=ssz
+    addi rsp, rsp, dsz, dataSize=ssz
     wrflags t1, t0
 };
+
+def macroop POPF_VIRT {
+    panic "Virtual mode popf isn't implemented!"
+};
 '''
diff --git a/src/arch/x86/isa/insts/general_purpose/flags/set_and_clear.py b/src/arch/x86/isa/insts/general_purpose/flags/set_and_clear.py
index f1d43bb..2f2b2ce 100644
--- a/src/arch/x86/isa/insts/general_purpose/flags/set_and_clear.py
+++ b/src/arch/x86/isa/insts/general_purpose/flags/set_and_clear.py
@@ -35,47 +35,144 @@
 
 microcode = '''
 def macroop CLD {
-    ruflags t1
+    ruflags t1, dataSize=8
     limm t2, "~((uint64_t)DFBit)", dataSize=8
-    and t1, t1, t2
-    wruflags t1, t0
+    and t1, t1, t2, dataSize=8
+    wruflags t1, t0, dataSize=8
 };
 
 def macroop STD {
-    ruflags t1
+    ruflags t1, dataSize=8
     limm t2, "DFBit", dataSize=8
-    or t1, t1, t2
-    wruflags t1, t0
+    or t1, t1, t2, dataSize=8
+    wruflags t1, t0, dataSize=8
 };
 
 def macroop CLC {
-    ruflags t1
-    andi t2, t1, "CFBit"
-    wruflags t1, t2
+    ruflags t1, dataSize=8
+    limm t2, "~((uint64_t)CFBit)", dataSize=8
+    and t1, t1, t2, dataSize=8
+    wruflags t1, t0, dataSize=8
 };
 
 def macroop STC {
-    ruflags t1
-    ori t1, t1, "CFBit"
-    wruflags t1, t0
+    ruflags t1, dataSize=8
+    ori t1, t1, "CFBit", dataSize=8
+    wruflags t1, t0, dataSize=8
 };
 
 def macroop CMC {
-    ruflags t1
-    wruflagsi t1, "CFBit"
+    ruflags t1, dataSize=8
+    wruflagsi t1, "CFBit", dataSize=8
 };
 
 def macroop STI {
-    rflags t1
+    rflags t1, dataSize=8
+
+    # Extract the IOPL.
+    srli t2, t1, 12, dataSize=8
+    andi t2, t1, 0x3, dataSize=8
+
+    # Find the CPL.
+    rdm5reg t3, dataSize=8
+    srli t3, t3, 4, dataSize=8
+    andi t3, t3, 0x3, dataSize=8
+
+    # if !(CPL > IOPL), jump down to setting IF.
+    sub t0, t2, t3, flags=(ECF,), dataSize=8
+    br label("setIf"), flags=(nCECF,)
+
+    # if (CR4.PVI == 1 && CPL == 3) {
+    # } else GP
+
+    # Check CR4.PVI
+    rdcr t4, cr4, dataSize=8
+    andi t0, t4, 0x1, flags=(CEZF,)
+    fault "std::make_shared<GeneralProtection>(0)", flags=(CEZF,)
+
+    # Check CPL.
+    andi t4, t3, 0x3, dataSize=8
+    xori t4, t4, 0x3, dataSize=8, flags=(CEZF,)
+    fault "std::make_shared<GeneralProtection>(0)", flags=(nCEZF,)
+
+    #     if (RFLAGS.VIP == 1)
+    #         EXCEPTION[#GP(0)]
+
+    # Check RFLAGS.VIP == 1
+    rflag t0, "VIPBit"
+    fault "std::make_shared<GeneralProtection>(0)", flags=(nCEZF,)
+
+    limm t2, "VIFBit", dataSize=8
+    br label("setBitInT2")
+
+setIf:
     limm t2, "IFBit", dataSize=8
-    or t1, t1, t2
-    wrflags t1, t0
+
+setBitInT2:
+    or t1, t1, t2, dataSize=8
+    wrflags t1, t0, dataSize=8
+};
+
+def macroop STI_REAL {
+    rflags t1, dataSize=8
+    limm t2, "IFBit", dataSize=8
+    or t1, t1, t2, dataSize=8
+    wrflags t1, t0, dataSize=8
+};
+
+def macroop STI_VIRT {
+    panic "Virtual mode sti isn't implemented!"
 };
 
 def macroop CLI {
-    rflags t1
-    limm t2, "~IFBit", dataSize=8
-    and t1, t1, t2
-    wrflags t1, t0
+    rflags t1, dataSize=8
+
+    # Extract the IOPL.
+    srli t2, t1, 12, dataSize=8
+    andi t2, t1, 0x3, dataSize=8
+
+    # Find the CPL.
+    rdm5reg t3, dataSize=8
+    srli t3, t3, 4, dataSize=8
+    andi t3, t3, 0x3, dataSize=8
+
+    # if !(CPL > IOPL), jump down to clearing IF.
+    sub t0, t2, t3, flags=(ECF,), dataSize=8
+    br label("maskIf"), flags=(nCECF,)
+
+    # if (CR4.PVI == 1 && CPL == 3) {
+    # } else GP
+
+    # Check CR4.PVI
+    rdcr t4, cr4, dataSize=8
+    andi t0, t4, 0x1, flags=(CEZF,)
+    fault "std::make_shared<GeneralProtection>(0)", flags=(CEZF,)
+
+    # Check CPL.
+    andi t4, t3, 0x3, dataSize=8
+    xori t4, t4, 0x3, dataSize=8, flags=(CEZF,)
+    fault "std::make_shared<GeneralProtection>(0)", flags=(nCEZF,)
+
+    # RFLAGS.VIF = 0
+    limm t2, "~((uint64_t)VIFBit)", dataSize=8
+    br label("maskWithT2")
+
+maskIf:
+    limm t2, "~((uint64_t)IFBit)", dataSize=8
+
+maskWithT2:
+    and t1, t1, t2, dataSize=8
+    wrflags t1, t0, dataSize=8
+};
+
+def macroop CLI_REAL {
+    rflags t1, dataSize=8
+    limm t2, "~((uint64_t)IFBit)", dataSize=8
+    and t1, t1, t2, dataSize=8
+    wrflags t1, t0, dataSize=8
+};
+
+def macroop CLI_VIRT {
+    panic "Virtual mode cli isn't implemented!"
 };
 '''
diff --git a/src/arch/x86/isa/insts/general_purpose/input_output/general_io.py b/src/arch/x86/isa/insts/general_purpose/input_output/general_io.py
index 27396e2..94d026c 100644
--- a/src/arch/x86/isa/insts/general_purpose/input_output/general_io.py
+++ b/src/arch/x86/isa/insts/general_purpose/input_output/general_io.py
@@ -39,13 +39,17 @@
 microcode = '''
     def macroop IN_R_I {
         .adjust_imm trimImm(8)
-        limm t1, imm, dataSize=asz
+        limm t1, imm, dataSize=8
         mfence
         ld reg, intseg, [1, t1, t0], "IntAddrPrefixIO << 3", addressSize=8, \
             nonSpec=True
         mfence
     };
 
+    def macroop IN_VIRT_R_I {
+        panic "Virtual mode in isn't implemented!"
+    };
+
     def macroop IN_R_R {
         zexti t2, regm, 15, dataSize=8
         mfence
@@ -54,6 +58,10 @@
         mfence
     };
 
+    def macroop IN_VIRT_R_R {
+        panic "Virtual mode in isn't implemented!"
+    };
+
     def macroop OUT_I_R {
         .adjust_imm trimImm(8)
         limm t1, imm, dataSize=8
@@ -63,6 +71,10 @@
         mfence
     };
 
+    def macroop OUT_VIRT_I_R {
+        panic "Virtual mode out isn't implemented!"
+    };
+
     def macroop OUT_R_R {
         zexti t2, reg, 15, dataSize=8
         mfence
@@ -70,4 +82,8 @@
             nonSpec=True
         mfence
     };
+
+    def macroop OUT_VIRT_R_R {
+        panic "Virtual mode out isn't implemented!"
+    };
 '''
diff --git a/src/arch/x86/isa/insts/general_purpose/input_output/string_io.py b/src/arch/x86/isa/insts/general_purpose/input_output/string_io.py
index b333526..ec386a0 100644
--- a/src/arch/x86/isa/insts/general_purpose/input_output/string_io.py
+++ b/src/arch/x86/isa/insts/general_purpose/input_output/string_io.py
@@ -52,6 +52,10 @@
     add rdi, rdi, t3, dataSize=asz
 };
 
+def macroop INS_VIRT_M_R {
+    panic "Virtual mode ins isn't implemented!"
+};
+
 def macroop INS_E_M_R {
     and t0, rcx, rcx, flags=(EZF,), dataSize=asz
     br label("end"), flags=(CEZF,)
@@ -77,6 +81,10 @@
     fault "NoFault"
 };
 
+def macroop INS_VIRT_E_M_R {
+    panic "Virtual mode ins isn't implemented!"
+};
+
 def macroop OUTS_R_M {
     # Find the constant we need to either add or subtract from rdi
     ruflag t0, 10
@@ -95,6 +103,10 @@
     add rsi, rsi, t3, dataSize=asz
 };
 
+def macroop OUTS_VIRT_R_M {
+    panic "Virtual mode outs isn't implemented!"
+};
+
 def macroop OUTS_E_R_M {
     and t0, rcx, rcx, flags=(EZF,), dataSize=asz
     br label("end"), flags=(CEZF,)
@@ -119,4 +131,8 @@
     mfence
     fault "NoFault"
 };
+
+def macroop OUTS_VIRT_E_R_M {
+    panic "Virtual mode outs isn't implemented!"
+};
 '''
diff --git a/src/arch/x86/isa/insts/general_purpose/load_segment_registers.py b/src/arch/x86/isa/insts/general_purpose/load_segment_registers.py
index e10c31f..1967820 100644
--- a/src/arch/x86/isa/insts/general_purpose/load_segment_registers.py
+++ b/src/arch/x86/isa/insts/general_purpose/load_segment_registers.py
@@ -33,7 +33,121 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-microcode = ""
+microcode = '''
+
+#
+# Real mode versions of the load far pointer instructions.
+#
+
+def macroop LDS_REAL_R_M {
+    # Calculate the address of the pointer.
+    lea t1, seg, sib, disp, dataSize=asz
+
+    # Load the selector into a temporary.
+    ld t2, seg, [1, t0, t1], dsz, dataSize=2
+    # Load the offset.
+    ld reg, seg, [1, t0, t1], 0
+    # Install the selector value.
+    wrsel ds, t2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t2, t0, t2
+    # Compute and set the base.
+    slli t2, t2, 4, dataSize=8
+    wrbase ds, t2, dataSize=8
+};
+
+def macroop LES_REAL_R_M {
+    # Calculate the address of the pointer.
+    lea t1, seg, sib, disp, dataSize=asz
+
+    # Load the selector into a temporary.
+    ld t2, seg, [1, t0, t1], dsz, dataSize=2
+    # Load the offset.
+    ld reg, seg, [1, t0, t1], 0
+    # Install the selector value.
+    wrsel es, t2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t2, t0, t2
+    # Compute and set the base.
+    slli t2, t2, 4, dataSize=8
+    wrbase es, t2, dataSize=8
+};
+
+def macroop LFS_REAL_R_M {
+    # Calculate the address of the pointer.
+    lea t1, seg, sib, disp, dataSize=asz
+
+    # Load the selector into a temporary.
+    ld t2, seg, [1, t0, t1], dsz, dataSize=2
+    # Load the offset.
+    ld reg, seg, [1, t0, t1], 0
+    # Install the selector value.
+    wrsel fs, t2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t2, t0, t2
+    # Compute and set the base.
+    slli t2, t2, 4, dataSize=8
+    wrbase fs, t2, dataSize=8
+};
+
+def macroop LGS_REAL_R_M {
+    # Calculate the address of the pointer.
+    lea t1, seg, sib, disp, dataSize=asz
+
+    # Load the selector into a temporary.
+    ld t2, seg, [1, t0, t1], dsz, dataSize=2
+    # Load the offset.
+    ld reg, seg, [1, t0, t1], 0
+    # Install the selector value.
+    wrsel gs, t2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t2, t0, t2
+    # Compute and set the base.
+    slli t2, t2, 4, dataSize=8
+    wrbase gs, t2, dataSize=8
+};
+
+def macroop LSS_REAL_R_M {
+    # Calculate the address of the pointer.
+    lea t1, seg, sib, disp, dataSize=asz
+
+    # Load the selector into a temporary.
+    ld t2, seg, [1, t0, t1], dsz, dataSize=2
+    # Load the offset.
+    ld reg, seg, [1, t0, t1], 0
+    # Install the selector value.
+    wrsel ss, t2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t2, t0, t2
+    # Compute and set the base.
+    slli t2, t2, 4, dataSize=8
+    wrbase ss, t2, dataSize=8
+};
+
+# Versions for RIP relative addressing, even though these instructions are
+# illegal in 64 bit mode, where RIP relative addressing is available.
+
+def macroop LDS_REAL_R_P {
+    panic "Real mode LDS doesn't support RIP relative addressing."
+};
+
+def macroop LES_REAL_R_P {
+    panic "Real mode LES doesn't support RIP relative addressing."
+};
+
+def macroop LFS_REAL_R_P {
+    panic "Real mode LFS doesn't support RIP relative addressing."
+};
+
+def macroop LGS_REAL_R_P {
+    panic "Real mode LGS doesn't support RIP relative addressing."
+};
+
+def macroop LSS_REAL_R_P {
+    panic "Real mode LSS doesn't support RIP relative addressing."
+};
+
+'''
 #let {{
 #    class LDS(Inst):
 #       "GenFault ${new UnimpInstFault}"
diff --git a/src/arch/x86/isa/insts/romutil.py b/src/arch/x86/isa/insts/romutil.py
index fd06197..847b9c4 100644
--- a/src/arch/x86/isa/insts/romutil.py
+++ b/src/arch/x86/isa/insts/romutil.py
@@ -216,4 +216,54 @@
     halt
     eret
 };
+
+def rom
+{
+    extern realModeInterrupt:
+
+    # t1 - The vector.
+    # t2 - The old CS.
+    # t7 - The old RIP.
+    # t3 - RFLAGS
+    # t4 - The new CS.
+    # t5 - The new RIP.
+
+    rdsel t2, cs, dataSize=8
+    rflags t3, dataSize=8
+
+    ld t4, idtr, [4, t1, t0], 2, dataSize=2, addressSize=2
+    ld t5, idtr, [4, t1, t0], dataSize=2, addressSize=2
+
+    # Make sure pushes after the first will also work.
+    cda ss, [1, t0, rsp], -4, dataSize=2, addressSize=2
+    cda ss, [1, t0, rsp], -6, dataSize=2, addressSize=2
+
+    # Push the low 16 bits of RFLAGS.
+    st t3, ss, [1, t0, rsp], -2, dataSize=2, addressSize=2
+    # Push CS.
+    st t2, ss, [1, t0, rsp], -4, dataSize=2, addressSize=2
+    # Push the old RIP.
+    st t7, ss, [1, t0, rsp], -6, dataSize=2, addressSize=2
+
+    # Update RSP.
+    subi rsp, rsp, 6, dataSize=2
+
+    # Set the new CS selector.
+    wrsel cs, t4, dataSize=2
+    # Make sure there isn't any junk in the upper bits of the base.
+    mov t4, t0, t4, dataSize=2
+    # Compute and set CS base.
+    slli t4, t4, 4, dataSize=8
+    wrbase cs, t4, dataSize=8
+
+    # If IF or TF are set, we want to flip them.
+    limm t6, "(TFBit | IFBit)", dataSize=8
+    and t6, t6, t3, dataSize=8
+    wrflags t3, t6, dataSize=8
+
+    # Set the new RIP.
+    wrip t5, t0, dataSize=2
+
+    eret
+};
 '''
diff --git a/src/arch/x86/isa/insts/simd128/integer/save_and_restore_state/save_and_restore_control_and_status.py b/src/arch/x86/isa/insts/simd128/integer/save_and_restore_state/save_and_restore_control_and_status.py
index cab4735..0fcc3dc 100644
--- a/src/arch/x86/isa/insts/simd128/integer/save_and_restore_state/save_and_restore_control_and_status.py
+++ b/src/arch/x86/isa/insts/simd128/integer/save_and_restore_state/save_and_restore_control_and_status.py
@@ -35,24 +35,24 @@
 
 microcode = '''
 def macroop STMXCSR_M {
-    rdval t1, ctrlRegIdx("MISCREG_MXCSR")
+    rdval t1, ctrlRegIdx("misc_reg::Mxcsr")
     st t1, seg, sib, disp
 };
 
 def macroop STMXCSR_P {
-    rdval t1, ctrlRegIdx("MISCREG_MXCSR")
+    rdval t1, ctrlRegIdx("misc_reg::Mxcsr")
     rdip t7
     st t1, seg, riprel, disp
 };
 
 def macroop LDMXCSR_M {
     ld t1, seg, sib, disp
-    wrval ctrlRegIdx("MISCREG_MXCSR"), t1
+    wrval ctrlRegIdx("misc_reg::Mxcsr"), t1
 };
 
 def macroop LDMXCSR_P {
     rdip t7
     ld t1, seg, riprel, disp
-    wrval ctrlRegIdx("MISCREG_MXCSR"), t1
+    wrval ctrlRegIdx("misc_reg::Mxcsr"), t1
 };
 '''
diff --git a/src/arch/x86/isa/insts/simd128/integer/save_and_restore_state/save_and_restore_state.py b/src/arch/x86/isa/insts/simd128/integer/save_and_restore_state/save_and_restore_state.py
index 906f270..9addadb 100644
--- a/src/arch/x86/isa/insts/simd128/integer/save_and_restore_state/save_and_restore_state.py
+++ b/src/arch/x86/isa/insts/simd128/integer/save_and_restore_state/save_and_restore_state.py
@@ -43,16 +43,16 @@
 '''
 
 loadXMMRegTemplate =  '''
-    ldfp fpRegIdx("FLOATREG_XMM_LOW(%(idx)i)"), seg, %(mode)s, \
+    ldfp fpRegIdx("float_reg::xmmLow(%(idx)i)"), seg, %(mode)s, \
          "DISPLACEMENT + 160 + 16 * %(idx)i", dataSize=8
-    ldfp fpRegIdx("FLOATREG_XMM_HIGH(%(idx)i)"), seg, %(mode)s, \
+    ldfp fpRegIdx("float_reg::xmmHigh(%(idx)i)"), seg, %(mode)s, \
          "DISPLACEMENT + 160 + 16 * %(idx)i + 8", dataSize=8
 '''
 
 storeXMMRegTemplate =  '''
-    stfp fpRegIdx("FLOATREG_XMM_LOW(%(idx)i)"), seg, %(mode)s, \
+    stfp fpRegIdx("float_reg::xmmLow(%(idx)i)"), seg, %(mode)s, \
          "DISPLACEMENT + 160 + 16 * %(idx)i", dataSize=8
-    stfp fpRegIdx("FLOATREG_XMM_HIGH(%(idx)i)"), seg, %(mode)s, \
+    stfp fpRegIdx("float_reg::xmmHigh(%(idx)i)"), seg, %(mode)s, \
          "DISPLACEMENT + 160 + 16 * %(idx)i + 8", dataSize=8
 '''
 
@@ -80,10 +80,10 @@
     rdxftw t1
     st t1, seg, %(mode)s, "DISPLACEMENT + 4", dataSize=1
 
-    rdval t1, ctrlRegIdx("MISCREG_FOP")
+    rdval t1, ctrlRegIdx("misc_reg::Fop")
     st t1, seg, %(mode)s, "DISPLACEMENT + 6", dataSize=2
 
-    rdval t1, ctrlRegIdx("MISCREG_MXCSR")
+    rdval t1, ctrlRegIdx("misc_reg::Mxcsr")
     st t1, seg, %(mode)s, "DISPLACEMENT + 16 + 8", dataSize=4
 
     # MXCSR_MASK, software assumes the default (0xFFBF) if 0.
@@ -92,24 +92,24 @@
 """ + storeAllDataRegs
 
 fxsave32Template = """
-    rdval t1, ctrlRegIdx("MISCREG_FIOFF")
+    rdval t1, ctrlRegIdx("misc_reg::Fioff")
     st t1, seg, %(mode)s, "DISPLACEMENT + 8", dataSize=4
 
-    rdval t1, ctrlRegIdx("MISCREG_FISEG")
+    rdval t1, ctrlRegIdx("misc_reg::Fiseg")
     st t1, seg, %(mode)s, "DISPLACEMENT + 12", dataSize=2
 
-    rdval t1, ctrlRegIdx("MISCREG_FOOFF")
+    rdval t1, ctrlRegIdx("misc_reg::Fooff")
     st t1, seg, %(mode)s, "DISPLACEMENT + 16 + 0", dataSize=4
 
-    rdval t1, ctrlRegIdx("MISCREG_FOSEG")
+    rdval t1, ctrlRegIdx("misc_reg::Foseg")
     st t1, seg, %(mode)s, "DISPLACEMENT + 16 + 4", dataSize=2
 """ + fxsaveCommonTemplate
 
 fxsave64Template = """
-    rdval t1, ctrlRegIdx("MISCREG_FIOFF")
+    rdval t1, ctrlRegIdx("misc_reg::Fioff")
     st t1, seg, %(mode)s, "DISPLACEMENT + 8", dataSize=8
 
-    rdval t1, ctrlRegIdx("MISCREG_FOOFF")
+    rdval t1, ctrlRegIdx("misc_reg::Fooff")
     st t1, seg, %(mode)s, "DISPLACEMENT + 16 + 0", dataSize=8
 """ + fxsaveCommonTemplate
 
@@ -126,36 +126,36 @@
     wrxftw t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 6", dataSize=2
-    wrval ctrlRegIdx("MISCREG_FOP"), t1
+    wrval ctrlRegIdx("misc_reg::Fop"), t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 16 + 8", dataSize=4
-    wrval ctrlRegIdx("MISCREG_MXCSR"), t1
+    wrval ctrlRegIdx("misc_reg::Mxcsr"), t1
 """ + loadAllDataRegs
 
 fxrstor32Template = """
     ld t1, seg, %(mode)s, "DISPLACEMENT + 8", dataSize=4
-    wrval ctrlRegIdx("MISCREG_FIOFF"), t1
+    wrval ctrlRegIdx("misc_reg::Fioff"), t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 12", dataSize=2
-    wrval ctrlRegIdx("MISCREG_FISEG"), t1
+    wrval ctrlRegIdx("misc_reg::Fiseg"), t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 16 + 0", dataSize=4
-    wrval ctrlRegIdx("MISCREG_FOOFF"), t1
+    wrval ctrlRegIdx("misc_reg::Fooff"), t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 16 + 4", dataSize=2
-    wrval ctrlRegIdx("MISCREG_FOSEG"), t1
+    wrval ctrlRegIdx("misc_reg::Foseg"), t1
 """ + fxrstorCommonTemplate
 
 fxrstor64Template = """
     limm t2, 0, dataSize=8
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 8", dataSize=8
-    wrval ctrlRegIdx("MISCREG_FIOFF"), t1
-    wrval ctrlRegIdx("MISCREG_FISEG"), t2
+    wrval ctrlRegIdx("misc_reg::Fioff"), t1
+    wrval ctrlRegIdx("misc_reg::Fiseg"), t2
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 16 + 0", dataSize=8
-    wrval ctrlRegIdx("MISCREG_FOOFF"), t1
-    wrval ctrlRegIdx("MISCREG_FOSEG"), t2
+    wrval ctrlRegIdx("misc_reg::Fooff"), t1
+    wrval ctrlRegIdx("misc_reg::Foseg"), t2
 """ + fxrstorCommonTemplate
 
 microcode = '''
diff --git a/src/arch/x86/isa/insts/system/msrs.py b/src/arch/x86/isa/insts/system/msrs.py
index e216898..78a9fa1 100644
--- a/src/arch/x86/isa/insts/system/msrs.py
+++ b/src/arch/x86/isa/insts/system/msrs.py
@@ -71,6 +71,6 @@
     rdtsc t1
     mov rax, rax, t1, dataSize=4
     srli rdx, t1, 32, dataSize=8
-    rdval rcx, ctrlRegIdx("MISCREG_TSC_AUX"), dataSize=4
+    rdval rcx, ctrlRegIdx("misc_reg::TscAux"), dataSize=4
 };
 '''
diff --git a/src/arch/x86/isa/insts/system/segmentation.py b/src/arch/x86/isa/insts/system/segmentation.py
index 814a32c..448f5c7 100644
--- a/src/arch/x86/isa/insts/system/segmentation.py
+++ b/src/arch/x86/isa/insts/system/segmentation.py
@@ -157,7 +157,7 @@
     wrlimit idtr, t1
 };
 
-def macroop LTR_R
+def macroop LTR_64_R
 {
     .serialize_after
     chks reg, t0, TRCheck
@@ -174,7 +174,7 @@
     st t1, tsg, [8, t4, t0], dataSize=8
 };
 
-def macroop LTR_M
+def macroop LTR_64_M
 {
     .serialize_after
     ld t5, seg, sib, disp, dataSize=2
@@ -192,7 +192,7 @@
     st t1, tsg, [8, t4, t0], dataSize=8
 };
 
-def macroop LTR_P
+def macroop LTR_64_P
 {
     .serialize_after
     rdip t7
@@ -211,6 +211,94 @@
     st t1, tsg, [8, t4, t0], dataSize=8
 };
 
+def macroop LTR_R
+{
+    .serialize_after
+    chks reg, t0, TRCheck
+    limm t4, 0, dataSize=8
+    srli t4, reg, 3, dataSize=2
+    ldst t1, tsg, [8, t4, t0], dataSize=8
+    chks reg, t1, TSSCheck
+    wrdl tr, t1, reg
+    limm t5, (1 << 9)
+    or t1, t1, t5
+    st t1, tsg, [8, t4, t0], dataSize=8
+};
+
+def macroop LTR_M
+{
+    .serialize_after
+    ld t5, seg, sib, disp, dataSize=2
+    chks t5, t0, TRCheck
+    limm t4, 0, dataSize=8
+    srli t4, t5, 3, dataSize=2
+    ldst t1, tsg, [8, t4, t0], dataSize=8
+    chks t5, t1, TSSCheck
+    wrdl tr, t1, t5
+    limm t5, (1 << 9)
+    or t1, t1, t5
+    st t1, tsg, [8, t4, t0], dataSize=8
+};
+
+def macroop LTR_P
+{
+    panic "LTR in non-64 bit mode doesn't support RIP addressing."
+};
+
+def macroop LLDT_64_R
+{
+    .serialize_after
+    chks reg, t0, InGDTCheck, flags=(EZF,)
+    br label("end"), flags=(CEZF,)
+    limm t4, 0, dataSize=8
+    srli t4, reg, 3, dataSize=2
+    ld t1, tsg, [8, t4, t0], dataSize=8
+    ld t2, tsg, [8, t4, t0], 8, dataSize=8
+    chks reg, t1, LDTCheck
+    wrdh t3, t1, t2
+    wrdl tsl, t1, reg
+    wrbase tsl, t3, dataSize=8
+end:
+    fault "NoFault"
+};
+
+def macroop LLDT_64_M
+{
+    .serialize_after
+    ld t5, seg, sib, disp, dataSize=2
+    chks t5, t0, InGDTCheck, flags=(EZF,)
+    br label("end"), flags=(CEZF,)
+    limm t4, 0, dataSize=8
+    srli t4, t5, 3, dataSize=2
+    ld t1, tsg, [8, t4, t0], dataSize=8
+    ld t2, tsg, [8, t4, t0], 8, dataSize=8
+    chks t5, t1, LDTCheck
+    wrdh t3, t1, t2
+    wrdl tsl, t1, t5
+    wrbase tsl, t3, dataSize=8
+end:
+    fault "NoFault"
+};
+
+def macroop LLDT_64_P
+{
+    .serialize_after
+    rdip t7
+    ld t5, seg, riprel, disp, dataSize=2
+    chks t5, t0, InGDTCheck, flags=(EZF,)
+    br label("end"), flags=(CEZF,)
+    limm t4, 0, dataSize=8
+    srli t4, t5, 3, dataSize=2
+    ld t1, tsg, [8, t4, t0], dataSize=8
+    ld t2, tsg, [8, t4, t0], 8, dataSize=8
+    chks t5, t1, LDTCheck
+    wrdh t3, t1, t2
+    wrdl tsl, t1, t5
+    wrbase tsl, t3, dataSize=8
+end:
+    fault "NoFault"
+};
+
 def macroop LLDT_R
 {
     .serialize_after
@@ -218,12 +306,9 @@
     br label("end"), flags=(CEZF,)
     limm t4, 0, dataSize=8
     srli t4, reg, 3, dataSize=2
-    ldst t1, tsg, [8, t4, t0], dataSize=8
-    ld t2, tsg, [8, t4, t0], 8, dataSize=8
+    ld t1, tsg, [8, t4, t0], dataSize=8
     chks reg, t1, LDTCheck
-    wrdh t3, t1, t2
     wrdl tsl, t1, reg
-    wrbase tsl, t3, dataSize=8
 end:
     fault "NoFault"
 };
@@ -236,33 +321,16 @@
     br label("end"), flags=(CEZF,)
     limm t4, 0, dataSize=8
     srli t4, t5, 3, dataSize=2
-    ldst t1, tsg, [8, t4, t0], dataSize=8
-    ld t2, tsg, [8, t4, t0], 8, dataSize=8
+    ld t1, tsg, [8, t4, t0], dataSize=8
     chks t5, t1, LDTCheck
-    wrdh t3, t1, t2
     wrdl tsl, t1, t5
-    wrbase tsl, t3, dataSize=8
 end:
     fault "NoFault"
 };
 
 def macroop LLDT_P
 {
-    .serialize_after
-    rdip t7
-    ld t5, seg, riprel, disp, dataSize=2
-    chks t5, t0, InGDTCheck, flags=(EZF,)
-    br label("end"), flags=(CEZF,)
-    limm t4, 0, dataSize=8
-    srli t4, t5, 3, dataSize=2
-    ldst t1, tsg, [8, t4, t0], dataSize=8
-    ld t2, tsg, [8, t4, t0], 8, dataSize=8
-    chks t5, t1, LDTCheck
-    wrdh t3, t1, t2
-    wrdl tsl, t1, t5
-    wrbase tsl, t3, dataSize=8
-end:
-    fault "NoFault"
+    panic "LLDT in non-64 bit mode doesn't support RIP addressing."
 };
 
 def macroop SWAPGS
diff --git a/src/arch/x86/isa/insts/x87/control/save_and_restore_x87_environment.py b/src/arch/x86/isa/insts/x87/control/save_and_restore_x87_environment.py
index 3fb14be..4e49ff6 100644
--- a/src/arch/x86/isa/insts/x87/control/save_and_restore_x87_environment.py
+++ b/src/arch/x86/isa/insts/x87/control/save_and_restore_x87_environment.py
@@ -39,19 +39,19 @@
     wrval ftw, t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 12", dataSize=4
-    wrval ctrlRegIdx("MISCREG_FIOFF"), t1
+    wrval ctrlRegIdx("misc_reg::Fioff"), t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 16 + 0", dataSize=2
-    wrval ctrlRegIdx("MISCREG_FISEG"), t1
+    wrval ctrlRegIdx("misc_reg::Fiseg"), t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 16 + 2", dataSize=2
-    wrval ctrlRegIdx("MISCREG_FOP"), t1
+    wrval ctrlRegIdx("misc_reg::Fop"), t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 20", dataSize=4
-    wrval ctrlRegIdx("MISCREG_FOOFF"), t1
+    wrval ctrlRegIdx("misc_reg::Fooff"), t1
 
     ld t1, seg, %(mode)s, "DISPLACEMENT + 24", dataSize=2
-    wrval ctrlRegIdx("MISCREG_FOSEG"), t1
+    wrval ctrlRegIdx("misc_reg::Foseg"), t1
 """
 
 fnstenvTemplate = """
@@ -63,24 +63,24 @@
     st t1, seg, %(mode)s, "DISPLACEMENT + 4", dataSize=2
     srli t1, t1, 11, dataSize=2
     andi t1, t1, 0x7, dataSize=2
-    wrval ctrlRegIdx("MISCREG_X87_TOP"), t1
+    wrval ctrlRegIdx("misc_reg::X87Top"), t1
 
     rdval t1, ftw
     st t1, seg, %(mode)s, "DISPLACEMENT + 8", dataSize=2
 
-    rdval t1, ctrlRegIdx("MISCREG_FIOFF")
+    rdval t1, ctrlRegIdx("misc_reg::Fioff")
     st t1, seg, %(mode)s, "DISPLACEMENT + 12", dataSize=4
 
-    rdval t1, ctrlRegIdx("MISCREG_FISEG")
+    rdval t1, ctrlRegIdx("misc_reg::Fiseg")
     st t1, seg, %(mode)s, "DISPLACEMENT + 16 + 0", dataSize=2
 
-    rdval t1, ctrlRegIdx("MISCREG_FOP")
+    rdval t1, ctrlRegIdx("misc_reg::Fop")
     st t1, seg, %(mode)s, "DISPLACEMENT + 16 + 2", dataSize=2
 
-    rdval t1, ctrlRegIdx("MISCREG_FOOFF")
+    rdval t1, ctrlRegIdx("misc_reg::Fooff")
     st t1, seg, %(mode)s, "DISPLACEMENT + 20", dataSize=4
 
-    rdval t1, ctrlRegIdx("MISCREG_FOSEG")
+    rdval t1, ctrlRegIdx("misc_reg::Foseg")
     st t1, seg, %(mode)s, "DISPLACEMENT + 24", dataSize=2
 
     # Mask exceptions
diff --git a/src/arch/x86/isa/macroop.isa b/src/arch/x86/isa/macroop.isa
index 3710cf4..6230760 100644
--- a/src/arch/x86/isa/macroop.isa
+++ b/src/arch/x86/isa/macroop.isa
@@ -131,12 +131,8 @@
 }};
 
 let {{
-    from micro_asm import Combinational_Macroop, Rom_Macroop
-    class X86Macroop(Combinational_Macroop):
-        def add_microop(self, mnemonic, microop):
-            microop.mnemonic = mnemonic
-            microop.micropc = len(self.microops)
-            self.microops.append(microop)
+    from micro_asm import CombinationalMacroop, RomMacroop
+    class X86Macroop(CombinationalMacroop):
         def setAdjustEnv(self, val):
             self.adjust_env = val
         def adjustImm(self, val):
@@ -256,7 +252,7 @@
                 memoryInst = "true"
             else:
                 memoryInst = "false"
-            regSize = '''(%s || (env.base == INTREG_RSP && %s) ?
+            regSize = '''(%s || (env.base == int_reg::Rsp && %s) ?
                          env.stackSize :
                          env.dataSize)''' % (useStackSize, memoryInst)
             iop = InstObjParams(self.getMnemonic(), self.name, "Macroop",
@@ -279,7 +275,7 @@
             self.regUsed = False
             self.regm = "0"
             self.regmUsed = False
-            self.seg = "SEGMENT_REG_DS"
+            self.seg = "segment_idx::Ds"
             self.size = None
             self.addressSize = "ADDRSIZE"
             self.dataSize = "OPSIZE"
diff --git a/src/arch/x86/isa/microasm.isa b/src/arch/x86/isa/microasm.isa
index 4d4409a..632cb07 100644
--- a/src/arch/x86/isa/microasm.isa
+++ b/src/arch/x86/isa/microasm.isa
@@ -52,9 +52,9 @@
     sys.path[0:0] = ["src/arch/x86/isa/"]
     from insts import microcode
     # print microcode
-    from micro_asm import MicroAssembler, Rom_Macroop
+    from micro_asm import MicroAssembler, RomMacroop
     mainRom = X86MicrocodeRom('main ROM')
-    assembler = MicroAssembler(X86Macroop, microopClasses, mainRom, Rom_Macroop)
+    assembler = MicroAssembler(X86Macroop, microopClasses, mainRom, RomMacroop)
 
     def gpRegIdx(idx):
         return "X86ISA::GpRegIndex(%s)" % idx
@@ -78,14 +78,14 @@
 
     # Add in symbols for the microcode registers
     for num in range(16):
-        assembler.symbols["t%d" % num] = gpRegIdx("INTREG_MICRO(%d)" % num)
+        assembler.symbols["t%d" % num] = gpRegIdx("intRegMicro(%d)" % num)
     for num in range(8):
         assembler.symbols["ufp%d" % num] = \
-            fpRegIdx("FLOATREG_MICROFP(%d)" % num)
+            fpRegIdx("float_reg::microfp(%d)" % num)
     # Add in symbols for the segment descriptor registers
-    for letter in ("C", "D", "E", "F", "G", "H", "S"):
+    for letter in ("c", "d", "e", "f", "g", "h", "s"):
         assembler.symbols["%ss" % letter.lower()] = \
-            segRegIdx("SEGMENT_REG_%sS" % letter)
+            segRegIdx(f"segment_idx::{letter.capitalize()}s")
 
     # Add in symbols for the various checks of segment selectors.
     for check in ("NoCheck", "CSCheck", "CallGateCheck", "IntGateCheck",
@@ -93,11 +93,11 @@
                   "TRCheck", "TSSCheck", "InGDTCheck", "LDTCheck"):
         assembler.symbols[check] = "Seg%s" % check
 
-    for reg in ("TR", "IDTR"):
-        assembler.symbols[reg.lower()] = segRegIdx("SYS_SEGMENT_REG_%s" % reg)
+    for reg in ("tr", "idtr"):
+        assembler.symbols[reg] = segRegIdx(f"segment_idx::{reg.capitalize()}")
 
-    for reg in ("TSL", "TSG"):
-        assembler.symbols[reg.lower()] = segRegIdx("SEGMENT_REG_%s" % reg)
+    for reg in ("tsl", "tsg"):
+        assembler.symbols[reg] = segRegIdx(f"segment_idx::{reg.capitalize()}")
 
     # Miscellaneous symbols
     symbols = {
@@ -105,16 +105,16 @@
         "cr" : crRegIdx("env.reg"),
         "dr" : drRegIdx("env.reg"),
         "sr" : segRegIdx("env.reg"),
-        "xmml" : fpRegIdx("FLOATREG_XMM_LOW(env.reg)"),
-        "xmmh" : fpRegIdx("FLOATREG_XMM_HIGH(env.reg)"),
+        "xmml" : fpRegIdx("float_reg::xmmLow(env.reg)"),
+        "xmmh" : fpRegIdx("float_reg::xmmHigh(env.reg)"),
         "regm" : gpRegIdx("env.regm"),
         "crm" : crRegIdx("env.regm"),
         "drm" : drRegIdx("env.regm"),
         "srm" : segRegIdx("env.regm"),
-        "xmmlm" : fpRegIdx("FLOATREG_XMM_LOW(env.regm)"),
-        "xmmhm" : fpRegIdx("FLOATREG_XMM_HIGH(env.regm)"),
-        "mmx" : fpRegIdx("FLOATREG_MMX(env.reg)"),
-        "mmxm" : fpRegIdx("FLOATREG_MMX(env.regm)"),
+        "xmmlm" : fpRegIdx("float_reg::xmmLow(env.regm)"),
+        "xmmhm" : fpRegIdx("float_reg::xmmHigh(env.regm)"),
+        "mmx" : fpRegIdx("float_reg::mmx(env.reg)"),
+        "mmxm" : fpRegIdx("float_reg::mmx(env.regm)"),
         "imm" : "adjustedImm",
         "disp" : "adjustedDisp",
         "seg" : segRegIdx("env.seg"),
@@ -144,20 +144,20 @@
 
     # This segment selects an internal address space mapped to MSRs,
     # CPUID info, etc.
-    assembler.symbols["intseg"] = segRegIdx("SEGMENT_REG_MS")
+    assembler.symbols["intseg"] = segRegIdx("segment_idx::Ms")
     # This segment always has base 0, and doesn't imply any special handling
     # like the internal segment above
-    assembler.symbols["flatseg"] = segRegIdx("SEGMENT_REG_LS")
+    assembler.symbols["flatseg"] = segRegIdx("segment_idx::Ls")
 
     for reg in ('ax', 'bx', 'cx', 'dx', 'sp', 'bp', 'si', 'di', \
                 '8',  '9',  '10', '11', '12', '13', '14', '15'):
         assembler.symbols["r%s" % reg] = \
-            gpRegIdx("INTREG_R%s" % reg.upper())
+            gpRegIdx("int_reg::R%s" % reg)
 
     for reg in ('ah', 'bh', 'ch', 'dh'):
         assembler.symbols[reg] = \
-            gpRegIdx("X86ISA::INTREG_FOLDED(INTREG_%s, IntFoldBit)" %
-                    reg.upper())
+            gpRegIdx("X86ISA::intRegFolded(int_reg::%s, IntFoldBit)" %
+                    reg.capitalize())
 
     for reg in range(16):
         assembler.symbols["cr%d" % reg] = crRegIdx("%d" % reg)
@@ -178,10 +178,14 @@
     assembler.symbols["CTrue"] = "condition_tests::True"
     assembler.symbols["CFalse"] = "condition_tests::False"
 
-    for reg in ('sysenter_cs', 'sysenter_esp', 'sysenter_eip',
-                'star', 'lstar', 'cstar', 'sf_mask',
-                'kernel_gs_base'):
-        assembler.symbols[reg] = ctrlRegIdx("MISCREG_%s" % reg.upper())
+    for reg in (('sysenter_cs', 'SysenterCs'), ('sysenter_esp', 'SysenterEsp'),
+                ('sysenter_eip', 'SysenterEip'), 'star', 'lstar', 'cstar',
+                ('sf_mask', 'SfMask'), ('kernel_gs_base', 'KernelGsBase')):
+        if isinstance(reg, tuple):
+            assembler.symbols[reg[0]] = ctrlRegIdx(f"misc_reg::{reg[1]}")
+        else:
+            assembler.symbols[reg] = \
+                ctrlRegIdx(f"misc_reg::{reg.capitalize()}")
 
     for flag in ('Scalar', 'MultHi', 'Signed'):
         assembler.symbols[flag] = 'Media%sOp' % flag
@@ -226,15 +230,15 @@
     assembler.symbols["rom_local_label"] = rom_local_labeler
 
     def stack_index(index):
-        return fpRegIdx("NUM_FLOATREGS + (((%s) + 8) %% 8)" % index)
+        return fpRegIdx("float_reg::NumRegs + (((%s) + 8) %% 8)" % index)
 
     assembler.symbols["st"] = stack_index
     assembler.symbols["sti"] = stack_index("env.reg")
     assembler.symbols["stim"] = stack_index("env.regm")
 
-    assembler.symbols["fsw"] = ctrlRegIdx("MISCREG_FSW")
-    assembler.symbols["fcw"] = ctrlRegIdx("MISCREG_FCW")
-    assembler.symbols["ftw"] = ctrlRegIdx("MISCREG_FTW")
+    assembler.symbols["fsw"] = ctrlRegIdx("misc_reg::Fsw")
+    assembler.symbols["fcw"] = ctrlRegIdx("misc_reg::Fcw")
+    assembler.symbols["ftw"] = ctrlRegIdx("misc_reg::Ftw")
 
     macroopDict = assembler.assemble(microcode)
 
diff --git a/src/arch/x86/isa/microops/fpop.isa b/src/arch/x86/isa/microops/fpop.isa
index 6fda08c..6c6c5c1 100644
--- a/src/arch/x86/isa/microops/fpop.isa
+++ b/src/arch/x86/isa/microops/fpop.isa
@@ -368,7 +368,7 @@
 
     class PremFp(Fp3Op):
         code = '''
-            RegVal new_fsw = FSW;
+            [[maybe_unused]] RegVal new_fsw = FSW;
             int src1_exp;
             int src2_exp;
             std::frexp(FpSrcReg1, &src1_exp);
diff --git a/src/arch/x86/isa/microops/ldstop.isa b/src/arch/x86/isa/microops/ldstop.isa
index 72cb7bf..99a381a 100644
--- a/src/arch/x86/isa/microops/ldstop.isa
+++ b/src/arch/x86/isa/microops/ldstop.isa
@@ -296,7 +296,7 @@
     class LdStOp(X86Microop):
         def __init__(self, data, segment, addr, disp,
                 dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec,
-                implicitStack, uncacheable):
+                uncacheable):
             self.data = data
             [self.scale, self.index, self.base] = addr
             self.disp = disp
@@ -305,7 +305,7 @@
             self.addressSize = addressSize
             self.memFlags = baseFlags
             if atCPL0:
-                self.memFlags += " | (CPL0FlagBit << FlagShift)"
+                self.memFlags += " | CPL0FlagBit"
             self.instFlags = ""
             if prefetch:
                 self.memFlags += " | Request::PREFETCH"
@@ -313,12 +313,10 @@
             if nonSpec:
                 self.instFlags += " | (1ULL << StaticInst::IsNonSpeculative)"
             if uncacheable:
-                self.instFlags += " | (Request::UNCACHEABLE)"
-            # For implicit stack operations, we should use *not* use the
-            # alternative addressing mode for loads/stores if the prefix is set
-            if not implicitStack:
-                self.memFlags += " | (machInst.legacy.addr ? " + \
-                                 "(AddrSizeFlagBit << FlagShift) : 0)"
+                self.instFlags += " | Request::UNCACHEABLE"
+            self.memFlags += \
+                " | ((log2i(%s) & AddrSizeFlagMask) << AddrSizeFlagShift)" % \
+                addressSize
 
         def getAllocator(self, microFlags):
             allocator = '''new %(class_name)s(machInst, macrocodeBlock,
@@ -338,7 +336,7 @@
     class BigLdStOp(X86Microop):
         def __init__(self, data, segment, addr, disp,
                 dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec,
-                implicitStack, uncacheable):
+                uncacheable):
             self.data = data
             [self.scale, self.index, self.base] = addr
             self.disp = disp
@@ -347,7 +345,7 @@
             self.addressSize = addressSize
             self.memFlags = baseFlags
             if atCPL0:
-                self.memFlags += " | (CPL0FlagBit << FlagShift)"
+                self.memFlags += " | CPL0FlagBit"
             self.instFlags = ""
             if prefetch:
                 self.memFlags += " | Request::PREFETCH"
@@ -355,12 +353,10 @@
             if nonSpec:
                 self.instFlags += " | (1ULL << StaticInst::IsNonSpeculative)"
             if uncacheable:
-                self.instFlags += " | (Request::UNCACHEABLE)"
-            # For implicit stack operations, we should use *not* use the
-            # alternative addressing mode for loads/stores if the prefix is set
-            if not implicitStack:
-                self.memFlags += " | (machInst.legacy.addr ? " + \
-                                 "(AddrSizeFlagBit << FlagShift) : 0)"
+                self.instFlags += " | Request::UNCACHEABLE"
+            self.memFlags += \
+                " | ((log2i(%s) & AddrSizeFlagMask) << AddrSizeFlagShift)" % \
+                addressSize
 
         def getAllocator(self, microFlags):
             allocString = '''
@@ -388,10 +384,10 @@
     class LdStSplitOp(LdStOp):
         def __init__(self, data, segment, addr, disp,
                 dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec,
-                implicitStack, uncacheable):
+                uncacheable):
             super().__init__(0, segment, addr, disp,
                 dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec,
-                implicitStack, uncacheable)
+                uncacheable)
             (self.dataLow, self.dataHi) = data
 
         def getAllocator(self, microFlags):
@@ -422,8 +418,9 @@
             self.dataSize = dataSize
             self.addressSize = addressSize
             self.instFlags = ""
-            self.memFlags = baseFlags + "| " \
-                "(machInst.legacy.addr ? (AddrSizeFlagBit << FlagShift) : 0)"
+            self.memFlags = baseFlags + \
+                " | ((log2i(%s) & AddrSizeFlagMask) << AddrSizeFlagShift)" % \
+                addressSize
 
         def getAllocator(self, microFlags):
             allocator = '''new %(class_name)s(machInst, macrocodeBlock,
@@ -457,7 +454,7 @@
 
     def defineMicroLoadOp(mnemonic, code, bigCode='',
                           mem_flags="0", big=True, nonSpec=False,
-                          implicitStack=False, is_float=False):
+                          is_float=False):
         global header_output
         global decoder_output
         global exec_output
@@ -486,26 +483,18 @@
             exec_output += MicroLoadInitiateAcc.subst(iop)
             exec_output += MicroLoadCompleteAcc.subst(iop)
 
-        if implicitStack:
-            # For instructions that implicitly access the stack, the address
-            # size is the same as the stack segment pointer size, not the
-            # address size if specified by the instruction prefix
-            addressSize = "env.stackSize"
-        else:
-            addressSize = "env.addressSize"
-
         base = LdStOp
         if big:
             base = BigLdStOp
         class LoadOp(base):
             def __init__(self, data, segment, addr, disp = 0,
                     dataSize="env.dataSize",
-                    addressSize=addressSize,
+                    addressSize="env.addressSize",
                     atCPL0=False, prefetch=False, nonSpec=nonSpec,
-                    implicitStack=implicitStack, uncacheable=False):
+                    uncacheable=False):
                 super().__init__(data, segment, addr,
                         disp, dataSize, addressSize, mem_flags,
-                        atCPL0, prefetch, nonSpec, implicitStack, uncacheable)
+                        atCPL0, prefetch, nonSpec, uncacheable)
                 self.className = Name
                 self.mnemonic = name
 
@@ -513,9 +502,6 @@
 
     defineMicroLoadOp('Ld', 'Data = merge(Data, data, Mem, dataSize);',
                             'Data = Mem & mask(dataSize * 8);')
-    defineMicroLoadOp('Ldis', 'Data = merge(Data, data, Mem, dataSize);',
-                              'Data = Mem & mask(dataSize * 8);',
-                               implicitStack=True)
     defineMicroLoadOp('Ldst', 'Data = merge(Data, data, Mem, dataSize);',
                               'Data = Mem & mask(dataSize * 8);',
                       'Request::READ_MODIFY_WRITE')
@@ -584,10 +570,10 @@
                     dataSize="env.dataSize",
                     addressSize="env.addressSize",
                     atCPL0=False, prefetch=False, nonSpec=nonSpec,
-                    implicitStack=False, uncacheable=False):
+                    uncacheable=False):
                 super().__init__(data, segment, addr,
                         disp, dataSize, addressSize, mem_flags,
-                        atCPL0, prefetch, nonSpec, implicitStack, uncacheable)
+                        atCPL0, prefetch, nonSpec, uncacheable)
                 self.className = Name
                 self.mnemonic = name
 
@@ -606,7 +592,7 @@
                            nonSpec=True)
 
     def defineMicroStoreOp(mnemonic, code, completeCode="", mem_flags="0",
-                           implicitStack=False, is_float=False, has_data=True):
+                           is_float=False, has_data=True):
         global header_output
         global decoder_output
         global exec_output
@@ -632,29 +618,22 @@
         exec_output += MicroStoreInitiateAcc.subst(iop)
         exec_output += MicroStoreCompleteAcc.subst(iop)
 
-        if implicitStack:
-            # For instructions that implicitly access the stack, the address
-            # size is the same as the stack segment pointer size, not the
-            # address size if specified by the instruction prefix
-            addressSize = "env.stackSize"
-        else:
-            addressSize = "env.addressSize"
 
         if has_data:
             class StoreOp(LdStOp):
                 def __init__(self, data, segment, addr, disp=0,
-                        dataSize="env.dataSize", addressSize=addressSize,
-                        atCPL0=False, nonSpec=False,
-                        implicitStack=implicitStack, uncacheable=False):
+                        dataSize="env.dataSize", addressSize="env.addressSize",
+                        atCPL0=False, nonSpec=False, uncacheable=False):
                     super().__init__(data, segment, addr, disp,
                             dataSize, addressSize, mem_flags, atCPL0, False,
-                            nonSpec, implicitStack, uncacheable)
+                            nonSpec, uncacheable)
                     self.className = Name
                     self.mnemonic = name
         else:
             class StoreOp(MemNoDataOp):
                 def __init__(self, segment, addr, disp=0,
-                        dataSize="env.dataSize", addressSize=addressSize):
+                        dataSize="env.dataSize",
+                        addressSize="env.addressSize"):
                     super().__init__(segment, addr, disp,
                             dataSize, addressSize, mem_flags)
                     self.className = Name
@@ -663,10 +642,8 @@
         microopClasses[name] = StoreOp
 
     defineMicroStoreOp('St', 'Mem = PData;')
-    defineMicroStoreOp('Stis', 'Mem = PData;',
-                       implicitStack=True)
-    defineMicroStoreOp('Stul', 'Mem = PData;',
-            mem_flags="Request::LOCKED_RMW")
+    defineMicroStoreOp('Stis', 'Mem = PData;')
+    defineMicroStoreOp('Stul', 'Mem = PData;', mem_flags="Request::LOCKED_RMW")
 
     defineMicroStoreOp('Stfp', code='Mem = FpData_uqw;', is_float=True)
 
@@ -718,11 +695,10 @@
             def __init__(self, data, segment, addr, disp = 0,
                     dataSize="env.dataSize",
                     addressSize="env.addressSize",
-                    atCPL0=False, nonSpec=False, implicitStack=False,
-                    uncacheable=False):
+                    atCPL0=False, nonSpec=False, uncacheable=False):
                 super().__init__(data, segment, addr, disp,
                         dataSize, addressSize, mem_flags, atCPL0, False,
-                        nonSpec, implicitStack, uncacheable)
+                        nonSpec, uncacheable)
                 self.className = Name
                 self.mnemonic = name
 
@@ -750,7 +726,7 @@
                 dataSize="env.dataSize", addressSize="env.addressSize"):
             super().__init__(data, segment, addr, disp,
                     dataSize, addressSize, "0",
-                    False, False, False, False, False)
+                    False, False, False, False)
             self.className = "Lea"
             self.mnemonic = "lea"
 
diff --git a/src/arch/x86/isa/microops/regop.isa b/src/arch/x86/isa/microops/regop.isa
index 153060c..404abb2 100644
--- a/src/arch/x86/isa/microops/regop.isa
+++ b/src/arch/x86/isa/microops/regop.isa
@@ -404,7 +404,7 @@
                     PredezfBit, ext & ~mask, result, PSrcReg1, op2);
             PredezfBit = newFlags & EZFBit;
             PreddfBit = newFlags & DFBit;
-            PredccFlagBits = newFlags & ccFlagMask;
+            PredccFlagBits = newFlags & CcFlagMask;
 
             //If a logic microop wants to set these, it wants to set them to 0.
             PredcfofBits = PredcfofBits & ~((CFBit | OFBit) & ext);
@@ -418,11 +418,11 @@
                                     PreddfBit | PredecfBit | PredezfBit,
                                     ext, result, PSrcReg1, op2);
 
-            PredcfofBits = newFlags & cfofMask;
+            PredcfofBits = newFlags & CfofMask;
             PredecfBit = newFlags & ECFBit;
             PredezfBit = newFlags & EZFBit;
             PreddfBit = newFlags & DFBit;
-            PredccFlagBits = newFlags & ccFlagMask;
+            PredccFlagBits = newFlags & CcFlagMask;
         '''
 
     class SubRegOp(BasicRegOp):
@@ -432,11 +432,11 @@
                                          PreddfBit | PredecfBit | PredezfBit,
                                          ext, result, PSrcReg1, ~op2, true);
 
-            PredcfofBits = newFlags & cfofMask;
+            PredcfofBits = newFlags & CfofMask;
             PredecfBit = newFlags & ECFBit;
             PredezfBit = newFlags & EZFBit;
             PreddfBit = newFlags & DFBit;
-            PredccFlagBits = newFlags & ccFlagMask;
+            PredccFlagBits = newFlags & CcFlagMask;
         '''
 
     class CondRegOp(RegOp):
@@ -748,7 +748,7 @@
 
                 PredezfBit = newFlags & EZFBit;
                 PreddfBit = newFlags & DFBit;
-                PredccFlagBits = newFlags & ccFlagMask;
+                PredccFlagBits = newFlags & CcFlagMask;
             }
         '''
 
@@ -794,7 +794,7 @@
 
                 PredezfBit = newFlags & EZFBit;
                 PreddfBit = newFlags & DFBit;
-                PredccFlagBits = newFlags & ccFlagMask;
+                PredccFlagBits = newFlags & CcFlagMask;
             }
         '''
 
@@ -840,7 +840,7 @@
 
                 PredezfBit = newFlags & EZFBit;
                 PreddfBit = newFlags & DFBit;
-                PredccFlagBits = newFlags & ccFlagMask;
+                PredccFlagBits = newFlags & CcFlagMask;
             }
         '''
 
@@ -884,7 +884,7 @@
 
                 PredezfBit = newFlags & EZFBit;
                 PreddfBit = newFlags & DFBit;
-                PredccFlagBits = newFlags & ccFlagMask;
+                PredccFlagBits = newFlags & CcFlagMask;
             }
         '''
 
@@ -933,7 +933,7 @@
 
                 PredezfBit = newFlags & EZFBit;
                 PreddfBit = newFlags & DFBit;
-                PredccFlagBits = newFlags & ccFlagMask;
+                PredccFlagBits = newFlags & CcFlagMask;
             }
         '''
 
@@ -978,7 +978,7 @@
 
                 PredezfBit = newFlags & EZFBit;
                 PreddfBit = newFlags & DFBit;
-                PredccFlagBits = newFlags & ccFlagMask;
+                PredccFlagBits = newFlags & CcFlagMask;
             }
         '''
 
@@ -1028,7 +1028,7 @@
 
                 PredezfBit = newFlags & EZFBit;
                 PreddfBit = newFlags & DFBit;
-                PredccFlagBits = newFlags & ccFlagMask;
+                PredccFlagBits = newFlags & CcFlagMask;
             }
         '''
 
@@ -1088,7 +1088,7 @@
 
                 PredezfBit = newFlags & EZFBit;
                 PreddfBit = newFlags & DFBit;
-                PredccFlagBits = newFlags & ccFlagMask;
+                PredccFlagBits = newFlags & CcFlagMask;
             }
         '''
 
@@ -1154,7 +1154,7 @@
 
                 PredezfBit = newFlags & EZFBit;
                 PreddfBit = newFlags & DFBit;
-                PredccFlagBits = newFlags & ccFlagMask;
+                PredccFlagBits = newFlags & CcFlagMask;
             }
         '''
 
@@ -1162,33 +1162,36 @@
         code = 'DoubleBits = PSrcReg1 ^ op2;'
 
     class Wrip(WrRegOp, CondRegOp):
-        code = 'NRIP = PSrcReg1 + sop2 + CSBase;'
+        code = 'NRIP = ((SrcReg1 + sop2) & mask(dataSize * 8)) + CSBase;'
         else_code = "NRIP = NRIP;"
 
     class Wruflags(WrRegOp):
         code = '''
             uint64_t newFlags = PSrcReg1 ^ op2;
-            cfofBits = newFlags & cfofMask;
+            cfofBits = newFlags & CfofMask;
             ecfBit = newFlags & ECFBit;
             ezfBit = newFlags & EZFBit;
             dfBit = newFlags & DFBit;
-            ccFlagBits = newFlags & ccFlagMask;
+            ccFlagBits = newFlags & CcFlagMask;
         '''
 
     class Wrflags(WrRegOp):
         code = '''
-            RegVal newFlags = PSrcReg1 ^ op2;
-            RegVal userFlagMask = 0xDD5;
+            const RegVal new_flags = PSrcReg1 ^ op2;
 
             // Get only the user flags
-            ccFlagBits = newFlags & ccFlagMask;
-            dfBit = newFlags & DFBit;
-            cfofBits = newFlags & cfofMask;
+            ccFlagBits = new_flags & CcFlagMask;
+            dfBit = new_flags & DFBit;
+            cfofBits = new_flags & CfofMask;
             ecfBit = 0;
             ezfBit = 0;
 
             // Get everything else
-            nccFlagBits = newFlags & ~userFlagMask;
+            const RegVal IOPLMask = mask(2) << 12;
+            const RegVal SysFlagMask =
+                TFBit | IFBit | IOPLMask | NTBit | RFBit | VMBit |
+                ACBit | VIFBit | VIPBit | IDBit;
+            nccFlagBits = new_flags & SysFlagMask;
         '''
 
     class Rdip(RdRegOp):
@@ -1200,7 +1203,7 @@
     class Rflags(RdRegOp):
         code = '''
             DestReg = ccFlagBits | cfofBits | dfBit |
-                      ecfBit | ezfBit | nccFlagBits;
+                      ecfBit | ezfBit | nccFlagBits | (1 << 1);
             '''
 
     class Ruflag(RegOp):
@@ -1530,14 +1533,12 @@
                     break;
                 }
               case SegIntCSCheck:
-                if (m5reg.mode == LongMode) {
+                if (desc.type.codeOrData != 1) {
+                    fault = std::make_shared<GeneralProtection>(selector);
+                } else if (m5reg.mode == LongMode) {
                     if (desc.l != 1 || desc.d != 0) {
                         fault = std::make_shared<GeneralProtection>(selector);
                     }
-                } else {
-                    fault = std::make_shared<GenericISA::M5PanicFault>(
-                            "Interrupt CS "
-                            "checks not implemented in legacy mode.\\n");
                 }
                 break;
               case SegTRCheck:
@@ -1669,9 +1670,15 @@
                     SegLimitDest = desc.limit;
                     SegAttrDest = attr;
                 } else {
+                    HandyM5Reg m5reg = M5Reg;
+
                     SegBaseDest = SegBaseDest;
                     SegLimitDest = SegLimitDest;
-                    SegAttrDest = SegAttrDest;
+
+                    SegAttr attr = SegAttrDest;
+                    if (m5reg != LongMode)
+                        attr.unusable = 1;
+                    SegAttrDest = attr;
                 }
                 break;
             }
diff --git a/src/arch/x86/isa/operands.isa b/src/arch/x86/isa/operands.isa
index f07db14..8c5df9b 100644
--- a/src/arch/x86/isa/operands.isa
+++ b/src/arch/x86/isa/operands.isa
@@ -54,89 +54,132 @@
 }};
 
 let {{
-    def intReg(idx, id):
-        return ('IntReg', 'uqw', idx, 'IsInteger', id)
-    def pickedReg(idx, id, size='dataSize'):
-        return ('IntReg', 'uqw', idx, 'IsInteger', id,
-                'pick(xc->readIntRegOperand(this, %(op_idx)s), '
-                '%(reg_idx)s, ' + size + ')')
-    def signedPickedReg(idx, id, size='dataSize'):
-        return ('IntReg', 'uqw', idx, 'IsInteger', id,
-                'signedPick(xc->readIntRegOperand(this, %(op_idx)s), '
-                '%(reg_idx)s, ' + size + ')')
-    def floatReg(idx, id):
-        return ('FloatReg', 'df', idx, 'IsFloating', id)
-    def ccReg(idx, id):
-        return ('CCReg', 'uqw', idx, None, id)
-    def controlReg(idx, id, ctype = 'uqw'):
-        return ('ControlReg', ctype, idx,
+    class IntReg(IntRegOp):
+        @overrideInOperand
+        def regId(self):
+            return f'(({self.reg_spec}) == gem5::X86ISA::int_reg::T0) ? ' \
+                   f'RegId() : RegId({self.reg_class}, {self.reg_spec})'
+        def __init__(self, idx, id, data_size='dataSize', *args, **kwargs):
+            super().__init__('uqw', idx, 'IsInteger', id, *args, **kwargs)
+            self.attrs['data_size'] = data_size
+
+    class PickedReg(IntReg):
+        @overrideInOperand
+        def makeRead(self):
+            return f'{self.base_name} = pick(xc->getRegOperand(' \
+                        f'this, {self.src_reg_idx}), {self.reg_spec}, ' \
+                        f'{self.data_size});\n'
+
+        def __init__(self, idx, id, data_size='dataSize'):
+            super().__init__(idx, id, data_size)
+
+    class SignedPickedReg(IntReg):
+        @overrideInOperand
+        def makeRead(self):
+            return f'{self.base_name} = signedPick(xc->getRegOperand(' \
+                        f'this, {self.src_reg_idx}), {self.reg_spec}, ' \
+                        f'{self.data_size});\n'
+
+        def __init__(self, idx, id, data_size='dataSize'):
+            super().__init__(idx, id, data_size)
+
+    class FloatReg(FloatRegOp):
+        def __init__(self, idx, id):
+            super().__init__('df', idx, 'IsFloating', id)
+
+    class CCReg(CCRegOp):
+        def __init__(self, idx, id):
+            super().__init__('uqw', idx, None, id)
+
+    class CCRegPred(CCRegOp):
+        @overrideInOperand
+        def srcRegId(self):
+            return f'({self.readPredicate}) ? {self.regId()} : RegId()'
+        @overrideInOperand
+        def destRegId(self):
+            return f'({self.writePredicate}) ? {self.regId()} : RegId()'
+        def __init__(self, idx, id, *, read_predicate, write_predicate):
+            super().__init__('uqw', idx, None, id)
+            self.attrs['readPredicate'] = read_predicate
+            self.attrs['writePredicate'] = write_predicate
+
+    class ControlReg(ControlRegOp):
+        def __init__(self, idx, id, ctype='uqw'):
+            super().__init__(ctype, idx,
                 (None, None, ['IsSerializeAfter',
                               'IsSerializing',
                               'IsNonSpeculative']),
                 id)
-    def squashCheckReg(idx, id, check, ctype = 'uqw'):
-        return ('ControlReg', ctype, idx,
-                (None, None, ['((%s) ? ' % check+ \
-                                'IsSquashAfter : IsSerializeAfter)',
-                              'IsSerializing',
-                              'IsNonSpeculative']),
+
+    class SquashCheckReg(ControlRegOp):
+        def __init__(self, idx, id, check, ctype='uqw'):
+            super().__init__(ctype, idx,
+                (None, None,
+                 [f'(({check}) ? IsSquashAfter : IsSerializeAfter)',
+                  'IsSerializing', 'IsNonSpeculative']),
                 id)
-    def squashCReg(idx, id, ctype = 'uqw'):
-        return squashCheckReg(idx, id, 'true', ctype)
-    def squashCSReg(idx, id, ctype = 'uqw'):
-        return squashCheckReg(idx, id, 'dest == X86ISA::SEGMENT_REG_CS', ctype)
-    def squashCR0Reg(idx, id, ctype = 'uqw'):
-        return squashCheckReg(idx, id, 'dest == 0', ctype)
+
+    class SquashCReg(SquashCheckReg):
+        def __init__(self, idx, id, ctype='uqw'):
+            super().__init__(idx, id, 'true', ctype)
+
+    class SquashCSReg(SquashCheckReg):
+        def __init__(self, idx, id, ctype='uqw'):
+            super().__init__(idx, id, 'dest == X86ISA::segment_idx::Cs', ctype)
+
+    class SquashCR0Reg(SquashCheckReg):
+        def __init__(self, idx, id, ctype='uqw'):
+            super().__init__(idx, id, 'dest == 0', ctype)
 }};
 
 def operands {{
-        'SrcReg1':       intReg('src1', 1),
-        'PSrcReg1':      pickedReg('src1', 1),
-        'PMSrcReg1':     pickedReg('src1', 1, 'srcSize'),
-        'SPSrcReg1':     signedPickedReg('src1', 1),
-        'SrcReg2':       intReg('src2', 2),
-        'PSrcReg2':      pickedReg('src2', 2),
-        'SPSrcReg2':     signedPickedReg('src2', 2),
-        'Index':         intReg('index', 3),
-        'Base':          intReg('base', 4),
-        'DestReg':       intReg('dest', 5),
-        'Data':          intReg('data', 6),
-        'PData':         pickedReg('data', 6),
-        'DataLow':       intReg('dataLow', 6),
-        'DataHi':        intReg('dataHi', 6),
-        'ProdLow':       intReg('X86ISA::INTREG_PRODLOW', 7),
-        'ProdHi':        intReg('X86ISA::INTREG_PRODHI', 8),
-        'Quotient':      intReg('X86ISA::INTREG_QUOTIENT', 9),
-        'Remainder':     intReg('X86ISA::INTREG_REMAINDER', 10),
-        'Divisor':       intReg('X86ISA::INTREG_DIVISOR', 11),
-        'DoubleBits':    intReg('X86ISA::INTREG_DOUBLEBITS', 11),
-        'Rax':           intReg('X86ISA::INTREG_RAX', 12),
-        'Rbx':           intReg('X86ISA::INTREG_RBX', 13),
-        'Rcx':           intReg('X86ISA::INTREG_RCX', 14),
-        'Rdx':           intReg('X86ISA::INTREG_RDX', 15),
-        'Rsp':           intReg('X86ISA::INTREG_RSP', 16),
-        'Rbp':           intReg('X86ISA::INTREG_RBP', 17),
-        'Rsi':           intReg('X86ISA::INTREG_RSI', 18),
-        'Rdi':           intReg('X86ISA::INTREG_RDI', 19),
-        'R8':            intReg('X86ISA::INTREG_R8', 20),
-        'R9':            intReg('X86ISA::INTREG_R9', 21),
-        'FpSrcReg1':     floatReg('src1', 22),
-        'FpSrcReg2':     floatReg('src2', 23),
-        'FpDestReg':     floatReg('dest', 24),
-        'FpData':        floatReg('data', 25),
-        'RIP':           ('PCState', 'uqw', 'pc',
+        'SrcReg1':       IntReg('src1', 1),
+        'PSrcReg1':      PickedReg('src1', 1),
+        'PMSrcReg1':     PickedReg('src1', 1, 'srcSize'),
+        'SPSrcReg1':     SignedPickedReg('src1', 1),
+        'SrcReg2':       IntReg('src2', 2),
+        'PSrcReg2':      PickedReg('src2', 2),
+        'SPSrcReg2':     SignedPickedReg('src2', 2),
+        'Index':         IntReg('index', 3),
+        'Base':          IntReg('base', 4),
+        'DestReg':       IntReg('dest', 5),
+        'Data':          IntReg('data', 6),
+        'PData':         PickedReg('data', 6),
+        'DataLow':       IntReg('dataLow', 6),
+        'DataHi':        IntReg('dataHi', 6),
+        'ProdLow':       IntReg('X86ISA::int_reg::Prodlow', 7),
+        'ProdHi':        IntReg('X86ISA::int_reg::Prodhi', 8),
+        'Quotient':      IntReg('X86ISA::int_reg::Quotient', 9),
+        'Remainder':     IntReg('X86ISA::int_reg::Remainder', 10),
+        'Divisor':       IntReg('X86ISA::int_reg::Divisor', 11),
+        'DoubleBits':    IntReg('X86ISA::int_reg::Doublebits', 11),
+        'Rax':           IntReg('X86ISA::int_reg::Rax', 12),
+        'Rbx':           IntReg('X86ISA::int_reg::Rbx', 13),
+        'Rcx':           IntReg('X86ISA::int_reg::Rcx', 14),
+        'Rdx':           IntReg('X86ISA::int_reg::Rdx', 15),
+        'Rsp':           IntReg('X86ISA::int_reg::Rsp', 16),
+        'Rbp':           IntReg('X86ISA::int_reg::Rbp', 17),
+        'Rsi':           IntReg('X86ISA::int_reg::Rsi', 18),
+        'Rdi':           IntReg('X86ISA::int_reg::Rdi', 19),
+        'R8':            IntReg('X86ISA::int_reg::R8', 20),
+        'R9':            IntReg('X86ISA::int_reg::R9', 21),
+        'FpSrcReg1':     FloatReg('src1', 22),
+        'FpSrcReg2':     FloatReg('src2', 23),
+        'FpDestReg':     FloatReg('dest', 24),
+        'FpData':        FloatReg('data', 25),
+        'RIP':           PCStateOp('uqw', 'pc',
                           (None, None, 'IsControl'), 50),
-        'NRIP':          ('PCState', 'uqw', 'npc',
+        'NRIP':          PCStateOp('uqw', 'npc',
                           (None, None, 'IsControl'), 50),
-        'nuIP':          ('PCState', 'uqw', 'nupc',
+        'nuIP':          PCStateOp('uqw', 'nupc',
                           (None, None, 'IsControl'), 50),
         # These registers hold the condition code portion of the flag
         # register. The nccFlagBits version holds the rest.
-        'ccFlagBits':    ccReg('X86ISA::CCREG_ZAPS', 60),
-        'cfofBits':      ccReg('X86ISA::CCREG_CFOF', 61),
-        'dfBit':         ccReg('X86ISA::CCREG_DF', 62),
-        'ecfBit':        ccReg('X86ISA::CCREG_ECF', 63),
-        'ezfBit':        ccReg('X86ISA::CCREG_EZF', 64),
+        'ccFlagBits':    CCReg('X86ISA::cc_reg::Zaps', 60),
+        'cfofBits':      CCReg('X86ISA::cc_reg::Cfof', 61),
+        'dfBit':         CCReg('X86ISA::cc_reg::Df', 62),
+        'ecfBit':        CCReg('X86ISA::cc_reg::Ecf', 63),
+        'ezfBit':        CCReg('X86ISA::cc_reg::Ezf', 64),
 
         # These Pred registers are to be used where reading the portions of
         # condition code registers is possibly optional, depending on how the
@@ -153,67 +196,68 @@
         # would be retained, the write predicate checks if any of the bits
         # are being written.
 
-        'PredccFlagBits': ('CCReg', 'uqw', 'X86ISA::CCREG_ZAPS', None,
-                60, None, None,
-                '(ext & X86ISA::ccFlagMask) != X86ISA::ccFlagMask && '
-                '(ext & X86ISA::ccFlagMask) != 0',
-                '(ext & X86ISA::ccFlagMask) != 0'),
-        'PredcfofBits':   ('CCReg', 'uqw', 'X86ISA::CCREG_CFOF', None,
-                61, None, None,
-                '(ext & X86ISA::cfofMask) != X86ISA::cfofMask && '
-                '(ext & X86ISA::cfofMask) != 0',
-                '(ext & X86ISA::cfofMask) != 0'),
-        'PreddfBit':     ('CCReg', 'uqw', 'X86ISA::CCREG_DF', None,
-                62, None, None, 'false', '(ext & X86ISA::DFBit) != 0'),
-        'PredecfBit':    ('CCReg', 'uqw', 'X86ISA::CCREG_ECF', None,
-                63, None, None, 'false', '(ext & X86ISA::ECFBit) != 0'),
-        'PredezfBit':    ('CCReg', 'uqw', 'X86ISA::CCREG_EZF', None,
-                64, None, None, 'false', '(ext & X86ISA::EZFBit) != 0'),
+        'PredccFlagBits': CCRegPred('X86ISA::cc_reg::Zaps', 60,
+                read_predicate='(ext & X86ISA::CcFlagMask) != '
+                'X86ISA::CcFlagMask && (ext & X86ISA::CcFlagMask) != 0',
+                write_predicate='(ext & X86ISA::CcFlagMask) != 0'),
+        'PredcfofBits':  CCRegPred('X86ISA::cc_reg::Cfof', 61,
+                read_predicate='(ext & X86ISA::CfofMask) '
+                '!= X86ISA::CfofMask && (ext & X86ISA::CfofMask) != 0',
+                write_predicate='(ext & X86ISA::CfofMask) != 0'),
+        'PreddfBit':     CCRegPred('X86ISA::cc_reg::Df', 62,
+                read_predicate='false',
+                write_predicate='(ext & X86ISA::DFBit) != 0'),
+        'PredecfBit':    CCRegPred('X86ISA::cc_reg::Ecf', 63,
+                read_predicate='false',
+                write_predicate='(ext & X86ISA::ECFBit) != 0'),
+        'PredezfBit':    CCRegPred('X86ISA::cc_reg::Ezf', 64,
+                read_predicate='false',
+                write_predicate='(ext & X86ISA::EZFBit) != 0'),
 
         # These register should needs to be more protected so that later
         # instructions don't map their indexes with an old value.
-        'nccFlagBits':   controlReg('X86ISA::MISCREG_RFLAGS', 65),
+        'nccFlagBits':   ControlReg('X86ISA::misc_reg::Rflags', 65),
 
         # Registers related to the state of x87 floating point unit.
-        'TOP':           controlReg('X86ISA::MISCREG_X87_TOP', 66, ctype='ub'),
-        'FSW':           controlReg('X86ISA::MISCREG_FSW', 67, ctype='uw'),
-        'FTW':           controlReg('X86ISA::MISCREG_FTW', 68, ctype='uw'),
-        'FCW':           controlReg('X86ISA::MISCREG_FCW', 69, ctype='uw'),
+        'TOP':           ControlReg('X86ISA::misc_reg::X87Top', 66,
+                ctype='ub'),
+        'FSW':           ControlReg('X86ISA::misc_reg::Fsw', 67, ctype='uw'),
+        'FTW':           ControlReg('X86ISA::misc_reg::Ftw', 68, ctype='uw'),
+        'FCW':           ControlReg('X86ISA::misc_reg::Fcw', 69, ctype='uw'),
 
         # The segment base as used by memory instructions.
-        'SegBase':       controlReg('X86ISA::MISCREG_SEG_EFF_BASE(segment)',
+        'SegBase':       ControlReg('X86ISA::misc_reg::segEffBase(segment)',
                 70),
 
         # Operands to get and set registers indexed by the operands of the
         # original instruction.
-        'ControlDest':   squashCR0Reg('X86ISA::MISCREG_CR(dest)', 100),
-        'ControlSrc1':   controlReg('X86ISA::MISCREG_CR(src1)', 101),
-        'DebugDest':     controlReg('X86ISA::MISCREG_DR(dest)', 102),
-        'DebugSrc1':     controlReg('X86ISA::MISCREG_DR(src1)', 103),
-        'SegBaseDest':   squashCSReg('X86ISA::MISCREG_SEG_BASE(dest)', 104),
-        'SegBaseSrc1':   controlReg('X86ISA::MISCREG_SEG_BASE(src1)', 105),
-        'SegLimitDest':  squashCSReg('X86ISA::MISCREG_SEG_LIMIT(dest)', 106),
-        'SegLimitSrc1':  controlReg('X86ISA::MISCREG_SEG_LIMIT(src1)', 107),
-        'SegSelDest':    controlReg('X86ISA::MISCREG_SEG_SEL(dest)', 108),
-        'SegSelSrc1':    controlReg('X86ISA::MISCREG_SEG_SEL(src1)', 109),
-        'SegAttrDest':   squashCSReg('X86ISA::MISCREG_SEG_ATTR(dest)', 110),
-        'SegAttrSrc1':   controlReg('X86ISA::MISCREG_SEG_ATTR(src1)', 111),
+        'ControlDest':   SquashCR0Reg('X86ISA::misc_reg::cr(dest)', 100),
+        'ControlSrc1':   ControlReg('X86ISA::misc_reg::cr(src1)', 101),
+        'DebugDest':     ControlReg('X86ISA::misc_reg::dr(dest)', 102),
+        'DebugSrc1':     ControlReg('X86ISA::misc_reg::dr(src1)', 103),
+        'SegBaseDest':   SquashCSReg('X86ISA::misc_reg::segBase(dest)', 104),
+        'SegBaseSrc1':   ControlReg('X86ISA::misc_reg::segBase(src1)', 105),
+        'SegLimitDest':  SquashCSReg('X86ISA::misc_reg::segLimit(dest)', 106),
+        'SegLimitSrc1':  ControlReg('X86ISA::misc_reg::segLimit(src1)', 107),
+        'SegSelDest':    ControlReg('X86ISA::misc_reg::segSel(dest)', 108),
+        'SegSelSrc1':    ControlReg('X86ISA::misc_reg::segSel(src1)', 109),
+        'SegAttrDest':   SquashCSReg('X86ISA::misc_reg::segAttr(dest)', 110),
+        'SegAttrSrc1':   ControlReg('X86ISA::misc_reg::segAttr(src1)', 111),
 
         # Operands to access specific control registers directly.
-        'EferOp':        squashCReg('X86ISA::MISCREG_EFER', 200),
-        'CR4Op':         controlReg('X86ISA::MISCREG_CR4', 201),
-        'DR7Op':         controlReg('X86ISA::MISCREG_DR7', 202),
-        'LDTRBase':      controlReg('X86ISA::MISCREG_TSL_BASE', 203),
-        'LDTRLimit':     controlReg('X86ISA::MISCREG_TSL_LIMIT', 204),
-        'LDTRSel':       controlReg('X86ISA::MISCREG_TSL', 205),
-        'GDTRBase':      controlReg('X86ISA::MISCREG_TSG_BASE', 206),
-        'GDTRLimit':     controlReg('X86ISA::MISCREG_TSG_LIMIT', 207),
-        'CSBase':        squashCReg('X86ISA::MISCREG_CS_EFF_BASE', 208),
-        'CSAttr':        squashCReg('X86ISA::MISCREG_CS_ATTR', 209),
-        'MiscRegDest':   controlReg('dest', 210),
-        'MiscRegSrc1':   controlReg('src1', 211),
-        'TscOp':         controlReg('X86ISA::MISCREG_TSC', 212),
-        'M5Reg':         squashCReg('X86ISA::MISCREG_M5_REG', 213),
-        'Mem':           ('Mem', 'uqw', None, \
-                          (None, 'IsLoad', 'IsStore'), 300)
+        'EferOp':        SquashCReg('X86ISA::misc_reg::Efer', 200),
+        'CR4Op':         ControlReg('X86ISA::misc_reg::Cr4', 201),
+        'DR7Op':         ControlReg('X86ISA::misc_reg::Dr7', 202),
+        'LDTRBase':      ControlReg('X86ISA::misc_reg::TslBase', 203),
+        'LDTRLimit':     ControlReg('X86ISA::misc_reg::TslLimit', 204),
+        'LDTRSel':       ControlReg('X86ISA::misc_reg::Tsl', 205),
+        'GDTRBase':      ControlReg('X86ISA::misc_reg::TsgBase', 206),
+        'GDTRLimit':     ControlReg('X86ISA::misc_reg::TsgLimit', 207),
+        'CSBase':        SquashCReg('X86ISA::misc_reg::CsEffBase', 208),
+        'CSAttr':        SquashCReg('X86ISA::misc_reg::CsAttr', 209),
+        'MiscRegDest':   ControlReg('dest', 210),
+        'MiscRegSrc1':   ControlReg('src1', 211),
+        'TscOp':         ControlReg('X86ISA::misc_reg::Tsc', 212),
+        'M5Reg':         SquashCReg('X86ISA::misc_reg::M5Reg', 213),
+        'Mem':           MemOp('uqw', None, (None, 'IsLoad', 'IsStore'), 300)
 }};
diff --git a/src/arch/x86/isa/rom.isa b/src/arch/x86/isa/rom.isa
index 4f97ac8..9aef3ba 100644
--- a/src/arch/x86/isa/rom.isa
+++ b/src/arch/x86/isa/rom.isa
@@ -41,16 +41,6 @@
     from micro_asm import Rom
 
     class X86MicrocodeRom(Rom):
-        def __init__(self, name):
-            super().__init__(name)
-            self.directives = {}
-
-        def add_microop(self, mnemonic, microop):
-            microop.mnemonic = mnemonic
-            microop.micropc = len(self.microops)
-            self.microops.append(microop)
-
-
         def getDeclaration(self):
             declareLabels = \
                 "GEM5_DEPRECATED_NAMESPACE(RomLabels, rom_labels);\n"
diff --git a/src/arch/x86/isa/specialize.isa b/src/arch/x86/isa/specialize.isa
index 839b66d..d1ce18e 100644
--- a/src/arch/x86/isa/specialize.isa
+++ b/src/arch/x86/isa/specialize.isa
@@ -104,17 +104,22 @@
 
 let {{
     class OpType(object):
-        parser = re.compile(r"(?P<tag>[A-Z]+)(?P<size>[a-z]*)|(r(?P<reg>[A-Z0-9]+)(?P<rsize>[a-z]*))")
+        parser = re.compile(r"(?P<tag>[A-Z]+)(?P<size>[a-z]*)|"
+                             "(r(?P<reg>[A-Z0-9]+)(?P<rsize>[a-z]*))|"
+                             "(s(?P<seg>[A-Z0-9]+)(?P<ssize>[a-z]*))")
         def __init__(self, opTypeString):
             match = OpType.parser.search(opTypeString)
             if match == None:
                 raise Exception("Problem parsing operand type {}".format(
                     opTypeString))
             self.reg = match.group("reg")
+            self.seg = match.group("seg")
             self.tag = match.group("tag")
             self.size = match.group("size")
             if not self.size:
                 self.size = match.group("rsize")
+            if not self.size:
+                self.size = match.group("ssize")
 
     ModRMRegIndex = "(MODRM_REG | (REX_R << 3))"
     ModRMRMIndex = "(MODRM_RM | (REX_B << 3))"
@@ -137,16 +142,30 @@
                 #Figure out what to do with fixed register operands
                 #This is the index to use, so we should stick it some place.
                 if opType.reg in ("A", "B", "C", "D"):
-                    regString = "INTREG_R%sX" % opType.reg
+                    regString = "int_reg::R%sx" % opType.reg.lower()
                 else:
-                    regString = "INTREG_R%s" % opType.reg
+                    regString = "int_reg::R%s" % opType.reg.lower()
                 env.addReg(regString)
+                if env.regmUsed:
+                    regString = "env.regm"
+                else:
+                    regString = "env.reg"
                 env.addToDisassembly(
                     "printReg(out, RegId(IntRegClass, %s), regSize);\n" %
                     regString)
 
                 Name += "_R"
 
+            elif opType.seg:
+                env.addReg("segment_idx::%ss" % opType.seg)
+                if env.regmUsed:
+                    regString = "env.regm"
+                else:
+                    regString = "env.reg"
+                env.addToDisassembly("printSegment(out, %s);\n" % regString)
+
+                Name += "_S"
+
             elif opType.tag == "B":
                 # This refers to registers whose index is encoded as part of
                 # the opcode.
@@ -256,12 +275,14 @@
                 if opType.tag == "X":
                     env.addToDisassembly(
                             '''printMem(out, env.seg,
-                                1, X86ISA::NUM_INTREGS, X86ISA::INTREG_RSI, 0,
+                                1, X86ISA::int_reg::NumRegs,
+                                X86ISA::int_reg::Rsi, 0,
                                 env.addressSize, false);''')
                 else:
                     env.addToDisassembly(
-                            '''printMem(out, SEGMENT_REG_ES,
-                                1, X86ISA::NUM_INTREGS, X86ISA::INTREG_RDI, 0,
+                            '''printMem(out, segment_idx::Es,
+                                1, X86ISA::int_reg::NumRegs,
+                                X86ISA::int_reg::Rdi, 0,
                                 env.addressSize, false);''')
                 Name += "_M"
             else:
diff --git a/src/arch/x86/kvm/SConscript b/src/arch/x86/kvm/SConscript
index 0b3d3c8..5dba1cb 100644
--- a/src/arch/x86/kvm/SConscript
+++ b/src/arch/x86/kvm/SConscript
@@ -37,7 +37,8 @@
 
 Import('*')
 
-if not env['USE_KVM'] or env['TARGET_ISA'] != env['KVM_ISA']:
+if not env['CONF']['USE_KVM'] or \
+        env['CONF']['TARGET_ISA'] != env['CONF']['KVM_ISA']:
     Return()
 
 SimObject('X86KvmCPU.py', sim_objects=['X86KvmCPU'], tags='x86 isa')
diff --git a/src/arch/x86/kvm/X86KvmCPU.py b/src/arch/x86/kvm/X86KvmCPU.py
index 54cf0f2..59de5ea 100644
--- a/src/arch/x86/kvm/X86KvmCPU.py
+++ b/src/arch/x86/kvm/X86KvmCPU.py
@@ -28,12 +28,16 @@
 from m5.SimObject import *
 
 from m5.objects.BaseKvmCPU import BaseKvmCPU
+from m5.objects.X86CPU import X86CPU
+from m5.objects.X86MMU import X86MMU
 
-class X86KvmCPU(BaseKvmCPU):
+class X86KvmCPU(BaseKvmCPU, X86CPU):
     type = 'X86KvmCPU'
     cxx_header = "arch/x86/kvm/x86_cpu.hh"
     cxx_class = 'gem5::X86KvmCPU'
 
+    mmu = X86MMU()
+
     cxx_exports = [
         PyBindMethod("dumpFpuRegs"),
         PyBindMethod("dumpIntRegs"),
diff --git a/src/arch/x86/kvm/x86_cpu.cc b/src/arch/x86/kvm/x86_cpu.cc
index 34ac704..cc70a57 100644
--- a/src/arch/x86/kvm/x86_cpu.cc
+++ b/src/arch/x86/kvm/x86_cpu.cc
@@ -108,63 +108,63 @@
 
 static_assert(sizeof(FXSave) == 512, "Unexpected size of FXSave");
 
-#define FOREACH_IREG()                          \
-    do {                                        \
-        APPLY_IREG(rax, INTREG_RAX);            \
-        APPLY_IREG(rbx, INTREG_RBX);            \
-        APPLY_IREG(rcx, INTREG_RCX);            \
-        APPLY_IREG(rdx, INTREG_RDX);            \
-        APPLY_IREG(rsi, INTREG_RSI);            \
-        APPLY_IREG(rdi, INTREG_RDI);            \
-        APPLY_IREG(rsp, INTREG_RSP);            \
-        APPLY_IREG(rbp, INTREG_RBP);            \
-        APPLY_IREG(r8, INTREG_R8);              \
-        APPLY_IREG(r9, INTREG_R9);              \
-        APPLY_IREG(r10, INTREG_R10);            \
-        APPLY_IREG(r11, INTREG_R11);            \
-        APPLY_IREG(r12, INTREG_R12);            \
-        APPLY_IREG(r13, INTREG_R13);            \
-        APPLY_IREG(r14, INTREG_R14);            \
-        APPLY_IREG(r15, INTREG_R15);            \
+#define FOREACH_IREG() \
+    do { \
+        APPLY_IREG(rax, int_reg::Rax); \
+        APPLY_IREG(rbx, int_reg::Rbx); \
+        APPLY_IREG(rcx, int_reg::Rcx); \
+        APPLY_IREG(rdx, int_reg::Rdx); \
+        APPLY_IREG(rsi, int_reg::Rsi); \
+        APPLY_IREG(rdi, int_reg::Rdi); \
+        APPLY_IREG(rsp, int_reg::Rsp); \
+        APPLY_IREG(rbp, int_reg::Rbp); \
+        APPLY_IREG(r8, int_reg::R8); \
+        APPLY_IREG(r9, int_reg::R9); \
+        APPLY_IREG(r10, int_reg::R10); \
+        APPLY_IREG(r11, int_reg::R11); \
+        APPLY_IREG(r12, int_reg::R12); \
+        APPLY_IREG(r13, int_reg::R13); \
+        APPLY_IREG(r14, int_reg::R14); \
+        APPLY_IREG(r15, int_reg::R15); \
     } while (0)
 
-#define FOREACH_SREG()                                  \
-    do {                                                \
-        APPLY_SREG(cr0, MISCREG_CR0);                   \
-        APPLY_SREG(cr2, MISCREG_CR2);                   \
-        APPLY_SREG(cr3, MISCREG_CR3);                   \
-        APPLY_SREG(cr4, MISCREG_CR4);                   \
-        APPLY_SREG(cr8, MISCREG_CR8);                   \
-        APPLY_SREG(efer, MISCREG_EFER);                 \
-        APPLY_SREG(apic_base, MISCREG_APIC_BASE);       \
+#define FOREACH_SREG() \
+    do { \
+        APPLY_SREG(cr0, misc_reg::Cr0); \
+        APPLY_SREG(cr2, misc_reg::Cr2); \
+        APPLY_SREG(cr3, misc_reg::Cr3); \
+        APPLY_SREG(cr4, misc_reg::Cr4); \
+        APPLY_SREG(cr8, misc_reg::Cr8); \
+        APPLY_SREG(efer, misc_reg::Efer); \
+        APPLY_SREG(apic_base, misc_reg::ApicBase); \
     } while (0)
 
-#define FOREACH_DREG()                          \
-    do {                                        \
-        APPLY_DREG(db[0], MISCREG_DR0);         \
-        APPLY_DREG(db[1], MISCREG_DR1);         \
-        APPLY_DREG(db[2], MISCREG_DR2);         \
-        APPLY_DREG(db[3], MISCREG_DR3);         \
-        APPLY_DREG(dr6, MISCREG_DR6);           \
-        APPLY_DREG(dr7, MISCREG_DR7);           \
+#define FOREACH_DREG() \
+    do { \
+        APPLY_DREG(db[0], misc_reg::Dr0); \
+        APPLY_DREG(db[1], misc_reg::Dr1); \
+        APPLY_DREG(db[2], misc_reg::Dr2); \
+        APPLY_DREG(db[3], misc_reg::Dr3); \
+        APPLY_DREG(dr6, misc_reg::Dr6); \
+        APPLY_DREG(dr7, misc_reg::Dr7); \
     } while (0)
 
-#define FOREACH_SEGMENT()                                       \
-    do {                                                        \
-        APPLY_SEGMENT(cs, MISCREG_CS - MISCREG_SEG_SEL_BASE);   \
-        APPLY_SEGMENT(ds, MISCREG_DS - MISCREG_SEG_SEL_BASE);   \
-        APPLY_SEGMENT(es, MISCREG_ES - MISCREG_SEG_SEL_BASE);   \
-        APPLY_SEGMENT(fs, MISCREG_FS - MISCREG_SEG_SEL_BASE);   \
-        APPLY_SEGMENT(gs, MISCREG_GS - MISCREG_SEG_SEL_BASE);   \
-        APPLY_SEGMENT(ss, MISCREG_SS - MISCREG_SEG_SEL_BASE);   \
-        APPLY_SEGMENT(tr, MISCREG_TR - MISCREG_SEG_SEL_BASE);   \
-        APPLY_SEGMENT(ldt, MISCREG_TSL - MISCREG_SEG_SEL_BASE); \
+#define FOREACH_SEGMENT() \
+    do { \
+        APPLY_SEGMENT(cs, misc_reg::Cs - misc_reg::SegSelBase); \
+        APPLY_SEGMENT(ds, misc_reg::Ds - misc_reg::SegSelBase); \
+        APPLY_SEGMENT(es, misc_reg::Es - misc_reg::SegSelBase); \
+        APPLY_SEGMENT(fs, misc_reg::Fs - misc_reg::SegSelBase); \
+        APPLY_SEGMENT(gs, misc_reg::Gs - misc_reg::SegSelBase); \
+        APPLY_SEGMENT(ss, misc_reg::Ss - misc_reg::SegSelBase); \
+        APPLY_SEGMENT(tr, misc_reg::Tr - misc_reg::SegSelBase); \
+        APPLY_SEGMENT(ldt, misc_reg::Tsl - misc_reg::SegSelBase); \
     } while (0)
 
-#define FOREACH_DTABLE()                                        \
-    do {                                                        \
-        APPLY_DTABLE(gdt, MISCREG_TSG - MISCREG_SEG_SEL_BASE);  \
-        APPLY_DTABLE(idt, MISCREG_IDTR - MISCREG_SEG_SEL_BASE); \
+#define FOREACH_DTABLE() \
+    do { \
+        APPLY_DTABLE(gdt, misc_reg::Tsg - misc_reg::SegSelBase); \
+        APPLY_DTABLE(idt, misc_reg::Idtr - misc_reg::SegSelBase); \
     } while (0)
 
 template<typename Struct, typename Entry>
@@ -182,7 +182,7 @@
 {
     inform("KVM register state:\n");
 
-#define APPLY_IREG(kreg, mreg)                  \
+#define APPLY_IREG(kreg, mreg) \
     inform("\t" # kreg ": 0x%llx\n", regs.kreg)
 
     FOREACH_IREG();
@@ -396,21 +396,21 @@
 {
     // Check the register base
     switch (idx) {
-      case MISCREG_TSL:
-      case MISCREG_TR:
-      case MISCREG_FS:
-      case MISCREG_GS:
+      case misc_reg::Tsl:
+      case misc_reg::Tr:
+      case misc_reg::Fs:
+      case misc_reg::Gs:
         if (!isCanonicalAddress(seg.base))
             warn("Illegal %s base: 0x%x\n", name, seg.base);
         break;
 
-      case MISCREG_SS:
-      case MISCREG_DS:
-      case MISCREG_ES:
+      case misc_reg::Ss:
+      case misc_reg::Ds:
+      case misc_reg::Es:
         if (seg.unusable)
             break;
         [[fallthrough]];
-      case MISCREG_CS:
+      case misc_reg::Cs:
         if (seg.base & 0xffffffff00000000ULL)
             warn("Illegal %s base: 0x%x\n", name, seg.base);
         break;
@@ -418,7 +418,7 @@
 
     // Check the type
     switch (idx) {
-      case MISCREG_CS:
+      case misc_reg::Cs:
         switch (seg.type) {
           case 3:
             if (seg.dpl != 0)
@@ -440,7 +440,7 @@
         }
         break;
 
-      case MISCREG_SS:
+      case misc_reg::Ss:
         if (seg.unusable)
             break;
         switch (seg.type) {
@@ -458,10 +458,10 @@
         }
         break;
 
-      case MISCREG_DS:
-      case MISCREG_ES:
-      case MISCREG_FS:
-      case MISCREG_GS:
+      case misc_reg::Ds:
+      case misc_reg::Es:
+      case misc_reg::Fs:
+      case misc_reg::Gs:
         if (seg.unusable)
             break;
         if (!(seg.type & 0x1) ||
@@ -469,13 +469,13 @@
             warn("%s has an illegal type field: %i\n", name, seg.type);
         break;
 
-      case MISCREG_TR:
+      case misc_reg::Tr:
         // TODO: We should check the CPU mode
         if (seg.type != 3 && seg.type != 11)
             warn("%s: Illegal segment type (%i)\n", name, seg.type);
         break;
 
-      case MISCREG_TSL:
+      case misc_reg::Tsl:
         if (seg.unusable)
             break;
         if (seg.type != 2)
@@ -484,41 +484,41 @@
     }
 
     switch (idx) {
-      case MISCREG_SS:
-      case MISCREG_DS:
-      case MISCREG_ES:
-      case MISCREG_FS:
-      case MISCREG_GS:
+      case misc_reg::Ss:
+      case misc_reg::Ds:
+      case misc_reg::Es:
+      case misc_reg::Fs:
+      case misc_reg::Gs:
         if (seg.unusable)
             break;
         [[fallthrough]];
-      case MISCREG_CS:
+      case misc_reg::Cs:
         if (!seg.s)
             warn("%s: S flag not set\n", name);
         break;
 
-      case MISCREG_TSL:
+      case misc_reg::Tsl:
         if (seg.unusable)
             break;
         [[fallthrough]];
-      case MISCREG_TR:
+      case misc_reg::Tr:
         if (seg.s)
             warn("%s: S flag is set\n", name);
         break;
     }
 
     switch (idx) {
-      case MISCREG_SS:
-      case MISCREG_DS:
-      case MISCREG_ES:
-      case MISCREG_FS:
-      case MISCREG_GS:
-      case MISCREG_TSL:
+      case misc_reg::Ss:
+      case misc_reg::Ds:
+      case misc_reg::Es:
+      case misc_reg::Fs:
+      case misc_reg::Gs:
+      case misc_reg::Tsl:
         if (seg.unusable)
             break;
         [[fallthrough]];
-      case MISCREG_TR:
-      case MISCREG_CS:
+      case misc_reg::Tr:
+      case misc_reg::Cs:
         if (!seg.present)
             warn("%s: P flag not set\n", name);
 
@@ -536,8 +536,14 @@
 X86KvmCPU::X86KvmCPU(const X86KvmCPUParams &params)
     : BaseKvmCPU(params),
       useXSave(params.useXSave)
+{}
+
+void
+X86KvmCPU::init()
 {
-    Kvm &kvm(*vm.kvm);
+    BaseKvmCPU::init();
+
+    Kvm &kvm = *vm->kvm;
 
     if (!kvm.capSetTSSAddress())
         panic("KVM: Missing capability (KVM_CAP_SET_TSS_ADDR)\n");
@@ -667,7 +673,7 @@
 void
 X86KvmCPU::dumpMSRs() const
 {
-    const Kvm::MSRIndexVector &supported_msrs(vm.kvm->getSupportedMSRs());
+    const Kvm::MSRIndexVector &supported_msrs = vm->kvm->getSupportedMSRs();
     auto msrs = newVarStruct<struct kvm_msrs, struct kvm_msr_entry>(
             supported_msrs.size());
 
@@ -701,14 +707,14 @@
 {
     struct kvm_regs regs;
 
-#define APPLY_IREG(kreg, mreg) regs.kreg = tc->readIntReg(mreg)
+#define APPLY_IREG(kreg, mreg) regs.kreg = tc->getReg(mreg)
     FOREACH_IREG();
 #undef APPLY_IREG
 
-    regs.rip = tc->pcState().instAddr() - tc->readMiscReg(MISCREG_CS_BASE);
+    regs.rip = tc->pcState().instAddr() - tc->readMiscReg(misc_reg::CsBase);
 
     /* You might think that setting regs.rflags to the contents
-     * MISCREG_RFLAGS here would suffice. In that case you're
+     * misc_reg::Rflags here would suffice. In that case you're
      * mistaken. We need to reconstruct it from a bunch of ucode
      * registers and wave a dead chicken over it (aka mask out and set
      * reserved bits) to get it to work.
@@ -722,11 +728,11 @@
 setKvmSegmentReg(ThreadContext *tc, struct kvm_segment &kvm_seg,
                  const int index)
 {
-    SegAttr attr(tc->readMiscRegNoEffect(MISCREG_SEG_ATTR(index)));
+    SegAttr attr(tc->readMiscRegNoEffect(misc_reg::segAttr(index)));
 
-    kvm_seg.base = tc->readMiscRegNoEffect(MISCREG_SEG_BASE(index));
-    kvm_seg.limit = tc->readMiscRegNoEffect(MISCREG_SEG_LIMIT(index));
-    kvm_seg.selector = tc->readMiscRegNoEffect(MISCREG_SEG_SEL(index));
+    kvm_seg.base = tc->readMiscRegNoEffect(misc_reg::segBase(index));
+    kvm_seg.limit = tc->readMiscRegNoEffect(misc_reg::segLimit(index));
+    kvm_seg.selector = tc->readMiscRegNoEffect(misc_reg::segSel(index));
     kvm_seg.type = attr.type;
     kvm_seg.present = attr.present;
     kvm_seg.dpl = attr.dpl;
@@ -735,20 +741,15 @@
     kvm_seg.l = attr.longMode;
     kvm_seg.g = attr.granularity;
     kvm_seg.avl = attr.avl;
-
-    // A segment is normally unusable when the selector is zero. There
-    // is a attr.unusable flag in gem5, but it seems unused. qemu
-    // seems to set this to 0 all the time, so we just do the same and
-    // hope for the best.
-    kvm_seg.unusable = 0;
+    kvm_seg.unusable = attr.unusable;
 }
 
 static inline void
 setKvmDTableReg(ThreadContext *tc, struct kvm_dtable &kvm_dtable,
                 const int index)
 {
-    kvm_dtable.base = tc->readMiscRegNoEffect(MISCREG_SEG_BASE(index));
-    kvm_dtable.limit = tc->readMiscRegNoEffect(MISCREG_SEG_LIMIT(index));
+    kvm_dtable.base = tc->readMiscRegNoEffect(misc_reg::segBase(index));
+    kvm_dtable.limit = tc->readMiscRegNoEffect(misc_reg::segLimit(index));
 }
 
 static void
@@ -815,14 +816,14 @@
 
     // Do checks after fixing up the state to avoid getting excessive
     // amounts of warnings.
-    RFLAGS rflags_nocc(tc->readMiscReg(MISCREG_RFLAGS));
+    RFLAGS rflags_nocc(tc->readMiscReg(misc_reg::Rflags));
     if (!rflags_nocc.vm) {
         // Do segment verification if the CPU isn't entering virtual
         // 8086 mode.  We currently assume that unrestricted guest
         // mode is available.
 
 #define APPLY_SEGMENT(kreg, idx) \
-        checkSeg(# kreg, idx + MISCREG_SEG_SEL_BASE, sregs.kreg, sregs)
+        checkSeg(# kreg, idx + misc_reg::SegSelBase, sregs.kreg, sregs)
 
         FOREACH_SEGMENT();
 #undef APPLY_SEGMENT
@@ -835,22 +836,22 @@
 static void
 updateKvmStateFPUCommon(ThreadContext *tc, T &fpu)
 {
-    fpu.mxcsr = tc->readMiscRegNoEffect(MISCREG_MXCSR);
-    fpu.fcw = tc->readMiscRegNoEffect(MISCREG_FCW);
-    // No need to rebuild from MISCREG_FSW and MISCREG_TOP if we read
+    fpu.mxcsr = tc->readMiscRegNoEffect(misc_reg::Mxcsr);
+    fpu.fcw = tc->readMiscRegNoEffect(misc_reg::Fcw);
+    // No need to rebuild from misc_reg::Fsw and misc_reg::Top if we read
     // with effects.
-    fpu.fsw = tc->readMiscReg(MISCREG_FSW);
+    fpu.fsw = tc->readMiscReg(misc_reg::Fsw);
 
-    uint64_t ftw(tc->readMiscRegNoEffect(MISCREG_FTW));
+    uint64_t ftw(tc->readMiscRegNoEffect(misc_reg::Ftw));
     fpu.ftwx = X86ISA::convX87TagsToXTags(ftw);
 
-    fpu.last_opcode = tc->readMiscRegNoEffect(MISCREG_FOP);
+    fpu.last_opcode = tc->readMiscRegNoEffect(misc_reg::Fop);
 
     const unsigned top((fpu.fsw >> 11) & 0x7);
     for (int i = 0; i < 8; ++i) {
         const unsigned reg_idx((i + top) & 0x7);
         const double value(bitsToFloat64(
-                    tc->readFloatReg(FLOATREG_FPR(reg_idx))));
+                    tc->getReg(float_reg::fpr(reg_idx))));
         DPRINTF(KvmContext, "Setting KVM FP reg %i (st[%i]) := %f\n",
                 reg_idx, i, value);
         X86ISA::storeFloat80(fpu.fpr[i], value);
@@ -860,9 +861,9 @@
 
     for (int i = 0; i < 16; ++i) {
         *(uint64_t *)&fpu.xmm[i][0] =
-            tc->readFloatReg(FLOATREG_XMM_LOW(i));
+            tc->getReg(float_reg::xmmLow(i));
         *(uint64_t *)&fpu.xmm[i][8] =
-            tc->readFloatReg(FLOATREG_XMM_HIGH(i));
+            tc->getReg(float_reg::xmmHigh(i));
     }
 }
 
@@ -877,15 +878,15 @@
 
     updateKvmStateFPUCommon(tc, fpu);
 
-    if (tc->readMiscRegNoEffect(MISCREG_FISEG))
-        warn_once("MISCREG_FISEG is non-zero.\n");
+    if (tc->readMiscRegNoEffect(misc_reg::Fiseg))
+        warn_once("misc_reg::Fiseg is non-zero.\n");
 
-    fpu.last_ip = tc->readMiscRegNoEffect(MISCREG_FIOFF);
+    fpu.last_ip = tc->readMiscRegNoEffect(misc_reg::Fioff);
 
-    if (tc->readMiscRegNoEffect(MISCREG_FOSEG))
-        warn_once("MISCREG_FOSEG is non-zero.\n");
+    if (tc->readMiscRegNoEffect(misc_reg::Foseg))
+        warn_once("misc_reg::Foseg is non-zero.\n");
 
-    fpu.last_dp = tc->readMiscRegNoEffect(MISCREG_FOOFF);
+    fpu.last_dp = tc->readMiscRegNoEffect(misc_reg::Fooff);
 
     setFPUState(fpu);
 }
@@ -902,15 +903,15 @@
 
     updateKvmStateFPUCommon(tc, xsave);
 
-    if (tc->readMiscRegNoEffect(MISCREG_FISEG))
-        warn_once("MISCREG_FISEG is non-zero.\n");
+    if (tc->readMiscRegNoEffect(misc_reg::Fiseg))
+        warn_once("misc_reg::Fiseg is non-zero.\n");
 
-    xsave.ctrl64.fpu_ip = tc->readMiscRegNoEffect(MISCREG_FIOFF);
+    xsave.ctrl64.fpu_ip = tc->readMiscRegNoEffect(misc_reg::Fioff);
 
-    if (tc->readMiscRegNoEffect(MISCREG_FOSEG))
-        warn_once("MISCREG_FOSEG is non-zero.\n");
+    if (tc->readMiscRegNoEffect(misc_reg::Foseg))
+        warn_once("misc_reg::Foseg is non-zero.\n");
 
-    xsave.ctrl64.fpu_dp = tc->readMiscRegNoEffect(MISCREG_FOOFF);
+    xsave.ctrl64.fpu_dp = tc->readMiscRegNoEffect(misc_reg::Fooff);
 
     setXSave(kxsave);
 }
@@ -977,14 +978,14 @@
     // The M5 misc reg caches some values from other
     // registers. Writing to it with side effects causes it to be
     // updated from its source registers.
-    tc->setMiscReg(MISCREG_M5_REG, 0);
+    tc->setMiscReg(misc_reg::M5Reg, 0);
 }
 
 void
 X86KvmCPU::updateThreadContextRegs(const struct kvm_regs &regs,
                                    const struct kvm_sregs &sregs)
 {
-#define APPLY_IREG(kreg, mreg) tc->setIntReg(mreg, regs.kreg)
+#define APPLY_IREG(kreg, mreg) tc->setReg(mreg, regs.kreg)
 
     FOREACH_IREG();
 
@@ -1017,10 +1018,10 @@
     // We need some setMiscReg magic here to keep the effective base
     // addresses in sync. We need an up-to-date version of EFER, so
     // make sure this is called after the sregs have been synced.
-    tc->setMiscReg(MISCREG_SEG_BASE(index), kvm_seg.base);
-    tc->setMiscReg(MISCREG_SEG_LIMIT(index), kvm_seg.limit);
-    tc->setMiscReg(MISCREG_SEG_SEL(index), kvm_seg.selector);
-    tc->setMiscReg(MISCREG_SEG_ATTR(index), attr);
+    tc->setMiscReg(misc_reg::segBase(index), kvm_seg.base);
+    tc->setMiscReg(misc_reg::segLimit(index), kvm_seg.limit);
+    tc->setMiscReg(misc_reg::segSel(index), kvm_seg.selector);
+    tc->setMiscReg(misc_reg::segAttr(index), attr);
 }
 
 inline void
@@ -1030,8 +1031,8 @@
     // We need some setMiscReg magic here to keep the effective base
     // addresses in sync. We need an up-to-date version of EFER, so
     // make sure this is called after the sregs have been synced.
-    tc->setMiscReg(MISCREG_SEG_BASE(index), kvm_dtable.base);
-    tc->setMiscReg(MISCREG_SEG_LIMIT(index), kvm_dtable.limit);
+    tc->setMiscReg(misc_reg::segBase(index), kvm_dtable.base);
+    tc->setMiscReg(misc_reg::segLimit(index), kvm_dtable.limit);
 }
 
 void
@@ -1062,26 +1063,26 @@
         const double value(X86ISA::loadFloat80(fpu.fpr[i]));
         DPRINTF(KvmContext, "Setting gem5 FP reg %i (st[%i]) := %f\n",
                 reg_idx, i, value);
-        tc->setFloatReg(FLOATREG_FPR(reg_idx), floatToBits64(value));
+        tc->setReg(float_reg::fpr(reg_idx), floatToBits64(value));
     }
 
     // TODO: We should update the MMX state
 
-    tc->setMiscRegNoEffect(MISCREG_X87_TOP, top);
-    tc->setMiscRegNoEffect(MISCREG_MXCSR, fpu.mxcsr);
-    tc->setMiscRegNoEffect(MISCREG_FCW, fpu.fcw);
-    tc->setMiscRegNoEffect(MISCREG_FSW, fpu.fsw);
+    tc->setMiscRegNoEffect(misc_reg::X87Top, top);
+    tc->setMiscRegNoEffect(misc_reg::Mxcsr, fpu.mxcsr);
+    tc->setMiscRegNoEffect(misc_reg::Fcw, fpu.fcw);
+    tc->setMiscRegNoEffect(misc_reg::Fsw, fpu.fsw);
 
     uint64_t ftw(convX87XTagsToTags(fpu.ftwx));
     // TODO: Are these registers really the same?
-    tc->setMiscRegNoEffect(MISCREG_FTW, ftw);
-    tc->setMiscRegNoEffect(MISCREG_FTAG, ftw);
+    tc->setMiscRegNoEffect(misc_reg::Ftw, ftw);
+    tc->setMiscRegNoEffect(misc_reg::Ftag, ftw);
 
-    tc->setMiscRegNoEffect(MISCREG_FOP, fpu.last_opcode);
+    tc->setMiscRegNoEffect(misc_reg::Fop, fpu.last_opcode);
 
     for (int i = 0; i < 16; ++i) {
-        tc->setFloatReg(FLOATREG_XMM_LOW(i), *(uint64_t *)&fpu.xmm[i][0]);
-        tc->setFloatReg(FLOATREG_XMM_HIGH(i), *(uint64_t *)&fpu.xmm[i][8]);
+        tc->setReg(float_reg::xmmLow(i), *(uint64_t *)&fpu.xmm[i][0]);
+        tc->setReg(float_reg::xmmHigh(i), *(uint64_t *)&fpu.xmm[i][8]);
     }
 }
 
@@ -1090,10 +1091,10 @@
 {
     updateThreadContextFPUCommon(tc, fpu);
 
-    tc->setMiscRegNoEffect(MISCREG_FISEG, 0);
-    tc->setMiscRegNoEffect(MISCREG_FIOFF, fpu.last_ip);
-    tc->setMiscRegNoEffect(MISCREG_FOSEG, 0);
-    tc->setMiscRegNoEffect(MISCREG_FOOFF, fpu.last_dp);
+    tc->setMiscRegNoEffect(misc_reg::Fiseg, 0);
+    tc->setMiscRegNoEffect(misc_reg::Fioff, fpu.last_ip);
+    tc->setMiscRegNoEffect(misc_reg::Foseg, 0);
+    tc->setMiscRegNoEffect(misc_reg::Fooff, fpu.last_dp);
 }
 
 void
@@ -1103,10 +1104,10 @@
 
     updateThreadContextFPUCommon(tc, xsave);
 
-    tc->setMiscRegNoEffect(MISCREG_FISEG, 0);
-    tc->setMiscRegNoEffect(MISCREG_FIOFF, xsave.ctrl64.fpu_ip);
-    tc->setMiscRegNoEffect(MISCREG_FOSEG, 0);
-    tc->setMiscRegNoEffect(MISCREG_FOOFF, xsave.ctrl64.fpu_dp);
+    tc->setMiscRegNoEffect(misc_reg::Fiseg, 0);
+    tc->setMiscRegNoEffect(misc_reg::Fioff, xsave.ctrl64.fpu_ip);
+    tc->setMiscRegNoEffect(misc_reg::Foseg, 0);
+    tc->setMiscRegNoEffect(misc_reg::Fooff, xsave.ctrl64.fpu_dp);
 }
 
 void
@@ -1323,11 +1324,11 @@
      * right location in the PCI configuration space.
      */
     if (port == IO_PCI_CONF_ADDR) {
-        handleIOMiscReg32(MISCREG_PCI_CONFIG_ADDRESS);
+        handleIOMiscReg32(misc_reg::PciConfigAddress);
         return 0;
     } else if ((port & ~0x3) == IO_PCI_CONF_DATA_BASE) {
         Addr pciConfigAddr(tc->readMiscRegNoEffect(
-                    MISCREG_PCI_CONFIG_ADDRESS));
+                    misc_reg::PciConfigAddress));
         if (pciConfigAddr & 0x80000000) {
             pAddr = X86ISA::x86PciConfigAddress((pciConfigAddr & 0x7ffffffc) |
                                                 (port & 0x3));
@@ -1406,13 +1407,13 @@
     // Synchronize the APIC base and CR8 here since they are present
     // in the kvm_run struct, which makes the synchronization really
     // cheap.
-    kvm_run.apic_base = tc->readMiscReg(MISCREG_APIC_BASE);
-    kvm_run.cr8 = tc->readMiscReg(MISCREG_CR8);
+    kvm_run.apic_base = tc->readMiscReg(misc_reg::ApicBase);
+    kvm_run.cr8 = tc->readMiscReg(misc_reg::Cr8);
 
     BaseKvmCPU::ioctlRun();
 
-    tc->setMiscReg(MISCREG_APIC_BASE, kvm_run.apic_base);
-    kvm_run.cr8 = tc->readMiscReg(MISCREG_CR8);
+    tc->setMiscReg(misc_reg::ApicBase, kvm_run.apic_base);
+    kvm_run.cr8 = tc->readMiscReg(misc_reg::Cr8);
 }
 
 static struct kvm_cpuid_entry2
@@ -1549,7 +1550,7 @@
 X86KvmCPU::getMsrIntersection() const
 {
     if (cachedMsrIntersection.empty()) {
-        const Kvm::MSRIndexVector &kvm_msrs(vm.kvm->getSupportedMSRs());
+        const Kvm::MSRIndexVector &kvm_msrs = vm->kvm->getSupportedMSRs();
 
         DPRINTF(Kvm, "kvm-x86: Updating MSR intersection\n");
         for (auto it = kvm_msrs.cbegin(); it != kvm_msrs.cend(); ++it) {
diff --git a/src/arch/x86/kvm/x86_cpu.hh b/src/arch/x86/kvm/x86_cpu.hh
index ae41b6b..33f58d7 100644
--- a/src/arch/x86/kvm/x86_cpu.hh
+++ b/src/arch/x86/kvm/x86_cpu.hh
@@ -56,6 +56,7 @@
     virtual ~X86KvmCPU();
 
     void startup() override;
+    void init() override;
 
     /** @{ */
     void dump() const override;
diff --git a/src/arch/x86/ldstflags.hh b/src/arch/x86/ldstflags.hh
index 7465d57..c18033e 100644
--- a/src/arch/x86/ldstflags.hh
+++ b/src/arch/x86/ldstflags.hh
@@ -50,13 +50,13 @@
  */
 namespace X86ISA
 {
-    [[maybe_unused]] const Request::FlagsType SegmentFlagMask = mask(4);
-    const int FlagShift = 4;
-    enum FlagBit
-    {
-        CPL0FlagBit = 1,
-        AddrSizeFlagBit = 2,
-    };
+
+constexpr Request::FlagsType SegmentFlagMask = mask(4);
+constexpr auto CPL0FlagShift = 4;
+constexpr auto CPL0FlagBit = 1 << CPL0FlagShift;
+constexpr auto AddrSizeFlagShift = CPL0FlagShift + 1;
+constexpr auto AddrSizeFlagMask = mask(2);
+
 } // namespace X86ISA
 } // namespace gem5
 
diff --git a/src/arch/x86/linux/fs_workload.cc b/src/arch/x86/linux/fs_workload.cc
index f3c1b87..d30eca3 100644
--- a/src/arch/x86/linux/fs_workload.cc
+++ b/src/arch/x86/linux/fs_workload.cc
@@ -126,7 +126,7 @@
      * Pass the location of the real mode data structure to the kernel
      * using register %esi. We'll use %rsi which should be equivalent.
      */
-    system->threads[0]->setIntReg(INTREG_RSI, realModeData);
+    system->threads[0]->setReg(int_reg::Rsi, realModeData);
 }
 
 } // namespace X86ISA
diff --git a/src/arch/x86/linux/linux.hh b/src/arch/x86/linux/linux.hh
index 928c580b..7f71b5f 100644
--- a/src/arch/x86/linux/linux.hh
+++ b/src/arch/x86/linux/linux.hh
@@ -40,6 +40,8 @@
 
 #include <map>
 
+#include "arch/x86/regs/int.hh"
+#include "arch/x86/regs/misc.hh"
 #include "arch/x86/utility.hh"
 #include "base/compiler.hh"
 #include "kern/linux/flag_tables.hh"
@@ -64,12 +66,12 @@
         ctc->getIsaPtr()->copyRegsFrom(ptc);
 
         if (flags & TGT_CLONE_SETTLS) {
-            ctc->setMiscRegNoEffect(X86ISA::MISCREG_FS_BASE, tls);
-            ctc->setMiscRegNoEffect(X86ISA::MISCREG_FS_EFF_BASE, tls);
+            ctc->setMiscRegNoEffect(X86ISA::misc_reg::FsBase, tls);
+            ctc->setMiscRegNoEffect(X86ISA::misc_reg::FsEffBase, tls);
         }
 
         if (stack)
-            ctc->setIntReg(X86ISA::INTREG_RSP, stack);
+            ctc->setReg(X86ISA::int_reg::Rsp, stack);
     }
 
     class SyscallABI {};
@@ -86,7 +88,7 @@
     static void
     store(ThreadContext *tc, const SyscallReturn &ret)
     {
-        tc->setIntReg(X86ISA::INTREG_RAX, ret.encodedValue());
+        tc->setReg(X86ISA::int_reg::Rax, ret.encodedValue());
     }
 };
 
diff --git a/src/arch/x86/linux/se_workload.cc b/src/arch/x86/linux/se_workload.cc
index d280c7c..329dbbd 100644
--- a/src/arch/x86/linux/se_workload.cc
+++ b/src/arch/x86/linux/se_workload.cc
@@ -44,6 +44,7 @@
 #include "arch/x86/page_size.hh"
 #include "arch/x86/process.hh"
 #include "arch/x86/regs/int.hh"
+#include "arch/x86/regs/misc.hh"
 #include "arch/x86/se_workload.hh"
 #include "base/trace.hh"
 #include "cpu/thread_context.hh"
@@ -96,12 +97,14 @@
 EmuLinux::EmuLinux(const Params &p) : SEWorkload(p, PageShift)
 {}
 
-const std::vector<IntRegIndex> EmuLinux::SyscallABI64::ArgumentRegs = {
-    INTREG_RDI, INTREG_RSI, INTREG_RDX, INTREG_R10W, INTREG_R8W, INTREG_R9W
+const std::vector<RegIndex> EmuLinux::SyscallABI64::ArgumentRegs = {
+    int_reg::Rdi, int_reg::Rsi, int_reg::Rdx,
+    int_reg::R10, int_reg::R8, int_reg::R9
 };
 
-const std::vector<IntRegIndex> EmuLinux::SyscallABI32::ArgumentRegs = {
-    INTREG_EBX, INTREG_ECX, INTREG_EDX, INTREG_ESI, INTREG_EDI, INTREG_EBP
+const std::vector<RegIndex> EmuLinux::SyscallABI32::ArgumentRegs = {
+    int_reg::Ebx, int_reg::Ecx, int_reg::Edx,
+    int_reg::Esi, int_reg::Edi, int_reg::Ebp
 };
 
 void
@@ -112,7 +115,7 @@
     // This will move into the base SEWorkload function at some point.
     process->Process::syscall(tc);
 
-    RegVal rax = tc->readIntReg(INTREG_RAX);
+    RegVal rax = tc->getReg(int_reg::Rax);
     if (dynamic_cast<X86_64Process *>(process)) {
         syscallDescs64.get(rax)->doSyscall(tc);
     } else if (auto *proc32 = dynamic_cast<I386Process *>(process)) {
@@ -152,7 +155,7 @@
 EmuLinux::pageFault(ThreadContext *tc)
 {
     Process *p = tc->getProcessPtr();
-    if (!p->fixupFault(tc->readMiscReg(MISCREG_CR2))) {
+    if (!p->fixupFault(tc->readMiscReg(misc_reg::Cr2))) {
         SETranslatingPortProxy proxy(tc);
         // at this point we should have 6 values on the interrupt stack
         int size = 6;
@@ -167,7 +170,7 @@
                 "\tcs: %#x\n"
                 "\trip: %#x\n"
                 "\terr_code: %#x\n",
-                tc->readMiscReg(MISCREG_CR2),
+                tc->readMiscReg(misc_reg::Cr2),
                 is[5], is[4], is[3], is[2], is[1], is[0]);
    }
 }
diff --git a/src/arch/x86/linux/se_workload.hh b/src/arch/x86/linux/se_workload.hh
index 85b0d69..4cb3ade 100644
--- a/src/arch/x86/linux/se_workload.hh
+++ b/src/arch/x86/linux/se_workload.hh
@@ -40,6 +40,7 @@
 #define __ARCH_X86_LINUX_SE_WORKLOAD_HH__
 
 #include "arch/x86/linux/linux.hh"
+#include "arch/x86/regs/int.hh"
 #include "arch/x86/remote_gdb.hh"
 #include "params/X86EmuLinux.hh"
 #include "sim/process.hh"
@@ -78,13 +79,13 @@
     struct SyscallABI64 :
         public GenericSyscallABI64, public X86Linux::SyscallABI
     {
-        static const std::vector<IntRegIndex> ArgumentRegs;
+        static const std::vector<RegIndex> ArgumentRegs;
     };
 
     struct SyscallABI32 :
         public GenericSyscallABI32, public X86Linux::SyscallABI
     {
-        static const std::vector<IntRegIndex> ArgumentRegs;
+        static const std::vector<RegIndex> ArgumentRegs;
     };
 
   private:
diff --git a/src/arch/x86/linux/syscall_tbl32.cc b/src/arch/x86/linux/syscall_tbl32.cc
index 7f8e905..2de334c 100644
--- a/src/arch/x86/linux/syscall_tbl32.cc
+++ b/src/arch/x86/linux/syscall_tbl32.cc
@@ -370,7 +370,8 @@
     { 320, "utimensat" },
     { 321, "signalfd" },
     { 322, "timerfd" },
-    { 323, "eventfd", eventfdFunc<X86Linux32> }
+    { 323, "eventfd", eventfdFunc<X86Linux32> },
+    { 355, "getrandom", getrandomFunc<X86Linux32>}
 };
 
 } // namespace X86ISA
diff --git a/src/arch/x86/linux/syscall_tbl64.cc b/src/arch/x86/linux/syscall_tbl64.cc
index 27ee8ec..6b6fa2a 100644
--- a/src/arch/x86/linux/syscall_tbl64.cc
+++ b/src/arch/x86/linux/syscall_tbl64.cc
@@ -361,6 +361,7 @@
     { 311, "proess_vm_writev" },
     { 312, "kcmp" },
     { 313, "finit_module" },
+    { 318, "getrandom", getrandomFunc<X86Linux64> }
 };
 
 } // namespace X86ISA
diff --git a/src/arch/x86/linux/syscalls.cc b/src/arch/x86/linux/syscalls.cc
index 496a137..df80ec1 100644
--- a/src/arch/x86/linux/syscalls.cc
+++ b/src/arch/x86/linux/syscalls.cc
@@ -76,19 +76,19 @@
     {
       // Each of these valid options should actually check addr.
       case SetFS:
-        tc->setMiscRegNoEffect(MISCREG_FS_BASE, addr);
-        tc->setMiscRegNoEffect(MISCREG_FS_EFF_BASE, addr);
+        tc->setMiscRegNoEffect(misc_reg::FsBase, addr);
+        tc->setMiscRegNoEffect(misc_reg::FsEffBase, addr);
         return 0;
       case GetFS:
-        fsBase = tc->readMiscRegNoEffect(MISCREG_FS_BASE);
+        fsBase = tc->readMiscRegNoEffect(misc_reg::FsBase);
         p.write(addr, fsBase);
         return 0;
       case SetGS:
-        tc->setMiscRegNoEffect(MISCREG_GS_BASE, addr);
-        tc->setMiscRegNoEffect(MISCREG_GS_EFF_BASE, addr);
+        tc->setMiscRegNoEffect(misc_reg::GsBase, addr);
+        tc->setMiscRegNoEffect(misc_reg::GsEffBase, addr);
         return 0;
       case GetGS:
-        gsBase = tc->readMiscRegNoEffect(MISCREG_GS_BASE);
+        gsBase = tc->readMiscRegNoEffect(misc_reg::GsBase);
         p.write(addr, gsBase);
         return 0;
       default:
diff --git a/src/arch/x86/nativetrace.cc b/src/arch/x86/nativetrace.cc
index 697fb18..864825c 100644
--- a/src/arch/x86/nativetrace.cc
+++ b/src/arch/x86/nativetrace.cc
@@ -72,28 +72,28 @@
 void
 X86NativeTrace::ThreadState::update(ThreadContext *tc)
 {
-    rax = tc->readIntReg(X86ISA::INTREG_RAX);
-    rcx = tc->readIntReg(X86ISA::INTREG_RCX);
-    rdx = tc->readIntReg(X86ISA::INTREG_RDX);
-    rbx = tc->readIntReg(X86ISA::INTREG_RBX);
-    rsp = tc->readIntReg(X86ISA::INTREG_RSP);
-    rbp = tc->readIntReg(X86ISA::INTREG_RBP);
-    rsi = tc->readIntReg(X86ISA::INTREG_RSI);
-    rdi = tc->readIntReg(X86ISA::INTREG_RDI);
-    r8 = tc->readIntReg(X86ISA::INTREG_R8);
-    r9 = tc->readIntReg(X86ISA::INTREG_R9);
-    r10 = tc->readIntReg(X86ISA::INTREG_R10);
-    r11 = tc->readIntReg(X86ISA::INTREG_R11);
-    r12 = tc->readIntReg(X86ISA::INTREG_R12);
-    r13 = tc->readIntReg(X86ISA::INTREG_R13);
-    r14 = tc->readIntReg(X86ISA::INTREG_R14);
-    r15 = tc->readIntReg(X86ISA::INTREG_R15);
+    rax = tc->getReg(X86ISA::int_reg::Rax);
+    rcx = tc->getReg(X86ISA::int_reg::Rcx);
+    rdx = tc->getReg(X86ISA::int_reg::Rdx);
+    rbx = tc->getReg(X86ISA::int_reg::Rbx);
+    rsp = tc->getReg(X86ISA::int_reg::Rsp);
+    rbp = tc->getReg(X86ISA::int_reg::Rbp);
+    rsi = tc->getReg(X86ISA::int_reg::Rsi);
+    rdi = tc->getReg(X86ISA::int_reg::Rdi);
+    r8 = tc->getReg(X86ISA::int_reg::R8);
+    r9 = tc->getReg(X86ISA::int_reg::R9);
+    r10 = tc->getReg(X86ISA::int_reg::R10);
+    r11 = tc->getReg(X86ISA::int_reg::R11);
+    r12 = tc->getReg(X86ISA::int_reg::R12);
+    r13 = tc->getReg(X86ISA::int_reg::R13);
+    r14 = tc->getReg(X86ISA::int_reg::R14);
+    r15 = tc->getReg(X86ISA::int_reg::R15);
     rip = tc->pcState().as<X86ISA::PCState>().npc();
     //This should be expanded if x87 registers are considered
     for (int i = 0; i < 8; i++)
-        mmx[i] = tc->readFloatReg(X86ISA::FLOATREG_MMX(i));
+        mmx[i] = tc->getReg(X86ISA::float_reg::mmx(i));
     for (int i = 0; i < 32; i++)
-        xmm[i] = tc->readFloatReg(X86ISA::FLOATREG_XMM_BASE + i);
+        xmm[i] = tc->getReg(X86ISA::float_reg::xmm(i));
 }
 
 
diff --git a/src/arch/x86/pagetable_walker.cc b/src/arch/x86/pagetable_walker.cc
index cf84d17..705a509 100644
--- a/src/arch/x86/pagetable_walker.cc
+++ b/src/arch/x86/pagetable_walker.cc
@@ -53,6 +53,7 @@
 
 #include "arch/x86/faults.hh"
 #include "arch/x86/pagetable.hh"
+#include "arch/x86/regs/misc.hh"
 #include "arch/x86/tlb.hh"
 #include "base/bitfield.hh"
 #include "base/trie.hh"
@@ -298,9 +299,8 @@
     bool badNX = pte.nx && mode == BaseMMU::Execute && enableNX;
     switch(state) {
       case LongPML4:
-        DPRINTF(PageTableWalker,
-                "Got long mode PML4 entry %#016x.\n", (uint64_t)pte);
-        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl3 * dataSize;
+        DPRINTF(PageTableWalker, "Got long mode PML4 entry %#016x.\n", pte);
+        nextRead = mbits(pte, 51, 12) + vaddr.longl3 * dataSize;
         doWrite = !pte.a;
         pte.a = 1;
         entry.writable = pte.w;
@@ -314,9 +314,8 @@
         nextState = LongPDP;
         break;
       case LongPDP:
-        DPRINTF(PageTableWalker,
-                "Got long mode PDP entry %#016x.\n", (uint64_t)pte);
-        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl2 * dataSize;
+        DPRINTF(PageTableWalker, "Got long mode PDP entry %#016x.\n", pte);
+        nextRead = mbits(pte, 51, 12) + vaddr.longl2 * dataSize;
         doWrite = !pte.a;
         pte.a = 1;
         entry.writable = entry.writable && pte.w;
@@ -329,8 +328,7 @@
         nextState = LongPD;
         break;
       case LongPD:
-        DPRINTF(PageTableWalker,
-                "Got long mode PD entry %#016x.\n", (uint64_t)pte);
+        DPRINTF(PageTableWalker, "Got long mode PD entry %#016x.\n", pte);
         doWrite = !pte.a;
         pte.a = 1;
         entry.writable = entry.writable && pte.w;
@@ -343,25 +341,23 @@
         if (!pte.ps) {
             // 4 KB page
             entry.logBytes = 12;
-            nextRead =
-                ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl1 * dataSize;
+            nextRead = mbits(pte, 51, 12) + vaddr.longl1 * dataSize;
             nextState = LongPTE;
             break;
         } else {
             // 2 MB page
             entry.logBytes = 21;
-            entry.paddr = (uint64_t)pte & (mask(31) << 21);
+            entry.paddr = mbits(pte, 51, 21);
             entry.uncacheable = uncacheable;
             entry.global = pte.g;
             entry.patBit = bits(pte, 12);
-            entry.vaddr = entry.vaddr & ~((2 * (1 << 20)) - 1);
+            entry.vaddr = mbits(entry.vaddr, 63, 21);
             doTLBInsert = true;
             doEndWalk = true;
             break;
         }
       case LongPTE:
-        DPRINTF(PageTableWalker,
-                "Got long mode PTE entry %#016x.\n", (uint64_t)pte);
+        DPRINTF(PageTableWalker, "Got long mode PTE entry %#016x.\n", pte);
         doWrite = !pte.a;
         pte.a = 1;
         entry.writable = entry.writable && pte.w;
@@ -371,18 +367,18 @@
             fault = pageFault(pte.p);
             break;
         }
-        entry.paddr = (uint64_t)pte & (mask(40) << 12);
+        entry.paddr = mbits(pte, 51, 12);
         entry.uncacheable = uncacheable;
         entry.global = pte.g;
         entry.patBit = bits(pte, 12);
-        entry.vaddr = entry.vaddr & ~((4 * (1 << 10)) - 1);
+        entry.vaddr = mbits(entry.vaddr, 63, 12);
         doTLBInsert = true;
         doEndWalk = true;
         break;
       case PAEPDP:
         DPRINTF(PageTableWalker,
-                "Got legacy mode PAE PDP entry %#08x.\n", (uint32_t)pte);
-        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael2 * dataSize;
+                "Got legacy mode PAE PDP entry %#08x.\n", pte);
+        nextRead = mbits(pte, 51, 12) + vaddr.pael2 * dataSize;
         if (!pte.p) {
             doEndWalk = true;
             fault = pageFault(pte.p);
@@ -391,8 +387,7 @@
         nextState = PAEPD;
         break;
       case PAEPD:
-        DPRINTF(PageTableWalker,
-                "Got legacy mode PAE PD entry %#08x.\n", (uint32_t)pte);
+        DPRINTF(PageTableWalker, "Got legacy mode PAE PD entry %#08x.\n", pte);
         doWrite = !pte.a;
         pte.a = 1;
         entry.writable = pte.w;
@@ -405,24 +400,24 @@
         if (!pte.ps) {
             // 4 KB page
             entry.logBytes = 12;
-            nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael1 * dataSize;
+            nextRead = mbits(pte, 51, 12) + vaddr.pael1 * dataSize;
             nextState = PAEPTE;
             break;
         } else {
             // 2 MB page
             entry.logBytes = 21;
-            entry.paddr = (uint64_t)pte & (mask(31) << 21);
+            entry.paddr = mbits(pte, 51, 21);
             entry.uncacheable = uncacheable;
             entry.global = pte.g;
             entry.patBit = bits(pte, 12);
-            entry.vaddr = entry.vaddr & ~((2 * (1 << 20)) - 1);
+            entry.vaddr = mbits(entry.vaddr, 63, 21);
             doTLBInsert = true;
             doEndWalk = true;
             break;
         }
       case PAEPTE:
         DPRINTF(PageTableWalker,
-                "Got legacy mode PAE PTE entry %#08x.\n", (uint32_t)pte);
+                "Got legacy mode PAE PTE entry %#08x.\n", pte);
         doWrite = !pte.a;
         pte.a = 1;
         entry.writable = entry.writable && pte.w;
@@ -432,17 +427,16 @@
             fault = pageFault(pte.p);
             break;
         }
-        entry.paddr = (uint64_t)pte & (mask(40) << 12);
+        entry.paddr = mbits(pte, 51, 12);
         entry.uncacheable = uncacheable;
         entry.global = pte.g;
         entry.patBit = bits(pte, 7);
-        entry.vaddr = entry.vaddr & ~((4 * (1 << 10)) - 1);
+        entry.vaddr = mbits(entry.vaddr, 63, 12);
         doTLBInsert = true;
         doEndWalk = true;
         break;
       case PSEPD:
-        DPRINTF(PageTableWalker,
-                "Got legacy mode PSE PD entry %#08x.\n", (uint32_t)pte);
+        DPRINTF(PageTableWalker, "Got legacy mode PSE PD entry %#08x.\n", pte);
         doWrite = !pte.a;
         pte.a = 1;
         entry.writable = pte.w;
@@ -455,25 +449,23 @@
         if (!pte.ps) {
             // 4 KB page
             entry.logBytes = 12;
-            nextRead =
-                ((uint64_t)pte & (mask(20) << 12)) + vaddr.norml2 * dataSize;
+            nextRead = mbits(pte, 31, 12) + vaddr.norml2 * dataSize;
             nextState = PTE;
             break;
         } else {
             // 4 MB page
             entry.logBytes = 21;
-            entry.paddr = bits(pte, 20, 13) << 32 | bits(pte, 31, 22) << 22;
+            entry.paddr = bits(pte, 20, 13) << 32 | mbits(pte, 31, 22);
             entry.uncacheable = uncacheable;
             entry.global = pte.g;
             entry.patBit = bits(pte, 12);
-            entry.vaddr = entry.vaddr & ~((4 * (1 << 20)) - 1);
+            entry.vaddr = mbits(entry.vaddr, 63, 22);
             doTLBInsert = true;
             doEndWalk = true;
             break;
         }
       case PD:
-        DPRINTF(PageTableWalker,
-                "Got legacy mode PD entry %#08x.\n", (uint32_t)pte);
+        DPRINTF(PageTableWalker, "Got legacy mode PD entry %#08x.\n", pte);
         doWrite = !pte.a;
         pte.a = 1;
         entry.writable = pte.w;
@@ -485,12 +477,11 @@
         }
         // 4 KB page
         entry.logBytes = 12;
-        nextRead = ((uint64_t)pte & (mask(20) << 12)) + vaddr.norml2 * dataSize;
+        nextRead = mbits(pte, 31, 12) + vaddr.norml1 * dataSize;
         nextState = PTE;
         break;
       case PTE:
-        DPRINTF(PageTableWalker,
-                "Got legacy mode PTE entry %#08x.\n", (uint32_t)pte);
+        DPRINTF(PageTableWalker, "Got legacy mode PTE entry %#08x.\n", pte);
         doWrite = !pte.a;
         pte.a = 1;
         entry.writable = pte.w;
@@ -500,11 +491,11 @@
             fault = pageFault(pte.p);
             break;
         }
-        entry.paddr = (uint64_t)pte & (mask(20) << 12);
+        entry.paddr = mbits(pte, 31, 12);
         entry.uncacheable = uncacheable;
         entry.global = pte.g;
         entry.patBit = bits(pte, 7);
-        entry.vaddr = entry.vaddr & ~((4 * (1 << 10)) - 1);
+        entry.vaddr = mbits(entry.vaddr, 31, 12);
         doTLBInsert = true;
         doEndWalk = true;
         break;
@@ -529,7 +520,10 @@
         // value back to memory.
         if (doWrite) {
             write = oldRead;
-            write->setLE<uint64_t>(pte);
+            if (dataSize == 8)
+                write->setLE<uint64_t>(pte);
+            else
+                write->setLE<uint32_t>(pte);
             write->cmd = MemCmd::WriteReq;
         } else {
             write = NULL;
@@ -551,9 +545,9 @@
 Walker::WalkerState::setupWalk(Addr vaddr)
 {
     VAddr addr = vaddr;
-    CR3 cr3 = tc->readMiscRegNoEffect(MISCREG_CR3);
+    CR3 cr3 = tc->readMiscRegNoEffect(misc_reg::Cr3);
     // Check if we're in long mode or not
-    Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
+    Efer efer = tc->readMiscRegNoEffect(misc_reg::Efer);
     dataSize = 8;
     Addr topAddr;
     if (efer.lma) {
@@ -563,7 +557,7 @@
         enableNX = efer.nxe;
     } else {
         // We're in some flavor of legacy mode.
-        CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
+        CR4 cr4 = tc->readMiscRegNoEffect(misc_reg::Cr4);
         if (cr4.pae) {
             // Do legacy PAE.
             state = PAEPDP;
@@ -731,7 +725,7 @@
 Walker::WalkerState::pageFault(bool present)
 {
     DPRINTF(PageTableWalker, "Raising page fault.\n");
-    HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
+    HandyM5Reg m5reg = tc->readMiscRegNoEffect(misc_reg::M5Reg);
     if (mode == BaseMMU::Execute && !enableNX)
         mode = BaseMMU::Read;
     return std::make_shared<PageFault>(entry.vaddr, present, mode,
diff --git a/src/arch/x86/process.cc b/src/arch/x86/process.cc
index c646c02..bca74d5 100644
--- a/src/arch/x86/process.cc
+++ b/src/arch/x86/process.cc
@@ -46,6 +46,7 @@
 
 #include "arch/x86/fs_workload.hh"
 #include "arch/x86/page_size.hh"
+#include "arch/x86/regs/int.hh"
 #include "arch/x86/regs/misc.hh"
 #include "arch/x86/regs/segment.hh"
 #include "arch/x86/se_workload.hh"
@@ -311,36 +312,37 @@
         for (int i = 0; i < contextIds.size(); i++) {
             ThreadContext *tc = system->threads[contextIds[i]];
 
-            tc->setMiscReg(MISCREG_CS, cs);
-            tc->setMiscReg(MISCREG_DS, ds);
-            tc->setMiscReg(MISCREG_ES, ds);
-            tc->setMiscReg(MISCREG_FS, ds);
-            tc->setMiscReg(MISCREG_GS, ds);
-            tc->setMiscReg(MISCREG_SS, ds);
+            tc->setMiscReg(misc_reg::Cs, cs);
+            tc->setMiscReg(misc_reg::Ds, ds);
+            tc->setMiscReg(misc_reg::Es, ds);
+            tc->setMiscReg(misc_reg::Fs, ds);
+            tc->setMiscReg(misc_reg::Gs, ds);
+            tc->setMiscReg(misc_reg::Ss, ds);
 
             // LDT
-            tc->setMiscReg(MISCREG_TSL, 0);
+            tc->setMiscReg(misc_reg::Tsl, 0);
             SegAttr tslAttr = 0;
-            tslAttr.present = 1;
+            tslAttr.unusable = 1;
+            tslAttr.present = 0;
             tslAttr.type = 2;
-            tc->setMiscReg(MISCREG_TSL_ATTR, tslAttr);
+            tc->setMiscReg(misc_reg::TslAttr, tslAttr);
 
-            tc->setMiscReg(MISCREG_TSG_BASE, GDTVirtAddr);
-            tc->setMiscReg(MISCREG_TSG_LIMIT, 8 * numGDTEntries - 1);
+            tc->setMiscReg(misc_reg::TsgBase, GDTVirtAddr);
+            tc->setMiscReg(misc_reg::TsgLimit, 8 * numGDTEntries - 1);
 
-            tc->setMiscReg(MISCREG_TR, tssSel);
-            tc->setMiscReg(MISCREG_TR_BASE, tss_base_addr);
-            tc->setMiscReg(MISCREG_TR_EFF_BASE, tss_base_addr);
-            tc->setMiscReg(MISCREG_TR_LIMIT, tss_limit);
-            tc->setMiscReg(MISCREG_TR_ATTR, tss_attr);
+            tc->setMiscReg(misc_reg::Tr, tssSel);
+            tc->setMiscReg(misc_reg::TrBase, tss_base_addr);
+            tc->setMiscReg(misc_reg::TrEffBase, tss_base_addr);
+            tc->setMiscReg(misc_reg::TrLimit, tss_limit);
+            tc->setMiscReg(misc_reg::TrAttr, tss_attr);
 
             //Start using longmode segments.
-            installSegDesc(tc, SEGMENT_REG_CS, csDesc, true);
-            installSegDesc(tc, SEGMENT_REG_DS, dsDesc, true);
-            installSegDesc(tc, SEGMENT_REG_ES, dsDesc, true);
-            installSegDesc(tc, SEGMENT_REG_FS, dsDesc, true);
-            installSegDesc(tc, SEGMENT_REG_GS, dsDesc, true);
-            installSegDesc(tc, SEGMENT_REG_SS, dsDesc, true);
+            installSegDesc(tc, segment_idx::Cs, csDesc, true);
+            installSegDesc(tc, segment_idx::Ds, dsDesc, true);
+            installSegDesc(tc, segment_idx::Es, dsDesc, true);
+            installSegDesc(tc, segment_idx::Fs, dsDesc, true);
+            installSegDesc(tc, segment_idx::Gs, dsDesc, true);
+            installSegDesc(tc, segment_idx::Ss, dsDesc, true);
 
             Efer efer = 0;
             efer.sce = 1; // Enable system call extensions.
@@ -349,7 +351,7 @@
             efer.nxe = 1; // Enable nx support.
             efer.svme = 0; // Disable svm support for now.
             efer.ffxsr = 0; // Disable fast fxsave and fxrstor.
-            tc->setMiscReg(MISCREG_EFER, efer);
+            tc->setMiscReg(misc_reg::Efer, efer);
 
             //Set up the registers that describe the operating mode.
             CR0 cr0 = 0;
@@ -366,13 +368,13 @@
             cr0.mp = 1; // This doesn't really matter, but the manual suggests
                         // setting it to one.
             cr0.pe = 1; // We're definitely in protected mode.
-            tc->setMiscReg(MISCREG_CR0, cr0);
+            tc->setMiscReg(misc_reg::Cr0, cr0);
 
             CR0 cr2 = 0;
-            tc->setMiscReg(MISCREG_CR2, cr2);
+            tc->setMiscReg(misc_reg::Cr2, cr2);
 
             CR3 cr3 = dynamic_cast<ArchPageTable *>(pTable)->basePtr();
-            tc->setMiscReg(MISCREG_CR3, cr3);
+            tc->setMiscReg(misc_reg::Cr3, cr3);
 
             CR4 cr4 = 0;
             //Turn on pae.
@@ -389,28 +391,28 @@
             cr4.pvi = 0; // Protected-Mode Virtual Interrupts
             cr4.vme = 0; // Virtual-8086 Mode Extensions
 
-            tc->setMiscReg(MISCREG_CR4, cr4);
+            tc->setMiscReg(misc_reg::Cr4, cr4);
 
             CR8 cr8 = 0;
-            tc->setMiscReg(MISCREG_CR8, cr8);
+            tc->setMiscReg(misc_reg::Cr8, cr8);
 
-            tc->setMiscReg(MISCREG_MXCSR, 0x1f80);
+            tc->setMiscReg(misc_reg::Mxcsr, 0x1f80);
 
-            tc->setMiscReg(MISCREG_APIC_BASE, 0xfee00900);
+            tc->setMiscReg(misc_reg::ApicBase, 0xfee00900);
 
-            tc->setMiscReg(MISCREG_TSG_BASE, GDTVirtAddr);
-            tc->setMiscReg(MISCREG_TSG_LIMIT, 0xffff);
+            tc->setMiscReg(misc_reg::TsgBase, GDTVirtAddr);
+            tc->setMiscReg(misc_reg::TsgLimit, 0xffff);
 
-            tc->setMiscReg(MISCREG_IDTR_BASE, IDTVirtAddr);
-            tc->setMiscReg(MISCREG_IDTR_LIMIT, 0xffff);
+            tc->setMiscReg(misc_reg::IdtrBase, IDTVirtAddr);
+            tc->setMiscReg(misc_reg::IdtrLimit, 0xffff);
 
             /* enabling syscall and sysret */
             RegVal star = ((RegVal)sret << 48) | ((RegVal)scall << 32);
-            tc->setMiscReg(MISCREG_STAR, star);
+            tc->setMiscReg(misc_reg::Star, star);
             RegVal lstar = (RegVal)syscallCodeVirtAddr;
-            tc->setMiscReg(MISCREG_LSTAR, lstar);
+            tc->setMiscReg(misc_reg::Lstar, lstar);
             RegVal sfmask = (1 << 8) | (1 << 10); // TF | DF
-            tc->setMiscReg(MISCREG_SF_MASK, sfmask);
+            tc->setMiscReg(misc_reg::SfMask, sfmask);
         }
 
         /* Set up the content of the TSS and write it to physical memory. */
@@ -518,8 +520,11 @@
         /* PF handler */
         pTable->map(PFHandlerVirtAddr, pfHandlerPhysAddr, PageBytes, false);
         /* MMIO region for m5ops */
-        pTable->map(MMIORegionVirtAddr, MMIORegionPhysAddr,
-                    16 * PageBytes, false);
+        auto m5op_range = system->m5opRange();
+        if (m5op_range.size()) {
+            pTable->map(MMIORegionVirtAddr, m5op_range.start(),
+                        m5op_range.size(), false);
+        }
     } else {
         for (int i = 0; i < contextIds.size(); i++) {
             ThreadContext * tc = system->threads[contextIds[i]];
@@ -539,10 +544,10 @@
             dataAttr.system = 1;
 
             // Initialize the segment registers.
-            for (int seg = 0; seg < NUM_SEGMENTREGS; seg++) {
-                tc->setMiscRegNoEffect(MISCREG_SEG_BASE(seg), 0);
-                tc->setMiscRegNoEffect(MISCREG_SEG_EFF_BASE(seg), 0);
-                tc->setMiscRegNoEffect(MISCREG_SEG_ATTR(seg), dataAttr);
+            for (int seg = 0; seg < segment_idx::NumIdxs; seg++) {
+                tc->setMiscRegNoEffect(misc_reg::segBase(seg), 0);
+                tc->setMiscRegNoEffect(misc_reg::segEffBase(seg), 0);
+                tc->setMiscRegNoEffect(misc_reg::segAttr(seg), dataAttr);
             }
 
             SegAttr csAttr = 0;
@@ -559,7 +564,7 @@
             csAttr.expandDown = 0;
             csAttr.system = 1;
 
-            tc->setMiscRegNoEffect(MISCREG_CS_ATTR, csAttr);
+            tc->setMiscRegNoEffect(misc_reg::CsAttr, csAttr);
 
             Efer efer = 0;
             efer.sce = 1; // Enable system call extensions.
@@ -568,7 +573,7 @@
             efer.nxe = 1; // Enable nx support.
             efer.svme = 0; // Disable svm support for now. It isn't implemented.
             efer.ffxsr = 1; // Turn on fast fxsave and fxrstor.
-            tc->setMiscReg(MISCREG_EFER, efer);
+            tc->setMiscReg(misc_reg::Efer, efer);
 
             // Set up the registers that describe the operating mode.
             CR0 cr0 = 0;
@@ -585,9 +590,9 @@
             cr0.mp = 1; // This doesn't really matter, but the manual suggests
                         // setting it to one.
             cr0.pe = 1; // We're definitely in protected mode.
-            tc->setMiscReg(MISCREG_CR0, cr0);
+            tc->setMiscReg(misc_reg::Cr0, cr0);
 
-            tc->setMiscReg(MISCREG_MXCSR, 0x1f80);
+            tc->setMiscReg(misc_reg::Mxcsr, 0x1f80);
         }
     }
 }
@@ -650,12 +655,12 @@
         dataAttr.system = 1;
 
         // Initialize the segment registers.
-        for (int seg = 0; seg < NUM_SEGMENTREGS; seg++) {
-            tc->setMiscRegNoEffect(MISCREG_SEG_BASE(seg), 0);
-            tc->setMiscRegNoEffect(MISCREG_SEG_EFF_BASE(seg), 0);
-            tc->setMiscRegNoEffect(MISCREG_SEG_ATTR(seg), dataAttr);
-            tc->setMiscRegNoEffect(MISCREG_SEG_SEL(seg), 0xB);
-            tc->setMiscRegNoEffect(MISCREG_SEG_LIMIT(seg), (uint32_t)(-1));
+        for (int seg = 0; seg < segment_idx::NumIdxs; seg++) {
+            tc->setMiscRegNoEffect(misc_reg::segBase(seg), 0);
+            tc->setMiscRegNoEffect(misc_reg::segEffBase(seg), 0);
+            tc->setMiscRegNoEffect(misc_reg::segAttr(seg), dataAttr);
+            tc->setMiscRegNoEffect(misc_reg::segSel(seg), 0xB);
+            tc->setMiscRegNoEffect(misc_reg::segLimit(seg), (uint32_t)(-1));
         }
 
         SegAttr csAttr = 0;
@@ -672,14 +677,17 @@
         csAttr.expandDown = 0;
         csAttr.system = 1;
 
-        tc->setMiscRegNoEffect(MISCREG_CS_ATTR, csAttr);
+        tc->setMiscRegNoEffect(misc_reg::CsAttr, csAttr);
 
-        tc->setMiscRegNoEffect(MISCREG_TSG_BASE, _gdtStart);
-        tc->setMiscRegNoEffect(MISCREG_TSG_EFF_BASE, _gdtStart);
-        tc->setMiscRegNoEffect(MISCREG_TSG_LIMIT, _gdtStart + _gdtSize - 1);
+        tc->setMiscRegNoEffect(misc_reg::TsgBase, _gdtStart);
+        tc->setMiscRegNoEffect(misc_reg::TsgEffBase, _gdtStart);
+        tc->setMiscRegNoEffect(misc_reg::TsgLimit, _gdtStart + _gdtSize - 1);
 
         // Set the LDT selector to 0 to deactivate it.
-        tc->setMiscRegNoEffect(MISCREG_TSL, 0);
+        tc->setMiscRegNoEffect(misc_reg::Tsl, 0);
+        SegAttr attr = 0;
+        attr.unusable = 1;
+        tc->setMiscRegNoEffect(misc_reg::TslAttr, attr);
 
         Efer efer = 0;
         efer.sce = 1; // Enable system call extensions.
@@ -688,7 +696,7 @@
         efer.nxe = 1; // Enable nx support.
         efer.svme = 0; // Disable svm support for now. It isn't implemented.
         efer.ffxsr = 1; // Turn on fast fxsave and fxrstor.
-        tc->setMiscReg(MISCREG_EFER, efer);
+        tc->setMiscReg(misc_reg::Efer, efer);
 
         // Set up the registers that describe the operating mode.
         CR0 cr0 = 0;
@@ -705,9 +713,9 @@
         cr0.mp = 1; // This doesn't really matter, but the manual suggests
                     // setting it to one.
         cr0.pe = 1; // We're definitely in protected mode.
-        tc->setMiscReg(MISCREG_CR0, cr0);
+        tc->setMiscReg(misc_reg::Cr0, cr0);
 
-        tc->setMiscReg(MISCREG_MXCSR, 0x1f80);
+        tc->setMiscReg(misc_reg::Mxcsr, 0x1f80);
     }
 }
 
@@ -986,7 +994,7 @@
 
     ThreadContext *tc = system->threads[contextIds[0]];
     // Set the stack pointer register
-    tc->setIntReg(INTREG_RSP, stack_min);
+    tc->setReg(int_reg::Rsp, stack_min);
 
     // There doesn't need to be any segment base added in since we're dealing
     // with the flat segmentation model.
diff --git a/src/arch/x86/pseudo_inst_abi.hh b/src/arch/x86/pseudo_inst_abi.hh
index 456a18e..05bf66f 100644
--- a/src/arch/x86/pseudo_inst_abi.hh
+++ b/src/arch/x86/pseudo_inst_abi.hh
@@ -59,7 +59,7 @@
         // This assumes that all pseudo ops have their return value set
         // by the pseudo op instruction. This may need to be revisited if we
         // modify the pseudo op ABI in util/m5/m5op_x86.S
-        tc->setIntReg(X86ISA::INTREG_RAX, ret);
+        tc->setReg(X86ISA::int_reg::Rax, ret);
     }
 };
 
@@ -76,12 +76,12 @@
 
         using namespace X86ISA;
 
-        const int int_reg_map[] = {
-            INTREG_RDI, INTREG_RSI, INTREG_RDX,
-            INTREG_RCX, INTREG_R8, INTREG_R9
+        constexpr RegId int_reg_map[] = {
+            int_reg::Rdi, int_reg::Rsi, int_reg::Rdx,
+            int_reg::Rcx, int_reg::R8, int_reg::R9
         };
 
-        return tc->readIntReg(int_reg_map[state++]);
+        return tc->getReg(int_reg_map[state++]);
     }
 };
 
diff --git a/src/arch/x86/regs/ccr.hh b/src/arch/x86/regs/ccr.hh
index 0a68e06..1073edd 100644
--- a/src/arch/x86/regs/ccr.hh
+++ b/src/arch/x86/regs/ccr.hh
@@ -35,27 +35,38 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef __ARCH_X86_CCREGS_HH__
-#define __ARCH_X86_CCREGS_HH__
+#ifndef __ARCH_X86_REGS_CCR_HH__
+#define __ARCH_X86_REGS_CCR_HH__
 
-#include "arch/x86/x86_traits.hh"
+#include "cpu/reg_class.hh"
 
 namespace gem5
 {
-
 namespace X86ISA
 {
-    enum CCRegIndex
-    {
-        CCREG_ZAPS,
-        CCREG_CFOF,
-        CCREG_DF,
-        CCREG_ECF,
-        CCREG_EZF,
+namespace cc_reg
+{
 
-        NUM_CCREGS
-    };
+enum : RegIndex
+{
+    _ZapsIdx,
+    _CfofIdx,
+    _DfIdx,
+    _EcfIdx,
+    _EzfIdx,
+
+    NumRegs
+};
+
+inline constexpr RegId
+    Zaps(CCRegClass, _ZapsIdx),
+    Cfof(CCRegClass, _CfofIdx),
+    Df(CCRegClass, _DfIdx),
+    Ecf(CCRegClass, _EcfIdx),
+    Ezf(CCRegClass, _EzfIdx);
+
+} // namespace cc_reg
 } // namespace X86ISA
 } // namespace gem5
 
-#endif // __ARCH_X86_CCREGS_HH__
+#endif // __ARCH_X86_REGS_CCR_HH__
diff --git a/src/arch/x86/regs/float.hh b/src/arch/x86/regs/float.hh
index bd98567..45e2169 100644
--- a/src/arch/x86/regs/float.hh
+++ b/src/arch/x86/regs/float.hh
@@ -43,119 +43,123 @@
 
 namespace gem5
 {
-
 namespace X86ISA
 {
-    enum FloatRegIndex
-    {
-        // MMX/X87 registers
-        FLOATREG_MMX_BASE,
-        FLOATREG_FPR_BASE = FLOATREG_MMX_BASE,
-        FLOATREG_MMX0 = FLOATREG_MMX_BASE,
-        FLOATREG_MMX1,
-        FLOATREG_MMX2,
-        FLOATREG_MMX3,
-        FLOATREG_MMX4,
-        FLOATREG_MMX5,
-        FLOATREG_MMX6,
-        FLOATREG_MMX7,
+namespace float_reg
+{
+enum FloatRegIndex
+{
+    // MMX/X87 registers
+    MmxBase,
+    FprBase = MmxBase,
+    _Mmx0Idx = MmxBase,
+    _Mmx1Idx,
+    _Mmx2Idx,
+    _Mmx3Idx,
+    _Mmx4Idx,
+    _Mmx5Idx,
+    _Mmx6Idx,
+    _Mmx7Idx,
 
-        FLOATREG_FPR0 = FLOATREG_FPR_BASE,
-        FLOATREG_FPR1,
-        FLOATREG_FPR2,
-        FLOATREG_FPR3,
-        FLOATREG_FPR4,
-        FLOATREG_FPR5,
-        FLOATREG_FPR6,
-        FLOATREG_FPR7,
+    _Fpr0Idx = FprBase,
+    _Fpr1Idx,
+    _Fpr2Idx,
+    _Fpr3Idx,
+    _Fpr4Idx,
+    _Fpr5Idx,
+    _Fpr6Idx,
+    _Fpr7Idx,
 
-        FLOATREG_XMM_BASE = FLOATREG_MMX_BASE + NumMMXRegs,
-        FLOATREG_XMM0_LOW = FLOATREG_XMM_BASE,
-        FLOATREG_XMM0_HIGH,
-        FLOATREG_XMM1_LOW,
-        FLOATREG_XMM1_HIGH,
-        FLOATREG_XMM2_LOW,
-        FLOATREG_XMM2_HIGH,
-        FLOATREG_XMM3_LOW,
-        FLOATREG_XMM3_HIGH,
-        FLOATREG_XMM4_LOW,
-        FLOATREG_XMM4_HIGH,
-        FLOATREG_XMM5_LOW,
-        FLOATREG_XMM5_HIGH,
-        FLOATREG_XMM6_LOW,
-        FLOATREG_XMM6_HIGH,
-        FLOATREG_XMM7_LOW,
-        FLOATREG_XMM7_HIGH,
-        FLOATREG_XMM8_LOW,
-        FLOATREG_XMM8_HIGH,
-        FLOATREG_XMM9_LOW,
-        FLOATREG_XMM9_HIGH,
-        FLOATREG_XMM10_LOW,
-        FLOATREG_XMM10_HIGH,
-        FLOATREG_XMM11_LOW,
-        FLOATREG_XMM11_HIGH,
-        FLOATREG_XMM12_LOW,
-        FLOATREG_XMM12_HIGH,
-        FLOATREG_XMM13_LOW,
-        FLOATREG_XMM13_HIGH,
-        FLOATREG_XMM14_LOW,
-        FLOATREG_XMM14_HIGH,
-        FLOATREG_XMM15_LOW,
-        FLOATREG_XMM15_HIGH,
+    XmmBase = MmxBase + NumMMXRegs,
+    _Xmm0LowIdx = XmmBase,
+    _Xmm0HighIdx,
+    _Xmm1LowIdx,
+    _Xmm1HighIdx,
+    _Xmm2LowIdx,
+    _Xmm2HighIdx,
+    _Xmm3LowIdx,
+    _Xmm3HighIdx,
+    _Xmm4LowIdx,
+    _Xmm4HighIdx,
+    _Xmm5LowIdx,
+    _Xmm5HighIdx,
+    _Xmm6LowIdx,
+    _Xmm6HighIdx,
+    _Xmm7LowIdx,
+    _Xmm7HighIdx,
+    _Xmm8LowIdx,
+    _Xmm8HighIdx,
+    _Xmm9LowIdx,
+    _Xmm9HighIdx,
+    _Xmm10LowIdx,
+    _Xmm10HighIdx,
+    _Xmm11LowIdx,
+    _Xmm11HighIdx,
+    _Xmm12LowIdx,
+    _Xmm12HighIdx,
+    _Xmm13LowIdx,
+    _Xmm13HighIdx,
+    _Xmm14LowIdx,
+    _Xmm14HighIdx,
+    _Xmm15LowIdx,
+    _Xmm15HighIdx,
 
-        FLOATREG_MICROFP_BASE = FLOATREG_XMM_BASE + 2 * NumXMMRegs,
-        FLOATREG_MICROFP0 = FLOATREG_MICROFP_BASE,
-        FLOATREG_MICROFP1,
-        FLOATREG_MICROFP2,
-        FLOATREG_MICROFP3,
-        FLOATREG_MICROFP4,
-        FLOATREG_MICROFP5,
-        FLOATREG_MICROFP6,
-        FLOATREG_MICROFP7,
+    MicrofpBase = XmmBase + 2 * NumXMMRegs,
+    _Microfp0Idx = MicrofpBase,
+    _Microfp1Idx,
+    _Microfp2Idx,
+    _Microfp3Idx,
+    _Microfp4Idx,
+    _Microfp5Idx,
+    _Microfp6Idx,
+    _Microfp7Idx,
 
-        NUM_FLOATREGS = FLOATREG_MICROFP_BASE + NumMicroFpRegs
-    };
+    NumRegs = MicrofpBase + NumMicroFpRegs
+};
 
-    static inline FloatRegIndex
-    FLOATREG_MMX(int index)
-    {
-        return (FloatRegIndex)(FLOATREG_MMX_BASE + index);
-    }
+static inline RegId
+mmx(int index)
+{
+    return RegId(FloatRegClass, MmxBase + index);
+}
 
-    static inline FloatRegIndex
-    FLOATREG_FPR(int index)
-    {
-        return (FloatRegIndex)(FLOATREG_FPR_BASE + index);
-    }
+static inline RegId
+fpr(int index)
+{
+    return RegId(FloatRegClass, FprBase + index);
+}
 
-    static inline FloatRegIndex
-    FLOATREG_XMM_LOW(int index)
-    {
-        return (FloatRegIndex)(FLOATREG_XMM_BASE + 2 * index);
-    }
+static inline RegId
+xmm(int index)
+{
+    return RegId(FloatRegClass, XmmBase + index);
+}
 
-    static inline FloatRegIndex
-    FLOATREG_XMM_HIGH(int index)
-    {
-        return (FloatRegIndex)(FLOATREG_XMM_BASE + 2 * index + 1);
-    }
+static inline RegId
+xmmLow(int index)
+{
+    return RegId(FloatRegClass, XmmBase + 2 * index);
+}
 
-    static inline FloatRegIndex
-    FLOATREG_MICROFP(int index)
-    {
-        return (FloatRegIndex)(FLOATREG_MICROFP_BASE + index);
-    }
+static inline RegId
+xmmHigh(int index)
+{
+    return RegId(FloatRegClass, XmmBase + 2 * index + 1);
+}
 
-    static inline FloatRegIndex
-    FLOATREG_STACK(int index, int top)
-    {
-        return FLOATREG_FPR((top + index + 8) % 8);
-    }
+static inline RegId
+microfp(int index)
+{
+    return RegId(FloatRegClass, MicrofpBase + index);
+}
 
-    // Each 128 bit xmm register is broken into two effective 64 bit registers.
-    // Add 8 for the indices that are mapped over the fp stack
-    const int NumFloatRegs =
-        NumMMXRegs + 2 * NumXMMRegs + NumMicroFpRegs + 8;
+static inline RegId
+stack(int index, int top)
+{
+    return fpr((top + index + 8) % 8);
+}
+
+} // namespace float_reg
 
 } // namespace X86ISA
 } // namespace gem5
diff --git a/src/arch/x86/regs/int.hh b/src/arch/x86/regs/int.hh
index 014f5a4..445e6a8 100644
--- a/src/arch/x86/regs/int.hh
+++ b/src/arch/x86/regs/int.hh
@@ -41,151 +41,126 @@
 #include "arch/x86/x86_traits.hh"
 #include "base/bitunion.hh"
 #include "base/logging.hh"
+#include "cpu/reg_class.hh"
 
 namespace gem5
 {
 
 namespace X86ISA
 {
-    BitUnion64(X86IntReg)
-        Bitfield<63,0> R;
-        SignedBitfield<63,0> SR;
-        Bitfield<31,0> E;
-        SignedBitfield<31,0> SE;
-        Bitfield<15,0> X;
-        SignedBitfield<15,0> SX;
-        Bitfield<15,8> H;
-        SignedBitfield<15,8> SH;
-        Bitfield<7, 0> L;
-        SignedBitfield<7, 0> SL;
-    EndBitUnion(X86IntReg)
 
-    enum IntRegIndex
-    {
-        INTREG_RAX,
-        INTREG_EAX = INTREG_RAX,
-        INTREG_AX = INTREG_RAX,
-        INTREG_AL = INTREG_RAX,
+BitUnion64(X86IntReg)
+    Bitfield<63,0> R;
+    SignedBitfield<63,0> SR;
+    Bitfield<31,0> E;
+    SignedBitfield<31,0> SE;
+    Bitfield<15,0> X;
+    SignedBitfield<15,0> SX;
+    Bitfield<15,8> H;
+    SignedBitfield<15,8> SH;
+    Bitfield<7, 0> L;
+    SignedBitfield<7, 0> SL;
+EndBitUnion(X86IntReg)
 
-        INTREG_RCX,
-        INTREG_ECX = INTREG_RCX,
-        INTREG_CX = INTREG_RCX,
-        INTREG_CL = INTREG_RCX,
+namespace int_reg
+{
 
-        INTREG_RDX,
-        INTREG_EDX = INTREG_RDX,
-        INTREG_DX = INTREG_RDX,
-        INTREG_DL = INTREG_RDX,
+enum : RegIndex
+{
+    _RaxIdx,
+    _RcxIdx,
+    _RdxIdx,
+    _RbxIdx,
+    _RspIdx,
+    _RbpIdx,
+    _RsiIdx,
+    _RdiIdx,
+    _R8Idx,
+    _R9Idx,
+    _R10Idx,
+    _R11Idx,
+    _R12Idx,
+    _R13Idx,
+    _R14Idx,
+    _R15Idx,
 
-        INTREG_RBX,
-        INTREG_EBX = INTREG_RBX,
-        INTREG_BX = INTREG_RBX,
-        INTREG_BL = INTREG_RBX,
+    NumArchRegs,
+    MicroBegin = NumArchRegs,
+    _T0Idx = MicroBegin,
+    MicroEnd = MicroBegin + NumMicroIntRegs,
 
-        INTREG_RSP,
-        INTREG_ESP = INTREG_RSP,
-        INTREG_SP = INTREG_RSP,
-        INTREG_SPL = INTREG_RSP,
-        INTREG_AH = INTREG_RSP,
+    _ProdlowIdx,
+    _ProdhiIdx,
+    _QuotientIdx,
+    _RemainderIdx,
+    _DivisorIdx,
+    _DoublebitsIdx,
 
-        INTREG_RBP,
-        INTREG_EBP = INTREG_RBP,
-        INTREG_BP = INTREG_RBP,
-        INTREG_BPL = INTREG_RBP,
-        INTREG_CH = INTREG_RBP,
+    NumRegs
+};
 
-        INTREG_RSI,
-        INTREG_ESI = INTREG_RSI,
-        INTREG_SI = INTREG_RSI,
-        INTREG_SIL = INTREG_RSI,
-        INTREG_DH = INTREG_RSI,
+inline constexpr RegId
+    Rax(IntRegClass, _RaxIdx),
+    Rcx(IntRegClass, _RcxIdx),
+    Rdx(IntRegClass, _RdxIdx),
+    Rbx(IntRegClass, _RbxIdx),
+    Rsp(IntRegClass, _RspIdx),
+    Rbp(IntRegClass, _RbpIdx),
+    Rsi(IntRegClass, _RsiIdx),
+    Rdi(IntRegClass, _RdiIdx),
+    R8(IntRegClass, _R8Idx),
+    R9(IntRegClass, _R9Idx),
+    R10(IntRegClass, _R10Idx),
+    R11(IntRegClass, _R11Idx),
+    R12(IntRegClass, _R12Idx),
+    R13(IntRegClass, _R13Idx),
+    R14(IntRegClass, _R14Idx),
+    R15(IntRegClass, _R15Idx),
+    T0(IntRegClass, _T0Idx),
+    Prodlow(IntRegClass, _ProdlowIdx),
+    Prodhi(IntRegClass, _ProdhiIdx),
+    Quotient(IntRegClass, _QuotientIdx),
+    Remainder(IntRegClass, _RemainderIdx),
+    Divisor(IntRegClass, _DivisorIdx),
+    Doublebits(IntRegClass, _DoublebitsIdx);
 
-        INTREG_RDI,
-        INTREG_EDI = INTREG_RDI,
-        INTREG_DI = INTREG_RDI,
-        INTREG_DIL = INTREG_RDI,
-        INTREG_BH = INTREG_RDI,
+// Aliases for other register sizes.
+inline constexpr auto
+    &Eax = Rax, &Ax = Rax, &Al = Rax,
+    &Ecx = Rcx, &Cx = Rcx, &Cl = Rcx,
+    &Edx = Rdx, &Dx = Rdx, &Dl = Rdx,
+    &Ebx = Rbx, &Bx = Rbx, &Bl = Rbx,
+    &Esp = Rsp, &Sp = Rsp, &Spl = Rsp, &Ah = Rsp,
+    &Ebp = Rbp, &Bp = Rbp, &Bpl = Rbp, &Ch = Rbp,
+    &Esi = Rsi, &Si = Rsi, &Sil = Rsi, &Dh = Rsi,
+    &Edi = Rdi, &Di = Rdi, &Dil = Rdi, &Bh = Rdi,
+    &R8d = R8, &R8w = R8, &R8b = R8,
+    &R9d = R9, &R9w = R9, &R9b = R9,
+    &R10d = R10, &R10w = R10, &R10b = R10,
+    &R11d = R11, &R11w = R11, &R11b = R11,
+    &R12d = R12, &R12w = R12, &R12b = R12,
+    &R13d = R13, &R13w = R13, &R13b = R13,
+    &R14d = R14, &R14w = R14, &R14b = R14,
+    &R15d = R15, &R15w = R15, &R15b = R15;
 
-        INTREG_R8,
-        INTREG_R8D = INTREG_R8,
-        INTREG_R8W = INTREG_R8,
-        INTREG_R8B = INTREG_R8,
+} // namespace int_reg
 
-        INTREG_R9,
-        INTREG_R9D = INTREG_R9,
-        INTREG_R9W = INTREG_R9,
-        INTREG_R9B = INTREG_R9,
+// This needs to be large enough to miss all the other bits of an index.
+inline constexpr RegIndex IntFoldBit = 1 << 6;
 
-        INTREG_R10,
-        INTREG_R10D = INTREG_R10,
-        INTREG_R10W = INTREG_R10,
-        INTREG_R10B = INTREG_R10,
+inline static constexpr RegId
+intRegMicro(int index)
+{
+    return RegId(IntRegClass, int_reg::MicroBegin + index);
+}
 
-        INTREG_R11,
-        INTREG_R11D = INTREG_R11,
-        INTREG_R11W = INTREG_R11,
-        INTREG_R11B = INTREG_R11,
-
-        INTREG_R12,
-        INTREG_R12D = INTREG_R12,
-        INTREG_R12W = INTREG_R12,
-        INTREG_R12B = INTREG_R12,
-
-        INTREG_R13,
-        INTREG_R13D = INTREG_R13,
-        INTREG_R13W = INTREG_R13,
-        INTREG_R13B = INTREG_R13,
-
-        INTREG_R14,
-        INTREG_R14D = INTREG_R14,
-        INTREG_R14W = INTREG_R14,
-        INTREG_R14B = INTREG_R14,
-
-        INTREG_R15,
-        INTREG_R15D = INTREG_R15,
-        INTREG_R15W = INTREG_R15,
-        INTREG_R15B = INTREG_R15,
-
-        NUM_ARCH_INTREGS,
-
-        INTREG_MICRO_BEGIN = NUM_ARCH_INTREGS,
-        INTREG_T0 = INTREG_MICRO_BEGIN,
-        INTREG_MICRO_END = INTREG_MICRO_BEGIN + NumMicroIntRegs,
-
-        // The lower part of the result of multiplication.
-        INTREG_PRODLOW,
-        // The upper part of the result of multiplication.
-        INTREG_PRODHI,
-        // The quotient from division.
-        INTREG_QUOTIENT,
-        // The remainder from division.
-        INTREG_REMAINDER,
-        // The divisor for division.
-        INTREG_DIVISOR,
-        // The register to use for shift doubles.
-        INTREG_DOUBLEBITS,
-
-        NUM_INTREGS,
-    };
-
-    // This needs to be large enough to miss all the other bits of an index.
-    static const IntRegIndex IntFoldBit = (IntRegIndex)(1 << 6);
-
-    inline static IntRegIndex
-    INTREG_MICRO(int index)
-    {
-        return (IntRegIndex)(INTREG_MICRO_BEGIN + index);
-    }
-
-    inline static IntRegIndex
-    INTREG_FOLDED(int index, int foldBit)
-    {
-        if ((index & 0x1C) == 4 && foldBit)
-            index = (index - 4) | foldBit;
-        return (IntRegIndex)index;
-    }
-
-    const int NumIntRegs = NUM_INTREGS;
+inline static constexpr RegId
+intRegFolded(RegIndex index, RegIndex foldBit)
+{
+    if ((index & 0x1C) == 4 && foldBit)
+        index = (index - 4) | foldBit;
+    return RegId(IntRegClass, index);
+}
 
 } // namespace X86ISA
 } // namespace gem5
diff --git a/src/arch/x86/regs/misc.hh b/src/arch/x86/regs/misc.hh
index 89997dc..f9c526b 100644
--- a/src/arch/x86/regs/misc.hh
+++ b/src/arch/x86/regs/misc.hh
@@ -51,1012 +51,1006 @@
 
 namespace gem5
 {
-
 namespace X86ISA
 {
-    enum CondFlagBit
-    {
-        CFBit = 1 << 0,
-        PFBit = 1 << 2,
-        ECFBit = 1 << 3,
-        AFBit = 1 << 4,
-        EZFBit = 1 << 5,
-        ZFBit = 1 << 6,
-        SFBit = 1 << 7,
-        DFBit = 1 << 10,
-        OFBit = 1 << 11
-    };
 
-    const uint32_t cfofMask = CFBit | OFBit;
-    const uint32_t ccFlagMask = PFBit | AFBit | ZFBit | SFBit;
+enum CondFlagBit
+{
+    CFBit = 1 << 0,
+    PFBit = 1 << 2,
+    ECFBit = 1 << 3,
+    AFBit = 1 << 4,
+    EZFBit = 1 << 5,
+    ZFBit = 1 << 6,
+    SFBit = 1 << 7,
+    DFBit = 1 << 10,
+    OFBit = 1 << 11
+};
 
-    enum RFLAGBit
-    {
-        TFBit = 1 << 8,
-        IFBit = 1 << 9,
-        NTBit = 1 << 14,
-        RFBit = 1 << 16,
-        VMBit = 1 << 17,
-        ACBit = 1 << 18,
-        VIFBit = 1 << 19,
-        VIPBit = 1 << 20,
-        IDBit = 1 << 21
-    };
+constexpr uint32_t CfofMask = CFBit | OFBit;
+constexpr uint32_t CcFlagMask = PFBit | AFBit | ZFBit | SFBit;
 
-    enum X87StatusBit
-    {
-        // Exception Flags
-        IEBit = 1 << 0,
-        DEBit = 1 << 1,
-        ZEBit = 1 << 2,
-        OEBit = 1 << 3,
-        UEBit = 1 << 4,
-        PEBit = 1 << 5,
+enum RFLAGBit
+{
+    TFBit = 1 << 8,
+    IFBit = 1 << 9,
+    NTBit = 1 << 14,
+    RFBit = 1 << 16,
+    VMBit = 1 << 17,
+    ACBit = 1 << 18,
+    VIFBit = 1 << 19,
+    VIPBit = 1 << 20,
+    IDBit = 1 << 21
+};
 
-        // !Exception Flags
-        StackFaultBit = 1 << 6,
-        ErrSummaryBit = 1 << 7,
-        CC0Bit = 1 << 8,
-        CC1Bit = 1 << 9,
-        CC2Bit = 1 << 10,
-        CC3Bit = 1 << 14,
-        BusyBit = 1 << 15,
-    };
+enum X87StatusBit
+{
+    // Exception Flags
+    IEBit = 1 << 0,
+    DEBit = 1 << 1,
+    ZEBit = 1 << 2,
+    OEBit = 1 << 3,
+    UEBit = 1 << 4,
+    PEBit = 1 << 5,
 
-    enum MiscRegIndex
-    {
-        // Control registers
-        // Most of these are invalid.  See isValidMiscReg() below.
-        MISCREG_CR_BASE,
-        MISCREG_CR0 = MISCREG_CR_BASE,
-        MISCREG_CR1,
-        MISCREG_CR2,
-        MISCREG_CR3,
-        MISCREG_CR4,
-        MISCREG_CR5,
-        MISCREG_CR6,
-        MISCREG_CR7,
-        MISCREG_CR8,
-        MISCREG_CR9,
-        MISCREG_CR10,
-        MISCREG_CR11,
-        MISCREG_CR12,
-        MISCREG_CR13,
-        MISCREG_CR14,
-        MISCREG_CR15,
+    // !Exception Flags
+    StackFaultBit = 1 << 6,
+    ErrSummaryBit = 1 << 7,
+    CC0Bit = 1 << 8,
+    CC1Bit = 1 << 9,
+    CC2Bit = 1 << 10,
+    CC3Bit = 1 << 14,
+    BusyBit = 1 << 15,
+};
 
-        // Debug registers
-        MISCREG_DR_BASE = MISCREG_CR_BASE + NumCRegs,
-        MISCREG_DR0 = MISCREG_DR_BASE,
-        MISCREG_DR1,
-        MISCREG_DR2,
-        MISCREG_DR3,
-        MISCREG_DR4,
-        MISCREG_DR5,
-        MISCREG_DR6,
-        MISCREG_DR7,
+namespace misc_reg
+{
 
-        // Flags register
-        MISCREG_RFLAGS = MISCREG_DR_BASE + NumDRegs,
+enum : RegIndex
+{
+    // Control registers
+    // Most of these are invalid.  See isValid() below.
+    CrBase,
+    Cr0 = CrBase,
+    Cr1,
+    Cr2,
+    Cr3,
+    Cr4,
+    Cr5,
+    Cr6,
+    Cr7,
+    Cr8,
+    Cr9,
+    Cr10,
+    Cr11,
+    Cr12,
+    Cr13,
+    Cr14,
+    Cr15,
 
-        //Register to keep handy values like the CPU mode in.
-        MISCREG_M5_REG,
+    // Debug registers
+    DrBase = CrBase + NumCRegs,
+    Dr0 = DrBase,
+    Dr1,
+    Dr2,
+    Dr3,
+    Dr4,
+    Dr5,
+    Dr6,
+    Dr7,
 
-        /*
-         * Model Specific Registers
-         */
-        // Time stamp counter
-        MISCREG_TSC,
+    // Flags register
+    Rflags = DrBase + NumDRegs,
 
-        MISCREG_MTRRCAP,
+    //Register to keep handy values like the CPU mode in.
+    M5Reg,
 
-        MISCREG_SYSENTER_CS,
-        MISCREG_SYSENTER_ESP,
-        MISCREG_SYSENTER_EIP,
-
-        MISCREG_MCG_CAP,
-        MISCREG_MCG_STATUS,
-        MISCREG_MCG_CTL,
-
-        MISCREG_DEBUG_CTL_MSR,
-
-        MISCREG_LAST_BRANCH_FROM_IP,
-        MISCREG_LAST_BRANCH_TO_IP,
-        MISCREG_LAST_EXCEPTION_FROM_IP,
-        MISCREG_LAST_EXCEPTION_TO_IP,
-
-        MISCREG_MTRR_PHYS_BASE_BASE,
-        MISCREG_MTRR_PHYS_BASE_0 = MISCREG_MTRR_PHYS_BASE_BASE,
-        MISCREG_MTRR_PHYS_BASE_1,
-        MISCREG_MTRR_PHYS_BASE_2,
-        MISCREG_MTRR_PHYS_BASE_3,
-        MISCREG_MTRR_PHYS_BASE_4,
-        MISCREG_MTRR_PHYS_BASE_5,
-        MISCREG_MTRR_PHYS_BASE_6,
-        MISCREG_MTRR_PHYS_BASE_7,
-        MISCREG_MTRR_PHYS_BASE_END,
-
-        MISCREG_MTRR_PHYS_MASK_BASE = MISCREG_MTRR_PHYS_BASE_END,
-        MISCREG_MTRR_PHYS_MASK_0 = MISCREG_MTRR_PHYS_MASK_BASE,
-        MISCREG_MTRR_PHYS_MASK_1,
-        MISCREG_MTRR_PHYS_MASK_2,
-        MISCREG_MTRR_PHYS_MASK_3,
-        MISCREG_MTRR_PHYS_MASK_4,
-        MISCREG_MTRR_PHYS_MASK_5,
-        MISCREG_MTRR_PHYS_MASK_6,
-        MISCREG_MTRR_PHYS_MASK_7,
-        MISCREG_MTRR_PHYS_MASK_END,
-
-        MISCREG_MTRR_FIX_64K_00000 = MISCREG_MTRR_PHYS_MASK_END,
-        MISCREG_MTRR_FIX_16K_80000,
-        MISCREG_MTRR_FIX_16K_A0000,
-        MISCREG_MTRR_FIX_4K_C0000,
-        MISCREG_MTRR_FIX_4K_C8000,
-        MISCREG_MTRR_FIX_4K_D0000,
-        MISCREG_MTRR_FIX_4K_D8000,
-        MISCREG_MTRR_FIX_4K_E0000,
-        MISCREG_MTRR_FIX_4K_E8000,
-        MISCREG_MTRR_FIX_4K_F0000,
-        MISCREG_MTRR_FIX_4K_F8000,
-
-        MISCREG_PAT,
-
-        MISCREG_DEF_TYPE,
-
-        MISCREG_MC_CTL_BASE,
-        MISCREG_MC0_CTL = MISCREG_MC_CTL_BASE,
-        MISCREG_MC1_CTL,
-        MISCREG_MC2_CTL,
-        MISCREG_MC3_CTL,
-        MISCREG_MC4_CTL,
-        MISCREG_MC5_CTL,
-        MISCREG_MC6_CTL,
-        MISCREG_MC7_CTL,
-        MISCREG_MC_CTL_END,
-
-        MISCREG_MC_STATUS_BASE = MISCREG_MC_CTL_END,
-        MISCREG_MC0_STATUS = MISCREG_MC_STATUS_BASE,
-        MISCREG_MC1_STATUS,
-        MISCREG_MC2_STATUS,
-        MISCREG_MC3_STATUS,
-        MISCREG_MC4_STATUS,
-        MISCREG_MC5_STATUS,
-        MISCREG_MC6_STATUS,
-        MISCREG_MC7_STATUS,
-        MISCREG_MC_STATUS_END,
-
-        MISCREG_MC_ADDR_BASE = MISCREG_MC_STATUS_END,
-        MISCREG_MC0_ADDR = MISCREG_MC_ADDR_BASE,
-        MISCREG_MC1_ADDR,
-        MISCREG_MC2_ADDR,
-        MISCREG_MC3_ADDR,
-        MISCREG_MC4_ADDR,
-        MISCREG_MC5_ADDR,
-        MISCREG_MC6_ADDR,
-        MISCREG_MC7_ADDR,
-        MISCREG_MC_ADDR_END,
-
-        MISCREG_MC_MISC_BASE = MISCREG_MC_ADDR_END,
-        MISCREG_MC0_MISC = MISCREG_MC_MISC_BASE,
-        MISCREG_MC1_MISC,
-        MISCREG_MC2_MISC,
-        MISCREG_MC3_MISC,
-        MISCREG_MC4_MISC,
-        MISCREG_MC5_MISC,
-        MISCREG_MC6_MISC,
-        MISCREG_MC7_MISC,
-        MISCREG_MC_MISC_END,
-
-        // Extended feature enable register
-        MISCREG_EFER = MISCREG_MC_MISC_END,
-
-        MISCREG_STAR,
-        MISCREG_LSTAR,
-        MISCREG_CSTAR,
-
-        MISCREG_SF_MASK,
-
-        MISCREG_KERNEL_GS_BASE,
-
-        MISCREG_TSC_AUX,
-
-        MISCREG_PERF_EVT_SEL_BASE,
-        MISCREG_PERF_EVT_SEL0 = MISCREG_PERF_EVT_SEL_BASE,
-        MISCREG_PERF_EVT_SEL1,
-        MISCREG_PERF_EVT_SEL2,
-        MISCREG_PERF_EVT_SEL3,
-        MISCREG_PERF_EVT_SEL_END,
-
-        MISCREG_PERF_EVT_CTR_BASE = MISCREG_PERF_EVT_SEL_END,
-        MISCREG_PERF_EVT_CTR0 = MISCREG_PERF_EVT_CTR_BASE,
-        MISCREG_PERF_EVT_CTR1,
-        MISCREG_PERF_EVT_CTR2,
-        MISCREG_PERF_EVT_CTR3,
-        MISCREG_PERF_EVT_CTR_END,
-
-        MISCREG_SYSCFG = MISCREG_PERF_EVT_CTR_END,
-
-        MISCREG_IORR_BASE_BASE,
-        MISCREG_IORR_BASE0 = MISCREG_IORR_BASE_BASE,
-        MISCREG_IORR_BASE1,
-        MISCREG_IORR_BASE_END,
-
-        MISCREG_IORR_MASK_BASE = MISCREG_IORR_BASE_END,
-        MISCREG_IORR_MASK0 = MISCREG_IORR_MASK_BASE,
-        MISCREG_IORR_MASK1,
-        MISCREG_IORR_MASK_END,
-
-        MISCREG_TOP_MEM = MISCREG_IORR_MASK_END,
-        MISCREG_TOP_MEM2,
-
-        MISCREG_VM_CR,
-        MISCREG_IGNNE,
-        MISCREG_SMM_CTL,
-        MISCREG_VM_HSAVE_PA,
-
-        /*
-         * Segment registers
-         */
-        // Segment selectors
-        MISCREG_SEG_SEL_BASE,
-        MISCREG_ES = MISCREG_SEG_SEL_BASE,
-        MISCREG_CS,
-        MISCREG_SS,
-        MISCREG_DS,
-        MISCREG_FS,
-        MISCREG_GS,
-        MISCREG_HS,
-        MISCREG_TSL,
-        MISCREG_TSG,
-        MISCREG_LS,
-        MISCREG_MS,
-        MISCREG_TR,
-        MISCREG_IDTR,
-
-        // Hidden segment base field
-        MISCREG_SEG_BASE_BASE = MISCREG_SEG_SEL_BASE + NUM_SEGMENTREGS,
-        MISCREG_ES_BASE = MISCREG_SEG_BASE_BASE,
-        MISCREG_CS_BASE,
-        MISCREG_SS_BASE,
-        MISCREG_DS_BASE,
-        MISCREG_FS_BASE,
-        MISCREG_GS_BASE,
-        MISCREG_HS_BASE,
-        MISCREG_TSL_BASE,
-        MISCREG_TSG_BASE,
-        MISCREG_LS_BASE,
-        MISCREG_MS_BASE,
-        MISCREG_TR_BASE,
-        MISCREG_IDTR_BASE,
-
-        // The effective segment base, ie what is actually added to an
-        // address. In 64 bit mode this can be different from the above,
-        // namely 0.
-        MISCREG_SEG_EFF_BASE_BASE = MISCREG_SEG_BASE_BASE + NUM_SEGMENTREGS,
-        MISCREG_ES_EFF_BASE = MISCREG_SEG_EFF_BASE_BASE,
-        MISCREG_CS_EFF_BASE,
-        MISCREG_SS_EFF_BASE,
-        MISCREG_DS_EFF_BASE,
-        MISCREG_FS_EFF_BASE,
-        MISCREG_GS_EFF_BASE,
-        MISCREG_HS_EFF_BASE,
-        MISCREG_TSL_EFF_BASE,
-        MISCREG_TSG_EFF_BASE,
-        MISCREG_LS_EFF_BASE,
-        MISCREG_MS_EFF_BASE,
-        MISCREG_TR_EFF_BASE,
-        MISCREG_IDTR_EFF_BASE,
-
-        // Hidden segment limit field
-        MISCREG_SEG_LIMIT_BASE = MISCREG_SEG_EFF_BASE_BASE + NUM_SEGMENTREGS,
-        MISCREG_ES_LIMIT = MISCREG_SEG_LIMIT_BASE,
-        MISCREG_CS_LIMIT,
-        MISCREG_SS_LIMIT,
-        MISCREG_DS_LIMIT,
-        MISCREG_FS_LIMIT,
-        MISCREG_GS_LIMIT,
-        MISCREG_HS_LIMIT,
-        MISCREG_TSL_LIMIT,
-        MISCREG_TSG_LIMIT,
-        MISCREG_LS_LIMIT,
-        MISCREG_MS_LIMIT,
-        MISCREG_TR_LIMIT,
-        MISCREG_IDTR_LIMIT,
-
-        // Hidden segment limit attributes
-        MISCREG_SEG_ATTR_BASE = MISCREG_SEG_LIMIT_BASE + NUM_SEGMENTREGS,
-        MISCREG_ES_ATTR = MISCREG_SEG_ATTR_BASE,
-        MISCREG_CS_ATTR,
-        MISCREG_SS_ATTR,
-        MISCREG_DS_ATTR,
-        MISCREG_FS_ATTR,
-        MISCREG_GS_ATTR,
-        MISCREG_HS_ATTR,
-        MISCREG_TSL_ATTR,
-        MISCREG_TSG_ATTR,
-        MISCREG_LS_ATTR,
-        MISCREG_MS_ATTR,
-        MISCREG_TR_ATTR,
-        MISCREG_IDTR_ATTR,
-
-        // Floating point control registers
-        MISCREG_X87_TOP =
-            MISCREG_SEG_ATTR_BASE + NUM_SEGMENTREGS,
-
-        MISCREG_MXCSR,
-        MISCREG_FCW,
-        MISCREG_FSW,
-        MISCREG_FTW,
-        MISCREG_FTAG,
-        MISCREG_FISEG,
-        MISCREG_FIOFF,
-        MISCREG_FOSEG,
-        MISCREG_FOOFF,
-        MISCREG_FOP,
-
-        //XXX Add "Model-Specific Registers"
-
-        MISCREG_APIC_BASE,
-
-        // "Fake" MSRs for internally implemented devices
-        MISCREG_PCI_CONFIG_ADDRESS,
-
-        NUM_MISCREGS
-    };
-
-    static inline bool
-    isValidMiscReg(int index)
-    {
-        return (index >= MISCREG_CR0 && index < NUM_MISCREGS &&
-                index != MISCREG_CR1 &&
-                !(index > MISCREG_CR4 && index < MISCREG_CR8) &&
-                !(index > MISCREG_CR8 && index <= MISCREG_CR15));
-    }
-
-    static inline MiscRegIndex
-    MISCREG_CR(int index)
-    {
-        assert(index >= 0 && index < NumCRegs);
-        return (MiscRegIndex)(MISCREG_CR_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_DR(int index)
-    {
-        assert(index >= 0 && index < NumDRegs);
-        return (MiscRegIndex)(MISCREG_DR_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_MTRR_PHYS_BASE(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_MTRR_PHYS_BASE_END -
-                                      MISCREG_MTRR_PHYS_BASE_BASE));
-        return (MiscRegIndex)(MISCREG_MTRR_PHYS_BASE_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_MTRR_PHYS_MASK(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_MTRR_PHYS_MASK_END -
-                                      MISCREG_MTRR_PHYS_MASK_BASE));
-        return (MiscRegIndex)(MISCREG_MTRR_PHYS_MASK_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_MC_CTL(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_MC_CTL_END -
-                                      MISCREG_MC_CTL_BASE));
-        return (MiscRegIndex)(MISCREG_MC_CTL_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_MC_STATUS(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_MC_STATUS_END -
-                                      MISCREG_MC_STATUS_BASE));
-        return (MiscRegIndex)(MISCREG_MC_STATUS_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_MC_ADDR(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_MC_ADDR_END -
-                                      MISCREG_MC_ADDR_BASE));
-        return (MiscRegIndex)(MISCREG_MC_ADDR_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_MC_MISC(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_MC_MISC_END -
-                                      MISCREG_MC_MISC_BASE));
-        return (MiscRegIndex)(MISCREG_MC_MISC_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_PERF_EVT_SEL(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_PERF_EVT_SEL_END -
-                                      MISCREG_PERF_EVT_SEL_BASE));
-        return (MiscRegIndex)(MISCREG_PERF_EVT_SEL_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_PERF_EVT_CTR(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_PERF_EVT_CTR_END -
-                                      MISCREG_PERF_EVT_CTR_BASE));
-        return (MiscRegIndex)(MISCREG_PERF_EVT_CTR_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_IORR_BASE(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_IORR_BASE_END -
-                                      MISCREG_IORR_BASE_BASE));
-        return (MiscRegIndex)(MISCREG_IORR_BASE_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_IORR_MASK(int index)
-    {
-        assert(index >= 0 && index < (MISCREG_IORR_MASK_END -
-                                      MISCREG_IORR_MASK_BASE));
-        return (MiscRegIndex)(MISCREG_IORR_MASK_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_SEG_SEL(int index)
-    {
-        assert(index >= 0 && index < NUM_SEGMENTREGS);
-        return (MiscRegIndex)(MISCREG_SEG_SEL_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_SEG_BASE(int index)
-    {
-        assert(index >= 0 && index < NUM_SEGMENTREGS);
-        return (MiscRegIndex)(MISCREG_SEG_BASE_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_SEG_EFF_BASE(int index)
-    {
-        assert(index >= 0 && index < NUM_SEGMENTREGS);
-        return (MiscRegIndex)(MISCREG_SEG_EFF_BASE_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_SEG_LIMIT(int index)
-    {
-        assert(index >= 0 && index < NUM_SEGMENTREGS);
-        return (MiscRegIndex)(MISCREG_SEG_LIMIT_BASE + index);
-    }
-
-    static inline MiscRegIndex
-    MISCREG_SEG_ATTR(int index)
-    {
-        assert(index >= 0 && index < NUM_SEGMENTREGS);
-        return (MiscRegIndex)(MISCREG_SEG_ATTR_BASE + index);
-    }
-
-    /**
-     * A type to describe the condition code bits of the RFLAGS register,
-     * plus two flags, EZF and ECF, which are only visible to microcode.
+    /*
+     * Model Specific Registers
      */
-    BitUnion64(CCFlagBits)
-        Bitfield<11> of;
-        Bitfield<7> sf;
-        Bitfield<6> zf;
-        Bitfield<5> ezf;
-        Bitfield<4> af;
-        Bitfield<3> ecf;
-        Bitfield<2> pf;
-        Bitfield<0> cf;
-    EndBitUnion(CCFlagBits)
+    // Time stamp counter
+    Tsc,
 
-    /**
-     * RFLAGS
-     */
-    BitUnion64(RFLAGS)
-        Bitfield<21> id; // ID Flag
-        Bitfield<20> vip; // Virtual Interrupt Pending
-        Bitfield<19> vif; // Virtual Interrupt Flag
-        Bitfield<18> ac; // Alignment Check
-        Bitfield<17> vm; // Virtual-8086 Mode
-        Bitfield<16> rf; // Resume Flag
-        Bitfield<14> nt; // Nested Task
-        Bitfield<13, 12> iopl; // I/O Privilege Level
-        Bitfield<11> of; // Overflow Flag
-        Bitfield<10> df; // Direction Flag
-        Bitfield<9> intf; // Interrupt Flag
-        Bitfield<8> tf; // Trap Flag
-        Bitfield<7> sf; // Sign Flag
-        Bitfield<6> zf; // Zero Flag
-        Bitfield<4> af; // Auxiliary Flag
-        Bitfield<2> pf; // Parity Flag
-        Bitfield<0> cf; // Carry Flag
-    EndBitUnion(RFLAGS)
+    Mtrrcap,
 
-    BitUnion64(HandyM5Reg)
-        Bitfield<0> mode;
-        Bitfield<3, 1> submode;
-        Bitfield<5, 4> cpl;
-        Bitfield<6> paging;
-        Bitfield<7> prot;
-        Bitfield<9, 8> defOp;
-        Bitfield<11, 10> altOp;
-        Bitfield<13, 12> defAddr;
-        Bitfield<15, 14> altAddr;
-        Bitfield<17, 16> stack;
-    EndBitUnion(HandyM5Reg)
+    SysenterCs,
+    SysenterEsp,
+    SysenterEip,
 
-    /**
-     * Control registers
-     */
-    BitUnion64(CR0)
-        Bitfield<31> pg; // Paging
-        Bitfield<30> cd; // Cache Disable
-        Bitfield<29> nw; // Not Writethrough
-        Bitfield<18> am; // Alignment Mask
-        Bitfield<16> wp; // Write Protect
-        Bitfield<5> ne; // Numeric Error
-        Bitfield<4> et; // Extension Type
-        Bitfield<3> ts; // Task Switched
-        Bitfield<2> em; // Emulation
-        Bitfield<1> mp; // Monitor Coprocessor
-        Bitfield<0> pe; // Protection Enabled
-    EndBitUnion(CR0)
+    McgCap,
+    McgStatus,
+    McgCtl,
 
-    // Page Fault Virtual Address
-    BitUnion64(CR2)
-        Bitfield<31, 0> legacy;
-    EndBitUnion(CR2)
+    DebugCtlMsr,
 
-    BitUnion64(CR3)
-        Bitfield<51, 12> longPdtb; // Long Mode Page-Directory-Table
-                                   // Base Address
-        Bitfield<31, 12> pdtb; // Non-PAE Addressing Page-Directory-Table
-                               // Base Address
-        Bitfield<31, 5> paePdtb; // PAE Addressing Page-Directory-Table
-                                 // Base Address
-        Bitfield<4> pcd; // Page-Level Cache Disable
-        Bitfield<3> pwt; // Page-Level Writethrough
-    EndBitUnion(CR3)
+    LastBranchFromIp,
+    LastBranchToIp,
+    LastExceptionFromIp,
+    LastExceptionToIp,
 
-    BitUnion64(CR4)
-        Bitfield<18> osxsave; // Enable XSAVE and Proc Extended States
-        Bitfield<16> fsgsbase; // Enable RDFSBASE, RDGSBASE, WRFSBASE,
-                               // WRGSBASE instructions
-        Bitfield<10> osxmmexcpt; // Operating System Unmasked
-                                 // Exception Support
-        Bitfield<9> osfxsr; // Operating System FXSave/FSRSTOR Support
-        Bitfield<8> pce; // Performance-Monitoring Counter Enable
-        Bitfield<7> pge; // Page-Global Enable
-        Bitfield<6> mce; // Machine Check Enable
-        Bitfield<5> pae; // Physical-Address Extension
-        Bitfield<4> pse; // Page Size Extensions
-        Bitfield<3> de; // Debugging Extensions
-        Bitfield<2> tsd; // Time Stamp Disable
-        Bitfield<1> pvi; // Protected-Mode Virtual Interrupts
-        Bitfield<0> vme; // Virtual-8086 Mode Extensions
-    EndBitUnion(CR4)
+    MtrrPhysBaseBase,
+    MtrrPhysBase0 = MtrrPhysBaseBase,
+    MtrrPhysBase1,
+    MtrrPhysBase2,
+    MtrrPhysBase3,
+    MtrrPhysBase4,
+    MtrrPhysBase5,
+    MtrrPhysBase6,
+    MtrrPhysBase7,
+    MtrrPhysBaseEnd,
 
-    BitUnion64(CR8)
-        Bitfield<3, 0> tpr; // Task Priority Register
-    EndBitUnion(CR8)
+    MtrrPhysMaskBase = MtrrPhysBaseEnd,
+    MtrrPhysMask0 = MtrrPhysMaskBase,
+    MtrrPhysMask1,
+    MtrrPhysMask2,
+    MtrrPhysMask3,
+    MtrrPhysMask4,
+    MtrrPhysMask5,
+    MtrrPhysMask6,
+    MtrrPhysMask7,
+    MtrrPhysMaskEnd,
 
-    BitUnion64(DR6)
-        Bitfield<0> b0;
-        Bitfield<1> b1;
-        Bitfield<2> b2;
-        Bitfield<3> b3;
-        Bitfield<13> bd;
-        Bitfield<14> bs;
-        Bitfield<15> bt;
-    EndBitUnion(DR6)
+    MtrrFix64k00000 = MtrrPhysMaskEnd,
+    MtrrFix16k80000,
+    MtrrFix16kA0000,
+    MtrrFix4kC0000,
+    MtrrFix4kC8000,
+    MtrrFix4kD0000,
+    MtrrFix4kD8000,
+    MtrrFix4kE0000,
+    MtrrFix4kE8000,
+    MtrrFix4kF0000,
+    MtrrFix4kF8000,
 
-    BitUnion64(DR7)
-        Bitfield<0> l0;
-        Bitfield<1> g0;
-        Bitfield<2> l1;
-        Bitfield<3> g1;
-        Bitfield<4> l2;
-        Bitfield<5> g2;
-        Bitfield<6> l3;
-        Bitfield<7> g3;
-        Bitfield<8> le;
-        Bitfield<9> ge;
-        Bitfield<13> gd;
-        Bitfield<17, 16> rw0;
-        Bitfield<19, 18> len0;
-        Bitfield<21, 20> rw1;
-        Bitfield<23, 22> len1;
-        Bitfield<25, 24> rw2;
-        Bitfield<27, 26> len2;
-        Bitfield<29, 28> rw3;
-        Bitfield<31, 30> len3;
-    EndBitUnion(DR7)
+    Pat,
 
-    // MTRR capabilities
-    BitUnion64(MTRRcap)
-        Bitfield<7, 0> vcnt; // Variable-Range Register Count
-        Bitfield<8> fix; // Fixed-Range Registers
-        Bitfield<10> wc; // Write-Combining
-    EndBitUnion(MTRRcap)
+    DefType,
 
-    /**
-     * SYSENTER configuration registers
-     */
-    BitUnion64(SysenterCS)
-        Bitfield<15, 0> targetCS;
-    EndBitUnion(SysenterCS)
+    McCtlBase,
+    Mc0Ctl = McCtlBase,
+    Mc1Ctl,
+    Mc2Ctl,
+    Mc3Ctl,
+    Mc4Ctl,
+    Mc5Ctl,
+    Mc6Ctl,
+    Mc7Ctl,
+    McCtlEnd,
 
-    BitUnion64(SysenterESP)
-        Bitfield<31, 0> targetESP;
-    EndBitUnion(SysenterESP)
+    McStatusBase = McCtlEnd,
+    Mc0Status = McStatusBase,
+    Mc1Status,
+    Mc2Status,
+    Mc3Status,
+    Mc4Status,
+    Mc5Status,
+    Mc6Status,
+    Mc7Status,
+    McStatusEnd,
 
-    BitUnion64(SysenterEIP)
-        Bitfield<31, 0> targetEIP;
-    EndBitUnion(SysenterEIP)
+    McAddrBase = McStatusEnd,
+    Mc0Addr = McAddrBase,
+    Mc1Addr,
+    Mc2Addr,
+    Mc3Addr,
+    Mc4Addr,
+    Mc5Addr,
+    Mc6Addr,
+    Mc7Addr,
+    McAddrEnd,
 
-    /**
-     * Global machine check registers
-     */
-    BitUnion64(McgCap)
-        Bitfield<7, 0> count; // Number of error reporting register banks
-        Bitfield<8> MCGCP; // MCG_CTL register present.
-    EndBitUnion(McgCap)
-
-    BitUnion64(McgStatus)
-        Bitfield<0> ripv; // Restart-IP valid
-        Bitfield<1> eipv; // Error-IP valid
-        Bitfield<2> mcip; // Machine check in-progress
-    EndBitUnion(McgStatus)
-
-    BitUnion64(DebugCtlMsr)
-        Bitfield<0> lbr; // Last-branch record
-        Bitfield<1> btf; // Branch single step
-        Bitfield<2> pb0; // Performance monitoring pin control 0
-        Bitfield<3> pb1; // Performance monitoring pin control 1
-        Bitfield<4> pb2; // Performance monitoring pin control 2
-        Bitfield<5> pb3; // Performance monitoring pin control 3
-        /*uint64_t pb(int index)
-        {
-            return bits(__data, index + 2);
-        }*/
-    EndBitUnion(DebugCtlMsr)
-
-    BitUnion64(MtrrPhysBase)
-        Bitfield<7, 0> type; // Default memory type
-        Bitfield<51, 12> physbase; // Range physical base address
-    EndBitUnion(MtrrPhysBase)
-
-    BitUnion64(MtrrPhysMask)
-        Bitfield<11> valid; // MTRR pair enable
-        Bitfield<51, 12> physmask; // Range physical mask
-    EndBitUnion(MtrrPhysMask)
-
-    BitUnion64(MtrrFixed)
-        /*uint64_t type(int index)
-        {
-            return bits(__data, index * 8 + 7, index * 8);
-        }*/
-    EndBitUnion(MtrrFixed)
-
-    BitUnion64(Pat)
-        /*uint64_t pa(int index)
-        {
-            return bits(__data, index * 8 + 2, index * 8);
-        }*/
-    EndBitUnion(Pat)
-
-    BitUnion64(MtrrDefType)
-        Bitfield<7, 0> type; // Default type
-        Bitfield<10> fe; // Fixed range enable
-        Bitfield<11> e; // MTRR enable
-    EndBitUnion(MtrrDefType)
-
-    /**
-     * Machine check
-     */
-    BitUnion64(McStatus)
-        Bitfield<15,0> mcaErrorCode;
-        Bitfield<31,16> modelSpecificCode;
-        Bitfield<56,32> otherInfo;
-        Bitfield<57> pcc; // Processor-context corrupt
-        Bitfield<58> addrv; // Error-address register valid
-        Bitfield<59> miscv; // Miscellaneous-error register valid
-        Bitfield<60> en; // Error condition enabled
-        Bitfield<61> uc; // Uncorrected error
-        Bitfield<62> over; // Status register overflow
-        Bitfield<63> val; // Valid
-    EndBitUnion(McStatus)
-
-    BitUnion64(McCtl)
-        /*uint64_t en(int index)
-        {
-            return bits(__data, index);
-        }*/
-    EndBitUnion(McCtl)
+    McMiscBase = McAddrEnd,
+    Mc0Misc = McMiscBase,
+    Mc1Misc,
+    Mc2Misc,
+    Mc3Misc,
+    Mc4Misc,
+    Mc5Misc,
+    Mc6Misc,
+    Mc7Misc,
+    McMiscEnd,
 
     // Extended feature enable register
-    BitUnion64(Efer)
-        Bitfield<0> sce; // System call extensions
-        Bitfield<8> lme; // Long mode enable
-        Bitfield<10> lma; // Long mode active
-        Bitfield<11> nxe; // No-execute enable
-        Bitfield<12> svme; // Secure virtual machine enable
-        Bitfield<14> ffxsr; // Fast fxsave/fxrstor
-    EndBitUnion(Efer)
+    Efer = McMiscEnd,
 
-    BitUnion64(Star)
-        Bitfield<31,0> targetEip;
-        Bitfield<47,32> syscallCsAndSs;
-        Bitfield<63,48> sysretCsAndSs;
-    EndBitUnion(Star)
+    Star,
+    Lstar,
+    Cstar,
 
-    BitUnion64(SfMask)
-        Bitfield<31,0> mask;
-    EndBitUnion(SfMask)
+    SfMask,
 
-    BitUnion64(PerfEvtSel)
-        Bitfield<7,0> eventMask;
-        Bitfield<15,8> unitMask;
-        Bitfield<16> usr; // User mode
-        Bitfield<17> os; // Operating-system mode
-        Bitfield<18> e; // Edge detect
-        Bitfield<19> pc; // Pin control
-        Bitfield<20> intEn; // Interrupt enable
-        Bitfield<22> en; // Counter enable
-        Bitfield<23> inv; // Invert mask
-        Bitfield<31,24> counterMask;
-    EndBitUnion(PerfEvtSel)
+    KernelGsBase,
 
-    BitUnion32(Syscfg)
-        Bitfield<18> mfde; // MtrrFixDramEn
-        Bitfield<19> mfdm; // MtrrFixDramModEn
-        Bitfield<20> mvdm; // MtrrVarDramEn
-        Bitfield<21> tom2; // MtrrTom2En
-    EndBitUnion(Syscfg)
+    TscAux,
 
-    BitUnion64(IorrBase)
-        Bitfield<3> wr; // WrMem Enable
-        Bitfield<4> rd; // RdMem Enable
-        Bitfield<51,12> physbase; // Range physical base address
-    EndBitUnion(IorrBase)
+    PerfEvtSelBase,
+    PerfEvtSel0 = PerfEvtSelBase,
+    PerfEvtSel1,
+    PerfEvtSel2,
+    PerfEvtSel3,
+    PerfEvtSelEnd,
 
-    BitUnion64(IorrMask)
-        Bitfield<11> v; // I/O register pair enable (valid)
-        Bitfield<51,12> physmask; // Range physical mask
-    EndBitUnion(IorrMask)
+    PerfEvtCtrBase = PerfEvtSelEnd,
+    PerfEvtCtr0 = PerfEvtCtrBase,
+    PerfEvtCtr1,
+    PerfEvtCtr2,
+    PerfEvtCtr3,
+    PerfEvtCtrEnd,
 
-    BitUnion64(Tom)
-        Bitfield<51,23> physAddr; // Top of memory physical address
-    EndBitUnion(Tom)
+    Syscfg = PerfEvtCtrEnd,
 
-    BitUnion64(VmCrMsr)
-        Bitfield<0> dpd;
-        Bitfield<1> rInit;
-        Bitfield<2> disA20M;
-    EndBitUnion(VmCrMsr)
+    IorrBaseBase,
+    IorrBase0 = IorrBaseBase,
+    IorrBase1,
+    IorrBaseEnd,
 
-    BitUnion64(IgnneMsr)
-        Bitfield<0> ignne;
-    EndBitUnion(IgnneMsr)
+    IorrMaskBase = IorrBaseEnd,
+    IorrMask0 = IorrMaskBase,
+    IorrMask1,
+    IorrMaskEnd,
 
-    BitUnion64(SmmCtlMsr)
-        Bitfield<0> dismiss;
-        Bitfield<1> enter;
-        Bitfield<2> smiCycle;
-        Bitfield<3> exit;
-        Bitfield<4> rsmCycle;
-    EndBitUnion(SmmCtlMsr)
+    TopMem = IorrMaskEnd,
+    TopMem2,
 
-    /**
-     * Segment Selector
+    VmCr,
+    Ignne,
+    SmmCtl,
+    VmHsavePa,
+
+    /*
+     * Segment registers
      */
-    BitUnion64(SegSelector)
-        // The following bitfield is not defined in the ISA, but it's useful
-        // when checking selectors in larger data types to make sure they
-        // aren't too large.
-        Bitfield<63, 3> esi; // Extended selector
-        Bitfield<15, 3> si; // Selector Index
-        Bitfield<2> ti; // Table Indicator
-        Bitfield<1, 0> rpl; // Requestor Privilege Level
-    EndBitUnion(SegSelector)
+    // Segment selectors
+    SegSelBase,
+    Es = SegSelBase,
+    Cs,
+    Ss,
+    Ds,
+    Fs,
+    Gs,
+    Hs,
+    Tsl,
+    Tsg,
+    Ls,
+    Ms,
+    Tr,
+    Idtr,
 
-    /**
-     * Segment Descriptors
-     */
+    // Hidden segment base field
+    SegBaseBase = SegSelBase + segment_idx::NumIdxs,
+    EsBase = SegBaseBase,
+    CsBase,
+    SsBase,
+    DsBase,
+    FsBase,
+    GsBase,
+    HsBase,
+    TslBase,
+    TsgBase,
+    LsBase,
+    MsBase,
+    TrBase,
+    IdtrBase,
 
-    class SegDescriptorBase
+    // The effective segment base, ie what is actually added to an
+    // address. In 64 bit mode this can be different from the above,
+    // namely 0.
+    SegEffBaseBase = SegBaseBase + segment_idx::NumIdxs,
+    EsEffBase = SegEffBaseBase,
+    CsEffBase,
+    SsEffBase,
+    DsEffBase,
+    FsEffBase,
+    GsEffBase,
+    HsEffBase,
+    TslEffBase,
+    TsgEffBase,
+    LsEffBase,
+    MsEffBase,
+    TrEffBase,
+    IdtrEffBase,
+
+    // Hidden segment limit field
+    SegLimitBase = SegEffBaseBase + segment_idx::NumIdxs,
+    EsLimit = SegLimitBase,
+    CsLimit,
+    SsLimit,
+    DsLimit,
+    FsLimit,
+    GsLimit,
+    HsLimit,
+    TslLimit,
+    TsgLimit,
+    LsLimit,
+    MsLimit,
+    TrLimit,
+    IdtrLimit,
+
+    // Hidden segment limit attributes
+    SegAttrBase = SegLimitBase + segment_idx::NumIdxs,
+    EsAttr = SegAttrBase,
+    CsAttr,
+    SsAttr,
+    DsAttr,
+    FsAttr,
+    GsAttr,
+    HsAttr,
+    TslAttr,
+    TsgAttr,
+    LsAttr,
+    MsAttr,
+    TrAttr,
+    IdtrAttr,
+
+    // Floating point control registers
+    X87Top = SegAttrBase + segment_idx::NumIdxs,
+
+    Mxcsr,
+    Fcw,
+    Fsw,
+    Ftw,
+    Ftag,
+    Fiseg,
+    Fioff,
+    Foseg,
+    Fooff,
+    Fop,
+
+    //XXX Add "Model-Specific Registers"
+
+    ApicBase,
+
+    // "Fake" MSRs for internally implemented devices
+    PciConfigAddress,
+
+    NumRegs
+};
+
+static inline bool
+isValid(int index)
+{
+    return (index >= Cr0 && index < NumRegs &&
+            index != Cr1 &&
+            !(index > Cr4 && index < Cr8) &&
+            !(index > Cr8 && index <= Cr15));
+}
+
+static inline RegIndex
+cr(int index)
+{
+    assert(index >= 0 && index < NumCRegs);
+    return CrBase + index;
+}
+
+static inline RegIndex
+dr(int index)
+{
+    assert(index >= 0 && index < NumDRegs);
+    return DrBase + index;
+}
+
+static inline RegIndex
+mtrrPhysBase(int index)
+{
+    assert(index >= 0 && index < (MtrrPhysBaseEnd - MtrrPhysBaseBase));
+    return MtrrPhysBaseBase + index;
+}
+
+static inline RegIndex
+mtrrPhysMask(int index)
+{
+    assert(index >= 0 && index < (MtrrPhysMaskEnd - MtrrPhysMaskBase));
+    return MtrrPhysMaskBase + index;
+}
+
+static inline RegIndex
+mcCtl(int index)
+{
+    assert(index >= 0 && index < (McCtlEnd - McCtlBase));
+    return McCtlBase + index;
+}
+
+static inline RegIndex
+mcStatus(int index)
+{
+    assert(index >= 0 && index < (McStatusEnd - McStatusBase));
+    return McStatusBase + index;
+}
+
+static inline RegIndex
+mcAddr(int index)
+{
+    assert(index >= 0 && index < (McAddrEnd - McAddrBase));
+    return McAddrBase + index;
+}
+
+static inline RegIndex
+mcMisc(int index)
+{
+    assert(index >= 0 && index < (McMiscEnd - McMiscBase));
+    return McMiscBase + index;
+}
+
+static inline RegIndex
+perfEvtSel(int index)
+{
+    assert(index >= 0 && index < (PerfEvtSelEnd - PerfEvtSelBase));
+    return PerfEvtSelBase + index;
+}
+
+static inline RegIndex
+perfEvtCtr(int index)
+{
+    assert(index >= 0 && index < (PerfEvtCtrEnd - PerfEvtCtrBase));
+    return PerfEvtCtrBase + index;
+}
+
+static inline RegIndex
+iorrBase(int index)
+{
+    assert(index >= 0 && index < (IorrBaseEnd - IorrBaseBase));
+    return IorrBaseBase + index;
+}
+
+static inline RegIndex
+iorrMask(int index)
+{
+    assert(index >= 0 && index < (IorrMaskEnd - IorrMaskBase));
+    return IorrMaskBase + index;
+}
+
+static inline RegIndex
+segSel(int index)
+{
+    assert(index >= 0 && index < segment_idx::NumIdxs);
+    return SegSelBase + index;
+}
+
+static inline RegIndex
+segBase(int index)
+{
+    assert(index >= 0 && index < segment_idx::NumIdxs);
+    return SegBaseBase + index;
+}
+
+static inline RegIndex
+segEffBase(int index)
+{
+    assert(index >= 0 && index < segment_idx::NumIdxs);
+    return SegEffBaseBase + index;
+}
+
+static inline RegIndex
+segLimit(int index)
+{
+    assert(index >= 0 && index < segment_idx::NumIdxs);
+    return SegLimitBase + index;
+}
+
+static inline RegIndex
+segAttr(int index)
+{
+    assert(index >= 0 && index < segment_idx::NumIdxs);
+    return SegAttrBase + index;
+}
+
+} // namespace misc_reg
+
+/**
+ * A type to describe the condition code bits of the RFLAGS register,
+ * plus two flags, EZF and ECF, which are only visible to microcode.
+ */
+BitUnion64(CCFlagBits)
+    Bitfield<11> of;
+    Bitfield<7> sf;
+    Bitfield<6> zf;
+    Bitfield<5> ezf;
+    Bitfield<4> af;
+    Bitfield<3> ecf;
+    Bitfield<2> pf;
+    Bitfield<0> cf;
+EndBitUnion(CCFlagBits)
+
+/**
+ * RFLAGS
+ */
+BitUnion64(RFLAGS)
+    Bitfield<21> id; // ID Flag
+    Bitfield<20> vip; // Virtual Interrupt Pending
+    Bitfield<19> vif; // Virtual Interrupt Flag
+    Bitfield<18> ac; // Alignment Check
+    Bitfield<17> vm; // Virtual-8086 Mode
+    Bitfield<16> rf; // Resume Flag
+    Bitfield<14> nt; // Nested Task
+    Bitfield<13, 12> iopl; // I/O Privilege Level
+    Bitfield<11> of; // Overflow Flag
+    Bitfield<10> df; // Direction Flag
+    Bitfield<9> intf; // Interrupt Flag
+    Bitfield<8> tf; // Trap Flag
+    Bitfield<7> sf; // Sign Flag
+    Bitfield<6> zf; // Zero Flag
+    Bitfield<4> af; // Auxiliary Flag
+    Bitfield<2> pf; // Parity Flag
+    Bitfield<0> cf; // Carry Flag
+EndBitUnion(RFLAGS)
+
+BitUnion64(HandyM5Reg)
+    Bitfield<0> mode;
+    Bitfield<3, 1> submode;
+    Bitfield<5, 4> cpl;
+    Bitfield<6> paging;
+    Bitfield<7> prot;
+    Bitfield<9, 8> defOp;
+    Bitfield<11, 10> altOp;
+    Bitfield<13, 12> defAddr;
+    Bitfield<15, 14> altAddr;
+    Bitfield<17, 16> stack;
+EndBitUnion(HandyM5Reg)
+
+/**
+ * Control registers
+ */
+BitUnion64(CR0)
+    Bitfield<31> pg; // Paging
+    Bitfield<30> cd; // Cache Disable
+    Bitfield<29> nw; // Not Writethrough
+    Bitfield<18> am; // Alignment Mask
+    Bitfield<16> wp; // Write Protect
+    Bitfield<5> ne; // Numeric Error
+    Bitfield<4> et; // Extension Type
+    Bitfield<3> ts; // Task Switched
+    Bitfield<2> em; // Emulation
+    Bitfield<1> mp; // Monitor Coprocessor
+    Bitfield<0> pe; // Protection Enabled
+EndBitUnion(CR0)
+
+// Page Fault Virtual Address
+BitUnion64(CR2)
+    Bitfield<31, 0> legacy;
+EndBitUnion(CR2)
+
+BitUnion64(CR3)
+    Bitfield<51, 12> longPdtb; // Long Mode Page-Directory-Table
+                               // Base Address
+    Bitfield<31, 12> pdtb; // Non-PAE Addressing Page-Directory-Table
+                           // Base Address
+    Bitfield<31, 5> paePdtb; // PAE Addressing Page-Directory-Table
+                             // Base Address
+    Bitfield<4> pcd; // Page-Level Cache Disable
+    Bitfield<3> pwt; // Page-Level Writethrough
+EndBitUnion(CR3)
+
+BitUnion64(CR4)
+    Bitfield<18> osxsave; // Enable XSAVE and Proc Extended States
+    Bitfield<16> fsgsbase; // Enable RDFSBASE, RDGSBASE, WRFSBASE,
+                           // WRGSBASE instructions
+    Bitfield<10> osxmmexcpt; // Operating System Unmasked
+                             // Exception Support
+    Bitfield<9> osfxsr; // Operating System FXSave/FSRSTOR Support
+    Bitfield<8> pce; // Performance-Monitoring Counter Enable
+    Bitfield<7> pge; // Page-Global Enable
+    Bitfield<6> mce; // Machine Check Enable
+    Bitfield<5> pae; // Physical-Address Extension
+    Bitfield<4> pse; // Page Size Extensions
+    Bitfield<3> de; // Debugging Extensions
+    Bitfield<2> tsd; // Time Stamp Disable
+    Bitfield<1> pvi; // Protected-Mode Virtual Interrupts
+    Bitfield<0> vme; // Virtual-8086 Mode Extensions
+EndBitUnion(CR4)
+
+BitUnion64(CR8)
+    Bitfield<3, 0> tpr; // Task Priority Register
+EndBitUnion(CR8)
+
+BitUnion64(DR6)
+    Bitfield<0> b0;
+    Bitfield<1> b1;
+    Bitfield<2> b2;
+    Bitfield<3> b3;
+    Bitfield<13> bd;
+    Bitfield<14> bs;
+    Bitfield<15> bt;
+EndBitUnion(DR6)
+
+BitUnion64(DR7)
+    Bitfield<0> l0;
+    Bitfield<1> g0;
+    Bitfield<2> l1;
+    Bitfield<3> g1;
+    Bitfield<4> l2;
+    Bitfield<5> g2;
+    Bitfield<6> l3;
+    Bitfield<7> g3;
+    Bitfield<8> le;
+    Bitfield<9> ge;
+    Bitfield<13> gd;
+    Bitfield<17, 16> rw0;
+    Bitfield<19, 18> len0;
+    Bitfield<21, 20> rw1;
+    Bitfield<23, 22> len1;
+    Bitfield<25, 24> rw2;
+    Bitfield<27, 26> len2;
+    Bitfield<29, 28> rw3;
+    Bitfield<31, 30> len3;
+EndBitUnion(DR7)
+
+// MTRR capabilities
+BitUnion64(MTRRcap)
+    Bitfield<7, 0> vcnt; // Variable-Range Register Count
+    Bitfield<8> fix; // Fixed-Range Registers
+    Bitfield<10> wc; // Write-Combining
+EndBitUnion(MTRRcap)
+
+/**
+ * SYSENTER configuration registers
+ */
+BitUnion64(SysenterCS)
+    Bitfield<15, 0> targetCS;
+EndBitUnion(SysenterCS)
+
+BitUnion64(SysenterESP)
+    Bitfield<31, 0> targetESP;
+EndBitUnion(SysenterESP)
+
+BitUnion64(SysenterEIP)
+    Bitfield<31, 0> targetEIP;
+EndBitUnion(SysenterEIP)
+
+/**
+ * Global machine check registers
+ */
+BitUnion64(McgCap)
+    Bitfield<7, 0> count; // Number of error reporting register banks
+    Bitfield<8> MCGCP; // MCG_CTL register present.
+EndBitUnion(McgCap)
+
+BitUnion64(McgStatus)
+    Bitfield<0> ripv; // Restart-IP valid
+    Bitfield<1> eipv; // Error-IP valid
+    Bitfield<2> mcip; // Machine check in-progress
+EndBitUnion(McgStatus)
+
+BitUnion64(DebugCtlMsr)
+    Bitfield<0> lbr; // Last-branch record
+    Bitfield<1> btf; // Branch single step
+    Bitfield<2> pb0; // Performance monitoring pin control 0
+    Bitfield<3> pb1; // Performance monitoring pin control 1
+    Bitfield<4> pb2; // Performance monitoring pin control 2
+    Bitfield<5> pb3; // Performance monitoring pin control 3
+    /*uint64_t pb(int index)
     {
-      public:
-        uint32_t
-        getter(const uint64_t &storage) const
-        {
-            return (bits(storage, 63, 56) << 24) | bits(storage, 39, 16);
-        }
+        return bits(__data, index + 2);
+    }*/
+EndBitUnion(DebugCtlMsr)
 
-        void
-        setter(uint64_t &storage, uint32_t base)
-        {
-            replaceBits(storage, 63, 56, bits(base, 31, 24));
-            replaceBits(storage, 39, 16, bits(base, 23, 0));
-        }
-    };
+BitUnion64(MtrrPhysBase)
+    Bitfield<7, 0> type; // Default memory type
+    Bitfield<51, 12> physbase; // Range physical base address
+EndBitUnion(MtrrPhysBase)
 
-    class SegDescriptorLimit
+BitUnion64(MtrrPhysMask)
+    Bitfield<11> valid; // MTRR pair enable
+    Bitfield<51, 12> physmask; // Range physical mask
+EndBitUnion(MtrrPhysMask)
+
+BitUnion64(MtrrFixed)
+    /*uint64_t type(int index)
     {
-      public:
-        uint32_t
-        getter(const uint64_t &storage) const
-        {
-            uint32_t limit = (bits(storage, 51, 48) << 16) |
-                             bits(storage, 15, 0);
-            if (bits(storage, 55))
-                limit = (limit << 12) | mask(12);
-            return limit;
-        }
+        return bits(__data, index * 8 + 7, index * 8);
+    }*/
+EndBitUnion(MtrrFixed)
 
-        void
-        setter(uint64_t &storage, uint32_t limit)
-        {
-            bool g = (bits(limit, 31, 24) != 0);
-            panic_if(g && bits(limit, 11, 0) != mask(12),
-                     "Inlimitid segment limit %#x", limit);
-            if (g)
-                limit = limit >> 12;
-            replaceBits(storage, 51, 48, bits(limit, 23, 16));
-            replaceBits(storage, 15, 0, bits(limit, 15, 0));
-            replaceBits(storage, 55, g ? 1 : 0);
-        }
-    };
+BitUnion64(Pat)
+    /*uint64_t pa(int index)
+    {
+        return bits(__data, index * 8 + 2, index * 8);
+    }*/
+EndBitUnion(Pat)
 
-    BitUnion64(SegDescriptor)
-        Bitfield<63, 56> baseHigh;
-        Bitfield<39, 16> baseLow;
-        BitfieldType<SegDescriptorBase> base;
-        Bitfield<55> g; // Granularity
-        Bitfield<54> d; // Default Operand Size
-        Bitfield<54> b; // Default Operand Size
-        Bitfield<53> l; // Long Attribute Bit
-        Bitfield<52> avl; // Available To Software
-        Bitfield<51, 48> limitHigh;
-        Bitfield<15, 0> limitLow;
-        BitfieldType<SegDescriptorLimit> limit;
-        Bitfield<47> p; // Present
-        Bitfield<46, 45> dpl; // Descriptor Privilege-Level
-        Bitfield<44> s; // System
-        SubBitUnion(type, 43, 40)
-            // Specifies whether this descriptor is for code or data.
-            Bitfield<43> codeOrData;
+BitUnion64(MtrrDefType)
+    Bitfield<7, 0> type; // Default type
+    Bitfield<10> fe; // Fixed range enable
+    Bitfield<11> e; // MTRR enable
+EndBitUnion(MtrrDefType)
 
-            // These bit fields are for code segments
-            Bitfield<42> c; // Conforming
-            Bitfield<41> r; // Readable
+/**
+ * Machine check
+ */
+BitUnion64(McStatus)
+    Bitfield<15,0> mcaErrorCode;
+    Bitfield<31,16> modelSpecificCode;
+    Bitfield<56,32> otherInfo;
+    Bitfield<57> pcc; // Processor-context corrupt
+    Bitfield<58> addrv; // Error-address register valid
+    Bitfield<59> miscv; // Miscellaneous-error register valid
+    Bitfield<60> en; // Error condition enabled
+    Bitfield<61> uc; // Uncorrected error
+    Bitfield<62> over; // Status register overflow
+    Bitfield<63> val; // Valid
+EndBitUnion(McStatus)
 
-            // These bit fields are for data segments
-            Bitfield<42> e; // Expand-Down
-            Bitfield<41> w; // Writable
+BitUnion64(McCtl)
+    /*uint64_t en(int index)
+    {
+        return bits(__data, index);
+    }*/
+EndBitUnion(McCtl)
 
-            // This is used for both code and data segments.
-            Bitfield<40> a; // Accessed
-        EndSubBitUnion(type)
-    EndBitUnion(SegDescriptor)
+// Extended feature enable register
+BitUnion64(Efer)
+    Bitfield<0> sce; // System call extensions
+    Bitfield<8> lme; // Long mode enable
+    Bitfield<10> lma; // Long mode active
+    Bitfield<11> nxe; // No-execute enable
+    Bitfield<12> svme; // Secure virtual machine enable
+    Bitfield<14> ffxsr; // Fast fxsave/fxrstor
+EndBitUnion(Efer)
 
-    /**
-     * TSS Descriptor (long mode - 128 bits)
-     * the lower 64 bits
-     */
-    BitUnion64(TSSlow)
-        Bitfield<63, 56> baseHigh;
-        Bitfield<39, 16> baseLow;
-        BitfieldType<SegDescriptorBase> base;
-        Bitfield<55> g; // Granularity
-        Bitfield<52> avl; // Available To Software
-        Bitfield<51, 48> limitHigh;
-        Bitfield<15, 0> limitLow;
-        BitfieldType<SegDescriptorLimit> limit;
-        Bitfield<47> p; // Present
-        Bitfield<46, 45> dpl; // Descriptor Privilege-Level
-        SubBitUnion(type, 43, 40)
-            // Specifies whether this descriptor is for code or data.
-            Bitfield<43> codeOrData;
+BitUnion64(Star)
+    Bitfield<31,0> targetEip;
+    Bitfield<47,32> syscallCsAndSs;
+    Bitfield<63,48> sysretCsAndSs;
+EndBitUnion(Star)
 
-            // These bit fields are for code segments
-            Bitfield<42> c; // Conforming
-            Bitfield<41> r; // Readable
+BitUnion64(SfMask)
+    Bitfield<31,0> mask;
+EndBitUnion(SfMask)
 
-            // These bit fields are for data segments
-            Bitfield<42> e; // Expand-Down
-            Bitfield<41> w; // Writable
+BitUnion64(PerfEvtSel)
+    Bitfield<7,0> eventMask;
+    Bitfield<15,8> unitMask;
+    Bitfield<16> usr; // User mode
+    Bitfield<17> os; // Operating-system mode
+    Bitfield<18> e; // Edge detect
+    Bitfield<19> pc; // Pin control
+    Bitfield<20> intEn; // Interrupt enable
+    Bitfield<22> en; // Counter enable
+    Bitfield<23> inv; // Invert mask
+    Bitfield<31,24> counterMask;
+EndBitUnion(PerfEvtSel)
 
-            // This is used for both code and data segments.
-            Bitfield<40> a; // Accessed
-        EndSubBitUnion(type)
-    EndBitUnion(TSSlow)
+BitUnion32(Syscfg)
+    Bitfield<18> mfde; // MtrrFixDramEn
+    Bitfield<19> mfdm; // MtrrFixDramModEn
+    Bitfield<20> mvdm; // MtrrVarDramEn
+    Bitfield<21> tom2; // MtrrTom2En
+EndBitUnion(Syscfg)
 
-    /**
-     * TSS Descriptor (long mode - 128 bits)
-     * the upper 64 bits
-     */
-    BitUnion64(TSShigh)
-        Bitfield<31, 0> base;
-    EndBitUnion(TSShigh)
+BitUnion64(IorrBase)
+    Bitfield<3> wr; // WrMem Enable
+    Bitfield<4> rd; // RdMem Enable
+    Bitfield<51,12> physbase; // Range physical base address
+EndBitUnion(IorrBase)
 
-    BitUnion64(SegAttr)
-        Bitfield<1, 0> dpl;
-        Bitfield<2> unusable;
-        Bitfield<3> defaultSize;
-        Bitfield<4> longMode;
-        Bitfield<5> avl;
-        Bitfield<6> granularity;
-        Bitfield<7> present;
-        Bitfield<11, 8> type;
-        Bitfield<12> writable;
-        Bitfield<13> readable;
-        Bitfield<14> expandDown;
-        Bitfield<15> system;
-    EndBitUnion(SegAttr)
+BitUnion64(IorrMask)
+    Bitfield<11> v; // I/O register pair enable (valid)
+    Bitfield<51,12> physmask; // Range physical mask
+EndBitUnion(IorrMask)
 
-    BitUnion64(GateDescriptor)
-        Bitfield<63, 48> offsetHigh; // Target Code-Segment Offset
-        Bitfield<15, 0> offsetLow; // Target Code-Segment Offset
-        Bitfield<31, 16> selector; // Target Code-Segment Selector
-        Bitfield<47> p; // Present
-        Bitfield<46, 45> dpl; // Descriptor Privilege-Level
-        Bitfield<43, 40> type;
-        Bitfield<36, 32> count; // Parameter Count
-    EndBitUnion(GateDescriptor)
+BitUnion64(Tom)
+    Bitfield<51,23> physAddr; // Top of memory physical address
+EndBitUnion(Tom)
 
-    /**
-     * Long Mode Gate Descriptor
-     */
-    BitUnion64(GateDescriptorLow)
-        Bitfield<63, 48> offsetHigh; // Target Code-Segment Offset
-        Bitfield<47> p; // Present
-        Bitfield<46, 45> dpl; // Descriptor Privilege-Level
-        Bitfield<43, 40> type;
-        Bitfield<35, 32> IST; // IST pointer to TSS -- new stack for exception handling
-        Bitfield<31, 16> selector; // Target Code-Segment Selector
-        Bitfield<15, 0> offsetLow; // Target Code-Segment Offset
-    EndBitUnion(GateDescriptorLow)
+BitUnion64(VmCrMsr)
+    Bitfield<0> dpd;
+    Bitfield<1> rInit;
+    Bitfield<2> disA20M;
+EndBitUnion(VmCrMsr)
 
-    BitUnion64(GateDescriptorHigh)
-        Bitfield<31, 0> offset; // Target Code-Segment Offset
-    EndBitUnion(GateDescriptorHigh)
+BitUnion64(IgnneMsr)
+    Bitfield<0> ignne;
+EndBitUnion(IgnneMsr)
 
-    /**
-     * Descriptor-Table Registers
-     */
-    BitUnion64(GDTR)
-    EndBitUnion(GDTR)
+BitUnion64(SmmCtlMsr)
+    Bitfield<0> dismiss;
+    Bitfield<1> enter;
+    Bitfield<2> smiCycle;
+    Bitfield<3> exit;
+    Bitfield<4> rsmCycle;
+EndBitUnion(SmmCtlMsr)
 
-    BitUnion64(IDTR)
-    EndBitUnion(IDTR)
+/**
+ * Segment Selector
+ */
+BitUnion64(SegSelector)
+    // The following bitfield is not defined in the ISA, but it's useful
+    // when checking selectors in larger data types to make sure they
+    // aren't too large.
+    Bitfield<63, 3> esi; // Extended selector
+    Bitfield<15, 3> si; // Selector Index
+    Bitfield<2> ti; // Table Indicator
+    Bitfield<1, 0> rpl; // Requestor Privilege Level
+EndBitUnion(SegSelector)
 
-    BitUnion64(LDTR)
-    EndBitUnion(LDTR)
+/**
+ * Segment Descriptors
+ */
 
-    /**
-     * Task Register
-     */
-    BitUnion64(TR)
-    EndBitUnion(TR)
+class SegDescriptorBase
+{
+  public:
+    uint32_t
+    getter(const uint64_t &storage) const
+    {
+        return (bits(storage, 63, 56) << 24) | bits(storage, 39, 16);
+    }
+
+    void
+    setter(uint64_t &storage, uint32_t base)
+    {
+        replaceBits(storage, 63, 56, bits(base, 31, 24));
+        replaceBits(storage, 39, 16, bits(base, 23, 0));
+    }
+};
+
+class SegDescriptorLimit
+{
+  public:
+    uint32_t
+    getter(const uint64_t &storage) const
+    {
+        uint32_t limit = (bits(storage, 51, 48) << 16) |
+                         bits(storage, 15, 0);
+        if (bits(storage, 55))
+            limit = (limit << 12) | mask(12);
+        return limit;
+    }
+
+    void
+    setter(uint64_t &storage, uint32_t limit)
+    {
+        bool g = (bits(limit, 31, 24) != 0);
+        panic_if(g && bits(limit, 11, 0) != mask(12),
+                 "Inlimitid segment limit %#x", limit);
+        if (g)
+            limit = limit >> 12;
+        replaceBits(storage, 51, 48, bits(limit, 23, 16));
+        replaceBits(storage, 15, 0, bits(limit, 15, 0));
+        replaceBits(storage, 55, g ? 1 : 0);
+    }
+};
+
+BitUnion64(SegDescriptor)
+    Bitfield<63, 56> baseHigh;
+    Bitfield<39, 16> baseLow;
+    BitfieldType<SegDescriptorBase> base;
+    Bitfield<55> g; // Granularity
+    Bitfield<54> d; // Default Operand Size
+    Bitfield<54> b; // Default Operand Size
+    Bitfield<53> l; // Long Attribute Bit
+    Bitfield<52> avl; // Available To Software
+    Bitfield<51, 48> limitHigh;
+    Bitfield<15, 0> limitLow;
+    BitfieldType<SegDescriptorLimit> limit;
+    Bitfield<47> p; // Present
+    Bitfield<46, 45> dpl; // Descriptor Privilege-Level
+    Bitfield<44> s; // System
+    SubBitUnion(type, 43, 40)
+        // Specifies whether this descriptor is for code or data.
+        Bitfield<43> codeOrData;
+
+        // These bit fields are for code segments
+        Bitfield<42> c; // Conforming
+        Bitfield<41> r; // Readable
+
+        // These bit fields are for data segments
+        Bitfield<42> e; // Expand-Down
+        Bitfield<41> w; // Writable
+
+        // This is used for both code and data segments.
+        Bitfield<40> a; // Accessed
+    EndSubBitUnion(type)
+EndBitUnion(SegDescriptor)
+
+/**
+ * TSS Descriptor (long mode - 128 bits)
+ * the lower 64 bits
+ */
+BitUnion64(TSSlow)
+    Bitfield<63, 56> baseHigh;
+    Bitfield<39, 16> baseLow;
+    BitfieldType<SegDescriptorBase> base;
+    Bitfield<55> g; // Granularity
+    Bitfield<52> avl; // Available To Software
+    Bitfield<51, 48> limitHigh;
+    Bitfield<15, 0> limitLow;
+    BitfieldType<SegDescriptorLimit> limit;
+    Bitfield<47> p; // Present
+    Bitfield<46, 45> dpl; // Descriptor Privilege-Level
+    SubBitUnion(type, 43, 40)
+        // Specifies whether this descriptor is for code or data.
+        Bitfield<43> codeOrData;
+
+        // These bit fields are for code segments
+        Bitfield<42> c; // Conforming
+        Bitfield<41> r; // Readable
+
+        // These bit fields are for data segments
+        Bitfield<42> e; // Expand-Down
+        Bitfield<41> w; // Writable
+
+        // This is used for both code and data segments.
+        Bitfield<40> a; // Accessed
+    EndSubBitUnion(type)
+EndBitUnion(TSSlow)
+
+/**
+ * TSS Descriptor (long mode - 128 bits)
+ * the upper 64 bits
+ */
+BitUnion64(TSShigh)
+    Bitfield<31, 0> base;
+EndBitUnion(TSShigh)
+
+BitUnion64(SegAttr)
+    Bitfield<1, 0> dpl;
+    Bitfield<2> unusable;
+    Bitfield<3> defaultSize;
+    Bitfield<4> longMode;
+    Bitfield<5> avl;
+    Bitfield<6> granularity;
+    Bitfield<7> present;
+    Bitfield<11, 8> type;
+    Bitfield<12> writable;
+    Bitfield<13> readable;
+    Bitfield<14> expandDown;
+    Bitfield<15> system;
+EndBitUnion(SegAttr)
+
+BitUnion64(GateDescriptor)
+    Bitfield<63, 48> offsetHigh; // Target Code-Segment Offset
+    Bitfield<15, 0> offsetLow; // Target Code-Segment Offset
+    Bitfield<31, 16> selector; // Target Code-Segment Selector
+    Bitfield<47> p; // Present
+    Bitfield<46, 45> dpl; // Descriptor Privilege-Level
+    Bitfield<43, 40> type;
+    Bitfield<36, 32> count; // Parameter Count
+EndBitUnion(GateDescriptor)
+
+/**
+ * Long Mode Gate Descriptor
+ */
+BitUnion64(GateDescriptorLow)
+    Bitfield<63, 48> offsetHigh; // Target Code-Segment Offset
+    Bitfield<47> p; // Present
+    Bitfield<46, 45> dpl; // Descriptor Privilege-Level
+    Bitfield<43, 40> type;
+    Bitfield<35, 32> IST; // IST pointer to TSS, new stack for exceptions
+    Bitfield<31, 16> selector; // Target Code-Segment Selector
+    Bitfield<15, 0> offsetLow; // Target Code-Segment Offset
+EndBitUnion(GateDescriptorLow)
+
+BitUnion64(GateDescriptorHigh)
+    Bitfield<31, 0> offset; // Target Code-Segment Offset
+EndBitUnion(GateDescriptorHigh)
+
+/**
+ * Descriptor-Table Registers
+ */
+BitUnion64(GDTR)
+EndBitUnion(GDTR)
+
+BitUnion64(IDTR)
+EndBitUnion(IDTR)
+
+BitUnion64(LDTR)
+EndBitUnion(LDTR)
+
+/**
+ * Task Register
+ */
+BitUnion64(TR)
+EndBitUnion(TR)
 
 
-    /**
-     * Local APIC Base Register
-     */
-    BitUnion64(LocalApicBase)
-        Bitfield<51, 12> base;
-        Bitfield<11> enable;
-        Bitfield<8> bsp;
-    EndBitUnion(LocalApicBase)
+/**
+ * Local APIC Base Register
+ */
+BitUnion64(LocalApicBase)
+    Bitfield<51, 12> base;
+    Bitfield<11> enable;
+    Bitfield<8> bsp;
+EndBitUnion(LocalApicBase)
 
 } // namespace X86ISA
 } // namespace gem5
diff --git a/src/arch/x86/regs/msr.cc b/src/arch/x86/regs/msr.cc
index 6d9a520..7fdb884 100644
--- a/src/arch/x86/regs/msr.cc
+++ b/src/arch/x86/regs/msr.cc
@@ -37,109 +37,109 @@
 typedef MsrMap::value_type MsrVal;
 
 const MsrMap::value_type msrMapData[] = {
-    MsrVal(0x10, MISCREG_TSC),
-    MsrVal(0x1B, MISCREG_APIC_BASE),
-    MsrVal(0xFE, MISCREG_MTRRCAP),
-    MsrVal(0x174, MISCREG_SYSENTER_CS),
-    MsrVal(0x175, MISCREG_SYSENTER_ESP),
-    MsrVal(0x176, MISCREG_SYSENTER_EIP),
-    MsrVal(0x179, MISCREG_MCG_CAP),
-    MsrVal(0x17A, MISCREG_MCG_STATUS),
-    MsrVal(0x17B, MISCREG_MCG_CTL),
-    MsrVal(0x1D9, MISCREG_DEBUG_CTL_MSR),
-    MsrVal(0x1DB, MISCREG_LAST_BRANCH_FROM_IP),
-    MsrVal(0x1DC, MISCREG_LAST_BRANCH_TO_IP),
-    MsrVal(0x1DD, MISCREG_LAST_EXCEPTION_FROM_IP),
-    MsrVal(0x1DE, MISCREG_LAST_EXCEPTION_TO_IP),
-    MsrVal(0x200, MISCREG_MTRR_PHYS_BASE_0),
-    MsrVal(0x201, MISCREG_MTRR_PHYS_MASK_0),
-    MsrVal(0x202, MISCREG_MTRR_PHYS_BASE_1),
-    MsrVal(0x203, MISCREG_MTRR_PHYS_MASK_1),
-    MsrVal(0x204, MISCREG_MTRR_PHYS_BASE_2),
-    MsrVal(0x205, MISCREG_MTRR_PHYS_MASK_2),
-    MsrVal(0x206, MISCREG_MTRR_PHYS_BASE_3),
-    MsrVal(0x207, MISCREG_MTRR_PHYS_MASK_3),
-    MsrVal(0x208, MISCREG_MTRR_PHYS_BASE_4),
-    MsrVal(0x209, MISCREG_MTRR_PHYS_MASK_4),
-    MsrVal(0x20A, MISCREG_MTRR_PHYS_BASE_5),
-    MsrVal(0x20B, MISCREG_MTRR_PHYS_MASK_5),
-    MsrVal(0x20C, MISCREG_MTRR_PHYS_BASE_6),
-    MsrVal(0x20D, MISCREG_MTRR_PHYS_MASK_6),
-    MsrVal(0x20E, MISCREG_MTRR_PHYS_BASE_7),
-    MsrVal(0x20F, MISCREG_MTRR_PHYS_MASK_7),
-    MsrVal(0x250, MISCREG_MTRR_FIX_64K_00000),
-    MsrVal(0x258, MISCREG_MTRR_FIX_16K_80000),
-    MsrVal(0x259, MISCREG_MTRR_FIX_16K_A0000),
-    MsrVal(0x268, MISCREG_MTRR_FIX_4K_C0000),
-    MsrVal(0x269, MISCREG_MTRR_FIX_4K_C8000),
-    MsrVal(0x26A, MISCREG_MTRR_FIX_4K_D0000),
-    MsrVal(0x26B, MISCREG_MTRR_FIX_4K_D8000),
-    MsrVal(0x26C, MISCREG_MTRR_FIX_4K_E0000),
-    MsrVal(0x26D, MISCREG_MTRR_FIX_4K_E8000),
-    MsrVal(0x26E, MISCREG_MTRR_FIX_4K_F0000),
-    MsrVal(0x26F, MISCREG_MTRR_FIX_4K_F8000),
-    MsrVal(0x277, MISCREG_PAT),
-    MsrVal(0x2FF, MISCREG_DEF_TYPE),
-    MsrVal(0x400, MISCREG_MC0_CTL),
-    MsrVal(0x404, MISCREG_MC1_CTL),
-    MsrVal(0x408, MISCREG_MC2_CTL),
-    MsrVal(0x40C, MISCREG_MC3_CTL),
-    MsrVal(0x410, MISCREG_MC4_CTL),
-    MsrVal(0x414, MISCREG_MC5_CTL),
-    MsrVal(0x418, MISCREG_MC6_CTL),
-    MsrVal(0x41C, MISCREG_MC7_CTL),
-    MsrVal(0x401, MISCREG_MC0_STATUS),
-    MsrVal(0x405, MISCREG_MC1_STATUS),
-    MsrVal(0x409, MISCREG_MC2_STATUS),
-    MsrVal(0x40D, MISCREG_MC3_STATUS),
-    MsrVal(0x411, MISCREG_MC4_STATUS),
-    MsrVal(0x415, MISCREG_MC5_STATUS),
-    MsrVal(0x419, MISCREG_MC6_STATUS),
-    MsrVal(0x41D, MISCREG_MC7_STATUS),
-    MsrVal(0x402, MISCREG_MC0_ADDR),
-    MsrVal(0x406, MISCREG_MC1_ADDR),
-    MsrVal(0x40A, MISCREG_MC2_ADDR),
-    MsrVal(0x40E, MISCREG_MC3_ADDR),
-    MsrVal(0x412, MISCREG_MC4_ADDR),
-    MsrVal(0x416, MISCREG_MC5_ADDR),
-    MsrVal(0x41A, MISCREG_MC6_ADDR),
-    MsrVal(0x41E, MISCREG_MC7_ADDR),
-    MsrVal(0x403, MISCREG_MC0_MISC),
-    MsrVal(0x407, MISCREG_MC1_MISC),
-    MsrVal(0x40B, MISCREG_MC2_MISC),
-    MsrVal(0x40F, MISCREG_MC3_MISC),
-    MsrVal(0x413, MISCREG_MC4_MISC),
-    MsrVal(0x417, MISCREG_MC5_MISC),
-    MsrVal(0x41B, MISCREG_MC6_MISC),
-    MsrVal(0x41F, MISCREG_MC7_MISC),
-    MsrVal(0xC0000080, MISCREG_EFER),
-    MsrVal(0xC0000081, MISCREG_STAR),
-    MsrVal(0xC0000082, MISCREG_LSTAR),
-    MsrVal(0xC0000083, MISCREG_CSTAR),
-    MsrVal(0xC0000084, MISCREG_SF_MASK),
-    MsrVal(0xC0000100, MISCREG_FS_BASE),
-    MsrVal(0xC0000101, MISCREG_GS_BASE),
-    MsrVal(0xC0000102, MISCREG_KERNEL_GS_BASE),
-    MsrVal(0xC0000103, MISCREG_TSC_AUX),
-    MsrVal(0xC0010000, MISCREG_PERF_EVT_SEL0),
-    MsrVal(0xC0010001, MISCREG_PERF_EVT_SEL1),
-    MsrVal(0xC0010002, MISCREG_PERF_EVT_SEL2),
-    MsrVal(0xC0010003, MISCREG_PERF_EVT_SEL3),
-    MsrVal(0xC0010004, MISCREG_PERF_EVT_CTR0),
-    MsrVal(0xC0010005, MISCREG_PERF_EVT_CTR1),
-    MsrVal(0xC0010006, MISCREG_PERF_EVT_CTR2),
-    MsrVal(0xC0010007, MISCREG_PERF_EVT_CTR3),
-    MsrVal(0xC0010010, MISCREG_SYSCFG),
-    MsrVal(0xC0010016, MISCREG_IORR_BASE0),
-    MsrVal(0xC0010017, MISCREG_IORR_BASE1),
-    MsrVal(0xC0010018, MISCREG_IORR_MASK0),
-    MsrVal(0xC0010019, MISCREG_IORR_MASK1),
-    MsrVal(0xC001001A, MISCREG_TOP_MEM),
-    MsrVal(0xC001001D, MISCREG_TOP_MEM2),
-    MsrVal(0xC0010114, MISCREG_VM_CR),
-    MsrVal(0xC0010115, MISCREG_IGNNE),
-    MsrVal(0xC0010116, MISCREG_SMM_CTL),
-    MsrVal(0xC0010117, MISCREG_VM_HSAVE_PA)
+    MsrVal(0x10, misc_reg::Tsc),
+    MsrVal(0x1B, misc_reg::ApicBase),
+    MsrVal(0xFE, misc_reg::Mtrrcap),
+    MsrVal(0x174, misc_reg::SysenterCs),
+    MsrVal(0x175, misc_reg::SysenterEsp),
+    MsrVal(0x176, misc_reg::SysenterEip),
+    MsrVal(0x179, misc_reg::McgCap),
+    MsrVal(0x17A, misc_reg::McgStatus),
+    MsrVal(0x17B, misc_reg::McgCtl),
+    MsrVal(0x1D9, misc_reg::DebugCtlMsr),
+    MsrVal(0x1DB, misc_reg::LastBranchFromIp),
+    MsrVal(0x1DC, misc_reg::LastBranchToIp),
+    MsrVal(0x1DD, misc_reg::LastExceptionFromIp),
+    MsrVal(0x1DE, misc_reg::LastExceptionToIp),
+    MsrVal(0x200, misc_reg::MtrrPhysBase0),
+    MsrVal(0x201, misc_reg::MtrrPhysMask0),
+    MsrVal(0x202, misc_reg::MtrrPhysBase1),
+    MsrVal(0x203, misc_reg::MtrrPhysMask1),
+    MsrVal(0x204, misc_reg::MtrrPhysBase2),
+    MsrVal(0x205, misc_reg::MtrrPhysMask2),
+    MsrVal(0x206, misc_reg::MtrrPhysBase3),
+    MsrVal(0x207, misc_reg::MtrrPhysMask3),
+    MsrVal(0x208, misc_reg::MtrrPhysBase4),
+    MsrVal(0x209, misc_reg::MtrrPhysMask4),
+    MsrVal(0x20A, misc_reg::MtrrPhysBase5),
+    MsrVal(0x20B, misc_reg::MtrrPhysMask5),
+    MsrVal(0x20C, misc_reg::MtrrPhysBase6),
+    MsrVal(0x20D, misc_reg::MtrrPhysMask6),
+    MsrVal(0x20E, misc_reg::MtrrPhysBase7),
+    MsrVal(0x20F, misc_reg::MtrrPhysMask7),
+    MsrVal(0x250, misc_reg::MtrrFix64k00000),
+    MsrVal(0x258, misc_reg::MtrrFix16k80000),
+    MsrVal(0x259, misc_reg::MtrrFix16kA0000),
+    MsrVal(0x268, misc_reg::MtrrFix4kC0000),
+    MsrVal(0x269, misc_reg::MtrrFix4kC8000),
+    MsrVal(0x26A, misc_reg::MtrrFix4kD0000),
+    MsrVal(0x26B, misc_reg::MtrrFix4kD8000),
+    MsrVal(0x26C, misc_reg::MtrrFix4kE0000),
+    MsrVal(0x26D, misc_reg::MtrrFix4kE8000),
+    MsrVal(0x26E, misc_reg::MtrrFix4kF0000),
+    MsrVal(0x26F, misc_reg::MtrrFix4kF8000),
+    MsrVal(0x277, misc_reg::Pat),
+    MsrVal(0x2FF, misc_reg::DefType),
+    MsrVal(0x400, misc_reg::Mc0Ctl),
+    MsrVal(0x404, misc_reg::Mc1Ctl),
+    MsrVal(0x408, misc_reg::Mc2Ctl),
+    MsrVal(0x40C, misc_reg::Mc3Ctl),
+    MsrVal(0x410, misc_reg::Mc4Ctl),
+    MsrVal(0x414, misc_reg::Mc5Ctl),
+    MsrVal(0x418, misc_reg::Mc6Ctl),
+    MsrVal(0x41C, misc_reg::Mc7Ctl),
+    MsrVal(0x401, misc_reg::Mc0Status),
+    MsrVal(0x405, misc_reg::Mc1Status),
+    MsrVal(0x409, misc_reg::Mc2Status),
+    MsrVal(0x40D, misc_reg::Mc3Status),
+    MsrVal(0x411, misc_reg::Mc4Status),
+    MsrVal(0x415, misc_reg::Mc5Status),
+    MsrVal(0x419, misc_reg::Mc6Status),
+    MsrVal(0x41D, misc_reg::Mc7Status),
+    MsrVal(0x402, misc_reg::Mc0Addr),
+    MsrVal(0x406, misc_reg::Mc1Addr),
+    MsrVal(0x40A, misc_reg::Mc2Addr),
+    MsrVal(0x40E, misc_reg::Mc3Addr),
+    MsrVal(0x412, misc_reg::Mc4Addr),
+    MsrVal(0x416, misc_reg::Mc5Addr),
+    MsrVal(0x41A, misc_reg::Mc6Addr),
+    MsrVal(0x41E, misc_reg::Mc7Addr),
+    MsrVal(0x403, misc_reg::Mc0Misc),
+    MsrVal(0x407, misc_reg::Mc1Misc),
+    MsrVal(0x40B, misc_reg::Mc2Misc),
+    MsrVal(0x40F, misc_reg::Mc3Misc),
+    MsrVal(0x413, misc_reg::Mc4Misc),
+    MsrVal(0x417, misc_reg::Mc5Misc),
+    MsrVal(0x41B, misc_reg::Mc6Misc),
+    MsrVal(0x41F, misc_reg::Mc7Misc),
+    MsrVal(0xC0000080, misc_reg::Efer),
+    MsrVal(0xC0000081, misc_reg::Star),
+    MsrVal(0xC0000082, misc_reg::Lstar),
+    MsrVal(0xC0000083, misc_reg::Cstar),
+    MsrVal(0xC0000084, misc_reg::SfMask),
+    MsrVal(0xC0000100, misc_reg::FsBase),
+    MsrVal(0xC0000101, misc_reg::GsBase),
+    MsrVal(0xC0000102, misc_reg::KernelGsBase),
+    MsrVal(0xC0000103, misc_reg::TscAux),
+    MsrVal(0xC0010000, misc_reg::PerfEvtSel0),
+    MsrVal(0xC0010001, misc_reg::PerfEvtSel1),
+    MsrVal(0xC0010002, misc_reg::PerfEvtSel2),
+    MsrVal(0xC0010003, misc_reg::PerfEvtSel3),
+    MsrVal(0xC0010004, misc_reg::PerfEvtCtr0),
+    MsrVal(0xC0010005, misc_reg::PerfEvtCtr1),
+    MsrVal(0xC0010006, misc_reg::PerfEvtCtr2),
+    MsrVal(0xC0010007, misc_reg::PerfEvtCtr3),
+    MsrVal(0xC0010010, misc_reg::Syscfg),
+    MsrVal(0xC0010016, misc_reg::IorrBase0),
+    MsrVal(0xC0010017, misc_reg::IorrBase1),
+    MsrVal(0xC0010018, misc_reg::IorrMask0),
+    MsrVal(0xC0010019, misc_reg::IorrMask1),
+    MsrVal(0xC001001A, misc_reg::TopMem),
+    MsrVal(0xC001001D, misc_reg::TopMem2),
+    MsrVal(0xC0010114, misc_reg::VmCr),
+    MsrVal(0xC0010115, misc_reg::Ignne),
+    MsrVal(0xC0010116, misc_reg::SmmCtl),
+    MsrVal(0xC0010117, misc_reg::VmHsavePa)
 };
 
 static const unsigned msrMapSize = sizeof(msrMapData) / sizeof(msrMapData[0]);
@@ -147,13 +147,13 @@
 const MsrMap msrMap(msrMapData, msrMapData + msrMapSize);
 
 bool
-msrAddrToIndex(MiscRegIndex &regNum, Addr addr)
+msrAddrToIndex(RegIndex &reg_num, Addr addr)
 {
-    MsrMap::const_iterator it(msrMap.find(addr));
+    auto it = msrMap.find(addr);
     if (it == msrMap.end()) {
         return false;
     } else {
-        regNum = it->second;
+        reg_num = it->second;
         return true;
     }
 }
diff --git a/src/arch/x86/regs/msr.hh b/src/arch/x86/regs/msr.hh
index b9ce737..922c720 100644
--- a/src/arch/x86/regs/msr.hh
+++ b/src/arch/x86/regs/msr.hh
@@ -40,7 +40,7 @@
 namespace X86ISA
 {
 
-typedef std::unordered_map<Addr, MiscRegIndex> MsrMap;
+typedef std::unordered_map<Addr, RegIndex> MsrMap;
 
 /**
  * Map between MSR addresses and their corresponding misc registers.
@@ -62,7 +62,7 @@
  * @param addr MSR address
  * @return True if the MSR was found, false otherwise.
  */
-bool msrAddrToIndex(MiscRegIndex &regNum, Addr addr);
+bool msrAddrToIndex(RegIndex &regNum, Addr addr);
 
 } // namespace X86ISA
 } // namespace gem5
diff --git a/src/arch/x86/regs/segment.hh b/src/arch/x86/regs/segment.hh
index 714bc2e..96be72c 100644
--- a/src/arch/x86/regs/segment.hh
+++ b/src/arch/x86/regs/segment.hh
@@ -40,30 +40,34 @@
 
 namespace gem5
 {
-
 namespace X86ISA
 {
-    enum SegmentRegIndex
-    {
-        SEGMENT_REG_ES,
-        SEGMENT_REG_CS,
-        SEGMENT_REG_SS,
-        SEGMENT_REG_DS,
-        SEGMENT_REG_FS,
-        SEGMENT_REG_GS,
-        SEGMENT_REG_HS, // Temporary descriptor
-        SEGMENT_REG_TSL, // Local descriptor table
-        SEGMENT_REG_TSG, // Global descriptor table
-        SEGMENT_REG_LS, // Flat segment
-        SEGMENT_REG_MS, // Emulation memory
-        // These shouldn't be used directly in a load or store since they
-        // are likely accessed in other ways in a real machine. For instance,
-        // they may be loaded into the temporary segment register on demand.
-        SYS_SEGMENT_REG_TR,
-        SYS_SEGMENT_REG_IDTR,
+namespace segment_idx
+{
 
-        NUM_SEGMENTREGS
-    };
+enum
+{
+    Es,
+    Cs,
+    Ss,
+    Ds,
+    Fs,
+    Gs,
+    Hs, // Temporary descriptor
+    Tsl, // Local descriptor table
+    Tsg, // Global descriptor table
+    Ls, // Flat segment
+    Ms, // Emulation memory
+    // These shouldn't be used directly in a load or store since they
+    // are likely accessed in other ways in a real machine. For instance,
+    // they may be loaded into the temporary segment register on demand.
+    Tr,
+    Idtr,
+
+    NumIdxs
+};
+
+} // namespace segment_idx
 } // namespace X86ISA
 } // namespace gem5
 
diff --git a/src/arch/x86/remote_gdb.cc b/src/arch/x86/remote_gdb.cc
index abb2154..244f80da 100644
--- a/src/arch/x86/remote_gdb.cc
+++ b/src/arch/x86/remote_gdb.cc
@@ -112,7 +112,7 @@
     }
 
     // If that didn't work, decide based on the current mode of the context.
-    HandyM5Reg m5reg = context()->readMiscRegNoEffect(MISCREG_M5_REG);
+    HandyM5Reg m5reg = context()->readMiscRegNoEffect(misc_reg::M5Reg);
     if (m5reg.submode == SixtyFourBitMode)
         return &regCache64;
     else
@@ -125,87 +125,87 @@
 RemoteGDB::AMD64GdbRegCache::getRegs(ThreadContext *context)
 {
     DPRINTF(GDBAcc, "getRegs in remotegdb \n");
-    r.rax = context->readIntReg(INTREG_RAX);
-    r.rbx = context->readIntReg(INTREG_RBX);
-    r.rcx = context->readIntReg(INTREG_RCX);
-    r.rdx = context->readIntReg(INTREG_RDX);
-    r.rsi = context->readIntReg(INTREG_RSI);
-    r.rdi = context->readIntReg(INTREG_RDI);
-    r.rbp = context->readIntReg(INTREG_RBP);
-    r.rsp = context->readIntReg(INTREG_RSP);
-    r.r8 = context->readIntReg(INTREG_R8);
-    r.r9 = context->readIntReg(INTREG_R9);
-    r.r10 = context->readIntReg(INTREG_R10);
-    r.r11 = context->readIntReg(INTREG_R11);
-    r.r12 = context->readIntReg(INTREG_R12);
-    r.r13 = context->readIntReg(INTREG_R13);
-    r.r14 = context->readIntReg(INTREG_R14);
-    r.r15 = context->readIntReg(INTREG_R15);
+    r.rax = context->getReg(int_reg::Rax);
+    r.rbx = context->getReg(int_reg::Rbx);
+    r.rcx = context->getReg(int_reg::Rcx);
+    r.rdx = context->getReg(int_reg::Rdx);
+    r.rsi = context->getReg(int_reg::Rsi);
+    r.rdi = context->getReg(int_reg::Rdi);
+    r.rbp = context->getReg(int_reg::Rbp);
+    r.rsp = context->getReg(int_reg::Rsp);
+    r.r8 = context->getReg(int_reg::R8);
+    r.r9 = context->getReg(int_reg::R9);
+    r.r10 = context->getReg(int_reg::R10);
+    r.r11 = context->getReg(int_reg::R11);
+    r.r12 = context->getReg(int_reg::R12);
+    r.r13 = context->getReg(int_reg::R13);
+    r.r14 = context->getReg(int_reg::R14);
+    r.r15 = context->getReg(int_reg::R15);
     r.rip = context->pcState().instAddr();
-    r.eflags = context->readMiscRegNoEffect(MISCREG_RFLAGS);
-    r.cs = context->readMiscRegNoEffect(MISCREG_CS);
-    r.ss = context->readMiscRegNoEffect(MISCREG_SS);
-    r.ds = context->readMiscRegNoEffect(MISCREG_DS);
-    r.es = context->readMiscRegNoEffect(MISCREG_ES);
-    r.fs = context->readMiscRegNoEffect(MISCREG_FS);
-    r.gs = context->readMiscRegNoEffect(MISCREG_GS);
+    r.eflags = context->readMiscRegNoEffect(misc_reg::Rflags);
+    r.cs = context->readMiscRegNoEffect(misc_reg::Cs);
+    r.ss = context->readMiscRegNoEffect(misc_reg::Ss);
+    r.ds = context->readMiscRegNoEffect(misc_reg::Ds);
+    r.es = context->readMiscRegNoEffect(misc_reg::Es);
+    r.fs = context->readMiscRegNoEffect(misc_reg::Fs);
+    r.gs = context->readMiscRegNoEffect(misc_reg::Gs);
 }
 
 void
 RemoteGDB::X86GdbRegCache::getRegs(ThreadContext *context)
 {
     DPRINTF(GDBAcc, "getRegs in remotegdb \n");
-    r.eax = context->readIntReg(INTREG_RAX);
-    r.ecx = context->readIntReg(INTREG_RCX);
-    r.edx = context->readIntReg(INTREG_RDX);
-    r.ebx = context->readIntReg(INTREG_RBX);
-    r.esp = context->readIntReg(INTREG_RSP);
-    r.ebp = context->readIntReg(INTREG_RBP);
-    r.esi = context->readIntReg(INTREG_RSI);
-    r.edi = context->readIntReg(INTREG_RDI);
+    r.eax = context->getReg(int_reg::Rax);
+    r.ecx = context->getReg(int_reg::Rcx);
+    r.edx = context->getReg(int_reg::Rdx);
+    r.ebx = context->getReg(int_reg::Rbx);
+    r.esp = context->getReg(int_reg::Rsp);
+    r.ebp = context->getReg(int_reg::Rbp);
+    r.esi = context->getReg(int_reg::Rsi);
+    r.edi = context->getReg(int_reg::Rdi);
     r.eip = context->pcState().instAddr();
-    r.eflags = context->readMiscRegNoEffect(MISCREG_RFLAGS);
-    r.cs = context->readMiscRegNoEffect(MISCREG_CS);
-    r.ss = context->readMiscRegNoEffect(MISCREG_SS);
-    r.ds = context->readMiscRegNoEffect(MISCREG_DS);
-    r.es = context->readMiscRegNoEffect(MISCREG_ES);
-    r.fs = context->readMiscRegNoEffect(MISCREG_FS);
-    r.gs = context->readMiscRegNoEffect(MISCREG_GS);
+    r.eflags = context->readMiscRegNoEffect(misc_reg::Rflags);
+    r.cs = context->readMiscRegNoEffect(misc_reg::Cs);
+    r.ss = context->readMiscRegNoEffect(misc_reg::Ss);
+    r.ds = context->readMiscRegNoEffect(misc_reg::Ds);
+    r.es = context->readMiscRegNoEffect(misc_reg::Es);
+    r.fs = context->readMiscRegNoEffect(misc_reg::Fs);
+    r.gs = context->readMiscRegNoEffect(misc_reg::Gs);
 }
 
 void
 RemoteGDB::AMD64GdbRegCache::setRegs(ThreadContext *context) const
 {
     DPRINTF(GDBAcc, "setRegs in remotegdb \n");
-    context->setIntReg(INTREG_RAX, r.rax);
-    context->setIntReg(INTREG_RBX, r.rbx);
-    context->setIntReg(INTREG_RCX, r.rcx);
-    context->setIntReg(INTREG_RDX, r.rdx);
-    context->setIntReg(INTREG_RSI, r.rsi);
-    context->setIntReg(INTREG_RDI, r.rdi);
-    context->setIntReg(INTREG_RBP, r.rbp);
-    context->setIntReg(INTREG_RSP, r.rsp);
-    context->setIntReg(INTREG_R8, r.r8);
-    context->setIntReg(INTREG_R9, r.r9);
-    context->setIntReg(INTREG_R10, r.r10);
-    context->setIntReg(INTREG_R11, r.r11);
-    context->setIntReg(INTREG_R12, r.r12);
-    context->setIntReg(INTREG_R13, r.r13);
-    context->setIntReg(INTREG_R14, r.r14);
-    context->setIntReg(INTREG_R15, r.r15);
+    context->setReg(int_reg::Rax, r.rax);
+    context->setReg(int_reg::Rbx, r.rbx);
+    context->setReg(int_reg::Rcx, r.rcx);
+    context->setReg(int_reg::Rdx, r.rdx);
+    context->setReg(int_reg::Rsi, r.rsi);
+    context->setReg(int_reg::Rdi, r.rdi);
+    context->setReg(int_reg::Rbp, r.rbp);
+    context->setReg(int_reg::Rsp, r.rsp);
+    context->setReg(int_reg::R8, r.r8);
+    context->setReg(int_reg::R9, r.r9);
+    context->setReg(int_reg::R10, r.r10);
+    context->setReg(int_reg::R11, r.r11);
+    context->setReg(int_reg::R12, r.r12);
+    context->setReg(int_reg::R13, r.r13);
+    context->setReg(int_reg::R14, r.r14);
+    context->setReg(int_reg::R15, r.r15);
     context->pcState(r.rip);
-    context->setMiscReg(MISCREG_RFLAGS, r.eflags);
-    if (r.cs != context->readMiscRegNoEffect(MISCREG_CS))
+    context->setMiscReg(misc_reg::Rflags, r.eflags);
+    if (r.cs != context->readMiscRegNoEffect(misc_reg::Cs))
         warn("Remote gdb: Ignoring update to CS.\n");
-    if (r.ss != context->readMiscRegNoEffect(MISCREG_SS))
+    if (r.ss != context->readMiscRegNoEffect(misc_reg::Ss))
         warn("Remote gdb: Ignoring update to SS.\n");
-    if (r.ds != context->readMiscRegNoEffect(MISCREG_DS))
+    if (r.ds != context->readMiscRegNoEffect(misc_reg::Ds))
         warn("Remote gdb: Ignoring update to DS.\n");
-    if (r.es != context->readMiscRegNoEffect(MISCREG_ES))
+    if (r.es != context->readMiscRegNoEffect(misc_reg::Es))
         warn("Remote gdb: Ignoring update to ES.\n");
-    if (r.fs != context->readMiscRegNoEffect(MISCREG_FS))
+    if (r.fs != context->readMiscRegNoEffect(misc_reg::Fs))
         warn("Remote gdb: Ignoring update to FS.\n");
-    if (r.gs != context->readMiscRegNoEffect(MISCREG_GS))
+    if (r.gs != context->readMiscRegNoEffect(misc_reg::Gs))
         warn("Remote gdb: Ignoring update to GS.\n");
 }
 
@@ -213,27 +213,27 @@
 RemoteGDB::X86GdbRegCache::setRegs(ThreadContext *context) const
 {
     DPRINTF(GDBAcc, "setRegs in remotegdb \n");
-    context->setIntReg(INTREG_RAX, r.eax);
-    context->setIntReg(INTREG_RCX, r.ecx);
-    context->setIntReg(INTREG_RDX, r.edx);
-    context->setIntReg(INTREG_RBX, r.ebx);
-    context->setIntReg(INTREG_RSP, r.esp);
-    context->setIntReg(INTREG_RBP, r.ebp);
-    context->setIntReg(INTREG_RSI, r.esi);
-    context->setIntReg(INTREG_RDI, r.edi);
+    context->setReg(int_reg::Rax, r.eax);
+    context->setReg(int_reg::Rcx, r.ecx);
+    context->setReg(int_reg::Rdx, r.edx);
+    context->setReg(int_reg::Rbx, r.ebx);
+    context->setReg(int_reg::Rsp, r.esp);
+    context->setReg(int_reg::Rbp, r.ebp);
+    context->setReg(int_reg::Rsi, r.esi);
+    context->setReg(int_reg::Rdi, r.edi);
     context->pcState(r.eip);
-    context->setMiscReg(MISCREG_RFLAGS, r.eflags);
-    if (r.cs != context->readMiscRegNoEffect(MISCREG_CS))
+    context->setMiscReg(misc_reg::Rflags, r.eflags);
+    if (r.cs != context->readMiscRegNoEffect(misc_reg::Cs))
         warn("Remote gdb: Ignoring update to CS.\n");
-    if (r.ss != context->readMiscRegNoEffect(MISCREG_SS))
+    if (r.ss != context->readMiscRegNoEffect(misc_reg::Ss))
         warn("Remote gdb: Ignoring update to SS.\n");
-    if (r.ds != context->readMiscRegNoEffect(MISCREG_DS))
+    if (r.ds != context->readMiscRegNoEffect(misc_reg::Ds))
         warn("Remote gdb: Ignoring update to DS.\n");
-    if (r.es != context->readMiscRegNoEffect(MISCREG_ES))
+    if (r.es != context->readMiscRegNoEffect(misc_reg::Es))
         warn("Remote gdb: Ignoring update to ES.\n");
-    if (r.fs != context->readMiscRegNoEffect(MISCREG_FS))
+    if (r.fs != context->readMiscRegNoEffect(misc_reg::Fs))
         warn("Remote gdb: Ignoring update to FS.\n");
-    if (r.gs != context->readMiscRegNoEffect(MISCREG_GS))
+    if (r.gs != context->readMiscRegNoEffect(misc_reg::Gs))
         warn("Remote gdb: Ignoring update to GS.\n");
 }
 
diff --git a/src/arch/x86/remote_gdb.hh b/src/arch/x86/remote_gdb.hh
index 62176a5..dfa9177 100644
--- a/src/arch/x86/remote_gdb.hh
+++ b/src/arch/x86/remote_gdb.hh
@@ -58,7 +58,7 @@
 {
   protected:
     bool acc(Addr addr, size_t len);
-    bool checkBpLen(size_t len) { return len == 1; }
+    bool checkBpKind(size_t kind) { return kind == 1; }
     class X86GdbRegCache : public BaseGdbRegCache
     {
       using BaseGdbRegCache::BaseGdbRegCache;
diff --git a/src/arch/x86/se_workload.hh b/src/arch/x86/se_workload.hh
index 49ddf20..0795a51 100644
--- a/src/arch/x86/se_workload.hh
+++ b/src/arch/x86/se_workload.hh
@@ -45,7 +45,6 @@
 const Addr ISTVirtAddr = 0xffff800000004000;
 const Addr PFHandlerVirtAddr = 0xffff800000005000;
 const Addr MMIORegionVirtAddr = 0xffffc90000000000;
-const Addr MMIORegionPhysAddr = 0xffff0000;
 
 } // namespace X86ISA
 } // namespace gem5
diff --git a/src/arch/x86/tlb.cc b/src/arch/x86/tlb.cc
index 2fc34fb..9d26653 100644
--- a/src/arch/x86/tlb.cc
+++ b/src/arch/x86/tlb.cc
@@ -176,7 +176,7 @@
 {
 
 Cycles
-localMiscRegAccess(bool read, MiscRegIndex regNum,
+localMiscRegAccess(bool read, RegIndex regNum,
                    ThreadContext *tc, PacketPtr pkt)
 {
     if (read) {
@@ -205,7 +205,7 @@
     } else if (prefix == IntAddrPrefixMSR) {
         vaddr = (vaddr >> 3) & ~IntAddrPrefixMask;
 
-        MiscRegIndex regNum;
+        RegIndex regNum;
         if (!msrAddrToIndex(regNum, vaddr))
             return std::make_shared<GeneralProtection>(0);
 
@@ -232,13 +232,13 @@
                 [read](ThreadContext *tc, PacketPtr pkt)
                 {
                     return localMiscRegAccess(
-                            read, MISCREG_PCI_CONFIG_ADDRESS, tc, pkt);
+                            read, misc_reg::PciConfigAddress, tc, pkt);
                 }
             );
         } else if ((IOPort & ~mask(2)) == 0xCFC) {
             req->setFlags(Request::UNCACHEABLE | Request::STRICT_ORDER);
             Addr configAddress =
-                tc->readMiscRegNoEffect(MISCREG_PCI_CONFIG_ADDRESS);
+                tc->readMiscRegNoEffect(misc_reg::PciConfigAddress);
             if (bits(configAddress, 31, 31)) {
                 req->setPaddr(PhysAddrPrefixPciConfig |
                         mbits(configAddress, 30, 2) |
@@ -280,7 +280,7 @@
     } else if (FullSystem) {
         // Check for an access to the local APIC
         LocalApicBase localApicBase =
-            tc->readMiscRegNoEffect(MISCREG_APIC_BASE);
+            tc->readMiscRegNoEffect(misc_reg::ApicBase);
         AddrRange apicRange(localApicBase.base * PageBytes,
                             (localApicBase.base + 1) * PageBytes);
 
@@ -319,14 +319,18 @@
 
     // If this is true, we're dealing with a request to a non-memory address
     // space.
-    if (seg == SEGMENT_REG_MS) {
+    if (seg == segment_idx::Ms) {
         return translateInt(mode == BaseMMU::Read, req, tc);
     }
 
     Addr vaddr = req->getVaddr();
     DPRINTF(TLB, "Translating vaddr %#x.\n", vaddr);
 
-    HandyM5Reg m5Reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
+    HandyM5Reg m5Reg = tc->readMiscRegNoEffect(misc_reg::M5Reg);
+
+    const Addr logAddrSize = (flags >> AddrSizeFlagShift) & AddrSizeFlagMask;
+    const int addrSize = 8 << logAddrSize;
+    const Addr addrMask = mask(addrSize);
 
     // If protected mode has been enabled...
     if (m5Reg.prot) {
@@ -334,28 +338,38 @@
         // If we're not in 64-bit mode, do protection/limit checks
         if (m5Reg.mode != LongMode) {
             DPRINTF(TLB, "Not in long mode. Checking segment protection.\n");
-            // Check for a NULL segment selector.
-            if (!(seg == SEGMENT_REG_TSG || seg == SYS_SEGMENT_REG_IDTR ||
-                        seg == SEGMENT_REG_HS || seg == SEGMENT_REG_LS)
-                    && !tc->readMiscRegNoEffect(MISCREG_SEG_SEL(seg)))
+
+            // CPUs won't know to use CS when building fetch requests, so we
+            // need to override the value of "seg" here if this is a fetch.
+            if (mode == BaseMMU::Execute)
+                seg = segment_idx::Cs;
+
+            SegAttr attr = tc->readMiscRegNoEffect(misc_reg::segAttr(seg));
+            // Check for an unusable segment.
+            if (attr.unusable) {
+                DPRINTF(TLB, "Unusable segment.\n");
                 return std::make_shared<GeneralProtection>(0);
+            }
             bool expandDown = false;
-            SegAttr attr = tc->readMiscRegNoEffect(MISCREG_SEG_ATTR(seg));
-            if (seg >= SEGMENT_REG_ES && seg <= SEGMENT_REG_HS) {
-                if (!attr.writable && (mode == BaseMMU::Write || storeCheck))
+            if (seg >= segment_idx::Es && seg <= segment_idx::Hs) {
+                if (!attr.writable && (mode == BaseMMU::Write || storeCheck)) {
+                    DPRINTF(TLB, "Tried to write to unwritable segment.\n");
                     return std::make_shared<GeneralProtection>(0);
-                if (!attr.readable && mode == BaseMMU::Read)
+                }
+                if (!attr.readable && mode == BaseMMU::Read) {
+                    DPRINTF(TLB, "Tried to read from unreadble segment.\n");
                     return std::make_shared<GeneralProtection>(0);
+                }
                 expandDown = attr.expandDown;
 
             }
-            Addr base = tc->readMiscRegNoEffect(MISCREG_SEG_BASE(seg));
-            Addr limit = tc->readMiscRegNoEffect(MISCREG_SEG_LIMIT(seg));
-            bool sizeOverride = (flags & (AddrSizeFlagBit << FlagShift));
-            unsigned logSize = sizeOverride ? (unsigned)m5Reg.altAddr
-                                            : (unsigned)m5Reg.defAddr;
-            int size = (1 << logSize) * 8;
-            Addr offset = bits(vaddr - base, size - 1, 0);
+            Addr base = tc->readMiscRegNoEffect(misc_reg::segBase(seg));
+            Addr limit = tc->readMiscRegNoEffect(misc_reg::segLimit(seg));
+            Addr offset;
+            if (mode == BaseMMU::Execute)
+                offset = vaddr - base;
+            else
+                offset = (vaddr - base) & addrMask;
             Addr endOffset = offset + req->getSize() - 1;
             if (expandDown) {
                 DPRINTF(TLB, "Checking an expand down segment.\n");
@@ -363,12 +377,14 @@
                 if (offset <= limit || endOffset <= limit)
                     return std::make_shared<GeneralProtection>(0);
             } else {
-                if (offset > limit || endOffset > limit)
+                if (offset > limit || endOffset > limit) {
+                    DPRINTF(TLB, "Segment limit check failed, "
+                            "offset = %#x limit = %#x.\n", offset, limit);
                     return std::make_shared<GeneralProtection>(0);
+                }
             }
         }
-        if (m5Reg.submode != SixtyFourBitMode ||
-                (flags & (AddrSizeFlagBit << FlagShift)))
+        if (m5Reg.submode != SixtyFourBitMode && addrSize != 64)
             vaddr &= mask(32);
         // If paging is enabled, do the translation.
         if (m5Reg.paging) {
@@ -421,9 +437,8 @@
             DPRINTF(TLB, "Entry found with paddr %#x, "
                     "doing protection checks.\n", entry->paddr);
             // Do paging protection checks.
-            bool inUser = (m5Reg.cpl == 3 &&
-                    !(flags & (CPL0FlagBit << FlagShift)));
-            CR0 cr0 = tc->readMiscRegNoEffect(MISCREG_CR0);
+            bool inUser = m5Reg.cpl == 3 && !(flags & CPL0FlagBit);
+            CR0 cr0 = tc->readMiscRegNoEffect(misc_reg::Cr0);
             bool badWrite = (!entry->writable && (inUser || cr0.wp));
             if ((inUser && !entry->user) ||
                 (mode == BaseMMU::Write && badWrite)) {
diff --git a/src/arch/x86/types.hh b/src/arch/x86/types.hh
index a2c2771..f377806 100644
--- a/src/arch/x86/types.hh
+++ b/src/arch/x86/types.hh
@@ -187,6 +187,12 @@
     Bitfield<2,0> submode;
 EndBitUnion(OperatingMode)
 
+BitUnion8(OperatingModeAndCPL)
+    Bitfield<5,4> cpl;
+    Bitfield<3> mode;
+    Bitfield<2,0> submode;
+EndBitUnion(OperatingModeAndCPL)
+
 enum X86Mode
 {
     LongMode,
@@ -236,7 +242,7 @@
     uint8_t dispSize;
 
     //Mode information
-    OperatingMode mode;
+    OperatingModeAndCPL mode;
 };
 
 inline static std::ostream &
diff --git a/src/arch/x86/utility.cc b/src/arch/x86/utility.cc
index 6b31d55..6228df7 100644
--- a/src/arch/x86/utility.cc
+++ b/src/arch/x86/utility.cc
@@ -57,10 +57,10 @@
 uint64_t
 getRFlags(ThreadContext *tc)
 {
-    const uint64_t ncc_flags(tc->readMiscRegNoEffect(MISCREG_RFLAGS));
-    const uint64_t cc_flags(tc->readCCReg(X86ISA::CCREG_ZAPS));
-    const uint64_t cfof_bits(tc->readCCReg(X86ISA::CCREG_CFOF));
-    const uint64_t df_bit(tc->readCCReg(X86ISA::CCREG_DF));
+    const uint64_t ncc_flags(tc->readMiscRegNoEffect(misc_reg::Rflags));
+    const uint64_t cc_flags(tc->getReg(X86ISA::cc_reg::Zaps));
+    const uint64_t cfof_bits(tc->getReg(X86ISA::cc_reg::Cfof));
+    const uint64_t df_bit(tc->getReg(X86ISA::cc_reg::Df));
     // ecf (PSEUDO(3)) & ezf (PSEUDO(4)) are only visible to
     // microcode, so we can safely ignore them.
 
@@ -73,17 +73,17 @@
 void
 setRFlags(ThreadContext *tc, uint64_t val)
 {
-    tc->setCCReg(X86ISA::CCREG_ZAPS, val & ccFlagMask);
-    tc->setCCReg(X86ISA::CCREG_CFOF, val & cfofMask);
-    tc->setCCReg(X86ISA::CCREG_DF, val & DFBit);
+    tc->setReg(X86ISA::cc_reg::Zaps, val & CcFlagMask);
+    tc->setReg(X86ISA::cc_reg::Cfof, val & CfofMask);
+    tc->setReg(X86ISA::cc_reg::Df, val & DFBit);
 
     // Internal microcode registers (ECF & EZF)
-    tc->setCCReg(X86ISA::CCREG_ECF, 0);
-    tc->setCCReg(X86ISA::CCREG_EZF, 0);
+    tc->setReg(X86ISA::cc_reg::Ecf, (RegVal)0);
+    tc->setReg(X86ISA::cc_reg::Ezf, (RegVal)0);
 
     // Update the RFLAGS misc reg with whatever didn't go into the
     // magic registers.
-    tc->setMiscReg(MISCREG_RFLAGS, val & ~(ccFlagMask | cfofMask | DFBit));
+    tc->setMiscReg(misc_reg::Rflags, val & ~(CcFlagMask | CfofMask | DFBit));
 }
 
 uint8_t
diff --git a/src/arch/x86/utility.hh b/src/arch/x86/utility.hh
index 94a0a4b..3720b46 100644
--- a/src/arch/x86/utility.hh
+++ b/src/arch/x86/utility.hh
@@ -53,9 +53,9 @@
      *
      * gem5 stores rflags in several different registers to avoid
      * pipeline dependencies. In order to get the true rflags value,
-     * we can't simply read the value of MISCREG_RFLAGS. Instead, we
+     * we can't simply read the value of misc_reg::Rflags. Instead, we
      * need to read out various state from microcode registers and
-     * merge that with MISCREG_RFLAGS.
+     * merge that with misc_reg::Rflags.
      *
      * @param tc Thread context to read rflags from.
      * @return rflags as seen by the guest.
@@ -65,9 +65,9 @@
     /**
      * Set update the rflags register and internal gem5 state.
      *
-     * @note This function does not update MISCREG_M5_REG. You might
+     * @note This function does not update misc_reg::M5Reg. You might
      * need to update this register by writing anything to
-     * MISCREG_M5_REG with side-effects.
+     * misc_reg::M5Reg with side-effects.
      *
      * @see X86ISA::getRFlags()
      *
diff --git a/src/arch/x86/vecregs.hh b/src/arch/x86/vecregs.hh
index 578360b..64b7911 100644
--- a/src/arch/x86/vecregs.hh
+++ b/src/arch/x86/vecregs.hh
@@ -41,10 +41,6 @@
 
 #include "arch/generic/vec_pred_reg.hh"
 #include "arch/generic/vec_reg.hh"
-#include "arch/x86/regs/ccr.hh"
-#include "arch/x86/regs/float.hh"
-#include "arch/x86/regs/int.hh"
-#include "arch/x86/regs/misc.hh"
 
 namespace gem5
 {
@@ -53,11 +49,7 @@
 {
 
 // Not applicable to x86
-using VecElem = ::gem5::DummyVecElem;
 using VecRegContainer = ::gem5::DummyVecRegContainer;
-constexpr unsigned NumVecElemPerVecReg = ::gem5::DummyNumVecElemPerVecReg;
-
-// Not applicable to x86
 using VecPredRegContainer = ::gem5::DummyVecPredRegContainer;
 
 } // namespace X86ISA
diff --git a/src/arch/x86/x86_traits.hh b/src/arch/x86/x86_traits.hh
index 9e98820..e4a6392 100644
--- a/src/arch/x86/x86_traits.hh
+++ b/src/arch/x86/x86_traits.hh
@@ -72,6 +72,9 @@
     // accesses from the CPU, and the other is for all APICs to communicate.
     const Addr PhysAddrAPICRangeSize = 1 << 12;
 
+    // Put this in an unused part of the 16 bit IO port address space.
+    const Addr PhysAddrIntA = 0x8000000100000000ULL;
+
     static inline Addr
     x86IOAddress(const uint32_t port)
     {
diff --git a/src/base/SConscript b/src/base/SConscript
index c49e2ae..e309182 100644
--- a/src/base/SConscript
+++ b/src/base/SConscript
@@ -40,16 +40,11 @@
 Source('cprintf.cc', add_tags='gtest lib')
 GTest('cprintf.test', 'cprintf.test.cc')
 Executable('cprintftime', 'cprintftime.cc', 'cprintf.cc')
-Source('debug.cc', add_tags='gem5 trace')
+Source('debug.cc', add_tags=['gem5 trace', 'gem5 events'])
 GTest('debug.test', 'debug.test.cc', 'debug.cc')
-if env['HAVE_FENV']:
-    Source('fenv.cc')
-else:
-    warning("No IEEE FP rounding mode control.\n"
-            "FP results may deviate slightly from other platforms.")
-if env['HAVE_PNG']:
-    env.Append(LIBS=['png'])
-    Source('pngwriter.cc')
+Source('fenv.cc', tags='fenv')
+SourceLib('png', tags='png')
+Source('pngwriter.cc', tags='png')
 Source('fiber.cc')
 GTest('fiber.test', 'fiber.test.cc', 'fiber.cc')
 GTest('flags.test', 'flags.test.cc')
@@ -65,12 +60,13 @@
     'cprintf.cc', 'gtest/logging.cc', skip_lib=True)
 Source('match.cc', add_tags='gem5 trace')
 GTest('match.test', 'match.test.cc', 'match.cc', 'str.cc')
+GTest('memoizer.test', 'memoizer.test.cc')
 Source('output.cc')
 Source('pixel.cc')
 GTest('pixel.test', 'pixel.test.cc', 'pixel.cc')
 Source('pollevent.cc')
 Source('random.cc')
-if env['TARGET_ISA'] != 'null':
+if env['CONF']['TARGET_ISA'] != 'null':
     Source('remote_gdb.cc')
 Source('socket.cc')
 GTest('socket.test', 'socket.test.cc', 'socket.cc')
diff --git a/src/base/SConsopts b/src/base/SConsopts
index 9fafd64..8e06612 100644
--- a/src/base/SConsopts
+++ b/src/base/SConsopts
@@ -31,30 +31,35 @@
 
 with gem5_scons.Configure(main) as conf:
     # Check for <fenv.h> (C99 FP environment control)
-    conf.env['HAVE_FENV'] = conf.CheckHeader('fenv.h', '<>')
+    conf.env['CONF']['HAVE_FENV'] = conf.CheckHeader('fenv.h', '<>')
 
-    if not conf.env['HAVE_FENV']:
+    if conf.env['CONF']['HAVE_FENV']:
+        conf.env.TagImplies('fenv', 'gem5 lib')
+    else:
         warning("Header file <fenv.h> not found.\n"
                 "This host has no IEEE FP rounding mode control.")
 
     # Check for <png.h> (libpng library needed if wanting to dump
     # frame buffer image in png format)
-    conf.env['HAVE_PNG'] = conf.CheckHeader('png.h', '<>')
+    conf.env['CONF']['HAVE_PNG'] = conf.CheckHeader('png.h', '<>')
 
-    if not conf.env['HAVE_PNG']:
+    if conf.env['CONF']['HAVE_PNG']:
+        conf.env.TagImplies('png', 'gem5 lib')
+    else:
         warning("Header file <png.h> not found.\n"
                 "This host has no libpng library.\n"
                 "Disabling support for PNG framebuffers.")
 
-    have_posix_clock = \
+    conf.env['CONF']['HAVE_POSIX_CLOCK'] = \
         conf.CheckLibWithHeader([None, 'rt'], 'time.h', 'C',
                                 'clock_nanosleep(0,0,NULL,NULL);')
-    if not have_posix_clock:
+    if not conf.env['CONF']['HAVE_POSIX_CLOCK']:
         warning("Can't find library for POSIX clocks.")
 
     # Valgrind gets much less confused if you tell it when you're using
     # alternative stacks.
-    conf.env['HAVE_VALGRIND'] = conf.CheckCHeader('valgrind/valgrind.h')
+    conf.env['CONF']['HAVE_VALGRIND'] = \
+            conf.CheckCHeader('valgrind/valgrind.h')
 
 
 # Check if the compiler supports the [[gnu::deprecated]] attribute
@@ -64,21 +69,16 @@
 with gem5_scons.Configure(werror_env) as conf:
 
     # Store result in the main environment
-    main['HAVE_DEPRECATED_NAMESPACE'] = conf.TryCompile('''
+    main['CONF']['HAVE_DEPRECATED_NAMESPACE'] = conf.TryCompile('''
         int main() {return 0;}
         namespace [[gnu::deprecated("Test namespace deprecation")]]
         test_deprecated_namespace {}
     ''', '.cc')
 
-    if not main['HAVE_DEPRECATED_NAMESPACE']:
+    if not main['CONF']['HAVE_DEPRECATED_NAMESPACE']:
         warning("Deprecated namespaces are not supported by this compiler.\n"
                 "Please make sure to check the mailing list for deprecation "
                 "announcements.")
 
 sticky_vars.Add(BoolVariable('USE_POSIX_CLOCK', 'Use POSIX Clocks',
-                             have_posix_clock))
-
-
-export_vars.extend([
-        'HAVE_FENV', 'HAVE_PNG', 'USE_POSIX_CLOCK', 'HAVE_VALGRIND',
-        'HAVE_DEPRECATED_NAMESPACE'])
+                             '${CONF["HAVE_POSIX_CLOCK"]}'))
diff --git a/src/base/bitfield.hh b/src/base/bitfield.hh
index b2f3d63..288c5ca 100644
--- a/src/base/bitfield.hh
+++ b/src/base/bitfield.hh
@@ -408,6 +408,34 @@
     return value ? __builtin_ctzll(value) : 64;
 }
 
+/**
+ * Count leading zeros in a 32-bit value.
+ *
+ * @param An input value
+ * @return The number of trailing zeros or 32 if the value is zero.
+ *
+ * @ingroup api_bitfield
+ */
+constexpr inline int
+clz32(uint32_t value)
+{
+    return value ? __builtin_clz(value) : 32;
+}
+
+/**
+ * Count leading zeros in a 64-bit value.
+ *
+ * @param An input value
+ * @return The number of trailing zeros or 64 if the value is zero.
+ *
+ * @ingroup api_bitfield
+ */
+constexpr inline int
+clz64(uint64_t value)
+{
+    return value ? __builtin_clzll(value) : 64;
+}
+
 } // namespace gem5
 
 #endif // __BASE_BITFIELD_HH__
diff --git a/src/base/bitfield.test.cc b/src/base/bitfield.test.cc
index 516e751..37830ea 100644
--- a/src/base/bitfield.test.cc
+++ b/src/base/bitfield.test.cc
@@ -420,3 +420,40 @@
     uint64_t value = 0;
     EXPECT_EQ(64, ctz64(value));
 }
+
+/*
+ * The following tests test clz32/64. The value returned in all cases should
+ * be equal to the number of leading zeros (i.e., the number of zeroes before
+ * the first bit set to one counting from the MSB).
+ */
+
+TEST(BitfieldTest, CountLeadingZeros32BitsNoTrailing)
+{
+    int32_t value = 1;
+    EXPECT_EQ(31, clz32(value));
+}
+
+TEST(BitfieldTest, CountLeadingZeros32Bits)
+{
+    uint32_t value = (1 << 30) + (1 << 29);
+    EXPECT_EQ(1, clz32(value));
+}
+
+TEST(BitfieldTest, CountLeadingZeros64BitsNoTrailing)
+{
+    uint64_t value = (1 << 29) + 1;
+    EXPECT_EQ(34, clz64(value));
+}
+
+TEST(BitfieldTest, CountLeadingZeros64Bits)
+{
+    uint64_t value = 1ULL << 63;
+    EXPECT_EQ(0, clz64(value));
+}
+
+TEST(BitfieldTest, CountLeadingZero64AllZeros)
+{
+    uint64_t value = 0;
+    EXPECT_EQ(64, clz64(value));
+}
+
diff --git a/src/base/bitunion.hh b/src/base/bitunion.hh
index 92d747c..c8bb659 100644
--- a/src/base/bitunion.hh
+++ b/src/base/bitunion.hh
@@ -261,7 +261,7 @@
 
         BitUnionOperators(const BitUnionOperators &) = default;
 
-        BitUnionOperators() {}
+        BitUnionOperators() = default;
 
         //Conversion operators.
         operator const typename Base::__StorageType () const
diff --git a/src/base/bitunion.test.cc b/src/base/bitunion.test.cc
index 7300efe..06c7a61 100644
--- a/src/base/bitunion.test.cc
+++ b/src/base/bitunion.test.cc
@@ -129,8 +129,8 @@
 // Declare these as global so g++ doesn't ignore them. Initialize them in
 // various ways.
 EmptySixtyFour emptySixtyFour = 0;
-EmptyThirtyTwo emptyThirtyTwo;
-EmptySixteen emptySixteen;
+EmptyThirtyTwo emptyThirtyTwo{};
+[[maybe_unused]] EmptySixteen emptySixteen;
 EmptyEight emptyEight(0);
 
 class BitUnionData : public testing::Test
diff --git a/src/base/loader/dtb_file.cc b/src/base/loader/dtb_file.cc
index f89b1ae..13e0264 100644
--- a/src/base/loader/dtb_file.cc
+++ b/src/base/loader/dtb_file.cc
@@ -63,12 +63,15 @@
 }
 
 bool
-DtbFile::addBootCmdLine(const char *_args, size_t len)
+DtbFile::addBootData(const char *_cmdline, size_t cmdline_len,
+                     off_t initrd_start, size_t initrd_len)
 {
     const char *root_path = "/";
     const char *node_name = "chosen";
     const char *full_path_node_name = "/chosen";
-    const char *property_name = "bootargs";
+    const char *bootargs_property_name = "bootargs";
+    const char *linux_initrd_start_property_name = "linux,initrd-start";
+    const char *linux_initrd_end_property_name = "linux,initrd-end";
 
     // Make a new buffer that has extra space to add nodes/properties
     int newLen = 2 * length;
@@ -102,8 +105,8 @@
     }
 
     // Set the bootargs property in the /chosen node
-    ret = fdt_setprop((void *)fdt_buf_w_space, offset, property_name,
-                      (const void *)_args, len+1);
+    ret = fdt_setprop((void *)fdt_buf_w_space, offset, bootargs_property_name,
+                      (const void *)_cmdline, cmdline_len+1);
     if (ret < 0) {
         warn("Error setting \"bootargs\" property to flattened device tree, "
              "errno: %d\n", ret);
@@ -111,6 +114,30 @@
         return false;
     }
 
+    if (initrd_len != 0) {
+        // Set the linux,initrd-start property in the /chosen node
+        ret = fdt_setprop_u64((void *)fdt_buf_w_space, offset,
+                              linux_initrd_start_property_name,
+                              (uint64_t)initrd_start);
+        if (ret < 0) {
+            warn("Error setting \"linux,initrd-start\" property to flattened "
+                 "device tree, errno: %d\n", ret);
+            delete [] fdt_buf_w_space;
+            return false;
+        }
+
+        // Set the computed linux,initrd-end property in the /chosen node
+        ret = fdt_setprop_u64((void *)fdt_buf_w_space, offset,
+                              linux_initrd_end_property_name,
+                              (uint64_t)initrd_start + initrd_len);
+        if (ret < 0) {
+            warn("Error setting \"linux,initrd-len\" property to flattened "
+                 "device tree, errno: %d\n", ret);
+            delete [] fdt_buf_w_space;
+            return false;
+        }
+    }
+
     // Repack the dtb for kernel use
     ret = fdt_pack((void *)fdt_buf_w_space);
     if (ret < 0) {
diff --git a/src/base/loader/dtb_file.hh b/src/base/loader/dtb_file.hh
index f0a211f..c11b195 100644
--- a/src/base/loader/dtb_file.hh
+++ b/src/base/loader/dtb_file.hh
@@ -58,13 +58,27 @@
     DtbFile(const std::string &name);
     ~DtbFile();
 
+    /** Adds the passed in Command Line options and initrd for the kernel
+      * to the proper location in the device tree.
+      * @param _cmdline command line to append
+      * @param cmdline_len length of the command line string
+      * @param initrd_addr starting physical address of initrd
+      * @param initrd_len length of initrd data in memory
+      * @return returns true on success, false otherwise
+      */
+    bool addBootData(const char* _cmdline, size_t cmdline_len,
+                     off_t initrd_addr, size_t initrd_len);
+
     /** Adds the passed in Command Line options for the kernel
       * to the proper location in the device tree.
       * @param _args command line to append
       * @param len length of the command line string
       * @return returns true on success, false otherwise
       */
-    bool addBootCmdLine(const char* _args, size_t len);
+    inline bool
+    addBootCmdLine(const char* _args, size_t len) {
+        return addBootData(_args, len, 0, 0);
+    }
 
     /** Parse the DTB file enough to find the provided release
      * address and return it.
diff --git a/src/base/loader/image_file_data.cc b/src/base/loader/image_file_data.cc
index f99c5f3..57fb47f 100644
--- a/src/base/loader/image_file_data.cc
+++ b/src/base/loader/image_file_data.cc
@@ -108,7 +108,9 @@
 
     // Open the file.
     int fd = open(fname.c_str(), O_RDONLY);
-    panic_if(fd < 0, "Failed to open file %s.\n", fname);
+    fatal_if(fd < 0, "Failed to open file %s.\n"
+        "This error typically occurs when the file path specified is "
+        "incorrect.\n", fname);
 
     // Decompress GZ files.
     if (hasGzipMagic(fd)) {
diff --git a/src/base/loader/object_file.cc b/src/base/loader/object_file.cc
index bfa0a1d..3aa5915 100644
--- a/src/base/loader/object_file.cc
+++ b/src/base/loader/object_file.cc
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2022 Arm Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2002-2004 The Regents of The University of Michigan
  * All rights reserved.
  *
@@ -136,5 +148,20 @@
     return nullptr;
 }
 
+bool
+archIs64Bit(const loader::Arch arch)
+{
+    switch (arch) {
+      case SPARC64:
+      case X86_64:
+      case Arm64:
+      case Power64:
+      case Riscv64:
+        return true;
+      default:
+        return false;
+    }
+}
+
 } // namespace loader
 } // namespace gem5
diff --git a/src/base/loader/object_file.hh b/src/base/loader/object_file.hh
index e8a96dc..0415bec 100644
--- a/src/base/loader/object_file.hh
+++ b/src/base/loader/object_file.hh
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2022 Arm Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2002-2004 The Regents of The University of Michigan
  * All rights reserved.
  *
@@ -139,6 +151,10 @@
 
 ObjectFile *createObjectFile(const std::string &fname, bool raw=false);
 
+/** Determine whether the loader::Arch is 64-bit or 32-bit. */
+bool
+archIs64Bit(const Arch arch);
+
 } // namespace loader
 } // namespace gem5
 
diff --git a/src/base/memoizer.hh b/src/base/memoizer.hh
new file mode 100644
index 0000000..c339088
--- /dev/null
+++ b/src/base/memoizer.hh
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2022 Arm Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BASE_MEMOIZER_HH__
+#define __BASE_MEMOIZER_HH__
+
+#include <functional>
+#include <map>
+#include <type_traits>
+#include <utility>
+
+namespace gem5
+{
+
+/**
+ * This class takes a function as a constructor argument and memoizes
+ * it: every time the function gets invoked through the Memoizer object
+ * (see operator()), the result gets saved in the internal cache, ready
+ * to be retrieved next time an invokation is made with the same
+ * arguments.
+ *
+ * Example usage:
+ *
+ * int fibonacci(int n);
+ *
+ * Memoizer fibonacci_memo(fibonacci);
+ * fibonacci_memo(5);
+ *
+ * Not every function is eligible for memoization. We explicitly
+ * validate the memoizer at compile time and produce an error if the
+ * function signature contains a reference.
+ *
+ * There are two ways to discard a memoization
+ *
+ * 1) Delete the Memoizer object
+ * 2) Use the Memoizer::flush method
+ *
+ * In some cases there is little or no reason to discard a memoization
+ * (like in the fibonacci example, where fibonacci(k) always returns
+ * the same value for the same input k)
+ * The memoizer could be used in more complex cases, where a change in
+ * the global state affects the output of the function, which
+ * effectively invalidates the cached results.
+ * It is up to the client to understand when memoization is no longer
+ * valid and to flush the result cache as a consequence.
+ */
+template <typename Ret, typename... Args>
+class Memoizer
+{
+  public:
+    using ret_type = Ret;
+    using args_type = std::tuple<Args...>;
+
+    constexpr Memoizer(Ret _func(Args...))
+     : func(_func)
+    {
+        validateMemoizer();
+    }
+
+    Memoizer() = delete;
+    Memoizer(const Memoizer &rhs) = delete;
+    Memoizer& operator=(const Memoizer &rhs) = delete;
+
+    auto
+    operator()(Args... args) const
+    {
+        auto key = std::make_tuple(args...);
+        if (auto it = cache.find(key); it != cache.end()) {
+            // Return the cached result
+            return it->second;
+        }
+        auto emplaced = cache.emplace(key, func(args...));
+        return emplaced.first->second;
+    };
+
+    /** Clear the memoization cache */
+    void flush() { cache.clear(); }
+
+  protected:
+    /** True if the passed value is cached, false otherwise */
+    bool
+    cached(args_type args) const
+    {
+        return cache.find(args) != cache.end();
+    }
+
+    /** Return the size of the memoizer cache */
+    size_t
+    cacheSize() const
+    {
+        return cache.size();
+    }
+
+  private:
+    /**
+     * Validate the memoizer and produce an error if the
+     * function signature contains a reference.
+     */
+    constexpr void
+    validateMemoizer()
+    {
+        constexpr size_t num_args = std::tuple_size_v<std::decay_t<args_type>>;
+        iterateTupleArgs<size_t(0), num_args, size_t(1)>([&](auto i) {
+            static_assert(!std::is_reference_v<
+                typename std::tuple_element<
+                    i.value, args_type>::type>);
+        });
+    }
+
+    template <size_t Start, size_t End, size_t Inc, class F>
+    constexpr void
+    iterateTupleArgs(F&& func)
+    {
+        if constexpr (Start < End) {
+            func(std::integral_constant<decltype(Start), Start>());
+            iterateTupleArgs<Start + Inc, End, Inc>(func);
+        }
+    }
+
+  private:
+    /** Memoized function */
+    const std::function<Ret(Args...)> func;
+    /** Result cache */
+    mutable std::map<args_type, ret_type> cache;
+
+};
+
+} // namespace gem5
+
+#endif // __BASE_MEMOIZER_HH__
diff --git a/src/base/memoizer.test.cc b/src/base/memoizer.test.cc
new file mode 100644
index 0000000..4e8592b
--- /dev/null
+++ b/src/base/memoizer.test.cc
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2022 Arm Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gtest/gtest.h>
+
+#include "base/memoizer.hh"
+
+using namespace gem5;
+
+namespace
+{
+
+uint32_t
+fibonacci(uint32_t n)
+{
+    if (n == 0) return 0;
+    if (n == 1) return 1;
+
+    return fibonacci(n-1) + fibonacci(n-2);
+}
+
+using FibonacciMemoizer = decltype(Memoizer(fibonacci));
+
+class FibonacciMemoizerFixture : public FibonacciMemoizer,
+                                 public ::testing::Test
+{
+  public:
+    FibonacciMemoizerFixture()
+      : FibonacciMemoizer(fibonacci)
+    {}
+
+};
+
+}
+
+/**
+ * Testing result cache before and after a memoized call
+ */
+TEST_F(FibonacciMemoizerFixture, Uncached)
+{
+    const auto res10 = fibonacci(10);
+
+    // Fresh memoizer, input = 10 shouldn't be present
+    ASSERT_FALSE(cached(10));
+
+    // We are now memoizing the result and making sure
+    // it provides the same value
+    EXPECT_EQ((*this)(10), res10);
+
+    // Now the fibonacci output for input = 10 should be cached
+    ASSERT_TRUE(cached(10));
+}
+
+/**
+ * Just checking memoization works for multiple values
+ */
+TEST_F(FibonacciMemoizerFixture, MultipleValues)
+{
+    const auto res0 = fibonacci(0);
+    const auto res10 = fibonacci(10);
+    const auto res20 = fibonacci(20);
+
+    EXPECT_EQ((*this)(0), res0);
+    EXPECT_EQ((*this)(10), res10);
+    EXPECT_EQ((*this)(20), res20);
+
+    EXPECT_EQ(cacheSize(), 3);
+
+    EXPECT_TRUE(cached(0));
+    EXPECT_TRUE(cached(10));
+    EXPECT_TRUE(cached(20));
+
+    // fibonacci(30) shouldn't be cached
+    EXPECT_FALSE(cached(30));
+}
+
+/**
+ * Testing the Memoizer::flush method
+ */
+TEST_F(FibonacciMemoizerFixture, CacheFlush)
+{
+    const auto res10 = fibonacci(10);
+
+    ASSERT_EQ(cacheSize(), 0);
+
+    // Memoizing fibonacci(10)
+    EXPECT_EQ((*this)(10), res10);
+    ASSERT_EQ(cacheSize(), 1);
+
+    // Flushing the cache
+    flush();
+
+    // Cache should be empty now
+    ASSERT_EQ(cacheSize(), 0);
+}
diff --git a/src/base/remote_gdb.cc b/src/base/remote_gdb.cc
index 1437b75..798d09f 100644
--- a/src/base/remote_gdb.cc
+++ b/src/base/remote_gdb.cc
@@ -784,28 +784,28 @@
 }
 
 void
-BaseRemoteGDB::insertSoftBreak(Addr addr, size_t len)
+BaseRemoteGDB::insertSoftBreak(Addr addr, size_t kind)
 {
-    if (!checkBpLen(len))
-        throw BadClient("Invalid breakpoint length\n");
+    if (!checkBpKind(kind))
+        throw BadClient("Invalid breakpoint kind.\n");
 
-    return insertHardBreak(addr, len);
+    return insertHardBreak(addr, kind);
 }
 
 void
-BaseRemoteGDB::removeSoftBreak(Addr addr, size_t len)
+BaseRemoteGDB::removeSoftBreak(Addr addr, size_t kind)
 {
-    if (!checkBpLen(len))
-        throw BadClient("Invalid breakpoint length.\n");
+    if (!checkBpKind(kind))
+        throw BadClient("Invalid breakpoint kind.\n");
 
-    return removeHardBreak(addr, len);
+    return removeHardBreak(addr, kind);
 }
 
 void
-BaseRemoteGDB::insertHardBreak(Addr addr, size_t len)
+BaseRemoteGDB::insertHardBreak(Addr addr, size_t kind)
 {
-    if (!checkBpLen(len))
-        throw BadClient("Invalid breakpoint length\n");
+    if (!checkBpKind(kind))
+        throw BadClient("Invalid breakpoint kind.\n");
 
     DPRINTF(GDBMisc, "Inserting hardware breakpoint at %#x\n", addr);
 
@@ -817,10 +817,10 @@
 }
 
 void
-BaseRemoteGDB::removeHardBreak(Addr addr, size_t len)
+BaseRemoteGDB::removeHardBreak(Addr addr, size_t kind)
 {
-    if (!checkBpLen(len))
-        throw BadClient("Invalid breakpoint length\n");
+    if (!checkBpKind(kind))
+        throw BadClient("Invalid breakpoint kind.\n");
 
     DPRINTF(GDBMisc, "Removing hardware breakpoint at %#x\n", addr);
 
@@ -917,7 +917,7 @@
 };
 
 bool
-BaseRemoteGDB::checkBpLen(size_t len)
+BaseRemoteGDB::checkBpKind(size_t kind)
 {
     return true;
 }
@@ -1302,17 +1302,17 @@
     Addr addr = hex2i(&p);
     if (*p++ != ',')
         throw CmdError("E0D");
-    size_t len = hex2i(&p);
+    size_t kind = hex2i(&p);
 
-    DPRINTF(GDBMisc, "clear %s, addr=%#x, len=%d\n",
-            breakType(sub_cmd), addr, len);
+    DPRINTF(GDBMisc, "clear %s, addr=%#x, kind=%d\n",
+            breakType(sub_cmd), addr, kind);
 
     switch (sub_cmd) {
       case GdbSoftBp:
-        removeSoftBreak(addr, len);
+        removeSoftBreak(addr, kind);
         break;
       case GdbHardBp:
-        removeHardBreak(addr, len);
+        removeHardBreak(addr, kind);
         break;
       case GdbWriteWp:
       case GdbReadWp:
@@ -1335,17 +1335,17 @@
     Addr addr = hex2i(&p);
     if (*p++ != ',')
         throw CmdError("E0D");
-    size_t len = hex2i(&p);
+    size_t kind = hex2i(&p);
 
-    DPRINTF(GDBMisc, "set %s, addr=%#x, len=%d\n",
-            breakType(sub_cmd), addr, len);
+    DPRINTF(GDBMisc, "set %s, addr=%#x, kind=%d\n",
+            breakType(sub_cmd), addr, kind);
 
     switch (sub_cmd) {
       case GdbSoftBp:
-        insertSoftBreak(addr, len);
+        insertSoftBreak(addr, kind);
         break;
       case GdbHardBp:
-        insertHardBreak(addr, len);
+        insertHardBreak(addr, kind);
         break;
       case GdbWriteWp:
       case GdbReadWp:
diff --git a/src/base/remote_gdb.hh b/src/base/remote_gdb.hh
index 59bccc5..b297e08 100644
--- a/src/base/remote_gdb.hh
+++ b/src/base/remote_gdb.hh
@@ -313,10 +313,10 @@
     void descheduleInstCommitEvent(Event *ev);
 
     // Breakpoints.
-    void insertSoftBreak(Addr addr, size_t len);
-    void removeSoftBreak(Addr addr, size_t len);
-    void insertHardBreak(Addr addr, size_t len);
-    void removeHardBreak(Addr addr, size_t len);
+    void insertSoftBreak(Addr addr, size_t kind);
+    void removeSoftBreak(Addr addr, size_t kind);
+    void insertHardBreak(Addr addr, size_t kind);
+    void removeHardBreak(Addr addr, size_t kind);
 
     /*
      * GDB commands.
@@ -401,8 +401,10 @@
     void encodeXferResponse(const std::string &unencoded,
         std::string &encoded, size_t offset, size_t unencoded_length) const;
 
-    // To be implemented by subclasses.
-    virtual bool checkBpLen(size_t len);
+    // checkBpKind checks if a kind of breakpoint is legal. This function should
+    // be implemented by subclasses by arch. The "kind" is considered to be
+    // breakpoint size in some arch.
+    virtual bool checkBpKind(size_t kind);
 
     virtual BaseGdbRegCache *gdbRegs() = 0;
 
diff --git a/src/base/sat_counter.hh b/src/base/sat_counter.hh
index 6644b05..a607c4c 100644
--- a/src/base/sat_counter.hh
+++ b/src/base/sat_counter.hh
@@ -340,9 +340,6 @@
 typedef GenericSatCounter<uint64_t> SatCounter64;
 /** @} */
 
-[[deprecated("Use SatCounter8 (or variants) instead")]]
-typedef SatCounter8 SatCounter;
-
 } // namespace gem5
 
 #endif // __BASE_SAT_COUNTER_HH__
diff --git a/src/base/socket.cc b/src/base/socket.cc
index 860249a..5cf67fd 100644
--- a/src/base/socket.cc
+++ b/src/base/socket.cc
@@ -155,7 +155,12 @@
     if (::listen(fd, 1) == -1) {
         if (errno != EADDRINUSE)
             panic("ListenSocket(listen): listen() failed!");
-
+        // User may decide to retry with a different port later; however, the
+        // socket is already bound to a port and the next bind will surely
+        // fail. We'll close the socket and reset fd to -1 so our user can
+        // retry with a cleaner state.
+        close(fd);
+        fd = -1;
         return false;
     }
 
diff --git a/src/base/socket.hh b/src/base/socket.hh
index 4ed6185..3375ccc 100644
--- a/src/base/socket.hh
+++ b/src/base/socket.hh
@@ -62,14 +62,6 @@
      */
     static void cleanup();
 
-  private:
-    /* Create a socket, adding SOCK_CLOEXEC if available. */
-    static int socketCloexec(int domain, int type, int protocol);
-    /* Accept a connection, adding SOCK_CLOEXEC if available. */
-    static int acceptCloexec(int sockfd, struct sockaddr *addr,
-                              socklen_t *addrlen);
-
-
   public:
     /**
      * @ingroup api_socket
@@ -84,6 +76,12 @@
 
     int getfd() const { return fd; }
     bool islistening() const { return listening; }
+
+    /* Create a socket, adding SOCK_CLOEXEC if available. */
+    static int socketCloexec(int domain, int type, int protocol);
+    /* Accept a connection, adding SOCK_CLOEXEC if available. */
+    static int acceptCloexec(int sockfd, struct sockaddr *addr,
+                              socklen_t *addrlen);
     /** @} */ // end of api_socket
 };
 
diff --git a/src/base/stats/SConscript b/src/base/stats/SConscript
index 841e455..0599d50 100644
--- a/src/base/stats/SConscript
+++ b/src/base/stats/SConscript
@@ -34,11 +34,10 @@
 Source('storage.cc')
 Source('text.cc')
 
-if env['HAVE_HDF5']:
-    if main['GCC']:
-        Source('hdf5.cc', append={'CXXFLAGS': '-Wno-deprecated-copy'})
-    else:
-        Source('hdf5.cc')
+if env['GCC']:
+    Source('hdf5.cc', append={'CXXFLAGS': '-Wno-deprecated-copy'}, tags='hdf5')
+else:
+    Source('hdf5.cc', tags='hdf5')
 
 GTest('group.test', 'group.test.cc', 'group.cc', 'info.cc',
     with_tag('gem5 trace'))
diff --git a/src/base/stats/SConsopts b/src/base/stats/SConsopts
index 0869a83..ce0e880 100644
--- a/src/base/stats/SConsopts
+++ b/src/base/stats/SConsopts
@@ -41,13 +41,13 @@
     # include path and library path provided by pkg-config. We perform
     # this check even if there isn't a pkg-config configuration for hdf5
     # since some installations don't use pkg-config.
-    conf.env['HAVE_HDF5'] = \
+    conf.env['CONF']['HAVE_HDF5'] = \
             conf.CheckLibWithHeader('hdf5', 'hdf5.h', 'C',
                                     'H5Fcreate("", 0, 0, 0);') and \
             conf.CheckLibWithHeader('hdf5_cpp', 'H5Cpp.h', 'C++',
                                     'H5::H5File("", 0);')
 
-    if not conf.env['HAVE_HDF5']:
+    if conf.env['CONF']['HAVE_HDF5']:
+        conf.env.TagImplies('hdf5', 'gem5 lib')
+    else:
         warning("Couldn't find HDF5 C++ libraries. Disabling HDF5 support.")
-
-export_vars.append('HAVE_HDF5')
diff --git a/src/base/types.hh b/src/base/types.hh
index 9a59053..913455f 100644
--- a/src/base/types.hh
+++ b/src/base/types.hh
@@ -175,13 +175,6 @@
 // Logical register index type.
 using RegIndex = uint16_t;
 
-/** Logical vector register elem index type. */
-using ElemIndex = uint16_t;
-
-/** ElemIndex value that indicates that the register is not a vector. */
-static const ElemIndex IllegalElemIndex =
-    std::numeric_limits<ElemIndex>::max();
-
 static inline uint32_t
 floatToBits32(float val)
 {
diff --git a/src/base/version.cc b/src/base/version.cc
index 057129b..a269987 100644
--- a/src/base/version.cc
+++ b/src/base/version.cc
@@ -32,6 +32,6 @@
 /**
  * @ingroup api_base_utils
  */
-const char *gem5Version = "21.2.1.1";
+const char *gem5Version = "22.0.0.0";
 
 } // namespace gem5
diff --git a/src/cpu/BaseCPU.py b/src/cpu/BaseCPU.py
index 5eeedd3..bf4d43c 100644
--- a/src/cpu/BaseCPU.py
+++ b/src/cpu/BaseCPU.py
@@ -56,41 +56,6 @@
 
 default_tracer = ExeTracer()
 
-if buildEnv['TARGET_ISA'] == 'sparc':
-    from m5.objects.SparcMMU import SparcMMU as ArchMMU
-    from m5.objects.SparcInterrupts import SparcInterrupts as ArchInterrupts
-    from m5.objects.SparcISA import SparcISA as ArchISA
-    from m5.objects.SparcDecoder import SparcDecoder as ArchDecoder
-elif buildEnv['TARGET_ISA'] == 'x86':
-    from m5.objects.X86MMU import X86MMU as ArchMMU
-    from m5.objects.X86LocalApic import X86LocalApic as ArchInterrupts
-    from m5.objects.X86ISA import X86ISA as ArchISA
-    from m5.objects.X86Decoder import X86Decoder as ArchDecoder
-elif buildEnv['TARGET_ISA'] == 'mips':
-    from m5.objects.MipsMMU import MipsMMU as ArchMMU
-    from m5.objects.MipsInterrupts import MipsInterrupts as ArchInterrupts
-    from m5.objects.MipsISA import MipsISA as ArchISA
-    from m5.objects.MipsDecoder import MipsDecoder as ArchDecoder
-elif buildEnv['TARGET_ISA'] == 'arm':
-    from m5.objects.ArmMMU import ArmMMU as ArchMMU
-    from m5.objects.ArmInterrupts import ArmInterrupts as ArchInterrupts
-    from m5.objects.ArmISA import ArmISA as ArchISA
-    from m5.objects.ArmDecoder import ArmDecoder as ArchDecoder
-elif buildEnv['TARGET_ISA'] == 'power':
-    from m5.objects.PowerMMU import PowerMMU as ArchMMU
-    from m5.objects.PowerInterrupts import PowerInterrupts as ArchInterrupts
-    from m5.objects.PowerISA import PowerISA as ArchISA
-    from m5.objects.PowerDecoder import PowerDecoder as ArchDecoder
-elif buildEnv['TARGET_ISA'] == 'riscv':
-    from m5.objects.RiscvMMU import RiscvMMU as ArchMMU
-    from m5.objects.RiscvInterrupts import RiscvInterrupts as ArchInterrupts
-    from m5.objects.RiscvISA import RiscvISA as ArchISA
-    from m5.objects.RiscvDecoder import RiscvDecoder as ArchDecoder
-else:
-    print("Don't know what object types to use for ISA %s" %
-            buildEnv['TARGET_ISA'])
-    sys.exit(1)
-
 class BaseCPU(ClockedObject):
     type = 'BaseCPU'
     abstract = True
@@ -155,7 +120,7 @@
 
     workload = VectorParam.Process([], "processes to run")
 
-    mmu = Param.BaseMMU(ArchMMU(), "CPU memory management unit")
+    mmu = Param.BaseMMU(NULL, "CPU memory management unit")
     interrupts = VectorParam.BaseInterrupts([], "Interrupt Controller")
     isa = VectorParam.BaseISA([], "ISA instance")
     decoder = VectorParam.InstDecoder([], "Decoder instance")
@@ -183,7 +148,8 @@
     _uncached_interrupt_request_ports = []
 
     def createInterruptController(self):
-        self.interrupts = [ArchInterrupts() for i in range(self.numThreads)]
+        self.interrupts = [
+                self.ArchInterrupts() for i in range(self.numThreads)]
 
     def connectCachedPorts(self, in_ports):
         for p in self._cached_ports:
@@ -217,13 +183,13 @@
             self._cached_ports += ["itb_walker_cache.mem_side", \
                                    "dtb_walker_cache.mem_side"]
         else:
-            self._cached_ports += ArchMMU.walkerPorts()
+            self._cached_ports += self.ArchMMU.walkerPorts()
 
         # Checker doesn't need its own tlb caches because it does
         # functional accesses only
         if self.checker != NULL:
             self._cached_ports += [ "checker." + port
-                for port in ArchMMU.walkerPorts() ]
+                for port in self.ArchMMU.walkerPorts() ]
 
     def addTwoLevelCacheHierarchy(self, ic, dc, l2c, iwc=None, dwc=None,
                                   xbar=None):
@@ -238,14 +204,14 @@
         # If no ISAs have been created, assume that the user wants the
         # default ISA.
         if len(self.isa) == 0:
-            self.isa = list([ ArchISA() for i in range(self.numThreads) ])
+            self.isa = [ self.ArchISA() for i in range(self.numThreads) ]
         else:
             if len(self.isa) != int(self.numThreads):
                 raise RuntimeError("Number of ISA instances doesn't "
                                    "match thread count")
         if len(self.decoder) != 0:
             raise RuntimeError("Decoders should not be set up manually")
-        self.decoder = list([ ArchDecoder(isa=isa) for isa in self.isa ])
+        self.decoder = list([ self.ArchDecoder(isa=isa) for isa in self.isa ])
         if self.checker != NULL:
             self.checker.createThreads()
 
@@ -308,18 +274,18 @@
         super().__init__(**kwargs)
         self.power_state.possible_states=['ON', 'CLK_GATED', 'OFF']
 
-        self._cached_ports = self._cached_ports + ArchMMU.walkerPorts()
+        self._cached_ports = self._cached_ports + self.ArchMMU.walkerPorts()
 
         # Practically speaking, these ports will exist on the x86 interrupt
         # controller class.
-        if "pio" in ArchInterrupts._ports:
+        if "pio" in self.ArchInterrupts._ports:
             self._uncached_interrupt_response_ports = \
                 self._uncached_interrupt_response_ports + ["interrupts[0].pio"]
-        if "int_responder" in ArchInterrupts._ports:
+        if "int_responder" in self.ArchInterrupts._ports:
             self._uncached_interrupt_response_ports = \
                     self._uncached_interrupt_response_ports + [
                     "interrupts[0].int_responder"]
-        if "int_requestor" in ArchInterrupts._ports:
+        if "int_requestor" in self.ArchInterrupts._ports:
             self._uncached_interrupt_request_ports = \
                     self._uncached_interrupt_request_ports + [
                     "interrupts[0].int_requestor"]
diff --git a/src/cpu/SConscript b/src/cpu/SConscript
index 4dcec7c..fad601e 100644
--- a/src/cpu/SConscript
+++ b/src/cpu/SConscript
@@ -85,13 +85,12 @@
 SimObject('FuncUnit.py', sim_objects=['OpDesc', 'FUDesc'], enums=['OpClass'])
 SimObject('StaticInstFlags.py', enums=['StaticInstFlags'])
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 # Only build the protobuf instructions tracer if we have protobuf support.
-if env['HAVE_PROTOBUF']:
-    SimObject('InstPBTrace.py', sim_objects=['InstPBTrace'])
-    Source('inst_pb_trace.cc')
+SimObject('InstPBTrace.py', sim_objects=['InstPBTrace'], tags='protobuf')
+Source('inst_pb_trace.cc', tags='protobuf')
 
 SimObject('CheckerCPU.py', sim_objects=['CheckerCPU'])
 
diff --git a/src/cpu/SConsopts b/src/cpu/SConsopts
deleted file mode 100644
index c39d1eb..0000000
--- a/src/cpu/SConsopts
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2020 Google, Inc.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-Import('*')
-
-def add_cpu_models_var():
-    sticky_vars.Add(ListVariable('CPU_MODELS', 'CPU models', [],
-                sorted(set(main.Split('${ALL_CPU_MODELS}')))))
-AfterSConsopts(add_cpu_models_var)
diff --git a/src/cpu/base.cc b/src/cpu/base.cc
index 741ee2d..5e8b7c1 100644
--- a/src/cpu/base.cc
+++ b/src/cpu/base.cc
@@ -431,15 +431,10 @@
              "per thread (%i)\n",
              name(), interrupts.size(), numThreads);
 
-    ThreadID size = threadContexts.size();
-    for (ThreadID tid = 0; tid < size; ++tid) {
+    for (ThreadID tid = 0; tid < threadContexts.size(); ++tid) {
         ThreadContext *tc = threadContexts[tid];
 
-        if (system->multiThread) {
-            system->registerThreadContext(tc);
-        } else {
-            system->registerThreadContext(tc, _cpuId);
-        }
+        system->registerThreadContext(tc);
 
         if (!FullSystem)
             tc->getProcessPtr()->assignThreadContext(tc->contextId());
diff --git a/src/cpu/checker/SConsopts b/src/cpu/checker/SConsopts
deleted file mode 100644
index 5a7a873..0000000
--- a/src/cpu/checker/SConsopts
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2003-2006 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-Import('*')
-
-main.Append(ALL_CPU_MODELS=['CheckerCPU'])
diff --git a/src/cpu/checker/cpu.cc b/src/cpu/checker/cpu.cc
index f374331..ca1f1a0 100644
--- a/src/cpu/checker/cpu.cc
+++ b/src/cpu/checker/cpu.cc
@@ -64,7 +64,6 @@
 
 CheckerCPU::CheckerCPU(const Params &p)
     : BaseCPU(p, true),
-      zeroReg(params().isa[0]->regClasses().at(IntRegClass).zeroReg()),
       systemPtr(NULL), icachePort(NULL), dcachePort(NULL),
       tc(NULL), thread(NULL),
       unverifiedReq(nullptr),
diff --git a/src/cpu/checker/cpu.hh b/src/cpu/checker/cpu.hh
index b76c034..c455cf5 100644
--- a/src/cpu/checker/cpu.hh
+++ b/src/cpu/checker/cpu.hh
@@ -87,8 +87,6 @@
     /** id attached to all issued requests */
     RequestorID requestorId;
 
-    const RegIndex zeroReg;
-
   public:
     void init() override;
 
@@ -176,128 +174,44 @@
     // to do).
 
     RegVal
-    readIntRegOperand(const StaticInst *si, int idx) override
+    getRegOperand(const StaticInst *si, int idx) override
     {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(IntRegClass));
-        return thread->readIntReg(reg.index());
-    }
-
-    RegVal
-    readFloatRegOperandBits(const StaticInst *si, int idx) override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(FloatRegClass));
-        return thread->readFloatReg(reg.index());
-    }
-
-    /**
-     * Read source vector register operand.
-     */
-    const TheISA::VecRegContainer &
-    readVecRegOperand(const StaticInst *si, int idx) const override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(VecRegClass));
-        return thread->readVecReg(reg);
-    }
-
-    /**
-     * Read destination vector register operand for modification.
-     */
-    TheISA::VecRegContainer &
-    getWritableVecRegOperand(const StaticInst *si, int idx) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecRegClass));
-        return thread->getWritableVecReg(reg);
-    }
-
-    RegVal
-    readVecElemOperand(const StaticInst *si, int idx) const override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        return thread->readVecElem(reg);
-    }
-
-    const TheISA::VecPredRegContainer&
-    readVecPredRegOperand(const StaticInst *si, int idx) const override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(VecPredRegClass));
-        return thread->readVecPredReg(reg);
-    }
-
-    TheISA::VecPredRegContainer&
-    getWritableVecPredRegOperand(const StaticInst *si, int idx) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecPredRegClass));
-        return thread->getWritableVecPredReg(reg);
-    }
-
-    RegVal
-    readCCRegOperand(const StaticInst *si, int idx) override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(CCRegClass));
-        return thread->readCCReg(reg.index());
+        const RegId& id = si->srcRegIdx(idx);
+        if (id.is(InvalidRegClass))
+            return 0;
+        return thread->getReg(id);
     }
 
     void
-    setIntRegOperand(const StaticInst *si, int idx, RegVal val) override
+    getRegOperand(const StaticInst *si, int idx, void *val) override
     {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(IntRegClass));
-        thread->setIntReg(reg.index(), val);
+        thread->getReg(si->srcRegIdx(idx), val);
+    }
+
+    void *
+    getWritableRegOperand(const StaticInst *si, int idx) override
+    {
+        return thread->getWritableReg(si->destRegIdx(idx));
+    }
+
+    void
+    setRegOperand(const StaticInst *si, int idx, RegVal val) override
+    {
+        const RegId& id = si->destRegIdx(idx);
+        if (id.is(InvalidRegClass))
+            return;
+        thread->setReg(id, val);
         result.emplace(val);
     }
 
     void
-    setFloatRegOperandBits(const StaticInst *si, int idx, RegVal val) override
+    setRegOperand(const StaticInst *si, int idx, const void *val) override
     {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(FloatRegClass));
-        thread->setFloatReg(reg.index(), val);
-        result.emplace(val);
-    }
-
-    void
-    setCCRegOperand(const StaticInst *si, int idx, RegVal val) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(CCRegClass));
-        thread->setCCReg(reg.index(), val);
-        result.emplace(val);
-    }
-
-    void
-    setVecRegOperand(const StaticInst *si, int idx,
-                     const TheISA::VecRegContainer& val) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecRegClass));
-        thread->setVecReg(reg, val);
-        result.emplace(val);
-    }
-
-    void
-    setVecElemOperand(const StaticInst *si, int idx, RegVal val) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecElemClass));
-        thread->setVecElem(reg, val);
-        result.emplace(val);
-    }
-
-    void
-    setVecPredRegOperand(const StaticInst *si, int idx,
-                         const TheISA::VecPredRegContainer& val) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecPredRegClass));
-        thread->setVecPredReg(reg, val);
-        result.emplace(val);
+        const RegId& id = si->destRegIdx(idx);
+        if (id.is(InvalidRegClass))
+            return;
+        thread->setReg(id, val);
+        //TODO setVecResult, setVecPredResult setVecElemResult?
     }
 
     bool readPredicate() const override { return thread->readPredicate(); }
@@ -335,7 +249,7 @@
     };
 
     Fault
-    initiateHtmCmd(Request::Flags flags) override
+    initiateMemMgmtCmd(Request::Flags flags) override
     {
         panic("not yet supported!");
         return NoFault;
diff --git a/src/cpu/checker/cpu_impl.hh b/src/cpu/checker/cpu_impl.hh
index 218d87f..a68b6f4 100644
--- a/src/cpu/checker/cpu_impl.hh
+++ b/src/cpu/checker/cpu_impl.hh
@@ -201,9 +201,6 @@
 
         Fault fault = NoFault;
 
-        // maintain $r0 semantics
-        thread->setIntReg(zeroReg, 0);
-
         // Check if any recent PC changes match up with anything we
         // expect to happen.  This is mostly to check if traps or
         // PC-based events have occurred in both the checker and CPU.
@@ -584,20 +581,19 @@
     if (start_idx >= 0) {
         const RegId& idx = inst->destRegIdx(start_idx);
         switch (idx.classValue()) {
-          case IntRegClass:
-            thread->setIntReg(idx.index(), mismatch_val.as<RegVal>());
+          case InvalidRegClass:
             break;
+          case IntRegClass:
           case FloatRegClass:
-            thread->setFloatReg(idx.index(), mismatch_val.as<RegVal>());
+          case VecElemClass:
+          case CCRegClass:
+            thread->setReg(idx, mismatch_val.as<RegVal>());
             break;
           case VecRegClass:
-            thread->setVecReg(idx, mismatch_val.as<TheISA::VecRegContainer>());
-            break;
-          case VecElemClass:
-            thread->setVecElem(idx, mismatch_val.as<RegVal>());
-            break;
-          case CCRegClass:
-            thread->setCCReg(idx.index(), mismatch_val.as<RegVal>());
+            {
+                auto val = mismatch_val.as<TheISA::VecRegContainer>();
+                thread->setReg(idx, &val);
+            }
             break;
           case MiscRegClass:
             thread->setMiscReg(idx.index(), mismatch_val.as<RegVal>());
@@ -612,20 +608,19 @@
         const RegId& idx = inst->destRegIdx(i);
         res = inst->popResult();
         switch (idx.classValue()) {
-          case IntRegClass:
-            thread->setIntReg(idx.index(), res.as<RegVal>());
+          case InvalidRegClass:
             break;
+          case IntRegClass:
           case FloatRegClass:
-            thread->setFloatReg(idx.index(), res.as<RegVal>());
+          case VecElemClass:
+          case CCRegClass:
+            thread->setReg(idx, res.as<RegVal>());
             break;
           case VecRegClass:
-            thread->setVecReg(idx, res.as<TheISA::VecRegContainer>());
-            break;
-          case VecElemClass:
-            thread->setVecElem(idx, res.as<RegVal>());
-            break;
-          case CCRegClass:
-            thread->setCCReg(idx.index(), res.as<RegVal>());
+            {
+                auto val = res.as<TheISA::VecRegContainer>();
+                thread->setReg(idx, &val);
+            }
             break;
           case MiscRegClass:
             // Try to get the proper misc register index for ARM here...
diff --git a/src/cpu/checker/thread_context.hh b/src/cpu/checker/thread_context.hh
index 8093a9d..13828f5 100644
--- a/src/cpu/checker/thread_context.hh
+++ b/src/cpu/checker/thread_context.hh
@@ -153,7 +153,7 @@
         return checkerCPU;
     }
 
-    BaseISA *getIsaPtr() override { return actualTC->getIsaPtr(); }
+    BaseISA *getIsaPtr() const override { return actualTC->getIsaPtr(); }
 
     InstDecoder *
     getDecoderPtr() override
@@ -227,97 +227,35 @@
     // New accessors for new decoder.
     //
     RegVal
-    readIntReg(RegIndex reg_idx) const override
+    getReg(const RegId &reg) const override
     {
-        return actualTC->readIntReg(reg_idx);
-    }
-
-    RegVal
-    readFloatReg(RegIndex reg_idx) const override
-    {
-        return actualTC->readFloatReg(reg_idx);
-    }
-
-    const TheISA::VecRegContainer &
-    readVecReg (const RegId &reg) const override
-    {
-        return actualTC->readVecReg(reg);
-    }
-
-    /**
-     * Read vector register for modification, hierarchical indexing.
-     */
-    TheISA::VecRegContainer &
-    getWritableVecReg (const RegId &reg) override
-    {
-        return actualTC->getWritableVecReg(reg);
-    }
-
-    RegVal
-    readVecElem(const RegId& reg) const override
-    {
-        return actualTC->readVecElem(reg);
-    }
-
-    const TheISA::VecPredRegContainer &
-    readVecPredReg(const RegId& reg) const override
-    {
-        return actualTC->readVecPredReg(reg);
-    }
-
-    TheISA::VecPredRegContainer &
-    getWritableVecPredReg(const RegId& reg) override
-    {
-        return actualTC->getWritableVecPredReg(reg);
-    }
-
-    RegVal
-    readCCReg(RegIndex reg_idx) const override
-    {
-        return actualTC->readCCReg(reg_idx);
+        return actualTC->getReg(reg);
     }
 
     void
-    setIntReg(RegIndex reg_idx, RegVal val) override
+    getReg(const RegId &reg, void *val) const override
     {
-        actualTC->setIntReg(reg_idx, val);
-        checkerTC->setIntReg(reg_idx, val);
+        actualTC->getReg(reg, val);
+    }
+
+    void *
+    getWritableReg(const RegId &reg) override
+    {
+        return actualTC->getWritableReg(reg);
     }
 
     void
-    setFloatReg(RegIndex reg_idx, RegVal val) override
+    setReg(const RegId &reg, RegVal val) override
     {
-        actualTC->setFloatReg(reg_idx, val);
-        checkerTC->setFloatReg(reg_idx, val);
+        actualTC->setReg(reg, val);
+        checkerTC->setReg(reg, val);
     }
 
     void
-    setVecReg(const RegId& reg, const TheISA::VecRegContainer& val) override
+    setReg(const RegId &reg, const void *val) override
     {
-        actualTC->setVecReg(reg, val);
-        checkerTC->setVecReg(reg, val);
-    }
-
-    void
-    setVecElem(const RegId& reg, RegVal val) override
-    {
-        actualTC->setVecElem(reg, val);
-        checkerTC->setVecElem(reg, val);
-    }
-
-    void
-    setVecPredReg(const RegId& reg,
-            const TheISA::VecPredRegContainer& val) override
-    {
-        actualTC->setVecPredReg(reg, val);
-        checkerTC->setVecPredReg(reg, val);
-    }
-
-    void
-    setCCReg(RegIndex reg_idx, RegVal val) override
-    {
-        actualTC->setCCReg(reg_idx, val);
-        checkerTC->setCCReg(reg_idx, val);
+        actualTC->setReg(reg, val);
+        checkerTC->setReg(reg, val);
     }
 
     /** Reads this thread's PC state. */
@@ -389,92 +327,35 @@
     }
 
     RegVal
-    readIntRegFlat(RegIndex idx) const override
+    getRegFlat(const RegId &reg) const override
     {
-        return actualTC->readIntRegFlat(idx);
+        return actualTC->getRegFlat(reg);
     }
 
     void
-    setIntRegFlat(RegIndex idx, RegVal val) override
+    getRegFlat(const RegId &reg, void *val) const override
     {
-        actualTC->setIntRegFlat(idx, val);
+        actualTC->getRegFlat(reg, val);
     }
 
-    RegVal
-    readFloatRegFlat(RegIndex idx) const override
+    void *
+    getWritableRegFlat(const RegId &reg) override
     {
-        return actualTC->readFloatRegFlat(idx);
+        return actualTC->getWritableRegFlat(reg);
     }
 
     void
-    setFloatRegFlat(RegIndex idx, RegVal val) override
+    setRegFlat(const RegId &reg, RegVal val) override
     {
-        actualTC->setFloatRegFlat(idx, val);
-    }
-
-    const TheISA::VecRegContainer &
-    readVecRegFlat(RegIndex idx) const override
-    {
-        return actualTC->readVecRegFlat(idx);
-    }
-
-    /**
-     * Read vector register for modification, flat indexing.
-     */
-    TheISA::VecRegContainer &
-    getWritableVecRegFlat(RegIndex idx) override
-    {
-        return actualTC->getWritableVecRegFlat(idx);
+        actualTC->setRegFlat(reg, val);
+        checkerTC->setRegFlat(reg, val);
     }
 
     void
-    setVecRegFlat(RegIndex idx, const TheISA::VecRegContainer& val) override
+    setRegFlat(const RegId &reg, const void *val) override
     {
-        actualTC->setVecRegFlat(idx, val);
-    }
-
-    RegVal
-    readVecElemFlat(RegIndex idx, const ElemIndex& elem_idx) const override
-    {
-        return actualTC->readVecElemFlat(idx, elem_idx);
-    }
-
-    void
-    setVecElemFlat(RegIndex idx, const ElemIndex& elem_idx,
-            RegVal val) override
-    {
-        actualTC->setVecElemFlat(idx, elem_idx, val);
-    }
-
-    const TheISA::VecPredRegContainer &
-    readVecPredRegFlat(RegIndex idx) const override
-    {
-        return actualTC->readVecPredRegFlat(idx);
-    }
-
-    TheISA::VecPredRegContainer &
-    getWritableVecPredRegFlat(RegIndex idx) override
-    {
-        return actualTC->getWritableVecPredRegFlat(idx);
-    }
-
-    void
-    setVecPredRegFlat(RegIndex idx,
-            const TheISA::VecPredRegContainer& val) override
-    {
-        actualTC->setVecPredRegFlat(idx, val);
-    }
-
-    RegVal
-    readCCRegFlat(RegIndex idx) const override
-    {
-        return actualTC->readCCRegFlat(idx);
-    }
-
-    void
-    setCCRegFlat(RegIndex idx, RegVal val) override
-    {
-        actualTC->setCCRegFlat(idx, val);
+        actualTC->setRegFlat(reg, val);
+        checkerTC->setRegFlat(reg, val);
     }
 
     // hardware transactional memory
diff --git a/src/cpu/exec_context.hh b/src/cpu/exec_context.hh
index b88c895..5fb2cac 100644
--- a/src/cpu/exec_context.hh
+++ b/src/cpu/exec_context.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016-2018, 2020 ARM Limited
+ * Copyright (c) 2014, 2016-2018, 2020-2021 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -73,87 +73,13 @@
 class ExecContext
 {
   public:
-    /**
-     * @{
-     * @name Integer Register Interfaces
-     *
-     */
 
-    /** Reads an integer register. */
-    virtual RegVal readIntRegOperand(const StaticInst *si, int idx) = 0;
-
-    /** Sets an integer register to a value. */
-    virtual void setIntRegOperand(const StaticInst *si,
-                                  int idx, RegVal val) = 0;
-
-    /** @} */
-
-
-    /**
-     * @{
-     * @name Floating Point Register Interfaces
-     */
-
-    /** Reads a floating point register in its binary format, instead
-     * of by value. */
-    virtual RegVal readFloatRegOperandBits(const StaticInst *si, int idx) = 0;
-
-    /** Sets the bits of a floating point register of single width
-     * to a binary value. */
-    virtual void setFloatRegOperandBits(const StaticInst *si,
-                                        int idx, RegVal val) = 0;
-
-    /** @} */
-
-    /** Vector Register Interfaces. */
-    /** @{ */
-    /** Reads source vector register operand. */
-    virtual const TheISA::VecRegContainer& readVecRegOperand(
-            const StaticInst *si, int idx) const = 0;
-
-    /** Gets destination vector register operand for modification. */
-    virtual TheISA::VecRegContainer& getWritableVecRegOperand(
-            const StaticInst *si, int idx) = 0;
-
-    /** Sets a destination vector register operand to a value. */
-    virtual void setVecRegOperand(const StaticInst *si, int idx,
-            const TheISA::VecRegContainer& val) = 0;
-    /** @} */
-
-    /** Vector Elem Interfaces. */
-    /** @{ */
-    /** Reads an element of a vector register. */
-    virtual RegVal readVecElemOperand(const StaticInst *si, int idx) const = 0;
-
-    /** Sets a vector register to a value. */
-    virtual void setVecElemOperand(
-            const StaticInst *si, int idx, RegVal val) = 0;
-    /** @} */
-
-    /** Predicate registers interface. */
-    /** @{ */
-    /** Reads source predicate register operand. */
-    virtual const TheISA::VecPredRegContainer& readVecPredRegOperand(
-            const StaticInst *si, int idx) const = 0;
-
-    /** Gets destination predicate register operand for modification. */
-    virtual TheISA::VecPredRegContainer& getWritableVecPredRegOperand(
-            const StaticInst *si, int idx) = 0;
-
-    /** Sets a destination predicate register operand to a value. */
-    virtual void setVecPredRegOperand(
-            const StaticInst *si, int idx,
-            const TheISA::VecPredRegContainer& val) = 0;
-    /** @} */
-
-    /**
-     * @{
-     * @name Condition Code Registers
-     */
-    virtual RegVal readCCRegOperand(const StaticInst *si, int idx) = 0;
-    virtual void setCCRegOperand(
-            const StaticInst *si, int idx, RegVal val) = 0;
-    /** @} */
+    virtual RegVal getRegOperand(const StaticInst *si, int idx) = 0;
+    virtual void getRegOperand(const StaticInst *si, int idx, void *val) = 0;
+    virtual void *getWritableRegOperand(const StaticInst *si, int idx) = 0;
+    virtual void setRegOperand(const StaticInst *si, int idx, RegVal val) = 0;
+    virtual void setRegOperand(const StaticInst *si, int idx,
+            const void *val) = 0;
 
     /**
      * @{
@@ -218,10 +144,14 @@
     }
 
     /**
-     * Initiate an HTM command,
-     * e.g. tell Ruby we're starting/stopping a transaction
+     * Initiate a memory management command with no valid address.
+     * Currently, these instructions need to bypass squashing in the O3 model
+     * Examples include HTM commands and TLBI commands.
+     * e.g. tell Ruby we're starting/stopping a HTM transaction,
+     *      or tell Ruby to issue a TLBI operation
      */
-    virtual Fault initiateHtmCmd(Request::Flags flags) = 0;
+    virtual Fault initiateMemMgmtCmd(Request::Flags flags) = 0;
+
     /**
      * For atomic-mode contexts, perform an atomic memory write operation.
      * For timing-mode contexts, initiate a timing memory write operation.
diff --git a/src/cpu/kvm/KvmVM.py b/src/cpu/kvm/KvmVM.py
index 29a90a4..aae9d98 100644
--- a/src/cpu/kvm/KvmVM.py
+++ b/src/cpu/kvm/KvmVM.py
@@ -45,3 +45,5 @@
 
     coalescedMMIO = \
       VectorParam.AddrRange([], "memory ranges for coalesced MMIO")
+
+    system = Param.System(Parent.any, "system this VM belongs to")
diff --git a/src/cpu/kvm/SConscript b/src/cpu/kvm/SConscript
index c8ade6e..2a87c76 100644
--- a/src/cpu/kvm/SConscript
+++ b/src/cpu/kvm/SConscript
@@ -37,7 +37,8 @@
 
 Import('*')
 
-if not env['USE_KVM'] or env['TARGET_ISA'] != env['KVM_ISA']:
+if not env['CONF']['USE_KVM'] or \
+        env['CONF']['TARGET_ISA'] != env['CONF']['KVM_ISA']:
     Return()
 
 SimObject('KvmVM.py', sim_objects=['KvmVM'])
diff --git a/src/cpu/kvm/SConsopts b/src/cpu/kvm/SConsopts
index 4117736..3bb549f 100644
--- a/src/cpu/kvm/SConsopts
+++ b/src/cpu/kvm/SConsopts
@@ -40,7 +40,9 @@
     # we rely on exists since version 2.6.36 of the kernel, but somehow
     # the KVM_API_VERSION does not reflect the change. We test for one of
     # the types as a fall back.
-    conf.env['KVM_ISA'] = None
+    # The default value of KVM_ISA should serialize to a string in the
+    # C++ header and test False in Scons/Python.
+    conf.env['CONF']['KVM_ISA'] = ''
     if not conf.CheckHeader('linux/kvm.h', '<>'):
         print("Info: Compatible header file <linux/kvm.h> not found, "
               "disabling KVM support.")
@@ -50,32 +52,28 @@
     elif host_isa == 'x86_64':
         if conf.CheckTypeSize('struct kvm_xsave',
                 '#include <linux/kvm.h>') != 0:
-            conf.env['KVM_ISA'] = 'x86'
+            conf.env['CONF']['KVM_ISA'] = 'x86'
         else:
             warning("KVM on x86 requires xsave support in kernel headers.")
     elif host_isa in ('armv7l', 'aarch64'):
-        conf.env['KVM_ISA'] = 'arm'
+        conf.env['CONF']['KVM_ISA'] = 'arm'
     else:
         warning("Failed to determine host ISA.")
 
-    if conf.env['KVM_ISA']:
+    if conf.env['CONF']['KVM_ISA']:
         # Check if the exclude_host attribute is available. We want this to
         # get accurate instruction counts in KVM.
-        conf.env['HAVE_PERF_ATTR_EXCLUDE_HOST'] = conf.CheckMember(
+        conf.env['CONF']['HAVE_PERF_ATTR_EXCLUDE_HOST'] = conf.CheckMember(
             'linux/perf_event.h', 'struct perf_event_attr', 'exclude_host')
 
         # Warn about missing optional functionality
-        if not conf.env['HAVE_PERF_ATTR_EXCLUDE_HOST']:
+        if not conf.env['CONF']['HAVE_PERF_ATTR_EXCLUDE_HOST']:
             warning("perf_event headers lack support for the exclude_host "
                     "attribute. KVM instruction counts will be inaccurate.")
 
-        export_vars.append('HAVE_PERF_ATTR_EXCLUDE_HOST')
-
-if main['KVM_ISA']:
+if main['CONF']['KVM_ISA']:
     sticky_vars.Add(BoolVariable('USE_KVM',
                 'Enable hardware virtualized (KVM) CPU models', True))
 else:
-    main['USE_KVM'] = False
+    main['CONF']['USE_KVM'] = False
     warning("Can not enable KVM, host seems to lack KVM support")
-
-export_vars.append('USE_KVM')
diff --git a/src/cpu/kvm/base.cc b/src/cpu/kvm/base.cc
index bc0e428..b76bddc 100644
--- a/src/cpu/kvm/base.cc
+++ b/src/cpu/kvm/base.cc
@@ -64,14 +64,14 @@
 
 BaseKvmCPU::BaseKvmCPU(const BaseKvmCPUParams &params)
     : BaseCPU(params),
-      vm(*params.system->getKvmVM()),
+      vm(nullptr),
       _status(Idle),
       dataPort(name() + ".dcache_port", this),
       instPort(name() + ".icache_port", this),
       alwaysSyncTC(params.alwaysSyncTC),
       threadContextDirty(true),
       kvmStateDirty(false),
-      vcpuID(vm.allocVCPUID()), vcpuFD(-1), vcpuMMapSize(0),
+      vcpuID(-1), vcpuFD(-1), vcpuMMapSize(0),
       _kvmRun(NULL), mmioRing(NULL),
       pageSize(sysconf(_SC_PAGE_SIZE)),
       tickEvent([this]{ tick(); }, "BaseKvmCPU tick",
@@ -108,6 +108,8 @@
 void
 BaseKvmCPU::init()
 {
+    vm = system->getKvmVM();
+    vcpuID = vm->allocVCPUID();
     BaseCPU::init();
     fatal_if(numThreads != 1, "KVM: Multithreading not supported");
 }
@@ -118,19 +120,19 @@
     const BaseKvmCPUParams &p =
         dynamic_cast<const BaseKvmCPUParams &>(params());
 
-    Kvm &kvm(*vm.kvm);
+    Kvm &kvm = *vm->kvm;
 
     BaseCPU::startup();
 
     assert(vcpuFD == -1);
 
     // Tell the VM that a CPU is about to start.
-    vm.cpuStartup();
+    vm->cpuStartup();
 
     // We can't initialize KVM CPUs in BaseKvmCPU::init() since we are
     // not guaranteed that the parent KVM VM has initialized at that
     // point. Initialize virtual CPUs here instead.
-    vcpuFD = vm.createVCPU(vcpuID);
+    vcpuFD = vm->createVCPU(vcpuID);
 
     // Map the KVM run structure
     vcpuMMapSize = kvm.getVCPUMMapSize();
diff --git a/src/cpu/kvm/base.hh b/src/cpu/kvm/base.hh
index 449c5db..6b4b88a 100644
--- a/src/cpu/kvm/base.hh
+++ b/src/cpu/kvm/base.hh
@@ -157,7 +157,7 @@
      */
     ThreadContext *tc;
 
-    KvmVM &vm;
+    KvmVM *vm;
 
   protected:
     /**
@@ -444,7 +444,7 @@
      * this queue when accessing devices. By convention, devices and
      * the VM use the same event queue.
      */
-    EventQueue *deviceEventQueue() { return vm.eventQueue(); }
+    EventQueue *deviceEventQueue() { return vm->eventQueue(); }
 
     /**
      * Update the KVM if the thread context is dirty.
@@ -654,7 +654,7 @@
     bool kvmStateDirty;
 
     /** KVM internal ID of the vCPU */
-    const long vcpuID;
+    long vcpuID;
 
     /** ID of the vCPU thread */
     pthread_t vcpuThread;
diff --git a/src/cpu/kvm/vm.cc b/src/cpu/kvm/vm.cc
index 1347c75..d3d8f1d 100644
--- a/src/cpu/kvm/vm.cc
+++ b/src/cpu/kvm/vm.cc
@@ -204,6 +204,15 @@
 #endif
 }
 
+bool
+Kvm::capIRQLineLayout2() const
+{
+#if defined(KVM_CAP_ARM_IRQ_LINE_LAYOUT_2) && defined(KVM_ARM_IRQ_VCPU2_SHIFT)
+    return checkExtension(KVM_CAP_ARM_IRQ_LINE_LAYOUT_2) != 0;
+#else
+    return false;
+#endif
+}
 
 #if defined(__i386__) || defined(__x86_64__)
 bool
@@ -306,12 +315,13 @@
 
 KvmVM::KvmVM(const KvmVMParams &params)
     : SimObject(params),
-      kvm(new Kvm()), system(nullptr),
+      kvm(new Kvm()), system(params.system),
       vmFD(kvm->createVM()),
       started(false),
       _hasKernelIRQChip(false),
       nextVCPUID(0)
 {
+    system->setKvmVM(this);
     maxMemorySlot = kvm->capNumMemSlots();
     /* If we couldn't determine how memory slots there are, guess 32. */
     if (!maxMemorySlot)
@@ -357,7 +367,6 @@
 void
 KvmVM::delayedStartup()
 {
-    assert(system); // set by the system during its construction
     const std::vector<memory::BackingStoreEntry> &memories(
         system->getPhysMem().getBackingStore());
 
@@ -542,18 +551,20 @@
 #endif
 }
 
-void
-KvmVM::setSystem(System *s)
+bool
+KvmVM::validEnvironment() const
 {
-    panic_if(system != nullptr, "setSystem() can only be called once");
-    panic_if(s == nullptr, "setSystem() called with null System*");
-    system = s;
+    for (auto *tc: system->threads) {
+        if (!dynamic_cast<BaseKvmCPU *>(tc->getCpuPtr()))
+            return false;
+    }
+
+    return true;
 }
 
 long
 KvmVM::contextIdToVCpuId(ContextID ctx) const
 {
-    assert(system != nullptr);
     return dynamic_cast<BaseKvmCPU*>
         (system->threads[ctx]->getCpuPtr())->getVCpuID();
 }
diff --git a/src/cpu/kvm/vm.hh b/src/cpu/kvm/vm.hh
index 2eb1909..e5e9d80 100644
--- a/src/cpu/kvm/vm.hh
+++ b/src/cpu/kvm/vm.hh
@@ -145,6 +145,10 @@
 
     /** Support for getting and setting the kvm_xsave structure. */
     bool capXSave() const;
+
+    /** Support for ARM IRQ line layout 2 **/
+    bool capIRQLineLayout2() const;
+
     /** @} */
 
 #if defined(__i386__) || defined(__x86_64__)
@@ -416,10 +420,8 @@
     /** Global KVM interface */
     Kvm *kvm;
 
-    /**
-     * Initialize system pointer. Invoked by system object.
-     */
-    void setSystem(System *s);
+    /** Verify gem5 configuration will support KVM emulation */
+    bool validEnvironment() const;
 
     /**
       * Get the VCPUID for a given context
diff --git a/src/cpu/minor/MinorCPU.py b/src/cpu/minor/BaseMinorCPU.py
similarity index 99%
rename from src/cpu/minor/MinorCPU.py
rename to src/cpu/minor/BaseMinorCPU.py
index 5b360ca..ac26743 100644
--- a/src/cpu/minor/MinorCPU.py
+++ b/src/cpu/minor/BaseMinorCPU.py
@@ -184,8 +184,8 @@
 
 class ThreadPolicy(Enum): vals = ['SingleThreaded', 'RoundRobin', 'Random']
 
-class MinorCPU(BaseCPU):
-    type = 'MinorCPU'
+class BaseMinorCPU(BaseCPU):
+    type = 'BaseMinorCPU'
     cxx_header = "cpu/minor/cpu.hh"
     cxx_class = 'gem5::MinorCPU'
 
diff --git a/src/cpu/minor/SConscript b/src/cpu/minor/SConscript
index 090ac44..cd1b8e3 100644
--- a/src/cpu/minor/SConscript
+++ b/src/cpu/minor/SConscript
@@ -40,10 +40,10 @@
 
 Import('*')
 
-if 'MinorCPU' in env['CPU_MODELS']:
-    SimObject('MinorCPU.py', sim_objects=[
+if env['CONF']['TARGET_ISA'] != 'null':
+    SimObject('BaseMinorCPU.py', sim_objects=[
         'MinorOpClass', 'MinorOpClassSet', 'MinorFUTiming', 'MinorFU',
-        'MinorFUPool', 'MinorCPU'],
+        'MinorFUPool', 'BaseMinorCPU'],
         enums=['ThreadPolicy'])
 
     Source('activity.cc')
diff --git a/src/cpu/minor/cpu.cc b/src/cpu/minor/cpu.cc
index 0bc26e3..2e554aa 100644
--- a/src/cpu/minor/cpu.cc
+++ b/src/cpu/minor/cpu.cc
@@ -47,7 +47,7 @@
 namespace gem5
 {
 
-MinorCPU::MinorCPU(const MinorCPUParams &params) :
+MinorCPU::MinorCPU(const BaseMinorCPUParams &params) :
     BaseCPU(params),
     threadPolicy(params.threadPolicy),
     stats(this)
diff --git a/src/cpu/minor/cpu.hh b/src/cpu/minor/cpu.hh
index 9ed93db..b5b04ae 100644
--- a/src/cpu/minor/cpu.hh
+++ b/src/cpu/minor/cpu.hh
@@ -51,7 +51,7 @@
 #include "cpu/minor/stats.hh"
 #include "cpu/simple_thread.hh"
 #include "enums/ThreadPolicy.hh"
-#include "params/MinorCPU.hh"
+#include "params/BaseMinorCPU.hh"
 
 namespace gem5
 {
@@ -126,7 +126,7 @@
     Port &getInstPort() override;
 
   public:
-    MinorCPU(const MinorCPUParams &params);
+    MinorCPU(const BaseMinorCPUParams &params);
 
     ~MinorCPU();
 
diff --git a/src/cpu/minor/decode.cc b/src/cpu/minor/decode.cc
index 5adf2ca..53c02f3 100644
--- a/src/cpu/minor/decode.cc
+++ b/src/cpu/minor/decode.cc
@@ -51,7 +51,7 @@
 
 Decode::Decode(const std::string &name,
     MinorCPU &cpu_,
-    const MinorCPUParams &params,
+    const BaseMinorCPUParams &params,
     Latch<ForwardInstData>::Output inp_,
     Latch<ForwardInstData>::Input out_,
     std::vector<InputBuffer<ForwardInstData>> &next_stage_input_buffer) :
diff --git a/src/cpu/minor/decode.hh b/src/cpu/minor/decode.hh
index 6e9cb62..156b920 100644
--- a/src/cpu/minor/decode.hh
+++ b/src/cpu/minor/decode.hh
@@ -141,7 +141,7 @@
   public:
     Decode(const std::string &name,
         MinorCPU &cpu_,
-        const MinorCPUParams &params,
+        const BaseMinorCPUParams &params,
         Latch<ForwardInstData>::Output inp_,
         Latch<ForwardInstData>::Input out_,
         std::vector<InputBuffer<ForwardInstData>> &next_stage_input_buffer);
diff --git a/src/cpu/minor/dyn_inst.cc b/src/cpu/minor/dyn_inst.cc
index dfff813..45889f9 100644
--- a/src/cpu/minor/dyn_inst.cc
+++ b/src/cpu/minor/dyn_inst.cc
@@ -139,6 +139,9 @@
 {
     const auto &reg_class = reg_classes.at(reg.classValue());
     switch (reg.classValue()) {
+      case InvalidRegClass:
+        os << 'z';
+        break;
       case MiscRegClass:
         {
             RegIndex misc_reg = reg.index();
@@ -152,14 +155,10 @@
         os << 'v' << reg.index();
         break;
       case VecElemClass:
-        os << 'v' << reg.index() << '[' << reg.elemIndex() << ']';
+        os << reg_class.regName(reg);
         break;
       case IntRegClass:
-        if (reg.index() == reg_class.zeroReg()) {
-            os << 'z';
-        } else {
-            os << 'r' << reg.index();
-        }
+        os << 'r' << reg.index();
         break;
       case CCRegClass:
         os << 'c' << reg.index();
diff --git a/src/cpu/minor/exec_context.hh b/src/cpu/minor/exec_context.hh
index 2773f9e..f3dc3ba 100644
--- a/src/cpu/minor/exec_context.hh
+++ b/src/cpu/minor/exec_context.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2014, 2016-2018, 2020 ARM Limited
+ * Copyright (c) 2011-2014, 2016-2018, 2020-2021 ARM Limited
  * Copyright (c) 2013 Advanced Micro Devices, Inc.
  * All rights reserved
  *
@@ -87,7 +87,7 @@
     ExecContext (
         MinorCPU &cpu_,
         SimpleThread &thread_, Execute &execute_,
-        MinorDynInstPtr inst_, RegIndex zeroReg) :
+        MinorDynInstPtr inst_) :
         cpu(cpu_),
         thread(thread_),
         execute(execute_),
@@ -97,7 +97,6 @@
         pcState(*inst->pc);
         setPredicate(inst->readPredicate());
         setMemAccPredicate(inst->readMemAccPredicate());
-        thread.setIntReg(zeroReg, 0);
     }
 
     ~ExecContext()
@@ -117,9 +116,10 @@
     }
 
     Fault
-    initiateHtmCmd(Request::Flags flags) override
+    initiateMemMgmtCmd(Request::Flags flags) override
     {
-        panic("ExecContext::initiateHtmCmd() not implemented on MinorCPU\n");
+        panic("ExecContext::initiateMemMgmtCmd() not implemented "
+              " on MinorCPU\n");
         return NoFault;
     }
 
@@ -145,101 +145,39 @@
     }
 
     RegVal
-    readIntRegOperand(const StaticInst *si, int idx) override
+    getRegOperand(const StaticInst *si, int idx) override
     {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(IntRegClass));
-        return thread.readIntReg(reg.index());
-    }
-
-    RegVal
-    readFloatRegOperandBits(const StaticInst *si, int idx) override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(FloatRegClass));
-        return thread.readFloatReg(reg.index());
-    }
-
-    const TheISA::VecRegContainer &
-    readVecRegOperand(const StaticInst *si, int idx) const override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(VecRegClass));
-        return thread.readVecReg(reg);
-    }
-
-    TheISA::VecRegContainer &
-    getWritableVecRegOperand(const StaticInst *si, int idx) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecRegClass));
-        return thread.getWritableVecReg(reg);
-    }
-
-    RegVal
-    readVecElemOperand(const StaticInst *si, int idx) const override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(VecElemClass));
-        return thread.readVecElem(reg);
-    }
-
-    const TheISA::VecPredRegContainer&
-    readVecPredRegOperand(const StaticInst *si, int idx) const override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(VecPredRegClass));
-        return thread.readVecPredReg(reg);
-    }
-
-    TheISA::VecPredRegContainer&
-    getWritableVecPredRegOperand(const StaticInst *si, int idx) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecPredRegClass));
-        return thread.getWritableVecPredReg(reg);
+        const RegId &reg = si->srcRegIdx(idx);
+        if (reg.is(InvalidRegClass))
+            return 0;
+        return thread.getReg(reg);
     }
 
     void
-    setIntRegOperand(const StaticInst *si, int idx, RegVal val) override
+    getRegOperand(const StaticInst *si, int idx, void *val) override
     {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(IntRegClass));
-        thread.setIntReg(reg.index(), val);
+        thread.getReg(si->srcRegIdx(idx), val);
+    }
+
+    void *
+    getWritableRegOperand(const StaticInst *si, int idx) override
+    {
+        return thread.getWritableReg(si->destRegIdx(idx));
     }
 
     void
-    setFloatRegOperandBits(const StaticInst *si, int idx, RegVal val) override
+    setRegOperand(const StaticInst *si, int idx, RegVal val) override
     {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(FloatRegClass));
-        thread.setFloatReg(reg.index(), val);
+        const RegId &reg = si->destRegIdx(idx);
+        if (reg.is(InvalidRegClass))
+            return;
+        thread.setReg(si->destRegIdx(idx), val);
     }
 
     void
-    setVecRegOperand(const StaticInst *si, int idx,
-                     const TheISA::VecRegContainer& val) override
+    setRegOperand(const StaticInst *si, int idx, const void *val) override
     {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecRegClass));
-        thread.setVecReg(reg, val);
-    }
-
-    void
-    setVecPredRegOperand(const StaticInst *si, int idx,
-                         const TheISA::VecPredRegContainer& val) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecPredRegClass));
-        thread.setVecPredReg(reg, val);
-    }
-
-    void
-    setVecElemOperand(const StaticInst *si, int idx, RegVal val) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecElemClass));
-        thread.setVecElem(reg, val);
+        thread.setReg(si->destRegIdx(idx), val);
     }
 
     bool
@@ -361,22 +299,6 @@
         thread.getMMUPtr()->demapPage(vaddr, asn);
     }
 
-    RegVal
-    readCCRegOperand(const StaticInst *si, int idx) override
-    {
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(CCRegClass));
-        return thread.readCCReg(reg.index());
-    }
-
-    void
-    setCCRegOperand(const StaticInst *si, int idx, RegVal val) override
-    {
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(CCRegClass));
-        thread.setCCReg(reg.index(), val);
-    }
-
     BaseCPU *getCpuPtr() { return &cpu; }
 
   public:
diff --git a/src/cpu/minor/execute.cc b/src/cpu/minor/execute.cc
index c09f3be..d320e67 100644
--- a/src/cpu/minor/execute.cc
+++ b/src/cpu/minor/execute.cc
@@ -63,15 +63,13 @@
 
 Execute::Execute(const std::string &name_,
     MinorCPU &cpu_,
-    const MinorCPUParams &params,
+    const BaseMinorCPUParams &params,
     Latch<ForwardInstData>::Output inp_,
     Latch<BranchData>::Input out_) :
     Named(name_),
     inp(inp_),
     out(out_),
     cpu(cpu_),
-    zeroReg(cpu.threads[0]->getIsaPtr()->regClasses().
-        at(IntRegClass).zeroReg()),
     issueLimit(params.executeIssueLimit),
     memoryIssueLimit(params.executeMemoryIssueLimit),
     commitLimit(params.executeCommitLimit),
@@ -90,8 +88,7 @@
         params.executeLSQRequestsQueueSize,
         params.executeLSQTransfersQueueSize,
         params.executeLSQStoreBufferSize,
-        params.executeLSQMaxStoreBufferStoresPerCycle,
-        zeroReg),
+        params.executeLSQMaxStoreBufferStoresPerCycle),
     executeInfo(params.numThreads,
             ExecuteThreadInfo(params.executeCommitLimit)),
     interruptPriority(0),
@@ -332,7 +329,7 @@
     ThreadID thread_id = inst->id.threadId;
     ThreadContext *thread = cpu.getContext(thread_id);
 
-    ExecContext context(cpu, *cpu.threads[thread_id], *this, inst, zeroReg);
+    ExecContext context(cpu, *cpu.threads[thread_id], *this, inst);
 
     PacketPtr packet = response->packet;
 
@@ -467,8 +464,7 @@
         ThreadContext *thread = cpu.getContext(inst->id.threadId);
         std::unique_ptr<PCStateBase> old_pc(thread->pcState().clone());
 
-        ExecContext context(cpu, *cpu.threads[inst->id.threadId],
-            *this, inst, zeroReg);
+        ExecContext context(cpu, *cpu.threads[inst->id.threadId], *this, inst);
 
         DPRINTF(MinorExecute, "Initiating memRef inst: %s\n", *inst);
 
@@ -914,8 +910,7 @@
         panic("We should never hit the case where we try to commit from a "
               "suspended thread as the streamSeqNum should not match");
     } else if (inst->isFault()) {
-        ExecContext context(cpu, *cpu.threads[thread_id], *this,
-                inst, zeroReg);
+        ExecContext context(cpu, *cpu.threads[thread_id], *this, inst);
 
         DPRINTF(MinorExecute, "Fault inst reached Execute: %s\n",
             inst->fault->name());
@@ -976,8 +971,7 @@
          * backwards, so no other branches may evaluate this cycle*/
         completed_inst = false;
     } else {
-        ExecContext context(cpu, *cpu.threads[thread_id], *this,
-                inst, zeroReg);
+        ExecContext context(cpu, *cpu.threads[thread_id], *this, inst);
 
         DPRINTF(MinorExecute, "Committing inst: %s\n", *inst);
 
diff --git a/src/cpu/minor/execute.hh b/src/cpu/minor/execute.hh
index 9d184f7..8a8c263 100644
--- a/src/cpu/minor/execute.hh
+++ b/src/cpu/minor/execute.hh
@@ -78,9 +78,6 @@
     /** Pointer back to the containing CPU */
     MinorCPU &cpu;
 
-    /** Index of the zero integer register. */
-    const RegIndex zeroReg;
-
     /** Number of instructions that can be issued per cycle */
     unsigned int issueLimit;
 
@@ -326,7 +323,7 @@
   public:
     Execute(const std::string &name_,
         MinorCPU &cpu_,
-        const MinorCPUParams &params,
+        const BaseMinorCPUParams &params,
         Latch<ForwardInstData>::Output inp_,
         Latch<BranchData>::Input out_);
 
diff --git a/src/cpu/minor/fetch1.cc b/src/cpu/minor/fetch1.cc
index 4829339..daf8d56 100644
--- a/src/cpu/minor/fetch1.cc
+++ b/src/cpu/minor/fetch1.cc
@@ -60,7 +60,7 @@
 
 Fetch1::Fetch1(const std::string &name_,
     MinorCPU &cpu_,
-    const MinorCPUParams &params,
+    const BaseMinorCPUParams &params,
     Latch<BranchData>::Output inp_,
     Latch<ForwardLineData>::Input out_,
     Latch<BranchData>::Output prediction_,
diff --git a/src/cpu/minor/fetch1.hh b/src/cpu/minor/fetch1.hh
index bd8ad7d..e33eb04 100644
--- a/src/cpu/minor/fetch1.hh
+++ b/src/cpu/minor/fetch1.hh
@@ -386,7 +386,7 @@
   public:
     Fetch1(const std::string &name_,
         MinorCPU &cpu_,
-        const MinorCPUParams &params,
+        const BaseMinorCPUParams &params,
         Latch<BranchData>::Output inp_,
         Latch<ForwardLineData>::Input out_,
         Latch<BranchData>::Output prediction_,
diff --git a/src/cpu/minor/fetch2.cc b/src/cpu/minor/fetch2.cc
index c5a7045..b506bc0 100644
--- a/src/cpu/minor/fetch2.cc
+++ b/src/cpu/minor/fetch2.cc
@@ -58,7 +58,7 @@
 
 Fetch2::Fetch2(const std::string &name,
     MinorCPU &cpu_,
-    const MinorCPUParams &params,
+    const BaseMinorCPUParams &params,
     Latch<ForwardLineData>::Output inp_,
     Latch<BranchData>::Output branchInp_,
     Latch<BranchData>::Input predictionOut_,
diff --git a/src/cpu/minor/fetch2.hh b/src/cpu/minor/fetch2.hh
index 2eb8a77..85012bf 100644
--- a/src/cpu/minor/fetch2.hh
+++ b/src/cpu/minor/fetch2.hh
@@ -52,7 +52,7 @@
 #include "cpu/minor/cpu.hh"
 #include "cpu/minor/pipe_data.hh"
 #include "cpu/pred/bpred_unit.hh"
-#include "params/MinorCPU.hh"
+#include "params/BaseMinorCPU.hh"
 
 namespace gem5
 {
@@ -201,7 +201,7 @@
   public:
     Fetch2(const std::string &name,
         MinorCPU &cpu_,
-        const MinorCPUParams &params,
+        const BaseMinorCPUParams &params,
         Latch<ForwardLineData>::Output inp_,
         Latch<BranchData>::Output branchInp_,
         Latch<BranchData>::Input predictionOut_,
diff --git a/src/cpu/minor/lsq.cc b/src/cpu/minor/lsq.cc
index e4c000b..f2fa5be 100644
--- a/src/cpu/minor/lsq.cc
+++ b/src/cpu/minor/lsq.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014,2017-2018,2020 ARM Limited
+ * Copyright (c) 2013-2014,2017-2018,2020-2021 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -58,10 +58,9 @@
 {
 
 LSQ::LSQRequest::LSQRequest(LSQ &port_, MinorDynInstPtr inst_, bool isLoad_,
-        RegIndex zero_reg, PacketDataPtr data_, uint64_t *res_) :
+        PacketDataPtr data_, uint64_t *res_) :
     SenderState(),
     port(port_),
-    zeroReg(zero_reg),
     inst(inst_),
     isLoad(isLoad_),
     data(data_),
@@ -81,7 +80,7 @@
 {
     SimpleThread &thread = *port.cpu.threads[inst->id.threadId];
     std::unique_ptr<PCStateBase> old_pc(thread.pcState().clone());
-    ExecContext context(port.cpu, thread, port.execute, inst, zeroReg);
+    ExecContext context(port.cpu, thread, port.execute, inst);
     [[maybe_unused]] Fault fault = inst->translationFault;
 
     // Give the instruction a chance to suppress a translation fault
@@ -104,7 +103,7 @@
     SimpleThread &thread = *port.cpu.threads[inst->id.threadId];
     std::unique_ptr<PCStateBase> old_pc(thread.pcState().clone());
 
-    ExecContext context(port.cpu, thread, port.execute, inst, zeroReg);
+    ExecContext context(port.cpu, thread, port.execute, inst);
 
     context.setMemAccPredicate(false);
     inst->staticInst->completeAcc(nullptr, &context, inst->traceData);
@@ -393,7 +392,7 @@
 
 LSQ::SplitDataRequest::SplitDataRequest(LSQ &port_, MinorDynInstPtr inst_,
     bool isLoad_, PacketDataPtr data_, uint64_t *res_) :
-    LSQRequest(port_, inst_, isLoad_, port_.zeroReg, data_, res_),
+    LSQRequest(port_, inst_, isLoad_, data_, res_),
     translationEvent([this]{ sendNextFragmentToTranslation(); },
                      "translationEvent"),
     numFragments(0),
@@ -1132,7 +1131,7 @@
         SimpleThread &thread = *cpu.threads[request->inst->id.threadId];
 
         std::unique_ptr<PCStateBase> old_pc(thread.pcState().clone());
-        ExecContext context(cpu, thread, execute, request->inst, zeroReg);
+        ExecContext context(cpu, thread, execute, request->inst);
 
         /* Handle LLSC requests and tests */
         if (is_load) {
@@ -1406,12 +1405,10 @@
     unsigned int in_memory_system_limit, unsigned int line_width,
     unsigned int requests_queue_size, unsigned int transfers_queue_size,
     unsigned int store_buffer_size,
-    unsigned int store_buffer_cycle_store_limit,
-    RegIndex zero_reg) :
+    unsigned int store_buffer_cycle_store_limit) :
     Named(name_),
     cpu(cpu_),
     execute(execute_),
-    zeroReg(zero_reg),
     dcachePort(dcache_port_name_, *this, cpu_),
     lastMemBarrier(cpu.numThreads, 0),
     state(MemoryRunning),
@@ -1651,6 +1648,14 @@
         inst->pc->instAddr(), std::move(amo_op));
     request->request->setByteEnable(byte_enable);
 
+    /* If the request is marked as NO_ACCESS, setup a local access
+     * doing nothing */
+    if (flags.isSet(Request::NO_ACCESS)) {
+        assert(!request->request->isLocalAccess());
+        request->request->setLocalAccessor(
+            [] (ThreadContext *tc, PacketPtr pkt) { return Cycles(1); });
+    }
+
     requests.push(request);
     inst->inLSQ = true;
     request->startAddrTranslation();
diff --git a/src/cpu/minor/lsq.hh b/src/cpu/minor/lsq.hh
index 40e95e2..4a95bf7 100644
--- a/src/cpu/minor/lsq.hh
+++ b/src/cpu/minor/lsq.hh
@@ -72,8 +72,6 @@
     MinorCPU &cpu;
     Execute &execute;
 
-    const RegIndex zeroReg;
-
   protected:
     /** State of memory access for head access. */
     enum MemoryState
@@ -134,8 +132,6 @@
         /** Owning port */
         LSQ &port;
 
-        const RegIndex zeroReg;
-
         /** Instruction which made this request */
         MinorDynInstPtr inst;
 
@@ -208,8 +204,7 @@
 
       public:
         LSQRequest(LSQ &port_, MinorDynInstPtr inst_, bool isLoad_,
-                RegIndex zero_reg, PacketDataPtr data_ = NULL,
-                uint64_t *res_ = NULL);
+                PacketDataPtr data_ = NULL, uint64_t *res_ = NULL);
 
         virtual ~LSQRequest();
 
@@ -319,7 +314,7 @@
       public:
         SpecialDataRequest(LSQ &port_, MinorDynInstPtr inst_) :
             /* Say this is a load, not actually relevant */
-            LSQRequest(port_, inst_, true, port_.zeroReg, NULL, 0)
+            LSQRequest(port_, inst_, true, NULL, 0)
         { }
     };
 
@@ -386,7 +381,7 @@
       public:
         SingleDataRequest(LSQ &port_, MinorDynInstPtr inst_,
             bool isLoad_, PacketDataPtr data_ = NULL, uint64_t *res_ = NULL) :
-            LSQRequest(port_, inst_, isLoad_, port_.zeroReg, data_, res_),
+            LSQRequest(port_, inst_, isLoad_, data_, res_),
             packetInFlight(false),
             packetSent(false)
         { }
@@ -656,8 +651,7 @@
         unsigned int max_accesses_in_memory_system, unsigned int line_width,
         unsigned int requests_queue_size, unsigned int transfers_queue_size,
         unsigned int store_buffer_size,
-        unsigned int store_buffer_cycle_store_limit,
-        RegIndex zero_reg);
+        unsigned int store_buffer_cycle_store_limit);
 
     virtual ~LSQ();
 
diff --git a/src/cpu/minor/pipeline.cc b/src/cpu/minor/pipeline.cc
index 358f4dfa..e94181f 100644
--- a/src/cpu/minor/pipeline.cc
+++ b/src/cpu/minor/pipeline.cc
@@ -55,7 +55,7 @@
 namespace minor
 {
 
-Pipeline::Pipeline(MinorCPU &cpu_, const MinorCPUParams &params) :
+Pipeline::Pipeline(MinorCPU &cpu_, const BaseMinorCPUParams &params) :
     Ticked(cpu_, &(cpu_.BaseCPU::baseStats.numCycles)),
     cpu(cpu_),
     allow_idling(params.enableIdling),
diff --git a/src/cpu/minor/pipeline.hh b/src/cpu/minor/pipeline.hh
index f2eab5d..ce0ae07 100644
--- a/src/cpu/minor/pipeline.hh
+++ b/src/cpu/minor/pipeline.hh
@@ -51,7 +51,7 @@
 #include "cpu/minor/execute.hh"
 #include "cpu/minor/fetch1.hh"
 #include "cpu/minor/fetch2.hh"
-#include "params/MinorCPU.hh"
+#include "params/BaseMinorCPU.hh"
 #include "sim/ticked_object.hh"
 
 namespace gem5
@@ -109,7 +109,7 @@
     bool needToSignalDrained;
 
   public:
-    Pipeline(MinorCPU &cpu_, const MinorCPUParams &params);
+    Pipeline(MinorCPU &cpu_, const BaseMinorCPUParams &params);
 
   public:
     /** Wake up the Fetch unit.  This is needed on thread activation esp.
diff --git a/src/cpu/minor/scoreboard.cc b/src/cpu/minor/scoreboard.cc
index 4637b8d..926d01d 100644
--- a/src/cpu/minor/scoreboard.cc
+++ b/src/cpu/minor/scoreboard.cc
@@ -55,13 +55,8 @@
 
     switch (reg.classValue()) {
       case IntRegClass:
-        if (reg.index() == zeroReg) {
-            /* Don't bother with the zero register */
-            ret = false;
-        } else {
-            scoreboard_index = reg.index();
-            ret = true;
-        }
+        scoreboard_index = reg.index();
+        ret = true;
         break;
       case FloatRegClass:
         scoreboard_index = floatRegOffset + reg.index();
@@ -84,6 +79,9 @@
           /* Don't bother with Misc registers */
         ret = false;
         break;
+      case InvalidRegClass:
+        ret = false;
+        break;
       default:
         panic("Unknown register class: %d", reg.classValue());
     }
@@ -135,8 +133,8 @@
                 " regIndex: %d final numResults: %d returnCycle: %d\n",
                 *inst, index, numResults[index], returnCycle[index]);
         } else {
-            /* Use zeroReg to mark invalid/untracked dests */
-            inst->flatDestRegIdx[dest_index] = RegId(IntRegClass, zeroReg);
+            /* Use an invalid ID to mark invalid/untracked dests */
+            inst->flatDestRegIdx[dest_index] = RegId();
         }
     }
 }
diff --git a/src/cpu/minor/scoreboard.hh b/src/cpu/minor/scoreboard.hh
index 3ae0b65..973be47 100644
--- a/src/cpu/minor/scoreboard.hh
+++ b/src/cpu/minor/scoreboard.hh
@@ -82,8 +82,6 @@
      *  [NumIntRegs+NumCCRegs, NumFloatRegs+NumIntRegs+NumCCRegs-1] */
     const unsigned numRegs;
 
-    const RegIndex zeroReg;
-
     /** Type to use when indexing numResults */
     typedef unsigned short int Index;
 
@@ -114,12 +112,12 @@
         Named(name),
         regClasses(reg_classes),
         intRegOffset(0),
-        floatRegOffset(intRegOffset + reg_classes.at(IntRegClass).size()),
-        ccRegOffset(floatRegOffset + reg_classes.at(FloatRegClass).size()),
-        vecRegOffset(ccRegOffset + reg_classes.at(CCRegClass).size()),
-        vecPredRegOffset(vecRegOffset + reg_classes.at(VecElemClass).size()),
-        numRegs(vecPredRegOffset + reg_classes.at(VecPredRegClass).size()),
-        zeroReg(reg_classes.at(IntRegClass).zeroReg()),
+        floatRegOffset(intRegOffset + reg_classes.at(IntRegClass).numRegs()),
+        ccRegOffset(floatRegOffset + reg_classes.at(FloatRegClass).numRegs()),
+        vecRegOffset(ccRegOffset + reg_classes.at(CCRegClass).numRegs()),
+        vecPredRegOffset(vecRegOffset +
+                reg_classes.at(VecElemClass).numRegs()),
+        numRegs(vecPredRegOffset + reg_classes.at(VecPredRegClass).numRegs()),
         numResults(numRegs, 0),
         numUnpredictableResults(numRegs, 0),
         fuIndices(numRegs, invalidFUIndex),
diff --git a/src/cpu/o3/O3CPU.py b/src/cpu/o3/BaseO3CPU.py
similarity index 79%
rename from src/cpu/o3/O3CPU.py
rename to src/cpu/o3/BaseO3CPU.py
index fb1a9dc..c58f9fe 100644
--- a/src/cpu/o3/O3CPU.py
+++ b/src/cpu/o3/BaseO3CPU.py
@@ -42,7 +42,7 @@
 
 from m5.objects.BaseCPU import BaseCPU
 from m5.objects.FUPool import *
-from m5.objects.O3Checker import O3Checker
+#from m5.objects.O3Checker import O3Checker
 from m5.objects.BranchPredictor import *
 
 class SMTFetchPolicy(ScopedEnum):
@@ -54,8 +54,8 @@
 class CommitPolicy(ScopedEnum):
     vals = [ 'RoundRobin', 'OldestReady' ]
 
-class O3CPU(BaseCPU):
-    type = 'O3CPU'
+class BaseO3CPU(BaseCPU):
+    type = 'BaseO3CPU'
     cxx_class = 'gem5::o3::CPU'
     cxx_header = 'cpu/o3/dyn_inst.hh'
 
@@ -120,40 +120,36 @@
     trapLatency = Param.Cycles(13, "Trap latency")
     fetchTrapLatency = Param.Cycles(1, "Fetch trap latency")
 
-    backComSize = Param.Unsigned(5, "Time buffer size for backwards communication")
-    forwardComSize = Param.Unsigned(5, "Time buffer size for forward communication")
+    backComSize = Param.Unsigned(5,
+            "Time buffer size for backwards communication")
+    forwardComSize = Param.Unsigned(5,
+            "Time buffer size for forward communication")
 
     LQEntries = Param.Unsigned(32, "Number of load queue entries")
     SQEntries = Param.Unsigned(32, "Number of store queue entries")
-    LSQDepCheckShift = Param.Unsigned(4, "Number of places to shift addr before check")
+    LSQDepCheckShift = Param.Unsigned(4,
+            "Number of places to shift addr before check")
     LSQCheckLoads = Param.Bool(True,
-        "Should dependency violations be checked for loads & stores or just stores")
+        "Should dependency violations be checked for "
+        "loads & stores or just stores")
     store_set_clear_period = Param.Unsigned(250000,
-            "Number of load/store insts before the dep predictor should be invalidated")
+            "Number of load/store insts before the dep predictor "
+            "should be invalidated")
     LFSTSize = Param.Unsigned(1024, "Last fetched store table size")
     SSITSize = Param.Unsigned(1024, "Store set ID table size")
 
     numRobs = Param.Unsigned(1, "Number of Reorder Buffers");
 
-    numPhysIntRegs = Param.Unsigned(256, "Number of physical integer registers")
+    numPhysIntRegs = Param.Unsigned(256,
+            "Number of physical integer registers")
     numPhysFloatRegs = Param.Unsigned(256, "Number of physical floating point "
                                       "registers")
-    # most ISAs don't use condition-code regs, so default is 0
-    _defaultNumPhysCCRegs = 0
-    if buildEnv['TARGET_ISA'] in ('arm','x86'):
-        # For x86, each CC reg is used to hold only a subset of the
-        # flags, so we need 4-5 times the number of CC regs as
-        # physical integer regs to be sure we don't run out.  In
-        # typical real machines, CC regs are not explicitly renamed
-        # (it's a side effect of int reg renaming), so they should
-        # never be the bottleneck here.
-        _defaultNumPhysCCRegs = Self.numPhysIntRegs * 5
     numPhysVecRegs = Param.Unsigned(256, "Number of physical vector "
                                       "registers")
     numPhysVecPredRegs = Param.Unsigned(32, "Number of physical predicate "
                                       "registers")
-    numPhysCCRegs = Param.Unsigned(_defaultNumPhysCCRegs,
-                                   "Number of physical cc registers")
+    # most ISAs don't use condition-code regs, so default is 0
+    numPhysCCRegs = Param.Unsigned(0, "Number of physical cc registers")
     numIQEntries = Param.Unsigned(64, "Number of instruction queue entries")
     numROBEntries = Param.Unsigned(192, "Number of reorder buffer entries")
 
@@ -173,25 +169,4 @@
     branchPred = Param.BranchPredictor(TournamentBP(numThreads =
                                                        Parent.numThreads),
                                        "Branch Predictor")
-    needsTSO = Param.Bool(buildEnv['TARGET_ISA'] == 'x86',
-                          "Enable TSO Memory model")
-
-    def addCheckerCpu(self):
-        if buildEnv['TARGET_ISA'] in ['arm']:
-            from m5.objects.ArmMMU import ArmMMU
-
-            self.checker = O3Checker(workload=self.workload,
-                                     exitOnError=False,
-                                     updateOnError=True,
-                                     warnOnlyOnLoadError=True)
-            self.checker.mmu = ArmMMU()
-            self.checker.mmu.itb.size = self.mmu.itb.size
-            self.checker.mmu.dtb.size = self.mmu.dtb.size
-            self.checker.cpu_id = self.cpu_id
-
-        else:
-            print("ERROR: Checker only supported under ARM ISA!")
-            exit(1)
-
-# Deprecated
-DerivO3CPU = O3CPU
+    needsTSO = Param.Bool(False, "Enable TSO Memory model")
diff --git a/src/cpu/o3/O3Checker.py b/src/cpu/o3/BaseO3Checker.py
similarity index 96%
rename from src/cpu/o3/O3Checker.py
rename to src/cpu/o3/BaseO3Checker.py
index c343cd6..6365491 100644
--- a/src/cpu/o3/O3Checker.py
+++ b/src/cpu/o3/BaseO3Checker.py
@@ -27,7 +27,7 @@
 from m5.params import *
 from m5.objects.CheckerCPU import CheckerCPU
 
-class O3Checker(CheckerCPU):
-    type = 'O3Checker'
+class BaseO3Checker(CheckerCPU):
+    type = 'BaseO3Checker'
     cxx_class = 'gem5::o3::Checker'
     cxx_header = 'cpu/o3/checker.hh'
diff --git a/src/cpu/o3/SConscript b/src/cpu/o3/SConscript
index ba021a8..e255d89 100755
--- a/src/cpu/o3/SConscript
+++ b/src/cpu/o3/SConscript
@@ -30,10 +30,10 @@
 
 Import('*')
 
-if 'O3CPU' in env['CPU_MODELS']:
+if env['CONF']['TARGET_ISA'] != 'null':
     SimObject('FUPool.py', sim_objects=['FUPool'])
     SimObject('FuncUnitConfig.py', sim_objects=[])
-    SimObject('O3CPU.py', sim_objects=['O3CPU'], enums=[
+    SimObject('BaseO3CPU.py', sim_objects=['BaseO3CPU'], enums=[
         'SMTFetchPolicy', 'SMTQueuePolicy', 'CommitPolicy'])
 
     Source('commit.cc')
@@ -74,5 +74,5 @@
         'IQ', 'ROB', 'FreeList', 'LSQ', 'LSQUnit', 'StoreSet', 'MemDepUnit',
         'DynInst', 'O3CPU', 'Activity', 'Scoreboard', 'Writeback' ])
 
-    SimObject('O3Checker.py', sim_objects=['O3Checker'])
+    SimObject('BaseO3Checker.py', sim_objects=['BaseO3Checker'])
     Source('checker.cc')
diff --git a/src/cpu/o3/commit.cc b/src/cpu/o3/commit.cc
index d2011c5..97def7e 100644
--- a/src/cpu/o3/commit.cc
+++ b/src/cpu/o3/commit.cc
@@ -64,7 +64,7 @@
 #include "debug/ExecFaulting.hh"
 #include "debug/HtmCpu.hh"
 #include "debug/O3PipeView.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 #include "sim/faults.hh"
 #include "sim/full_system.hh"
 
@@ -82,7 +82,7 @@
     trapSquash[tid] = true;
 }
 
-Commit::Commit(CPU *_cpu, const O3CPUParams &params)
+Commit::Commit(CPU *_cpu, const BaseO3CPUParams &params)
     : commitPolicy(params.smtCommitPolicy),
       cpu(_cpu),
       iewToCommitDelay(params.iewToCommitDelay),
diff --git a/src/cpu/o3/commit.hh b/src/cpu/o3/commit.hh
index da271ed..cf4eaf5 100644
--- a/src/cpu/o3/commit.hh
+++ b/src/cpu/o3/commit.hh
@@ -59,7 +59,7 @@
 namespace gem5
 {
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace o3
 {
@@ -132,7 +132,7 @@
 
   public:
     /** Construct a Commit with the given parameters. */
-    Commit(CPU *_cpu, const O3CPUParams &params);
+    Commit(CPU *_cpu, const BaseO3CPUParams &params);
 
     /** Returns the name of the Commit. */
     std::string name() const;
diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc
index 19660b2..301733c 100644
--- a/src/cpu/o3/cpu.cc
+++ b/src/cpu/o3/cpu.cc
@@ -70,7 +70,7 @@
 namespace o3
 {
 
-CPU::CPU(const O3CPUParams &params)
+CPU::CPU(const BaseO3CPUParams &params)
     : BaseCPU(params),
       mmu(params.mmu),
       tickEvent([this]{ tick(); }, "O3CPU tick",
@@ -98,8 +98,7 @@
 
       rob(this, params),
 
-      scoreboard(name() + ".scoreboard", regFile.totalNumPhysRegs(),
-              params.isa[0]->regClasses().at(IntRegClass).zeroReg()),
+      scoreboard(name() + ".scoreboard", regFile.totalNumPhysRegs()),
 
       isa(numThreads, NULL),
 
@@ -195,19 +194,20 @@
     const auto &regClasses = params.isa[0]->regClasses();
 
     assert(params.numPhysIntRegs >=
-            numThreads * regClasses.at(IntRegClass).size());
+            numThreads * regClasses.at(IntRegClass).numRegs());
     assert(params.numPhysFloatRegs >=
-            numThreads * regClasses.at(FloatRegClass).size());
+            numThreads * regClasses.at(FloatRegClass).numRegs());
     assert(params.numPhysVecRegs >=
-            numThreads * regClasses.at(VecRegClass).size());
+            numThreads * regClasses.at(VecRegClass).numRegs());
     assert(params.numPhysVecPredRegs >=
-            numThreads * regClasses.at(VecPredRegClass).size());
+            numThreads * regClasses.at(VecPredRegClass).numRegs());
     assert(params.numPhysCCRegs >=
-            numThreads * regClasses.at(CCRegClass).size());
+            numThreads * regClasses.at(CCRegClass).numRegs());
 
     // Just make this a warning and go ahead anyway, to keep from having to
     // add checks everywhere.
-    warn_if(regClasses.at(CCRegClass).size() == 0 && params.numPhysCCRegs != 0,
+    warn_if(regClasses.at(CCRegClass).numRegs() == 0 &&
+            params.numPhysCCRegs != 0,
             "Non-zero number of physical CC regs specified, even though\n"
             "    ISA does not use them.");
 
@@ -224,57 +224,18 @@
     // Initialize rename map to assign physical registers to the
     // architectural registers for active threads only.
     for (ThreadID tid = 0; tid < active_threads; tid++) {
-        for (RegIndex ridx = 0; ridx < regClasses.at(IntRegClass).size();
-                ++ridx) {
-            // Note that we can't use the rename() method because we don't
-            // want special treatment for the zero register at this point
-            PhysRegIdPtr phys_reg = freeList.getIntReg();
-            renameMap[tid].setEntry(RegId(IntRegClass, ridx), phys_reg);
-            commitRenameMap[tid].setEntry(RegId(IntRegClass, ridx), phys_reg);
-        }
-
-        for (RegIndex ridx = 0; ridx < regClasses.at(FloatRegClass).size();
-                ++ridx) {
-            PhysRegIdPtr phys_reg = freeList.getFloatReg();
-            renameMap[tid].setEntry(RegId(FloatRegClass, ridx), phys_reg);
-            commitRenameMap[tid].setEntry(
-                    RegId(FloatRegClass, ridx), phys_reg);
-        }
-
-        const size_t numVecs = regClasses.at(VecRegClass).size();
-        /* Initialize the full-vector interface */
-        for (RegIndex ridx = 0; ridx < numVecs; ++ridx) {
-            RegId rid = RegId(VecRegClass, ridx);
-            PhysRegIdPtr phys_reg = freeList.getVecReg();
-            renameMap[tid].setEntry(rid, phys_reg);
-            commitRenameMap[tid].setEntry(rid, phys_reg);
-        }
-        /* Initialize the vector-element interface */
-        const size_t numElems = regClasses.at(VecElemClass).size();
-        const size_t elemsPerVec = numElems / numVecs;
-        for (RegIndex ridx = 0; ridx < numVecs; ++ridx) {
-            for (ElemIndex ldx = 0; ldx < elemsPerVec; ++ldx) {
-                RegId lrid = RegId(VecElemClass, ridx, ldx);
-                PhysRegIdPtr phys_elem = freeList.getVecElem();
-                renameMap[tid].setEntry(lrid, phys_elem);
-                commitRenameMap[tid].setEntry(lrid, phys_elem);
+        for (auto type = (RegClassType)0; type <= CCRegClass;
+                type = (RegClassType)(type + 1)) {
+            for (RegIndex ridx = 0; ridx < regClasses.at(type).numRegs();
+                    ++ridx) {
+                // Note that we can't use the rename() method because we don't
+                // want special treatment for the zero register at this point
+                RegId rid = RegId(type, ridx);
+                PhysRegIdPtr phys_reg = freeList.getReg(type);
+                renameMap[tid].setEntry(rid, phys_reg);
+                commitRenameMap[tid].setEntry(rid, phys_reg);
             }
         }
-
-        for (RegIndex ridx = 0; ridx < regClasses.at(VecPredRegClass).size();
-                ++ridx) {
-            PhysRegIdPtr phys_reg = freeList.getVecPredReg();
-            renameMap[tid].setEntry(RegId(VecPredRegClass, ridx), phys_reg);
-            commitRenameMap[tid].setEntry(
-                    RegId(VecPredRegClass, ridx), phys_reg);
-        }
-
-        for (RegIndex ridx = 0; ridx < regClasses.at(CCRegClass).size();
-                ++ridx) {
-            PhysRegIdPtr phys_reg = freeList.getCCReg();
-            renameMap[tid].setEntry(RegId(CCRegClass, ridx), phys_reg);
-            commitRenameMap[tid].setEntry(RegId(CCRegClass, ridx), phys_reg);
-        }
     }
 
     rename.setRenameMap(renameMap);
@@ -731,24 +692,13 @@
     //Bind Int Regs to Rename Map
     const auto &regClasses = isa[tid]->regClasses();
 
-    for (RegIndex idx = 0; idx < regClasses.at(IntRegClass).size(); idx++) {
-        PhysRegIdPtr phys_reg = freeList.getIntReg();
-        renameMap[tid].setEntry(RegId(IntRegClass, idx), phys_reg);
-        scoreboard.setReg(phys_reg);
-    }
-
-    //Bind Float Regs to Rename Map
-    for (RegIndex idx = 0; idx < regClasses.at(FloatRegClass).size(); idx++) {
-        PhysRegIdPtr phys_reg = freeList.getFloatReg();
-        renameMap[tid].setEntry(RegId(FloatRegClass, idx), phys_reg);
-        scoreboard.setReg(phys_reg);
-    }
-
-    //Bind condition-code Regs to Rename Map
-    for (RegIndex idx = 0; idx < regClasses.at(CCRegClass).size(); idx++) {
-        PhysRegIdPtr phys_reg = freeList.getCCReg();
-        renameMap[tid].setEntry(RegId(CCRegClass, idx), phys_reg);
-        scoreboard.setReg(phys_reg);
+    for (auto type = (RegClassType)0; type <= CCRegClass;
+            type = (RegClassType)(type + 1)) {
+        for (RegIndex idx = 0; idx < regClasses.at(type).numRegs(); idx++) {
+            PhysRegIdPtr phys_reg = freeList.getReg(type);
+            renameMap[tid].setEntry(RegId(type, idx), phys_reg);
+            scoreboard.setReg(phys_reg);
+        }
     }
 
     //Copy Thread Data Into RegFile
@@ -1087,224 +1037,158 @@
 }
 
 RegVal
-CPU::readIntReg(PhysRegIdPtr phys_reg)
+CPU::getReg(PhysRegIdPtr phys_reg)
 {
-    cpuStats.intRegfileReads++;
-    return regFile.readIntReg(phys_reg);
+    switch (phys_reg->classValue()) {
+      case IntRegClass:
+        cpuStats.intRegfileReads++;
+        break;
+      case FloatRegClass:
+        cpuStats.fpRegfileReads++;
+        break;
+      case CCRegClass:
+        cpuStats.ccRegfileReads++;
+        break;
+      case VecRegClass:
+      case VecElemClass:
+        cpuStats.vecRegfileReads++;
+        break;
+      case VecPredRegClass:
+        cpuStats.vecPredRegfileReads++;
+        break;
+      default:
+        break;
+    }
+    return regFile.getReg(phys_reg);
+}
+
+void
+CPU::getReg(PhysRegIdPtr phys_reg, void *val)
+{
+    switch (phys_reg->classValue()) {
+      case IntRegClass:
+        cpuStats.intRegfileReads++;
+        break;
+      case FloatRegClass:
+        cpuStats.fpRegfileReads++;
+        break;
+      case CCRegClass:
+        cpuStats.ccRegfileReads++;
+        break;
+      case VecRegClass:
+      case VecElemClass:
+        cpuStats.vecRegfileReads++;
+        break;
+      case VecPredRegClass:
+        cpuStats.vecPredRegfileReads++;
+        break;
+      default:
+        break;
+    }
+    regFile.getReg(phys_reg, val);
+}
+
+void *
+CPU::getWritableReg(PhysRegIdPtr phys_reg)
+{
+    switch (phys_reg->classValue()) {
+      case VecRegClass:
+        cpuStats.vecRegfileReads++;
+        break;
+      case VecPredRegClass:
+        cpuStats.vecPredRegfileReads++;
+        break;
+      default:
+        break;
+    }
+    return regFile.getWritableReg(phys_reg);
+}
+
+void
+CPU::setReg(PhysRegIdPtr phys_reg, RegVal val)
+{
+    switch (phys_reg->classValue()) {
+      case IntRegClass:
+        cpuStats.intRegfileWrites++;
+        break;
+      case FloatRegClass:
+        cpuStats.fpRegfileWrites++;
+        break;
+      case CCRegClass:
+        cpuStats.ccRegfileWrites++;
+        break;
+      case VecRegClass:
+      case VecElemClass:
+        cpuStats.vecRegfileWrites++;
+        break;
+      case VecPredRegClass:
+        cpuStats.vecPredRegfileWrites++;
+        break;
+      default:
+        break;
+    }
+    regFile.setReg(phys_reg, val);
+}
+
+void
+CPU::setReg(PhysRegIdPtr phys_reg, const void *val)
+{
+    switch (phys_reg->classValue()) {
+      case IntRegClass:
+        cpuStats.intRegfileWrites++;
+        break;
+      case FloatRegClass:
+        cpuStats.fpRegfileWrites++;
+        break;
+      case CCRegClass:
+        cpuStats.ccRegfileWrites++;
+        break;
+      case VecRegClass:
+      case VecElemClass:
+        cpuStats.vecRegfileWrites++;
+        break;
+      case VecPredRegClass:
+        cpuStats.vecPredRegfileWrites++;
+        break;
+      default:
+        break;
+    }
+    regFile.setReg(phys_reg, val);
 }
 
 RegVal
-CPU::readFloatReg(PhysRegIdPtr phys_reg)
+CPU::getArchReg(const RegId &reg, ThreadID tid)
 {
-    cpuStats.fpRegfileReads++;
-    return regFile.readFloatReg(phys_reg);
-}
-
-const TheISA::VecRegContainer&
-CPU::readVecReg(PhysRegIdPtr phys_reg) const
-{
-    cpuStats.vecRegfileReads++;
-    return regFile.readVecReg(phys_reg);
-}
-
-TheISA::VecRegContainer&
-CPU::getWritableVecReg(PhysRegIdPtr phys_reg)
-{
-    cpuStats.vecRegfileWrites++;
-    return regFile.getWritableVecReg(phys_reg);
-}
-
-RegVal
-CPU::readVecElem(PhysRegIdPtr phys_reg) const
-{
-    cpuStats.vecRegfileReads++;
-    return regFile.readVecElem(phys_reg);
-}
-
-const TheISA::VecPredRegContainer&
-CPU::readVecPredReg(PhysRegIdPtr phys_reg) const
-{
-    cpuStats.vecPredRegfileReads++;
-    return regFile.readVecPredReg(phys_reg);
-}
-
-TheISA::VecPredRegContainer&
-CPU::getWritableVecPredReg(PhysRegIdPtr phys_reg)
-{
-    cpuStats.vecPredRegfileWrites++;
-    return regFile.getWritableVecPredReg(phys_reg);
-}
-
-RegVal
-CPU::readCCReg(PhysRegIdPtr phys_reg)
-{
-    cpuStats.ccRegfileReads++;
-    return regFile.readCCReg(phys_reg);
+    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(reg);
+    return regFile.getReg(phys_reg);
 }
 
 void
-CPU::setIntReg(PhysRegIdPtr phys_reg, RegVal val)
+CPU::getArchReg(const RegId &reg, void *val, ThreadID tid)
 {
-    cpuStats.intRegfileWrites++;
-    regFile.setIntReg(phys_reg, val);
+    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(reg);
+    regFile.getReg(phys_reg, val);
+}
+
+void *
+CPU::getWritableArchReg(const RegId &reg, ThreadID tid)
+{
+    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(reg);
+    return regFile.getWritableReg(phys_reg);
 }
 
 void
-CPU::setFloatReg(PhysRegIdPtr phys_reg, RegVal val)
+CPU::setArchReg(const RegId &reg, RegVal val, ThreadID tid)
 {
-    cpuStats.fpRegfileWrites++;
-    regFile.setFloatReg(phys_reg, val);
+    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(reg);
+    regFile.setReg(phys_reg, val);
 }
 
 void
-CPU::setVecReg(PhysRegIdPtr phys_reg, const TheISA::VecRegContainer& val)
+CPU::setArchReg(const RegId &reg, const void *val, ThreadID tid)
 {
-    cpuStats.vecRegfileWrites++;
-    regFile.setVecReg(phys_reg, val);
-}
-
-void
-CPU::setVecElem(PhysRegIdPtr phys_reg, RegVal val)
-{
-    cpuStats.vecRegfileWrites++;
-    regFile.setVecElem(phys_reg, val);
-}
-
-void
-CPU::setVecPredReg(PhysRegIdPtr phys_reg,
-                               const TheISA::VecPredRegContainer& val)
-{
-    cpuStats.vecPredRegfileWrites++;
-    regFile.setVecPredReg(phys_reg, val);
-}
-
-void
-CPU::setCCReg(PhysRegIdPtr phys_reg, RegVal val)
-{
-    cpuStats.ccRegfileWrites++;
-    regFile.setCCReg(phys_reg, val);
-}
-
-RegVal
-CPU::readArchIntReg(int reg_idx, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-            RegId(IntRegClass, reg_idx));
-
-    return regFile.readIntReg(phys_reg);
-}
-
-RegVal
-CPU::readArchFloatReg(int reg_idx, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-        RegId(FloatRegClass, reg_idx));
-
-    return regFile.readFloatReg(phys_reg);
-}
-
-const TheISA::VecRegContainer&
-CPU::readArchVecReg(int reg_idx, ThreadID tid) const
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-                RegId(VecRegClass, reg_idx));
-    return regFile.readVecReg(phys_reg);
-}
-
-TheISA::VecRegContainer&
-CPU::getWritableArchVecReg(int reg_idx, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-                RegId(VecRegClass, reg_idx));
-    return regFile.getWritableVecReg(phys_reg);
-}
-
-RegVal
-CPU::readArchVecElem(
-        const RegIndex& reg_idx, const ElemIndex& ldx, ThreadID tid) const
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-                                RegId(VecElemClass, reg_idx, ldx));
-    return regFile.readVecElem(phys_reg);
-}
-
-const TheISA::VecPredRegContainer&
-CPU::readArchVecPredReg(int reg_idx, ThreadID tid) const
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-                RegId(VecPredRegClass, reg_idx));
-    return regFile.readVecPredReg(phys_reg);
-}
-
-TheISA::VecPredRegContainer&
-CPU::getWritableArchVecPredReg(int reg_idx, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-                RegId(VecPredRegClass, reg_idx));
-    return regFile.getWritableVecPredReg(phys_reg);
-}
-
-RegVal
-CPU::readArchCCReg(int reg_idx, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-        RegId(CCRegClass, reg_idx));
-
-    return regFile.readCCReg(phys_reg);
-}
-
-void
-CPU::setArchIntReg(int reg_idx, RegVal val, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-            RegId(IntRegClass, reg_idx));
-
-    regFile.setIntReg(phys_reg, val);
-}
-
-void
-CPU::setArchFloatReg(int reg_idx, RegVal val, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-            RegId(FloatRegClass, reg_idx));
-
-    regFile.setFloatReg(phys_reg, val);
-}
-
-void
-CPU::setArchVecReg(int reg_idx, const TheISA::VecRegContainer& val,
-        ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-                RegId(VecRegClass, reg_idx));
-    regFile.setVecReg(phys_reg, val);
-}
-
-void
-CPU::setArchVecElem(const RegIndex& reg_idx, const ElemIndex& ldx,
-                    RegVal val, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-                RegId(VecElemClass, reg_idx, ldx));
-    regFile.setVecElem(phys_reg, val);
-}
-
-void
-CPU::setArchVecPredReg(int reg_idx,
-        const TheISA::VecPredRegContainer& val, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-                RegId(VecPredRegClass, reg_idx));
-    regFile.setVecPredReg(phys_reg, val);
-}
-
-void
-CPU::setArchCCReg(int reg_idx, RegVal val, ThreadID tid)
-{
-    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(
-            RegId(CCRegClass, reg_idx));
-
-    regFile.setCCReg(phys_reg, val);
+    PhysRegIdPtr phys_reg = commitRenameMap[tid].lookup(reg);
+    regFile.setReg(phys_reg, val);
 }
 
 const PCStateBase &
diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh
index 670b92f..db3474d 100644
--- a/src/cpu/o3/cpu.hh
+++ b/src/cpu/o3/cpu.hh
@@ -68,7 +68,7 @@
 #include "cpu/base.hh"
 #include "cpu/simple_thread.hh"
 #include "cpu/timebuf.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 #include "sim/process.hh"
 
 namespace gem5
@@ -169,7 +169,7 @@
 
   public:
     /** Constructs a CPU with the given parameters. */
-    CPU(const O3CPUParams &params);
+    CPU(const BaseO3CPUParams &params);
 
     ProbePointArg<PacketPtr> *ppInstAccessComplete;
     ProbePointArg<std::pair<DynInstPtr, PacketPtr> > *ppDataAccessComplete;
@@ -311,78 +311,25 @@
      */
     void setMiscReg(int misc_reg, RegVal val, ThreadID tid);
 
-    RegVal readIntReg(PhysRegIdPtr phys_reg);
+    RegVal getReg(PhysRegIdPtr phys_reg);
+    void getReg(PhysRegIdPtr phys_reg, void *val);
+    void *getWritableReg(PhysRegIdPtr phys_reg);
 
-    RegVal readFloatReg(PhysRegIdPtr phys_reg);
-
-    const TheISA::VecRegContainer& readVecReg(PhysRegIdPtr reg_idx) const;
-
-    /**
-     * Read physical vector register for modification.
-     */
-    TheISA::VecRegContainer& getWritableVecReg(PhysRegIdPtr reg_idx);
-
-    RegVal readVecElem(PhysRegIdPtr reg_idx) const;
-
-    const TheISA::VecPredRegContainer&
-        readVecPredReg(PhysRegIdPtr reg_idx) const;
-
-    TheISA::VecPredRegContainer& getWritableVecPredReg(PhysRegIdPtr reg_idx);
-
-    RegVal readCCReg(PhysRegIdPtr phys_reg);
-
-    void setIntReg(PhysRegIdPtr phys_reg, RegVal val);
-
-    void setFloatReg(PhysRegIdPtr phys_reg, RegVal val);
-
-    void setVecReg(PhysRegIdPtr reg_idx, const TheISA::VecRegContainer& val);
-
-    void setVecElem(PhysRegIdPtr reg_idx, RegVal val);
-
-    void setVecPredReg(PhysRegIdPtr reg_idx,
-            const TheISA::VecPredRegContainer& val);
-
-    void setCCReg(PhysRegIdPtr phys_reg, RegVal val);
-
-    RegVal readArchIntReg(int reg_idx, ThreadID tid);
-
-    RegVal readArchFloatReg(int reg_idx, ThreadID tid);
-
-    const TheISA::VecRegContainer&
-        readArchVecReg(int reg_idx, ThreadID tid) const;
-    /** Read architectural vector register for modification. */
-    TheISA::VecRegContainer& getWritableArchVecReg(int reg_idx, ThreadID tid);
-
-    RegVal readArchVecElem(const RegIndex& reg_idx,
-            const ElemIndex& ldx, ThreadID tid) const;
-
-    const TheISA::VecPredRegContainer& readArchVecPredReg(
-            int reg_idx, ThreadID tid) const;
-
-    TheISA::VecPredRegContainer&
-        getWritableArchVecPredReg(int reg_idx, ThreadID tid);
-
-    RegVal readArchCCReg(int reg_idx, ThreadID tid);
+    void setReg(PhysRegIdPtr phys_reg, RegVal val);
+    void setReg(PhysRegIdPtr phys_reg, const void *val);
 
     /** Architectural register accessors.  Looks up in the commit
      * rename table to obtain the true physical index of the
      * architected register first, then accesses that physical
      * register.
      */
-    void setArchIntReg(int reg_idx, RegVal val, ThreadID tid);
 
-    void setArchFloatReg(int reg_idx, RegVal val, ThreadID tid);
+    RegVal getArchReg(const RegId &reg, ThreadID tid);
+    void getArchReg(const RegId &reg, void *val, ThreadID tid);
+    void *getWritableArchReg(const RegId &reg, ThreadID tid);
 
-    void setArchVecPredReg(int reg_idx, const TheISA::VecPredRegContainer& val,
-                           ThreadID tid);
-
-    void setArchVecReg(int reg_idx, const TheISA::VecRegContainer& val,
-            ThreadID tid);
-
-    void setArchVecElem(const RegIndex& reg_idx, const ElemIndex& ldx,
-                        RegVal val, ThreadID tid);
-
-    void setArchCCReg(int reg_idx, RegVal val, ThreadID tid);
+    void setArchReg(const RegId &reg, RegVal val, ThreadID tid);
+    void setArchReg(const RegId &reg, const void *val, ThreadID tid);
 
     /** Sets the commit PC state of a specific thread. */
     void pcState(const PCStateBase &new_pc_state, ThreadID tid);
diff --git a/src/cpu/o3/decode.cc b/src/cpu/o3/decode.cc
index 362daee..40c9299 100644
--- a/src/cpu/o3/decode.cc
+++ b/src/cpu/o3/decode.cc
@@ -49,7 +49,7 @@
 #include "debug/Activity.hh"
 #include "debug/Decode.hh"
 #include "debug/O3PipeView.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 #include "sim/full_system.hh"
 
 // clang complains about std::set being overloaded with Packet::set if
@@ -62,7 +62,7 @@
 namespace o3
 {
 
-Decode::Decode(CPU *_cpu, const O3CPUParams &params)
+Decode::Decode(CPU *_cpu, const BaseO3CPUParams &params)
     : cpu(_cpu),
       renameToDecodeDelay(params.renameToDecodeDelay),
       iewToDecodeDelay(params.iewToDecodeDelay),
diff --git a/src/cpu/o3/decode.hh b/src/cpu/o3/decode.hh
index e8c2db2..79bacdc 100644
--- a/src/cpu/o3/decode.hh
+++ b/src/cpu/o3/decode.hh
@@ -52,7 +52,7 @@
 namespace gem5
 {
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace o3
 {
@@ -98,7 +98,7 @@
 
   public:
     /** Decode constructor. */
-    Decode(CPU *_cpu, const O3CPUParams &params);
+    Decode(CPU *_cpu, const BaseO3CPUParams &params);
 
     void startupStage();
 
diff --git a/src/cpu/o3/dyn_inst.cc b/src/cpu/o3/dyn_inst.cc
index 4769896..0b9a900 100644
--- a/src/cpu/o3/dyn_inst.cc
+++ b/src/cpu/o3/dyn_inst.cc
@@ -410,7 +410,7 @@
 }
 
 Fault
-DynInst::initiateHtmCmd(Request::Flags flags)
+DynInst::initiateMemMgmtCmd(Request::Flags flags)
 {
     const unsigned int size = 8;
     return cpu->pushRequest(
diff --git a/src/cpu/o3/dyn_inst.hh b/src/cpu/o3/dyn_inst.hh
index f97a525..c49581b 100644
--- a/src/cpu/o3/dyn_inst.hh
+++ b/src/cpu/o3/dyn_inst.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016 ARM Limited
+ * Copyright (c) 2010, 2016, 2021 ARM Limited
  * Copyright (c) 2013 Advanced Micro Devices, Inc.
  * All rights reserved
  *
@@ -397,7 +397,7 @@
     Fault initiateMemRead(Addr addr, unsigned size, Request::Flags flags,
             const std::vector<bool> &byte_enable) override;
 
-    Fault initiateHtmCmd(Request::Flags flags) override;
+    Fault initiateMemMgmtCmd(Request::Flags flags) override;
 
     Fault writeMem(uint8_t *data, unsigned size, Addr addr,
                    Request::Flags flags, uint64_t *res,
@@ -680,21 +680,10 @@
     /** Returns the number of destination registers. */
     size_t numDestRegs() const { return numDests(); }
 
-    // the following are used to track physical register usage
-    // for machines with separate int & FP reg files
-    int8_t numFPDestRegs()  const { return staticInst->numFPDestRegs(); }
-    int8_t numIntDestRegs() const { return staticInst->numIntDestRegs(); }
-    int8_t numCCDestRegs() const { return staticInst->numCCDestRegs(); }
-    int8_t numVecDestRegs() const { return staticInst->numVecDestRegs(); }
-    int8_t
-    numVecElemDestRegs() const
+    size_t
+    numDestRegs(RegClassType type) const
     {
-        return staticInst->numVecElemDestRegs();
-    }
-    int8_t
-    numVecPredDestRegs() const
-    {
-        return staticInst->numVecPredDestRegs();
+        return staticInst->numDestRegs(type);
     }
 
     /** Returns the logical register index of the i'th destination register. */
@@ -1091,29 +1080,30 @@
             const RegId& original_dest_reg = staticInst->destRegIdx(idx);
             switch (original_dest_reg.classValue()) {
               case IntRegClass:
-                setIntRegOperand(staticInst.get(), idx,
-                        cpu->readIntReg(prev_phys_reg));
-                break;
               case FloatRegClass:
-                setFloatRegOperandBits(staticInst.get(), idx,
-                        cpu->readFloatReg(prev_phys_reg));
+              case CCRegClass:
+                setRegOperand(staticInst.get(), idx,
+                        cpu->getReg(prev_phys_reg));
                 break;
               case VecRegClass:
-                setVecRegOperand(staticInst.get(), idx,
-                        cpu->readVecReg(prev_phys_reg));
+                {
+                    TheISA::VecRegContainer val;
+                    cpu->getReg(prev_phys_reg, &val);
+                    setRegOperand(staticInst.get(), idx, &val);
+                }
                 break;
               case VecElemClass:
-                setVecElemOperand(staticInst.get(), idx,
-                        cpu->readVecElem(prev_phys_reg));
+                setRegOperand(staticInst.get(), idx,
+                        cpu->getReg(prev_phys_reg));
                 break;
               case VecPredRegClass:
-                setVecPredRegOperand(staticInst.get(), idx,
-                        cpu->readVecPredReg(prev_phys_reg));
+                {
+                    TheISA::VecPredRegContainer val;
+                    cpu->getReg(prev_phys_reg, &val);
+                    setRegOperand(staticInst.get(), idx, &val);
+                }
                 break;
-              case CCRegClass:
-                setCCRegOperand(staticInst.get(), idx,
-                        cpu->readCCReg(prev_phys_reg));
-                break;
+              case InvalidRegClass:
               case MiscRegClass:
                 // no need to forward misc reg values
                 break;
@@ -1140,102 +1130,50 @@
     // to do).
 
     RegVal
-    readIntRegOperand(const StaticInst *si, int idx) override
+    getRegOperand(const StaticInst *si, int idx) override
     {
-        return cpu->readIntReg(renamedSrcIdx(idx));
+        const PhysRegIdPtr reg = renamedSrcIdx(idx);
+        if (reg->is(InvalidRegClass))
+            return 0;
+        return cpu->getReg(reg);
     }
 
-    RegVal
-    readFloatRegOperandBits(const StaticInst *si, int idx) override
+    void
+    getRegOperand(const StaticInst *si, int idx, void *val) override
     {
-        return cpu->readFloatReg(renamedSrcIdx(idx));
+        const PhysRegIdPtr reg = renamedSrcIdx(idx);
+        if (reg->is(InvalidRegClass))
+            return;
+        cpu->getReg(reg, val);
     }
 
-    const TheISA::VecRegContainer&
-    readVecRegOperand(const StaticInst *si, int idx) const override
+    void *
+    getWritableRegOperand(const StaticInst *si, int idx) override
     {
-        return cpu->readVecReg(renamedSrcIdx(idx));
-    }
-
-    /**
-     * Read destination vector register operand for modification.
-     */
-    TheISA::VecRegContainer&
-    getWritableVecRegOperand(const StaticInst *si, int idx) override
-    {
-        return cpu->getWritableVecReg(renamedDestIdx(idx));
-    }
-
-    RegVal
-    readVecElemOperand(const StaticInst *si, int idx) const override
-    {
-        return cpu->readVecElem(renamedSrcIdx(idx));
-    }
-
-    const TheISA::VecPredRegContainer&
-    readVecPredRegOperand(const StaticInst *si, int idx) const override
-    {
-        return cpu->readVecPredReg(renamedSrcIdx(idx));
-    }
-
-    TheISA::VecPredRegContainer&
-    getWritableVecPredRegOperand(const StaticInst *si, int idx) override
-    {
-        return cpu->getWritableVecPredReg(renamedDestIdx(idx));
-    }
-
-    RegVal
-    readCCRegOperand(const StaticInst *si, int idx) override
-    {
-        return cpu->readCCReg(renamedSrcIdx(idx));
+        return cpu->getWritableReg(renamedDestIdx(idx));
     }
 
     /** @todo: Make results into arrays so they can handle multiple dest
      *  registers.
      */
     void
-    setIntRegOperand(const StaticInst *si, int idx, RegVal val) override
+    setRegOperand(const StaticInst *si, int idx, RegVal val) override
     {
-        cpu->setIntReg(renamedDestIdx(idx), val);
+        const PhysRegIdPtr reg = renamedDestIdx(idx);
+        if (reg->is(InvalidRegClass))
+            return;
+        cpu->setReg(reg, val);
         setResult(val);
     }
 
     void
-    setFloatRegOperandBits(const StaticInst *si, int idx, RegVal val) override
+    setRegOperand(const StaticInst *si, int idx, const void *val) override
     {
-        cpu->setFloatReg(renamedDestIdx(idx), val);
-        setResult(val);
-    }
-
-    void
-    setVecRegOperand(const StaticInst *si, int idx,
-                     const TheISA::VecRegContainer& val) override
-    {
-        cpu->setVecReg(renamedDestIdx(idx), val);
-        setResult(val);
-    }
-
-    void
-    setVecElemOperand(const StaticInst *si, int idx, RegVal val) override
-    {
-        int reg_idx = idx;
-        cpu->setVecElem(renamedDestIdx(reg_idx), val);
-        setResult(val);
-    }
-
-    void
-    setVecPredRegOperand(const StaticInst *si, int idx,
-                         const TheISA::VecPredRegContainer& val) override
-    {
-        cpu->setVecPredReg(renamedDestIdx(idx), val);
-        setResult(val);
-    }
-
-    void
-    setCCRegOperand(const StaticInst *si, int idx, RegVal val) override
-    {
-        cpu->setCCReg(renamedDestIdx(idx), val);
-        setResult(val);
+        const PhysRegIdPtr reg = renamedDestIdx(idx);
+        if (reg->is(InvalidRegClass))
+            return;
+        cpu->setReg(reg, val);
+        //TODO setResult
     }
 };
 
diff --git a/src/cpu/o3/fetch.cc b/src/cpu/o3/fetch.cc
index 2105900..5358b33 100644
--- a/src/cpu/o3/fetch.cc
+++ b/src/cpu/o3/fetch.cc
@@ -63,7 +63,7 @@
 #include "debug/O3CPU.hh"
 #include "debug/O3PipeView.hh"
 #include "mem/packet.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 #include "sim/byteswap.hh"
 #include "sim/core.hh"
 #include "sim/eventq.hh"
@@ -81,7 +81,7 @@
 {}
 
 
-Fetch::Fetch(CPU *_cpu, const O3CPUParams &params)
+Fetch::Fetch(CPU *_cpu, const BaseO3CPUParams &params)
     : fetchPolicy(params.smtFetchPolicy),
       cpu(_cpu),
       branchPred(nullptr),
diff --git a/src/cpu/o3/fetch.hh b/src/cpu/o3/fetch.hh
index 392e7cb..1ca812b 100644
--- a/src/cpu/o3/fetch.hh
+++ b/src/cpu/o3/fetch.hh
@@ -61,7 +61,7 @@
 namespace gem5
 {
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace o3
 {
@@ -203,7 +203,7 @@
 
   public:
     /** Fetch constructor. */
-    Fetch(CPU *_cpu, const O3CPUParams &params);
+    Fetch(CPU *_cpu, const BaseO3CPUParams &params);
 
     /** Returns the name of fetch. */
     std::string name() const;
diff --git a/src/cpu/o3/free_list.hh b/src/cpu/o3/free_list.hh
index 54edfc1..408d6e0 100644
--- a/src/cpu/o3/free_list.hh
+++ b/src/cpu/o3/free_list.hh
@@ -42,6 +42,8 @@
 #ifndef __CPU_O3_FREE_LIST_HH__
 #define __CPU_O3_FREE_LIST_HH__
 
+#include <algorithm>
+#include <array>
 #include <iostream>
 #include <queue>
 
@@ -127,26 +129,7 @@
      *  explicitly because Scoreboard is not a SimObject. */
     const std::string _name;
 
-    /** The list of free integer registers. */
-    SimpleFreeList intList;
-
-    /** The list of free floating point registers. */
-    SimpleFreeList floatList;
-
-    /** The following two are exclusive interfaces. */
-    /** @{ */
-    /** The list of free vector registers. */
-    SimpleFreeList vecList;
-
-    /** The list of free vector element registers. */
-    SimpleFreeList vecElemList;
-    /** @} */
-
-    /** The list of free predicate registers. */
-    SimpleFreeList predList;
-
-    /** The list of free condition-code registers. */
-    SimpleFreeList ccList;
+    std::array<SimpleFreeList, CCRegClass + 1> freeLists;
 
     /**
      * The register file object is used only to distinguish integer
@@ -175,174 +158,39 @@
     /** Gives the name of the freelist. */
     std::string name() const { return _name; };
 
-    /** Returns a pointer to the condition-code free list */
-    SimpleFreeList *getCCList() { return &ccList; }
-
-    /** Gets a free integer register. */
-    PhysRegIdPtr getIntReg() { return intList.getReg(); }
-
-    /** Gets a free fp register. */
-    PhysRegIdPtr getFloatReg() { return floatList.getReg(); }
-
-    /** Gets a free vector register. */
-    PhysRegIdPtr getVecReg() { return vecList.getReg(); }
-
-    /** Gets a free vector elemenet register. */
-    PhysRegIdPtr getVecElem() { return vecElemList.getReg(); }
-
-    /** Gets a free predicate register. */
-    PhysRegIdPtr getVecPredReg() { return predList.getReg(); }
-
-    /** Gets a free cc register. */
-    PhysRegIdPtr getCCReg() { return ccList.getReg(); }
-
-    /** Adds a register back to the free list. */
-    void addReg(PhysRegIdPtr freed_reg);
+    /** Gets a free register of type type. */
+    PhysRegIdPtr getReg(RegClassType type) { return freeLists[type].getReg(); }
 
     /** Adds a register back to the free list. */
     template<class InputIt>
-    void addRegs(InputIt first, InputIt last);
-
-    /** Adds an integer register back to the free list. */
-    void addIntReg(PhysRegIdPtr freed_reg) { intList.addReg(freed_reg); }
-
-    /** Adds a fp register back to the free list. */
-    void addFloatReg(PhysRegIdPtr freed_reg) { floatList.addReg(freed_reg); }
-
-    /** Adds a vector register back to the free list. */
-    void addVecReg(PhysRegIdPtr freed_reg) { vecList.addReg(freed_reg); }
-
-    /** Adds a vector element register back to the free list. */
-    void addVecElem(PhysRegIdPtr freed_reg) {
-        vecElemList.addReg(freed_reg);
+    void
+    addRegs(InputIt first, InputIt last)
+    {
+        std::for_each(first, last, [this](auto &reg) { addReg(&reg); });
     }
 
-    /** Adds a predicate register back to the free list. */
-    void addVecPredReg(PhysRegIdPtr freed_reg) { predList.addReg(freed_reg); }
+    /** Adds a register back to the free list. */
+    void
+    addReg(PhysRegIdPtr freed_reg)
+    {
+        freeLists[freed_reg->classValue()].addReg(freed_reg);
+    }
 
-    /** Adds a cc register back to the free list. */
-    void addCCReg(PhysRegIdPtr freed_reg) { ccList.addReg(freed_reg); }
+    /** Checks if there are any free registers of type type. */
+    bool
+    hasFreeRegs(RegClassType type) const
+    {
+        return freeLists[type].hasFreeRegs();
+    }
 
-    /** Checks if there are any free integer registers. */
-    bool hasFreeIntRegs() const { return intList.hasFreeRegs(); }
-
-    /** Checks if there are any free fp registers. */
-    bool hasFreeFloatRegs() const { return floatList.hasFreeRegs(); }
-
-    /** Checks if there are any free vector registers. */
-    bool hasFreeVecRegs() const { return vecList.hasFreeRegs(); }
-
-    /** Checks if there are any free vector registers. */
-    bool hasFreeVecElems() const { return vecElemList.hasFreeRegs(); }
-
-    /** Checks if there are any free predicate registers. */
-    bool hasFreeVecPredRegs() const { return predList.hasFreeRegs(); }
-
-    /** Checks if there are any free cc registers. */
-    bool hasFreeCCRegs() const { return ccList.hasFreeRegs(); }
-
-    /** Returns the number of free integer registers. */
-    unsigned numFreeIntRegs() const { return intList.numFreeRegs(); }
-
-    /** Returns the number of free fp registers. */
-    unsigned numFreeFloatRegs() const { return floatList.numFreeRegs(); }
-
-    /** Returns the number of free vector registers. */
-    unsigned numFreeVecRegs() const { return vecList.numFreeRegs(); }
-
-    /** Returns the number of free vector registers. */
-    unsigned numFreeVecElems() const { return vecElemList.numFreeRegs(); }
-
-    /** Returns the number of free predicate registers. */
-    unsigned numFreeVecPredRegs() const { return predList.numFreeRegs(); }
-
-    /** Returns the number of free cc registers. */
-    unsigned numFreeCCRegs() const { return ccList.numFreeRegs(); }
+    /** Returns the number of free registers of type type. */
+    unsigned
+    numFreeRegs(RegClassType type) const
+    {
+        return freeLists[type].numFreeRegs();
+    }
 };
 
-template<class InputIt>
-inline void
-UnifiedFreeList::addRegs(InputIt first, InputIt last)
-{
-    // Are there any registers to add?
-    if (first == last)
-        return;
-
-    panic_if((first != last) &&
-            first->classValue() != (last-1)->classValue(),
-            "Attempt to add mixed type regs: %s and %s",
-            first->className(),
-            (last-1)->className());
-    switch (first->classValue()) {
-        case IntRegClass:
-            intList.addRegs(first, last);
-            break;
-        case FloatRegClass:
-            floatList.addRegs(first, last);
-            break;
-        case VecRegClass:
-            vecList.addRegs(first, last);
-            break;
-        case VecElemClass:
-            vecElemList.addRegs(first, last);
-            break;
-        case VecPredRegClass:
-            predList.addRegs(first, last);
-            break;
-        case CCRegClass:
-            ccList.addRegs(first, last);
-            break;
-        default:
-            panic("Unexpected RegClass (%s)",
-                                   first->className());
-    }
-
-}
-
-inline void
-UnifiedFreeList::addReg(PhysRegIdPtr freed_reg)
-{
-    DPRINTF(FreeList,"Freeing register %i (%s).\n", freed_reg->index(),
-            freed_reg->className());
-    //Might want to add in a check for whether or not this register is
-    //already in there.  A bit vector or something similar would be useful.
-    switch (freed_reg->classValue()) {
-        case IntRegClass:
-            intList.addReg(freed_reg);
-            break;
-        case FloatRegClass:
-            floatList.addReg(freed_reg);
-            break;
-        case VecRegClass:
-            vecList.addReg(freed_reg);
-            break;
-        case VecElemClass:
-            vecElemList.addReg(freed_reg);
-            break;
-        case VecPredRegClass:
-            predList.addReg(freed_reg);
-            break;
-        case CCRegClass:
-            ccList.addReg(freed_reg);
-            break;
-        default:
-            panic("Unexpected RegClass (%s)",
-                                   freed_reg->className());
-    }
-
-    // These assert conditions ensure that the number of free
-    // registers are not more than the # of total Physical  Registers.
-    // If this were false, it would mean that registers
-    // have been freed twice, overflowing the free register
-    // pool and potentially crashing SMT workloads.
-    // ----
-    // Comment out for now so as to not potentially break
-    // CMP and single-threaded workloads
-    // ----
-    // assert(freeIntRegs.size() <= numPhysicalIntRegs);
-    // assert(freeFloatRegs.size() <= numPhysicalFloatRegs);
-}
-
 } // namespace o3
 } // namespace gem5
 
diff --git a/src/cpu/o3/iew.cc b/src/cpu/o3/iew.cc
index 8dbd5b3..5c507f0 100644
--- a/src/cpu/o3/iew.cc
+++ b/src/cpu/o3/iew.cc
@@ -57,7 +57,7 @@
 #include "debug/Drain.hh"
 #include "debug/IEW.hh"
 #include "debug/O3PipeView.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 
 namespace gem5
 {
@@ -65,7 +65,7 @@
 namespace o3
 {
 
-IEW::IEW(CPU *_cpu, const O3CPUParams &params)
+IEW::IEW(CPU *_cpu, const BaseO3CPUParams &params)
     : issueToExecQueue(params.backComSize, params.forwardComSize),
       cpu(_cpu),
       instQueue(_cpu, this, params),
diff --git a/src/cpu/o3/iew.hh b/src/cpu/o3/iew.hh
index ea5350d..80fed29 100644
--- a/src/cpu/o3/iew.hh
+++ b/src/cpu/o3/iew.hh
@@ -58,7 +58,7 @@
 namespace gem5
 {
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace o3
 {
@@ -127,7 +127,7 @@
 
   public:
     /** Constructs a IEW with the given parameters. */
-    IEW(CPU *_cpu, const O3CPUParams &params);
+    IEW(CPU *_cpu, const BaseO3CPUParams &params);
 
     /** Returns the name of the IEW stage. */
     std::string name() const;
diff --git a/src/cpu/o3/inst_queue.cc b/src/cpu/o3/inst_queue.cc
index 0fac84f..9666926 100644
--- a/src/cpu/o3/inst_queue.cc
+++ b/src/cpu/o3/inst_queue.cc
@@ -50,7 +50,7 @@
 #include "cpu/o3/limits.hh"
 #include "debug/IQ.hh"
 #include "enums/OpClass.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 #include "sim/core.hh"
 
 // clang complains about std::set being overloaded with Packet::set if
@@ -85,7 +85,7 @@
 }
 
 InstructionQueue::InstructionQueue(CPU *cpu_ptr, IEW *iew_ptr,
-        const O3CPUParams &params)
+        const BaseO3CPUParams &params)
     : cpu(cpu_ptr),
       iewStage(iew_ptr),
       fuPool(params.fuPool),
@@ -99,11 +99,14 @@
 {
     assert(fuPool);
 
+    const auto &reg_classes = params.isa[0]->regClasses();
     // Set the number of total physical registers
     // As the vector registers have two addressing modes, they are added twice
     numPhysRegs = params.numPhysIntRegs + params.numPhysFloatRegs +
                     params.numPhysVecRegs +
-                    params.numPhysVecRegs * TheISA::NumVecElemPerVecReg +
+                    params.numPhysVecRegs * (
+                            reg_classes.at(VecElemClass).numRegs() /
+                            reg_classes.at(VecRegClass).numRegs()) +
                     params.numPhysVecPredRegs +
                     params.numPhysCCRegs;
 
diff --git a/src/cpu/o3/inst_queue.hh b/src/cpu/o3/inst_queue.hh
index b2d9303..57928e7 100644
--- a/src/cpu/o3/inst_queue.hh
+++ b/src/cpu/o3/inst_queue.hh
@@ -64,7 +64,7 @@
 namespace gem5
 {
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace memory
 {
@@ -130,7 +130,8 @@
     };
 
     /** Constructs an IQ. */
-    InstructionQueue(CPU *cpu_ptr, IEW *iew_ptr, const O3CPUParams &params);
+    InstructionQueue(CPU *cpu_ptr, IEW *iew_ptr,
+            const BaseO3CPUParams &params);
 
     /** Destructs the IQ. */
     ~InstructionQueue();
diff --git a/src/cpu/o3/lsq.cc b/src/cpu/o3/lsq.cc
index 78999ee..654fd67 100644
--- a/src/cpu/o3/lsq.cc
+++ b/src/cpu/o3/lsq.cc
@@ -56,7 +56,7 @@
 #include "debug/HtmCpu.hh"
 #include "debug/LSQ.hh"
 #include "debug/Writeback.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 
 namespace gem5
 {
@@ -68,11 +68,13 @@
     RequestPort(_cpu->name() + ".dcache_port", _cpu), lsq(_lsq), cpu(_cpu)
 {}
 
-LSQ::LSQ(CPU *cpu_ptr, IEW *iew_ptr, const O3CPUParams &params)
+LSQ::LSQ(CPU *cpu_ptr, IEW *iew_ptr, const BaseO3CPUParams &params)
     : cpu(cpu_ptr), iewStage(iew_ptr),
       _cacheBlocked(false),
       cacheStorePorts(params.cacheStorePorts), usedStorePorts(0),
       cacheLoadPorts(params.cacheLoadPorts), usedLoadPorts(0),
+      waitingForStaleTranslation(false),
+      staleTranslationWaitTxnId(0),
       lsqPolicy(params.smtLSQPolicy),
       LQEntries(params.LQEntries),
       SQEntries(params.SQEntries),
@@ -431,6 +433,10 @@
     // Update the LSQRequest state (this may delete the request)
     request->packetReplied();
 
+    if (waitingForStaleTranslation) {
+        checkStaleTranslations();
+    }
+
     return true;
 }
 
@@ -447,6 +453,19 @@
         for (ThreadID tid = 0; tid < numThreads; tid++) {
             thread[tid].checkSnoop(pkt);
         }
+    } else if (pkt->req && pkt->req->isTlbiExtSync()) {
+        DPRINTF(LSQ, "received TLBI Ext Sync\n");
+        assert(!waitingForStaleTranslation);
+
+        waitingForStaleTranslation = true;
+        staleTranslationWaitTxnId = pkt->req->getExtraData();
+
+        for (auto& unit : thread) {
+            unit.startStaleTranslationFlush();
+        }
+
+        // In case no units have pending ops, just go ahead
+        checkStaleTranslations();
     }
 }
 
@@ -784,15 +803,16 @@
     assert(!isAtomic || (isAtomic && !needs_burst));
 
     const bool htm_cmd = isLoad && (flags & Request::HTM_CMD);
+    const bool tlbi_cmd = isLoad && (flags & Request::TLBI_CMD);
 
     if (inst->translationStarted()) {
         request = inst->savedRequest;
         assert(request);
     } else {
-        if (htm_cmd) {
+        if (htm_cmd || tlbi_cmd) {
             assert(addr == 0x0lu);
             assert(size == 8);
-            request = new HtmCmdRequest(&thread[tid], inst, flags);
+            request = new UnsquashableDirectRequest(&thread[tid], inst, flags);
         } else if (needs_burst) {
             request = new SplitDataRequest(&thread[tid], inst, isLoad, addr,
                     size, flags, data, res);
@@ -1047,7 +1067,8 @@
 LSQ::LSQRequest::LSQRequest(
         LSQUnit *port, const DynInstPtr& inst, bool isLoad,
         const Addr& addr, const uint32_t& size, const Request::Flags& flags_,
-           PacketDataPtr data, uint64_t* res, AtomicOpFunctorPtr amo_op)
+        PacketDataPtr data, uint64_t* res, AtomicOpFunctorPtr amo_op,
+        bool stale_translation)
     : _state(State::NotIssued),
     numTranslatedFragments(0),
     numInTranslationFragments(0),
@@ -1055,7 +1076,8 @@
     _res(res), _addr(addr), _size(size),
     _flags(flags_),
     _numOutstandingPackets(0),
-    _amo_op(std::move(amo_op))
+    _amo_op(std::move(amo_op)),
+    _hasStaleTranslation(stale_translation)
 {
     flags.set(Flag::IsLoad, isLoad);
     flags.set(Flag::WriteBackToRegister,
@@ -1089,6 +1111,23 @@
                 _inst->pcState().instAddr(), _inst->contextId(),
                 std::move(_amo_op));
         req->setByteEnable(byte_enable);
+
+        /* If the request is marked as NO_ACCESS, setup a local access */
+        if (_flags.isSet(Request::NO_ACCESS)) {
+            req->setLocalAccessor(
+                [this, req](gem5::ThreadContext *tc, PacketPtr pkt) -> Cycles
+                {
+                    if ((req->isHTMStart() || req->isHTMCommit())) {
+                        auto& inst = this->instruction();
+                        assert(inst->inHtmTransactionalState());
+                        pkt->setHtmTransactional(
+                            inst->getHtmTransactionUid());
+                    }
+                    return Cycles(1);
+                }
+            );
+        }
+
         _reqs.push_back(req);
     }
 }
@@ -1116,6 +1155,36 @@
             this, isLoad() ? BaseMMU::Read : BaseMMU::Write);
 }
 
+void
+LSQ::SingleDataRequest::markAsStaleTranslation()
+{
+    // If this element has been translated and is currently being requested,
+    // then it may be stale
+    if ((!flags.isSet(Flag::Complete)) &&
+        (!flags.isSet(Flag::Discarded)) &&
+        (flags.isSet(Flag::TranslationStarted))) {
+        _hasStaleTranslation = true;
+    }
+
+    DPRINTF(LSQ, "SingleDataRequest %d 0x%08x isBlocking:%d\n",
+        (int)_state, (uint32_t)flags, _hasStaleTranslation);
+}
+
+void
+LSQ::SplitDataRequest::markAsStaleTranslation()
+{
+    // If this element has been translated and is currently being requested,
+    // then it may be stale
+    if ((!flags.isSet(Flag::Complete)) &&
+        (!flags.isSet(Flag::Discarded)) &&
+        (flags.isSet(Flag::TranslationStarted))) {
+        _hasStaleTranslation = true;
+    }
+
+    DPRINTF(LSQ, "SplitDataRequest %d 0x%08x isBlocking:%d\n",
+        (int)_state, (uint32_t)flags, _hasStaleTranslation);
+}
+
 bool
 LSQ::SingleDataRequest::recvTimingResp(PacketPtr pkt)
 {
@@ -1123,6 +1192,7 @@
     flags.set(Flag::Complete);
     assert(pkt == _packets.front());
     _port.completeDataAccess(pkt);
+    _hasStaleTranslation = false;
     return true;
 }
 
@@ -1148,6 +1218,7 @@
         _port.completeDataAccess(resp);
         delete resp;
     }
+    _hasStaleTranslation = false;
     return true;
 }
 
@@ -1360,15 +1431,17 @@
     lsq->recvReqRetry();
 }
 
-LSQ::HtmCmdRequest::HtmCmdRequest(LSQUnit* port, const DynInstPtr& inst,
-        const Request::Flags& flags_) :
+LSQ::UnsquashableDirectRequest::UnsquashableDirectRequest(
+    LSQUnit* port,
+    const DynInstPtr& inst,
+    const Request::Flags& flags_) :
     SingleDataRequest(port, inst, true, 0x0lu, 8, flags_,
         nullptr, nullptr, nullptr)
 {
 }
 
 void
-LSQ::HtmCmdRequest::initiateTranslation()
+LSQ::UnsquashableDirectRequest::initiateTranslation()
 {
     // Special commands are implemented as loads to avoid significant
     // changes to the cpu and memory interfaces
@@ -1404,14 +1477,53 @@
 }
 
 void
-LSQ::HtmCmdRequest::finish(const Fault &fault, const RequestPtr &req,
-        gem5::ThreadContext* tc, BaseMMU::Mode mode)
+LSQ::UnsquashableDirectRequest::markAsStaleTranslation()
+{
+    // HTM/TLBI operations do not translate,
+    // so cannot have stale translations
+    _hasStaleTranslation = false;
+}
+
+void
+LSQ::UnsquashableDirectRequest::finish(const Fault &fault,
+        const RequestPtr &req, gem5::ThreadContext* tc,
+        BaseMMU::Mode mode)
 {
     panic("unexpected behaviour - finish()");
 }
 
+void
+LSQ::checkStaleTranslations()
+{
+    assert(waitingForStaleTranslation);
+
+    DPRINTF(LSQ, "Checking pending TLBI sync\n");
+    // Check if all thread queues are complete
+    for (const auto& unit : thread) {
+        if (unit.checkStaleTranslations())
+            return;
+    }
+    DPRINTF(LSQ, "No threads have blocking TLBI sync\n");
+
+    // All thread queues have committed their sync operations
+    // => send a RubyRequest to the sequencer
+    auto req = Request::createMemManagement(
+        Request::TLBI_EXT_SYNC_COMP,
+        cpu->dataRequestorId());
+    req->setExtraData(staleTranslationWaitTxnId);
+    PacketPtr pkt = Packet::createRead(req);
+
+    // TODO - reserve some credit for these responses?
+    if (!dcachePort.sendTimingReq(pkt)) {
+        panic("Couldn't send TLBI_EXT_SYNC_COMP message");
+    }
+
+    waitingForStaleTranslation = false;
+    staleTranslationWaitTxnId = 0;
+}
+
 Fault
-LSQ::read(LSQRequest* request, int load_idx)
+LSQ::read(LSQRequest* request, ssize_t load_idx)
 {
     assert(request->req()->contextId() == request->contextId());
     ThreadID tid = cpu->contextToThread(request->req()->contextId());
@@ -1420,7 +1532,7 @@
 }
 
 Fault
-LSQ::write(LSQRequest* request, uint8_t *data, int store_idx)
+LSQ::write(LSQRequest* request, uint8_t *data, ssize_t store_idx)
 {
     ThreadID tid = cpu->contextToThread(request->req()->contextId());
 
diff --git a/src/cpu/o3/lsq.hh b/src/cpu/o3/lsq.hh
index 798ceb9..130c347 100644
--- a/src/cpu/o3/lsq.hh
+++ b/src/cpu/o3/lsq.hh
@@ -63,7 +63,7 @@
 namespace gem5
 {
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace o3
 {
@@ -257,6 +257,7 @@
         std::vector<bool> _byteEnable;
         uint32_t _numOutstandingPackets;
         AtomicOpFunctorPtr _amo_op;
+        bool _hasStaleTranslation;
 
       protected:
         LSQUnit* lsqUnit() { return &_port; }
@@ -264,7 +265,8 @@
         LSQRequest(LSQUnit* port, const DynInstPtr& inst, bool isLoad,
                 const Addr& addr, const uint32_t& size,
                 const Request::Flags& flags_, PacketDataPtr data=nullptr,
-                uint64_t* res=nullptr, AtomicOpFunctorPtr amo_op=nullptr);
+                uint64_t* res=nullptr, AtomicOpFunctorPtr amo_op=nullptr,
+                bool stale_translation=false);
 
         bool
         isLoad() const
@@ -331,6 +333,10 @@
 
         const DynInstPtr& instruction() { return _inst; }
 
+        bool hasStaleTranslation() const { return _hasStaleTranslation; }
+
+        virtual void markAsStaleTranslation() = 0;
+
         /** Set up virtual request.
          * For a previously allocated Request objects.
          */
@@ -571,6 +577,7 @@
                        std::move(amo_op)) {}
 
         virtual ~SingleDataRequest() {}
+        virtual void markAsStaleTranslation();
         virtual void initiateTranslation();
         virtual void finish(const Fault &fault, const RequestPtr &req,
                 gem5::ThreadContext* tc, BaseMMU::Mode mode);
@@ -583,19 +590,25 @@
         virtual std::string name() const { return "SingleDataRequest"; }
     };
 
-    // hardware transactional memory
-    // This class extends SingleDataRequest for the sole purpose
-    // of encapsulating hardware transactional memory command requests
-    class HtmCmdRequest : public SingleDataRequest
+    // This class extends SingleDataRequest for the purpose
+    // of allowing special requests (eg Hardware transactional memory, TLB
+    // shootdowns) to bypass irrelevant system elements like translation &
+    // squashing.
+    class UnsquashableDirectRequest : public SingleDataRequest
     {
       public:
-        HtmCmdRequest(LSQUnit* port, const DynInstPtr& inst,
+        UnsquashableDirectRequest(LSQUnit* port, const DynInstPtr& inst,
                 const Request::Flags& flags_);
-        virtual ~HtmCmdRequest() {}
+        inline virtual ~UnsquashableDirectRequest() {}
         virtual void initiateTranslation();
+        virtual void markAsStaleTranslation();
         virtual void finish(const Fault &fault, const RequestPtr &req,
                 gem5::ThreadContext* tc, BaseMMU::Mode mode);
-        virtual std::string name() const { return "HtmCmdRequest"; }
+        virtual std::string
+        name() const
+        {
+            return "UnsquashableDirectRequest";
+        }
     };
 
     class SplitDataRequest : public LSQRequest
@@ -630,6 +643,7 @@
                 _mainPacket = nullptr;
             }
         }
+        virtual void markAsStaleTranslation();
         virtual void finish(const Fault &fault, const RequestPtr &req,
                 gem5::ThreadContext* tc, BaseMMU::Mode mode);
         virtual bool recvTimingResp(PacketPtr pkt);
@@ -647,7 +661,7 @@
     };
 
     /** Constructs an LSQ with the given parameters. */
-    LSQ(CPU *cpu_ptr, IEW *iew_ptr, const O3CPUParams &params);
+    LSQ(CPU *cpu_ptr, IEW *iew_ptr, const BaseO3CPUParams &params);
 
     /** Returns the name of the LSQ. */
     std::string name() const;
@@ -827,12 +841,17 @@
     /** Executes a read operation, using the load specified at the load
      * index.
      */
-    Fault read(LSQRequest* request, int load_idx);
+    Fault read(LSQRequest* request, ssize_t load_idx);
 
     /** Executes a store operation, using the store specified at the store
      * index.
      */
-    Fault write(LSQRequest* request, uint8_t *data, int store_idx);
+    Fault write(LSQRequest* request, uint8_t *data, ssize_t store_idx);
+
+    /** Checks if queues have any marked operations left,
+     * and sends the appropriate Sync Completion message if not.
+     */
+    void checkStaleTranslations();
 
     /**
      * Retry the previous send that failed.
@@ -884,6 +903,10 @@
     /** The number of used cache ports in this cycle by loads. */
     int usedLoadPorts;
 
+    /** If the LSQ is currently waiting for stale translations */
+    bool waitingForStaleTranslation;
+    /** The ID if the transaction that made translations stale */
+    Addr staleTranslationWaitTxnId;
 
     /** The LSQ policy for SMT mode. */
     SMTQueuePolicy lsqPolicy;
diff --git a/src/cpu/o3/lsq_unit.cc b/src/cpu/o3/lsq_unit.cc
index baf0971..52cf8cb 100644
--- a/src/cpu/o3/lsq_unit.cc
+++ b/src/cpu/o3/lsq_unit.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2014, 2017-2020 ARM Limited
+ * Copyright (c) 2010-2014, 2017-2021 ARM Limited
  * Copyright (c) 2013 Advanced Micro Devices, Inc.
  * All rights reserved
  *
@@ -201,7 +201,7 @@
 }
 
 void
-LSQUnit::init(CPU *cpu_ptr, IEW *iew_ptr, const O3CPUParams &params,
+LSQUnit::init(CPU *cpu_ptr, IEW *iew_ptr, const BaseO3CPUParams &params,
         LSQ *lsq_ptr, unsigned id)
 {
     lsqID = id;
@@ -660,7 +660,7 @@
     // Make sure that a store exists.
     assert(storeQueue.size() != 0);
 
-    int store_idx = store_inst->sqIdx;
+    ssize_t store_idx = store_inst->sqIdx;
 
     DPRINTF(LSQUnit, "Executing store PC %s [sn:%lli]\n",
             store_inst->pcState(), store_inst->seqNum);
@@ -1046,7 +1046,7 @@
     if (isStalled() &&
         storeWBIt->instruction()->seqNum == stallingStoreIsn) {
         DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] "
-                "load idx:%i\n",
+                "load idx:%li\n",
                 stallingStoreIsn, stallingLoadIdx);
         stalled = false;
         stallingStoreIsn = 0;
@@ -1172,7 +1172,7 @@
     if (isStalled() &&
         store_inst->seqNum == stallingStoreIsn) {
         DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] "
-                "load idx:%i\n",
+                "load idx:%li\n",
                 stallingStoreIsn, stallingLoadIdx);
         stalled = false;
         stallingStoreIsn = 0;
@@ -1239,6 +1239,39 @@
 }
 
 void
+LSQUnit::startStaleTranslationFlush()
+{
+    DPRINTF(LSQUnit, "Unit %p marking stale translations %d %d\n", this,
+        storeQueue.size(), loadQueue.size());
+    for (auto& entry : storeQueue) {
+        if (entry.valid() && entry.hasRequest())
+            entry.request()->markAsStaleTranslation();
+    }
+    for (auto& entry : loadQueue) {
+        if (entry.valid() && entry.hasRequest())
+            entry.request()->markAsStaleTranslation();
+    }
+}
+
+bool
+LSQUnit::checkStaleTranslations() const
+{
+    DPRINTF(LSQUnit, "Unit %p checking stale translations\n", this);
+    for (auto& entry : storeQueue) {
+        if (entry.valid() && entry.hasRequest()
+            && entry.request()->hasStaleTranslation())
+            return true;
+    }
+    for (auto& entry : loadQueue) {
+        if (entry.valid() && entry.hasRequest()
+            && entry.request()->hasStaleTranslation())
+            return true;
+    }
+    DPRINTF(LSQUnit, "Unit %p found no stale translations\n", this);
+    return false;
+}
+
+void
 LSQUnit::recvRetry()
 {
     if (isStoreBlocked) {
@@ -1282,7 +1315,7 @@
 }
 
 Fault
-LSQUnit::read(LSQRequest *request, int load_idx)
+LSQUnit::read(LSQRequest *request, ssize_t load_idx)
 {
     LQEntry& load_entry = loadQueue[load_idx];
     const DynInstPtr& load_inst = load_entry.instruction();
@@ -1336,7 +1369,6 @@
 
     if (request->mainReq()->isLocalAccess()) {
         assert(!load_inst->memData);
-        assert(!load_inst->inHtmTransactionalState());
         load_inst->memData = new uint8_t[MaxDataBytes];
 
         gem5::ThreadContext *thread = cpu->tcBase(lsqID);
@@ -1351,37 +1383,6 @@
         return NoFault;
     }
 
-    // hardware transactional memory
-    if (request->mainReq()->isHTMStart() || request->mainReq()->isHTMCommit())
-    {
-        // don't want to send nested transactionStarts and
-        // transactionStops outside of core, e.g. to Ruby
-        if (request->mainReq()->getFlags().isSet(Request::NO_ACCESS)) {
-            Cycles delay(0);
-            PacketPtr data_pkt =
-                new Packet(request->mainReq(), MemCmd::ReadReq);
-
-            // Allocate memory if this is the first time a load is issued.
-            if (!load_inst->memData) {
-                load_inst->memData =
-                    new uint8_t[request->mainReq()->getSize()];
-                // sanity checks espect zero in request's data
-                memset(load_inst->memData, 0, request->mainReq()->getSize());
-            }
-
-            data_pkt->dataStatic(load_inst->memData);
-            if (load_inst->inHtmTransactionalState()) {
-                data_pkt->setHtmTransactional(
-                    load_inst->getHtmTransactionUid());
-            }
-            data_pkt->makeResponse();
-
-            WritebackEvent *wb = new WritebackEvent(load_inst, data_pkt, this);
-            cpu->schedule(wb, cpu->clockEdge(delay));
-            return NoFault;
-        }
-    }
-
     // Check the SQ for any previous stores that might lead to forwarding
     auto store_it = load_inst->sqIt;
     assert (store_it >= storeWBIt);
@@ -1599,7 +1600,7 @@
 }
 
 Fault
-LSQUnit::write(LSQRequest *request, uint8_t *data, int store_idx)
+LSQUnit::write(LSQRequest *request, uint8_t *data, ssize_t store_idx)
 {
     assert(storeQueue[store_idx].valid());
 
diff --git a/src/cpu/o3/lsq_unit.hh b/src/cpu/o3/lsq_unit.hh
index 0d2f80f..c0899ba 100644
--- a/src/cpu/o3/lsq_unit.hh
+++ b/src/cpu/o3/lsq_unit.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014,2017-2018,2020 ARM Limited
+ * Copyright (c) 2012-2014,2017-2018,2020-2021 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -67,7 +67,7 @@
 namespace gem5
 {
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace o3
 {
@@ -223,7 +223,7 @@
     }
 
     /** Initializes the LSQ unit with the specified number of entries. */
-    void init(CPU *cpu_ptr, IEW *iew_ptr, const O3CPUParams &params,
+    void init(CPU *cpu_ptr, IEW *iew_ptr, const BaseO3CPUParams &params,
             LSQ *lsq_ptr, unsigned id);
 
     /** Returns the name of the LSQ unit. */
@@ -317,6 +317,10 @@
         lastRetiredHtmUid = htm_uid;
     }
 
+    // Stale translation checks
+    void startStaleTranslationFlush();
+    bool checkStaleTranslations() const;
+
     /** Returns if either the LQ or SQ is full. */
     bool isFull() { return lqFull() || sqFull(); }
 
@@ -485,7 +489,7 @@
      */
     InstSeqNum stallingStoreIsn;
     /** The index of the above store. */
-    int stallingLoadIdx;
+    ssize_t stallingLoadIdx;
 
     /** The packet that needs to be retried. */
     PacketPtr retryPkt;
@@ -539,10 +543,10 @@
 
   public:
     /** Executes the load at the given index. */
-    Fault read(LSQRequest *request, int load_idx);
+    Fault read(LSQRequest *request, ssize_t load_idx);
 
     /** Executes the store at the given index. */
-    Fault write(LSQRequest *request, uint8_t *data, int store_idx);
+    Fault write(LSQRequest *requst, uint8_t *data, ssize_t store_idx);
 
     /** Returns the index of the head load instruction. */
     int getLoadHead() { return loadQueue.head(); }
diff --git a/src/cpu/o3/mem_dep_unit.cc b/src/cpu/o3/mem_dep_unit.cc
index 11a6135..bffbf23 100644
--- a/src/cpu/o3/mem_dep_unit.cc
+++ b/src/cpu/o3/mem_dep_unit.cc
@@ -38,7 +38,7 @@
 #include "cpu/o3/inst_queue.hh"
 #include "cpu/o3/limits.hh"
 #include "debug/MemDepUnit.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 
 namespace gem5
 {
@@ -54,7 +54,7 @@
 
 MemDepUnit::MemDepUnit() : iqPtr(NULL), stats(nullptr) {}
 
-MemDepUnit::MemDepUnit(const O3CPUParams &params)
+MemDepUnit::MemDepUnit(const BaseO3CPUParams &params)
     : _name(params.name + ".memdepunit"),
       depPred(params.store_set_clear_period, params.SSITSize,
               params.LFSTSize),
@@ -89,7 +89,7 @@
 }
 
 void
-MemDepUnit::init(const O3CPUParams &params, ThreadID tid, CPU *cpu)
+MemDepUnit::init(const BaseO3CPUParams &params, ThreadID tid, CPU *cpu)
 {
     DPRINTF(MemDepUnit, "Creating MemDepUnit %i object.\n",tid);
 
diff --git a/src/cpu/o3/mem_dep_unit.hh b/src/cpu/o3/mem_dep_unit.hh
index c6b270c..6609f8d 100644
--- a/src/cpu/o3/mem_dep_unit.hh
+++ b/src/cpu/o3/mem_dep_unit.hh
@@ -68,7 +68,7 @@
     }
 };
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace o3
 {
@@ -97,7 +97,7 @@
     MemDepUnit();
 
     /** Constructs a MemDepUnit with given parameters. */
-    MemDepUnit(const O3CPUParams &params);
+    MemDepUnit(const BaseO3CPUParams &params);
 
     /** Frees up any memory allocated. */
     ~MemDepUnit();
@@ -106,7 +106,7 @@
     std::string name() const { return _name; }
 
     /** Initializes the unit with parameters and a thread id. */
-    void init(const O3CPUParams &params, ThreadID tid, CPU *cpu);
+    void init(const BaseO3CPUParams &params, ThreadID tid, CPU *cpu);
 
     /** Determine if we are drained. */
     bool isDrained() const;
diff --git a/src/cpu/o3/probe/SConscript b/src/cpu/o3/probe/SConscript
index bd06b62..b2bbb3e 100644
--- a/src/cpu/o3/probe/SConscript
+++ b/src/cpu/o3/probe/SConscript
@@ -37,12 +37,11 @@
 
 Import('*')
 
-if 'O3CPU' in env['CPU_MODELS']:
+if env['CONF']['TARGET_ISA'] != 'null':
     SimObject('SimpleTrace.py', sim_objects=['SimpleTrace'])
     Source('simple_trace.cc')
     DebugFlag('SimpleTrace')
 
-    if env['HAVE_PROTOBUF']:
-        SimObject('ElasticTrace.py', sim_objects=['ElasticTrace'])
-        Source('elastic_trace.cc')
-        DebugFlag('ElasticTrace')
+    SimObject('ElasticTrace.py', sim_objects=['ElasticTrace'], tags='protobuf')
+    Source('elastic_trace.cc', tags='protobuf')
+    DebugFlag('ElasticTrace', tags='protobuf')
diff --git a/src/cpu/o3/probe/elastic_trace.cc b/src/cpu/o3/probe/elastic_trace.cc
index 0a48412..a56ef17 100644
--- a/src/cpu/o3/probe/elastic_trace.cc
+++ b/src/cpu/o3/probe/elastic_trace.cc
@@ -65,9 +65,6 @@
        stats(this)
 {
     cpu = dynamic_cast<CPU *>(params.manager);
-    const BaseISA::RegClasses &regClasses =
-        cpu->getContext(0)->getIsaPtr()->regClasses();
-    zeroReg = regClasses.at(IntRegClass).zeroReg();
 
     fatal_if(!cpu, "Manager of %s is not of type O3CPU and thus does not "\
                 "support dependency tracing.\n", name());
@@ -251,8 +248,7 @@
     for (int src_idx = 0; src_idx < max_regs; src_idx++) {
 
         const RegId& src_reg = dyn_inst->srcRegIdx(src_idx);
-        if (!src_reg.is(MiscRegClass) &&
-                !(src_reg.is(IntRegClass) && src_reg.index() == zeroReg)) {
+        if (!src_reg.is(MiscRegClass) && !src_reg.is(InvalidRegClass)) {
             // Get the physical register index of the i'th source register.
             PhysRegIdPtr phys_src_reg = dyn_inst->renamedSrcIdx(src_idx);
             DPRINTFR(ElasticTrace, "[sn:%lli] Check map for src reg"
@@ -283,8 +279,7 @@
         // For data dependency tracking the register must be an int, float or
         // CC register and not a Misc register.
         const RegId& dest_reg = dyn_inst->destRegIdx(dest_idx);
-        if (!dest_reg.is(MiscRegClass) &&
-                !(dest_reg.is(IntRegClass) && dest_reg.index() == zeroReg)) {
+        if (!dest_reg.is(MiscRegClass) && !dest_reg.is(InvalidRegClass)) {
             // Get the physical register index of the i'th destination
             // register.
             PhysRegIdPtr phys_dest_reg =
diff --git a/src/cpu/o3/probe/elastic_trace.hh b/src/cpu/o3/probe/elastic_trace.hh
index 53a6cbe..bf72f1d 100644
--- a/src/cpu/o3/probe/elastic_trace.hh
+++ b/src/cpu/o3/probe/elastic_trace.hh
@@ -190,8 +190,6 @@
      */
     bool firstWin;
 
-    RegIndex zeroReg;
-
     /**
      * @defgroup InstExecInfo Struct for storing information before an
      * instruction reaches the commit stage, e.g. execute timestamp.
diff --git a/src/cpu/o3/regfile.cc b/src/cpu/o3/regfile.cc
index 17fbe7f..0e5cc06 100644
--- a/src/cpu/o3/regfile.cc
+++ b/src/cpu/o3/regfile.cc
@@ -54,24 +54,27 @@
                          unsigned _numPhysicalVecRegs,
                          unsigned _numPhysicalVecPredRegs,
                          unsigned _numPhysicalCCRegs,
-                         const BaseISA::RegClasses &regClasses)
-    : intRegFile(_numPhysicalIntRegs),
-      floatRegFile(_numPhysicalFloatRegs),
-      vectorRegFile(_numPhysicalVecRegs),
-      vectorElemRegFile(_numPhysicalVecRegs * TheISA::NumVecElemPerVecReg),
-      vecPredRegFile(_numPhysicalVecPredRegs),
-      ccRegFile(_numPhysicalCCRegs),
+                         const BaseISA::RegClasses &reg_classes)
+    : intRegFile(reg_classes.at(IntRegClass), _numPhysicalIntRegs),
+      floatRegFile(reg_classes.at(FloatRegClass), _numPhysicalFloatRegs),
+      vectorRegFile(reg_classes.at(VecRegClass), _numPhysicalVecRegs),
+      vectorElemRegFile(reg_classes.at(VecElemClass), _numPhysicalVecRegs * (
+                  reg_classes.at(VecElemClass).numRegs() /
+                  reg_classes.at(VecRegClass).numRegs())),
+      vecPredRegFile(reg_classes.at(VecPredRegClass), _numPhysicalVecPredRegs),
+      ccRegFile(reg_classes.at(CCRegClass), _numPhysicalCCRegs),
       numPhysicalIntRegs(_numPhysicalIntRegs),
       numPhysicalFloatRegs(_numPhysicalFloatRegs),
       numPhysicalVecRegs(_numPhysicalVecRegs),
-      numPhysicalVecElemRegs(_numPhysicalVecRegs *
-                             TheISA::NumVecElemPerVecReg),
+      numPhysicalVecElemRegs(_numPhysicalVecRegs * (
+                  reg_classes.at(VecElemClass).numRegs() /
+                  reg_classes.at(VecRegClass).numRegs())),
       numPhysicalVecPredRegs(_numPhysicalVecPredRegs),
       numPhysicalCCRegs(_numPhysicalCCRegs),
       totalNumRegs(_numPhysicalIntRegs
                    + _numPhysicalFloatRegs
                    + _numPhysicalVecRegs
-                   + _numPhysicalVecRegs * TheISA::NumVecElemPerVecReg
+                   + numPhysicalVecElemRegs
                    + _numPhysicalVecPredRegs
                    + _numPhysicalCCRegs)
 {
@@ -83,8 +86,6 @@
         intRegIds.emplace_back(IntRegClass, phys_reg, flat_reg_idx++);
     }
 
-    zeroReg = RegId(IntRegClass, regClasses.at(IntRegClass).zeroReg());
-
     // The next batch of the registers are the floating-point physical
     // registers; put them onto the floating-point free list.
     for (phys_reg = 0; phys_reg < numPhysicalFloatRegs; phys_reg++) {
@@ -94,18 +95,12 @@
     // The next batch of the registers are the vector physical
     // registers; put them onto the vector free list.
     for (phys_reg = 0; phys_reg < numPhysicalVecRegs; phys_reg++) {
-        vectorRegFile[phys_reg].zero();
         vecRegIds.emplace_back(VecRegClass, phys_reg, flat_reg_idx++);
     }
     // The next batch of the registers are the vector element physical
-    // registers; they refer to the same containers as the vector
-    // registers, just a different (and incompatible) way to access
-    // them; put them onto the vector free list.
-    for (phys_reg = 0; phys_reg < numPhysicalVecRegs; phys_reg++) {
-        for (ElemIndex eIdx = 0; eIdx < TheISA::NumVecElemPerVecReg; eIdx++) {
-            vecElemIds.emplace_back(VecElemClass, phys_reg,
-                    eIdx, flat_reg_idx++);
-        }
+    // registers; put them onto the vector free list.
+    for (phys_reg = 0; phys_reg < numPhysicalVecElemRegs; phys_reg++) {
+        vecElemIds.emplace_back(VecElemClass, phys_reg, flat_reg_idx++);
     }
 
     // The next batch of the registers are the predicate physical
@@ -121,7 +116,7 @@
     }
 
     // Misc regs have a fixed mapping but still need PhysRegIds.
-    for (phys_reg = 0; phys_reg < regClasses.at(MiscRegClass).size();
+    for (phys_reg = 0; phys_reg < reg_classes.at(MiscRegClass).numRegs();
             phys_reg++) {
         miscRegIds.emplace_back(MiscRegClass, phys_reg, 0);
     }
@@ -151,15 +146,11 @@
      * registers; put them onto the vector free list. */
     for (reg_idx = 0; reg_idx < numPhysicalVecRegs; reg_idx++) {
         assert(vecRegIds[reg_idx].index() == reg_idx);
-        for (ElemIndex elemIdx = 0; elemIdx < TheISA::NumVecElemPerVecReg;
-                elemIdx++) {
-            assert(vecElemIds[reg_idx * TheISA::NumVecElemPerVecReg +
-                    elemIdx].index() == reg_idx);
-            assert(vecElemIds[reg_idx * TheISA::NumVecElemPerVecReg +
-                    elemIdx].elemIndex() == elemIdx);
-        }
     }
     freeList->addRegs(vecRegIds.begin(), vecRegIds.end());
+    for (reg_idx = 0; reg_idx < numPhysicalVecElemRegs; reg_idx++) {
+        assert(vecElemIds[reg_idx].index() == reg_idx);
+    }
     freeList->addRegs(vecElemIds.begin(), vecElemIds.end());
 
     // The next batch of the registers are the predicate physical
@@ -196,6 +187,8 @@
         return std::make_pair(ccRegIds.begin(), ccRegIds.end());
       case MiscRegClass:
         return std::make_pair(miscRegIds.begin(), miscRegIds.end());
+      case InvalidRegClass:
+        panic("Tried to get register IDs for the invalid class.");
     }
     /* There is no way to make an empty iterator */
     return std::make_pair(PhysIds::iterator(),
@@ -209,8 +202,7 @@
     case VecRegClass:
         return &vecRegIds[reg->index()];
     case VecElemClass:
-        return &vecElemIds[reg->index() * TheISA::NumVecElemPerVecReg +
-            reg->elemIndex()];
+        return &vecElemIds[reg->index()];
     default:
         panic_if(!reg->is(VecElemClass),
             "Trying to get the register of a %s register", reg->className());
diff --git a/src/cpu/o3/regfile.hh b/src/cpu/o3/regfile.hh
index cb3aee3..18d2d51 100644
--- a/src/cpu/o3/regfile.hh
+++ b/src/cpu/o3/regfile.hh
@@ -42,6 +42,7 @@
 #ifndef __CPU_O3_REGFILE_HH__
 #define __CPU_O3_REGFILE_HH__
 
+#include <cstring>
 #include <vector>
 
 #include "arch/generic/isa.hh"
@@ -49,6 +50,7 @@
 #include "base/trace.hh"
 #include "config/the_isa.hh"
 #include "cpu/o3/comm.hh"
+#include "cpu/regfile.hh"
 #include "debug/IEW.hh"
 
 namespace gem5
@@ -72,28 +74,27 @@
                               PhysIds::iterator>;
   private:
     /** Integer register file. */
-    std::vector<RegVal> intRegFile;
+    RegFile intRegFile;
     std::vector<PhysRegId> intRegIds;
-    RegId zeroReg;
 
     /** Floating point register file. */
-    std::vector<RegVal> floatRegFile;
+    RegFile floatRegFile;
     std::vector<PhysRegId> floatRegIds;
 
     /** Vector register file. */
-    std::vector<TheISA::VecRegContainer> vectorRegFile;
+    RegFile vectorRegFile;
     std::vector<PhysRegId> vecRegIds;
 
     /** Vector element register file. */
-    std::vector<RegVal> vectorElemRegFile;
+    RegFile vectorElemRegFile;
     std::vector<PhysRegId> vecElemIds;
 
     /** Predicate register file. */
-    std::vector<TheISA::VecPredRegContainer> vecPredRegFile;
+    RegFile vecPredRegFile;
     std::vector<PhysRegId> vecPredRegIds;
 
     /** Condition-code register file. */
-    std::vector<RegVal> ccRegFile;
+    RegFile ccRegFile;
     std::vector<PhysRegId> ccRegIds;
 
     /** Misc Reg Ids */
@@ -142,7 +143,7 @@
                 unsigned _numPhysicalVecRegs,
                 unsigned _numPhysicalVecPredRegs,
                 unsigned _numPhysicalCCRegs,
-                const BaseISA::RegClasses &regClasses);
+                const BaseISA::RegClasses &classes);
 
     /**
      * Destructor to free resources
@@ -152,22 +153,6 @@
     /** Initialize the free list */
     void initFreeList(UnifiedFreeList *freeList);
 
-    /** @return the number of integer physical registers. */
-    unsigned numIntPhysRegs() const { return numPhysicalIntRegs; }
-
-    /** @return the number of floating-point physical registers. */
-    unsigned numFloatPhysRegs() const { return numPhysicalFloatRegs; }
-    /** @return the number of vector physical registers. */
-    unsigned numVecPhysRegs() const { return numPhysicalVecRegs; }
-    /** @return the number of predicate physical registers. */
-    unsigned numPredPhysRegs() const { return numPhysicalVecPredRegs; }
-
-    /** @return the number of vector physical registers. */
-    unsigned numVecElemPhysRegs() const { return numPhysicalVecElemRegs; }
-
-    /** @return the number of condition-code physical registers. */
-    unsigned numCCPhysRegs() const { return numPhysicalCCRegs; }
-
     /** @return the total number of physical registers. */
     unsigned totalNumPhysRegs() const { return totalNumRegs; }
 
@@ -176,172 +161,155 @@
         return &miscRegIds[reg_idx];
     }
 
-    /** Reads an integer register. */
     RegVal
-    readIntReg(PhysRegIdPtr phys_reg) const
+    getReg(PhysRegIdPtr phys_reg) const
     {
-        assert(phys_reg->is(IntRegClass));
+        const RegClassType type = phys_reg->classValue();
+        const RegIndex idx = phys_reg->index();
 
-        DPRINTF(IEW, "RegFile: Access to int register %i, has data "
-                "%#x\n", phys_reg->index(), intRegFile[phys_reg->index()]);
-        return intRegFile[phys_reg->index()];
-    }
-
-    RegVal
-    readFloatReg(PhysRegIdPtr phys_reg) const
-    {
-        assert(phys_reg->is(FloatRegClass));
-
-        RegVal floatRegBits = floatRegFile[phys_reg->index()];
-
-        DPRINTF(IEW, "RegFile: Access to float register %i as int, "
-                "has data %#x\n", phys_reg->index(), floatRegBits);
-
-        return floatRegBits;
-    }
-
-    /** Reads a vector register. */
-    const TheISA::VecRegContainer &
-    readVecReg(PhysRegIdPtr phys_reg) const
-    {
-        assert(phys_reg->is(VecRegClass));
-
-        DPRINTF(IEW, "RegFile: Access to vector register %i, has "
-                "data %s\n", int(phys_reg->index()),
-                vectorRegFile[phys_reg->index()]);
-
-        return vectorRegFile[phys_reg->index()];
-    }
-
-    /** Reads a vector register for modification. */
-    TheISA::VecRegContainer &
-    getWritableVecReg(PhysRegIdPtr phys_reg)
-    {
-        /* const_cast for not duplicating code above. */
-        return const_cast<TheISA::VecRegContainer&>(readVecReg(phys_reg));
-    }
-
-    /** Reads a vector element. */
-    RegVal
-    readVecElem(PhysRegIdPtr phys_reg) const
-    {
-        assert(phys_reg->is(VecElemClass));
-        RegVal val = vectorElemRegFile[
-                phys_reg->index() * TheISA::NumVecElemPerVecReg +
-                phys_reg->elemIndex()];
-        DPRINTF(IEW, "RegFile: Access to element %d of vector register %i,"
-                " has data %#x\n", phys_reg->elemIndex(),
-                phys_reg->index(), val);
-
-        return val;
-    }
-
-    /** Reads a predicate register. */
-    const TheISA::VecPredRegContainer&
-    readVecPredReg(PhysRegIdPtr phys_reg) const
-    {
-        assert(phys_reg->is(VecPredRegClass));
-
-        DPRINTF(IEW, "RegFile: Access to predicate register %i, has "
-                "data %s\n", int(phys_reg->index()),
-                vecPredRegFile[phys_reg->index()]);
-
-        return vecPredRegFile[phys_reg->index()];
-    }
-
-    TheISA::VecPredRegContainer&
-    getWritableVecPredReg(PhysRegIdPtr phys_reg)
-    {
-        /* const_cast for not duplicating code above. */
-        return const_cast<TheISA::VecPredRegContainer&>(
-                readVecPredReg(phys_reg));
-    }
-
-    /** Reads a condition-code register. */
-    RegVal
-    readCCReg(PhysRegIdPtr phys_reg)
-    {
-        assert(phys_reg->is(CCRegClass));
-
-        DPRINTF(IEW, "RegFile: Access to cc register %i, has "
-                "data %#x\n", phys_reg->index(),
-                ccRegFile[phys_reg->index()]);
-
-        return ccRegFile[phys_reg->index()];
-    }
-
-    /** Sets an integer register to the given value. */
-    void
-    setIntReg(PhysRegIdPtr phys_reg, RegVal val)
-    {
-        assert(phys_reg->is(IntRegClass));
-
-        DPRINTF(IEW, "RegFile: Setting int register %i to %#x\n",
-                phys_reg->index(), val);
-
-        if (phys_reg->index() != zeroReg.index())
-            intRegFile[phys_reg->index()] = val;
+        RegVal val;
+        switch (type) {
+          case IntRegClass:
+            val = intRegFile.reg(idx);
+            DPRINTF(IEW, "RegFile: Access to int register %i, has data %#x\n",
+                    idx, val);
+            return val;
+          case FloatRegClass:
+            val = floatRegFile.reg(idx);
+            DPRINTF(IEW, "RegFile: Access to float register %i has data %#x\n",
+                    idx, val);
+            return val;
+          case VecElemClass:
+            val = vectorElemRegFile.reg(idx);
+            DPRINTF(IEW, "RegFile: Access to vector element register %i "
+                    "has data %#x\n", idx, val);
+            return val;
+          case CCRegClass:
+            val = ccRegFile.reg(idx);
+            DPRINTF(IEW, "RegFile: Access to cc register %i has data %#x\n",
+                    idx, val);
+            return val;
+          default:
+            panic("Unsupported register class type %d.", type);
+        }
     }
 
     void
-    setFloatReg(PhysRegIdPtr phys_reg, RegVal val)
+    getReg(PhysRegIdPtr phys_reg, void *val) const
     {
-        assert(phys_reg->is(FloatRegClass));
+        const RegClassType type = phys_reg->classValue();
+        const RegIndex idx = phys_reg->index();
 
-        DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n",
-                phys_reg->index(), (uint64_t)val);
-
-        floatRegFile[phys_reg->index()] = val;
+        switch (type) {
+          case IntRegClass:
+            *(RegVal *)val = getReg(phys_reg);
+            break;
+          case FloatRegClass:
+            *(RegVal *)val = getReg(phys_reg);
+            break;
+          case VecRegClass:
+            vectorRegFile.get(idx, val);
+            DPRINTF(IEW, "RegFile: Access to vector register %i, has "
+                    "data %s\n", idx, vectorRegFile.regClass.valString(val));
+            break;
+          case VecElemClass:
+            *(RegVal *)val = getReg(phys_reg);
+            break;
+          case VecPredRegClass:
+            vecPredRegFile.get(idx, val);
+            DPRINTF(IEW, "RegFile: Access to predicate register %i, has "
+                    "data %s\n", idx, vecPredRegFile.regClass.valString(val));
+            break;
+          case CCRegClass:
+            *(RegVal *)val = getReg(phys_reg);
+            break;
+          default:
+            panic("Unrecognized register class type %d.", type);
+        }
     }
 
-    /** Sets a vector register to the given value. */
-    void
-    setVecReg(PhysRegIdPtr phys_reg, const TheISA::VecRegContainer& val)
+    void *
+    getWritableReg(PhysRegIdPtr phys_reg)
     {
-        assert(phys_reg->is(VecRegClass));
+        const RegClassType type = phys_reg->classValue();
+        const RegIndex idx = phys_reg->index();
 
-        DPRINTF(IEW, "RegFile: Setting vector register %i to %s\n",
-                int(phys_reg->index()), val);
-
-        vectorRegFile[phys_reg->index()] = val;
+        switch (type) {
+          case VecRegClass:
+            return vectorRegFile.ptr(idx);
+          case VecPredRegClass:
+            return vecPredRegFile.ptr(idx);
+          default:
+            panic("Unrecognized register class type %d.", type);
+        }
     }
 
-    /** Sets a vector register to the given value. */
     void
-    setVecElem(PhysRegIdPtr phys_reg, RegVal val)
+    setReg(PhysRegIdPtr phys_reg, RegVal val)
     {
-        assert(phys_reg->is(VecElemClass));
+        const RegClassType type = phys_reg->classValue();
+        const RegIndex idx = phys_reg->index();
 
-        DPRINTF(IEW, "RegFile: Setting element %d of vector register %i to"
-                " %#x\n", phys_reg->elemIndex(), int(phys_reg->index()), val);
-
-        vectorElemRegFile[phys_reg->index() * TheISA::NumVecElemPerVecReg +
-                phys_reg->elemIndex()] = val;
+        switch (type) {
+          case InvalidRegClass:
+            break;
+          case IntRegClass:
+            intRegFile.reg(idx) = val;
+            DPRINTF(IEW, "RegFile: Setting int register %i to %#x\n",
+                    idx, val);
+            break;
+          case FloatRegClass:
+            floatRegFile.reg(idx) = val;
+            DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n",
+                    idx, val);
+            break;
+          case VecElemClass:
+            vectorElemRegFile.reg(idx) = val;
+            DPRINTF(IEW, "RegFile: Setting vector element register %i to "
+                    "%#x\n", idx, val);
+            break;
+          case CCRegClass:
+            ccRegFile.reg(idx) = val;
+            DPRINTF(IEW, "RegFile: Setting cc register %i to %#x\n",
+                    idx, val);
+            break;
+          default:
+            panic("Unsupported register class type %d.", type);
+        }
     }
 
-    /** Sets a predicate register to the given value. */
     void
-    setVecPredReg(PhysRegIdPtr phys_reg,
-            const TheISA::VecPredRegContainer& val)
+    setReg(PhysRegIdPtr phys_reg, const void *val)
     {
-        assert(phys_reg->is(VecPredRegClass));
+        const RegClassType type = phys_reg->classValue();
+        const RegIndex idx = phys_reg->index();
 
-        DPRINTF(IEW, "RegFile: Setting predicate register %i to %s\n",
-                int(phys_reg->index()), val);
-
-        vecPredRegFile[phys_reg->index()] = val;
-    }
-
-    /** Sets a condition-code register to the given value. */
-    void
-    setCCReg(PhysRegIdPtr phys_reg, RegVal val)
-    {
-        assert(phys_reg->is(CCRegClass));
-
-        DPRINTF(IEW, "RegFile: Setting cc register %i to %#x\n",
-                phys_reg->index(), (uint64_t)val);
-
-        ccRegFile[phys_reg->index()] = val;
+        switch (type) {
+          case IntRegClass:
+            setReg(phys_reg, *(RegVal *)val);
+            break;
+          case FloatRegClass:
+            setReg(phys_reg, *(RegVal *)val);
+            break;
+          case VecRegClass:
+            DPRINTF(IEW, "RegFile: Setting vector register %i to %s\n",
+                    idx, vectorRegFile.regClass.valString(val));
+            vectorRegFile.set(idx, val);
+            break;
+          case VecElemClass:
+            setReg(phys_reg, *(RegVal *)val);
+            break;
+          case VecPredRegClass:
+            DPRINTF(IEW, "RegFile: Setting predicate register %i to %s\n",
+                    idx, vectorRegFile.regClass.valString(val));
+            vecPredRegFile.set(idx, val);
+            break;
+          case CCRegClass:
+            setReg(phys_reg, *(RegVal *)val);
+            break;
+          default:
+            panic("Unrecognized register class type %d.", type);
+        }
     }
 
     /**
diff --git a/src/cpu/o3/rename.cc b/src/cpu/o3/rename.cc
index a3f8700..89d4542 100644
--- a/src/cpu/o3/rename.cc
+++ b/src/cpu/o3/rename.cc
@@ -50,7 +50,7 @@
 #include "debug/Activity.hh"
 #include "debug/O3PipeView.hh"
 #include "debug/Rename.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 
 namespace gem5
 {
@@ -58,7 +58,7 @@
 namespace o3
 {
 
-Rename::Rename(CPU *_cpu, const O3CPUParams &params)
+Rename::Rename(CPU *_cpu, const BaseO3CPUParams &params)
     : cpu(_cpu),
       iewToRenameDelay(params.iewToRenameDelay),
       decodeToRenameDelay(params.decodeToRenameDelay),
@@ -656,12 +656,7 @@
 
         // Check here to make sure there are enough destination registers
         // to rename to.  Otherwise block.
-        if (!renameMap[tid]->canRename(inst->numIntDestRegs(),
-                                       inst->numFPDestRegs(),
-                                       inst->numVecDestRegs(),
-                                       inst->numVecElemDestRegs(),
-                                       inst->numVecPredDestRegs(),
-                                       inst->numCCDestRegs())) {
+        if (!renameMap[tid]->canRename(inst)) {
             DPRINTF(Rename,
                     "Blocking due to "
                     " lack of free physical registers to rename to.\n");
@@ -1022,6 +1017,8 @@
 
         renamed_reg = map->lookup(tc->flattenRegId(src_reg));
         switch (src_reg.classValue()) {
+          case InvalidRegClass:
+            break;
           case IntRegClass:
             stats.intLookups++;
             break;
@@ -1255,12 +1252,12 @@
             freeEntries[tid].lqEntries,
             freeEntries[tid].sqEntries,
             renameMap[tid]->numFreeEntries(),
-            renameMap[tid]->numFreeIntEntries(),
-            renameMap[tid]->numFreeFloatEntries(),
-            renameMap[tid]->numFreeVecEntries(),
-            renameMap[tid]->numFreeVecElemEntries(),
-            renameMap[tid]->numFreePredEntries(),
-            renameMap[tid]->numFreeCCEntries());
+            renameMap[tid]->numFreeEntries(IntRegClass),
+            renameMap[tid]->numFreeEntries(FloatRegClass),
+            renameMap[tid]->numFreeEntries(VecRegClass),
+            renameMap[tid]->numFreeEntries(VecElemClass),
+            renameMap[tid]->numFreeEntries(VecPredRegClass),
+            renameMap[tid]->numFreeEntries(CCRegClass));
 
     DPRINTF(Rename, "[tid:%i] %i instructions not yet in ROB\n",
             tid, instsInProgress[tid]);
diff --git a/src/cpu/o3/rename.hh b/src/cpu/o3/rename.hh
index 0204109..0b42b6e 100644
--- a/src/cpu/o3/rename.hh
+++ b/src/cpu/o3/rename.hh
@@ -58,7 +58,7 @@
 namespace gem5
 {
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace o3
 {
@@ -125,7 +125,7 @@
 
   public:
     /** Rename constructor. */
-    Rename(CPU *_cpu, const O3CPUParams &params);
+    Rename(CPU *_cpu, const BaseO3CPUParams &params);
 
     /** Returns the name of rename. */
     std::string name() const;
diff --git a/src/cpu/o3/rename_map.cc b/src/cpu/o3/rename_map.cc
index dee113f..40e45f2 100644
--- a/src/cpu/o3/rename_map.cc
+++ b/src/cpu/o3/rename_map.cc
@@ -44,6 +44,7 @@
 #include <vector>
 
 #include "arch/vecregs.hh"
+#include "cpu/o3/dyn_inst.hh"
 #include "cpu/reg_class.hh"
 #include "debug/Rename.hh"
 
@@ -53,8 +54,7 @@
 namespace o3
 {
 
-SimpleRenameMap::SimpleRenameMap()
-    : freeList(NULL), zeroReg(IntRegClass, 0)
+SimpleRenameMap::SimpleRenameMap() : freeList(NULL)
 {
 }
 
@@ -65,9 +65,8 @@
     assert(freeList == NULL);
     assert(map.empty());
 
-    map.resize(reg_class.size());
+    map.resize(reg_class.numRegs());
     freeList = _freeList;
-    zeroReg = RegId(IntRegClass, reg_class.zeroReg());
 }
 
 SimpleRenameMap::RenameInfo
@@ -76,10 +75,10 @@
     PhysRegIdPtr renamed_reg;
     // Record the current physical register that is renamed to the
     // requested architected register.
-    PhysRegIdPtr prev_reg = map[arch_reg.flatIndex()];
+    PhysRegIdPtr prev_reg = map[arch_reg.index()];
 
-    if (arch_reg == zeroReg) {
-        assert(prev_reg->index() == zeroReg.index());
+    if (arch_reg.is(InvalidRegClass)) {
+        assert(prev_reg->is(InvalidRegClass));
         renamed_reg = prev_reg;
     } else if (prev_reg->getNumPinnedWrites() > 0) {
         // Do not rename if the register is pinned
@@ -91,7 +90,7 @@
         renamed_reg->decrNumPinnedWrites();
     } else {
         renamed_reg = freeList->getReg();
-        map[arch_reg.flatIndex()] = renamed_reg;
+        map[arch_reg.index()] = renamed_reg;
         renamed_reg->setNumPinnedWrites(arch_reg.getNumPinnedWrites());
         renamed_reg->setNumPinnedWritesToComplete(
             arch_reg.getNumPinnedWrites() + 1);
@@ -114,12 +113,20 @@
 {
     regFile = _regFile;
 
-    intMap.init(regClasses.at(IntRegClass), &(freeList->intList));
-    floatMap.init(regClasses.at(FloatRegClass), &(freeList->floatList));
-    vecMap.init(regClasses.at(VecRegClass), &(freeList->vecList));
-    vecElemMap.init(regClasses.at(VecElemClass), &(freeList->vecElemList));
-    predMap.init(regClasses.at(VecPredRegClass), &(freeList->predList));
-    ccMap.init(regClasses.at(CCRegClass), &(freeList->ccList));
+    for (int i = 0; i < renameMaps.size(); i++)
+        renameMaps[i].init(regClasses.at(i), &(freeList->freeLists[i]));
+}
+
+bool
+UnifiedRenameMap::canRename(DynInstPtr inst) const
+{
+    for (int i = 0; i < renameMaps.size(); i++) {
+        if (inst->numDestRegs((RegClassType)i) >
+                renameMaps[i].numFreeEntries()) {
+            return false;
+        }
+    }
+    return true;
 }
 
 } // namespace o3
diff --git a/src/cpu/o3/rename_map.hh b/src/cpu/o3/rename_map.hh
index 3f8936b..640c2ac 100644
--- a/src/cpu/o3/rename_map.hh
+++ b/src/cpu/o3/rename_map.hh
@@ -42,11 +42,15 @@
 #ifndef __CPU_O3_RENAME_MAP_HH__
 #define __CPU_O3_RENAME_MAP_HH__
 
+#include <algorithm>
+#include <array>
 #include <iostream>
+#include <limits>
 #include <utility>
 #include <vector>
 
 #include "arch/generic/isa.hh"
+#include "cpu/o3/dyn_inst_ptr.hh"
 #include "cpu/o3/free_list.hh"
 #include "cpu/o3/regfile.hh"
 #include "cpu/reg_class.hh"
@@ -81,15 +85,6 @@
      */
     SimpleFreeList *freeList;
 
-    /**
-     * The architectural index of the zero register. This register is
-     * mapped but read-only, so we ignore attempts to rename it via
-     * the rename() method.  If there is no such register for this map
-     * table, it should be set to an invalid index so that it never
-     * matches.
-     */
-    RegId zeroReg;
-
   public:
 
     SimpleRenameMap();
@@ -126,8 +121,8 @@
     PhysRegIdPtr
     lookup(const RegId& arch_reg) const
     {
-        assert(arch_reg.flatIndex() <= map.size());
-        return map[arch_reg.flatIndex()];
+        assert(arch_reg.index() <= map.size());
+        return map[arch_reg.index()];
     }
 
     /**
@@ -139,8 +134,8 @@
     void
     setEntry(const RegId& arch_reg, PhysRegIdPtr phys_reg)
     {
-        assert(arch_reg.flatIndex() <= map.size());
-        map[arch_reg.flatIndex()] = phys_reg;
+        assert(arch_reg.index() <= map.size());
+        map[arch_reg.index()] = phys_reg;
     }
 
     /** Return the number of free entries on the associated free list. */
@@ -173,23 +168,9 @@
 class UnifiedRenameMap
 {
   private:
-    /** The integer register rename map */
-    SimpleRenameMap intMap;
+    std::array<SimpleRenameMap, CCRegClass + 1> renameMaps;
 
-    /** The floating-point register rename map */
-    SimpleRenameMap floatMap;
-
-    /** The condition-code register rename map */
-    SimpleRenameMap ccMap;
-
-    /** The vector register rename map */
-    SimpleRenameMap vecMap;
-
-    /** The vector element register rename map */
-    SimpleRenameMap vecElemMap;
-
-    /** The predicate register rename map */
-    SimpleRenameMap predMap;
+    static inline PhysRegId invalidPhysRegId{};
 
     /**
      * The register file object is used only to get PhysRegIdPtr
@@ -222,32 +203,15 @@
     RenameInfo
     rename(const RegId& arch_reg)
     {
-        switch (arch_reg.classValue()) {
-          case IntRegClass:
-            return intMap.rename(arch_reg);
-          case FloatRegClass:
-            return floatMap.rename(arch_reg);
-          case VecRegClass:
-            return vecMap.rename(arch_reg);
-          case VecElemClass:
-            return vecElemMap.rename(arch_reg);
-          case VecPredRegClass:
-            return predMap.rename(arch_reg);
-          case CCRegClass:
-            return ccMap.rename(arch_reg);
-          case MiscRegClass:
-            {
-                // misc regs aren't really renamed, just remapped
-                PhysRegIdPtr phys_reg = lookup(arch_reg);
-                // Set the new register to the previous one to keep the same
-                // mapping throughout the execution.
-                return RenameInfo(phys_reg, phys_reg);
-            }
-
-          default:
-            panic("rename rename(): unknown reg class %s\n",
-                  arch_reg.className());
+        if (!arch_reg.isRenameable()) {
+            // misc regs aren't really renamed, just remapped
+            PhysRegIdPtr phys_reg = lookup(arch_reg);
+            // Set the new register to the previous one to keep the same
+            // mapping throughout the execution.
+            return RenameInfo(phys_reg, phys_reg);
         }
+
+        return renameMaps[arch_reg.classValue()].rename(arch_reg);
     }
 
     /**
@@ -260,34 +224,15 @@
     PhysRegIdPtr
     lookup(const RegId& arch_reg) const
     {
-        switch (arch_reg.classValue()) {
-          case IntRegClass:
-            return intMap.lookup(arch_reg);
-
-          case FloatRegClass:
-            return  floatMap.lookup(arch_reg);
-
-          case VecRegClass:
-            return  vecMap.lookup(arch_reg);
-
-          case VecElemClass:
-            return  vecElemMap.lookup(arch_reg);
-
-          case VecPredRegClass:
-            return predMap.lookup(arch_reg);
-
-          case CCRegClass:
-            return ccMap.lookup(arch_reg);
-
-          case MiscRegClass:
+        auto reg_class = arch_reg.classValue();
+        if (reg_class == InvalidRegClass) {
+            return &invalidPhysRegId;
+        } else if (reg_class == MiscRegClass) {
             // misc regs aren't really renamed, they keep the same
             // mapping throughout the execution.
-            return regFile->getMiscRegId(arch_reg.flatIndex());
-
-          default:
-            panic("rename lookup(): unknown reg class %s\n",
-                  arch_reg.className());
+            return regFile->getMiscRegId(arch_reg.index());
         }
+        return renameMaps[reg_class].lookup(arch_reg);
     }
 
     /**
@@ -302,37 +247,16 @@
     setEntry(const RegId& arch_reg, PhysRegIdPtr phys_reg)
     {
         assert(phys_reg->is(arch_reg.classValue()));
-        switch (arch_reg.classValue()) {
-          case IntRegClass:
-            return intMap.setEntry(arch_reg, phys_reg);
-
-          case FloatRegClass:
-            return floatMap.setEntry(arch_reg, phys_reg);
-
-          case VecRegClass:
-            return vecMap.setEntry(arch_reg, phys_reg);
-
-          case VecElemClass:
-            return vecElemMap.setEntry(arch_reg, phys_reg);
-
-          case VecPredRegClass:
-            return predMap.setEntry(arch_reg, phys_reg);
-
-          case CCRegClass:
-            return ccMap.setEntry(arch_reg, phys_reg);
-
-          case MiscRegClass:
+        if (!arch_reg.isRenameable()) {
             // Misc registers do not actually rename, so don't change
             // their mappings.  We end up here when a commit or squash
             // tries to update or undo a hardwired misc reg nmapping,
             // which should always be setting it to what it already is.
             assert(phys_reg == lookup(arch_reg));
             return;
-
-          default:
-            panic("rename setEntry(): unknown reg class %s\n",
-                  arch_reg.className());
         }
+
+        return renameMaps[arch_reg.classValue()].setEntry(arch_reg, phys_reg);
     }
 
     /**
@@ -344,39 +268,25 @@
     unsigned
     numFreeEntries() const
     {
-        return std::min({intMap.numFreeEntries(),
-                         floatMap.numFreeEntries(),
-                         vecMap.numFreeEntries(),
-                         vecElemMap.numFreeEntries(),
-                         predMap.numFreeEntries()});
+        auto min_free = std::numeric_limits<unsigned>::max();
+        for (auto &map: renameMaps) {
+            // If this map isn't empty (not used)...
+            if (map.numArchRegs())
+                min_free = std::min(min_free, map.numFreeEntries());
+        }
+        return min_free;
     }
 
-    unsigned numFreeIntEntries() const { return intMap.numFreeEntries(); }
-    unsigned numFreeFloatEntries() const { return floatMap.numFreeEntries(); }
-    unsigned numFreeVecEntries() const { return vecMap.numFreeEntries(); }
     unsigned
-    numFreeVecElemEntries() const
+    numFreeEntries(RegClassType type) const
     {
-        return vecElemMap.numFreeEntries();
+        return renameMaps[type].numFreeEntries();
     }
-    unsigned numFreePredEntries() const { return predMap.numFreeEntries(); }
-    unsigned numFreeCCEntries() const { return ccMap.numFreeEntries(); }
 
     /**
      * Return whether there are enough registers to serve the request.
      */
-    bool
-    canRename(uint32_t intRegs, uint32_t floatRegs, uint32_t vectorRegs,
-              uint32_t vecElemRegs, uint32_t vecPredRegs,
-              uint32_t ccRegs) const
-    {
-        return intRegs <= intMap.numFreeEntries() &&
-            floatRegs <= floatMap.numFreeEntries() &&
-            vectorRegs <= vecMap.numFreeEntries() &&
-            vecElemRegs <= vecElemMap.numFreeEntries() &&
-            vecPredRegs <= predMap.numFreeEntries() &&
-            ccRegs <= ccMap.numFreeEntries();
-    }
+    bool canRename(DynInstPtr inst) const;
 };
 
 } // namespace o3
diff --git a/src/cpu/o3/rob.cc b/src/cpu/o3/rob.cc
index 9e42651..5d0fac9 100644
--- a/src/cpu/o3/rob.cc
+++ b/src/cpu/o3/rob.cc
@@ -47,7 +47,7 @@
 #include "cpu/o3/limits.hh"
 #include "debug/Fetch.hh"
 #include "debug/ROB.hh"
-#include "params/O3CPU.hh"
+#include "params/BaseO3CPU.hh"
 
 namespace gem5
 {
@@ -55,7 +55,7 @@
 namespace o3
 {
 
-ROB::ROB(CPU *_cpu, const O3CPUParams &params)
+ROB::ROB(CPU *_cpu, const BaseO3CPUParams &params)
     : robPolicy(params.smtROBPolicy),
       cpu(_cpu),
       numEntries(params.numROBEntries),
diff --git a/src/cpu/o3/rob.hh b/src/cpu/o3/rob.hh
index 3889ef5..d36db73 100644
--- a/src/cpu/o3/rob.hh
+++ b/src/cpu/o3/rob.hh
@@ -57,7 +57,7 @@
 namespace gem5
 {
 
-struct O3CPUParams;
+struct BaseO3CPUParams;
 
 namespace o3
 {
@@ -95,7 +95,7 @@
      *  @param _cpu   The cpu object pointer.
      *  @param params The cpu params including several ROB-specific parameters.
      */
-    ROB(CPU *_cpu, const O3CPUParams &params);
+    ROB(CPU *_cpu, const BaseO3CPUParams &params);
 
     std::string name() const;
 
diff --git a/src/cpu/o3/scoreboard.cc b/src/cpu/o3/scoreboard.cc
index 5e9fc8b..c4c8bb2 100644
--- a/src/cpu/o3/scoreboard.cc
+++ b/src/cpu/o3/scoreboard.cc
@@ -35,9 +35,9 @@
 namespace o3
 {
 
-Scoreboard::Scoreboard(const std::string &_my_name, unsigned _numPhysicalRegs,
-        RegIndex zero_reg) :
-    _name(_my_name), zeroReg(zero_reg), regScoreBoard(_numPhysicalRegs, true),
+Scoreboard::Scoreboard(const std::string &_my_name,
+        unsigned _numPhysicalRegs) :
+    _name(_my_name), regScoreBoard(_numPhysicalRegs, true),
     numPhysRegs(_numPhysicalRegs)
 {}
 
diff --git a/src/cpu/o3/scoreboard.hh b/src/cpu/o3/scoreboard.hh
index 7a6b656..9cb719b 100644
--- a/src/cpu/o3/scoreboard.hh
+++ b/src/cpu/o3/scoreboard.hh
@@ -34,6 +34,7 @@
 #include <vector>
 
 #include "base/compiler.hh"
+#include "base/logging.hh"
 #include "base/trace.hh"
 #include "cpu/reg_class.hh"
 #include "debug/Scoreboard.hh"
@@ -57,9 +58,6 @@
      *  explicitly because Scoreboard is not a SimObject. */
     const std::string _name;
 
-    /** Index of the zero integer register. */
-    const RegIndex zeroReg;
-
     /** Scoreboard of physical integer registers, saying whether or not they
      *  are ready. */
     std::vector<bool> regScoreBoard;
@@ -72,8 +70,7 @@
      *  @param _numPhysicalRegs Number of physical registers.
      *  @param _numMiscRegs Number of miscellaneous registers.
      */
-    Scoreboard(const std::string &_my_name, unsigned _numPhysicalRegs,
-               RegIndex _zero_reg);
+    Scoreboard(const std::string &_my_name, unsigned _numPhysicalRegs);
 
     /** Destructor. */
     ~Scoreboard() {}
@@ -85,33 +82,28 @@
     bool
     getReg(PhysRegIdPtr phys_reg) const
     {
-        assert(phys_reg->flatIndex() < numPhysRegs);
-
         if (phys_reg->isFixedMapping()) {
             // Fixed mapping regs are always ready
             return true;
         }
 
-        bool ready = regScoreBoard[phys_reg->flatIndex()];
+        assert(phys_reg->flatIndex() < numPhysRegs);
 
-        if (phys_reg->is(IntRegClass) && phys_reg->index() == zeroReg)
-            assert(ready);
-
-        return ready;
+        return regScoreBoard[phys_reg->flatIndex()];
     }
 
     /** Sets the register as ready. */
     void
     setReg(PhysRegIdPtr phys_reg)
     {
-        assert(phys_reg->flatIndex() < numPhysRegs);
-
         if (phys_reg->isFixedMapping()) {
             // Fixed mapping regs are always ready, ignore attempts to change
             // that
             return;
         }
 
+        assert(phys_reg->flatIndex() < numPhysRegs);
+
         DPRINTF(Scoreboard, "Setting reg %i (%s) as ready\n",
                 phys_reg->index(), phys_reg->className());
 
@@ -122,17 +114,13 @@
     void
     unsetReg(PhysRegIdPtr phys_reg)
     {
-        assert(phys_reg->flatIndex() < numPhysRegs);
-
         if (phys_reg->isFixedMapping()) {
             // Fixed mapping regs are always ready, ignore attempts to
             // change that
             return;
         }
 
-        // zero reg should never be marked unready
-        if (phys_reg->is(IntRegClass) && phys_reg->index() == zeroReg)
-            return;
+        assert(phys_reg->flatIndex() < numPhysRegs);
 
         regScoreBoard[phys_reg->flatIndex()] = false;
     }
diff --git a/src/cpu/o3/thread_context.cc b/src/cpu/o3/thread_context.cc
index 01b92b7..e539521 100644
--- a/src/cpu/o3/thread_context.cc
+++ b/src/cpu/o3/thread_context.cc
@@ -150,100 +150,34 @@
 }
 
 RegVal
-ThreadContext::readIntRegFlat(RegIndex reg_idx) const
+ThreadContext::getRegFlat(const RegId &reg) const
 {
-    return cpu->readArchIntReg(reg_idx, thread->threadId());
+    return cpu->getArchReg(reg, thread->threadId());
 }
 
-RegVal
-ThreadContext::readFloatRegFlat(RegIndex reg_idx) const
+void *
+ThreadContext::getWritableRegFlat(const RegId &reg)
 {
-    return cpu->readArchFloatReg(reg_idx, thread->threadId());
-}
-
-const TheISA::VecRegContainer&
-ThreadContext::readVecRegFlat(RegIndex reg_id) const
-{
-    return cpu->readArchVecReg(reg_id, thread->threadId());
-}
-
-TheISA::VecRegContainer&
-ThreadContext::getWritableVecRegFlat(RegIndex reg_id)
-{
-    return cpu->getWritableArchVecReg(reg_id, thread->threadId());
-}
-
-RegVal
-ThreadContext::readVecElemFlat(RegIndex idx, const ElemIndex& elemIndex) const
-{
-    return cpu->readArchVecElem(idx, elemIndex, thread->threadId());
-}
-
-const TheISA::VecPredRegContainer&
-ThreadContext::readVecPredRegFlat(RegIndex reg_id) const
-{
-    return cpu->readArchVecPredReg(reg_id, thread->threadId());
-}
-
-TheISA::VecPredRegContainer&
-ThreadContext::getWritableVecPredRegFlat(RegIndex reg_id)
-{
-    return cpu->getWritableArchVecPredReg(reg_id, thread->threadId());
-}
-
-RegVal
-ThreadContext::readCCRegFlat(RegIndex reg_idx) const
-{
-    return cpu->readArchCCReg(reg_idx, thread->threadId());
+    return cpu->getWritableArchReg(reg, thread->threadId());
 }
 
 void
-ThreadContext::setIntRegFlat(RegIndex reg_idx, RegVal val)
+ThreadContext::getRegFlat(const RegId &reg, void *val) const
 {
-    cpu->setArchIntReg(reg_idx, val, thread->threadId());
+    cpu->getArchReg(reg, val, thread->threadId());
+}
 
+void
+ThreadContext::setRegFlat(const RegId &reg, RegVal val)
+{
+    cpu->setArchReg(reg, val, thread->threadId());
     conditionalSquash();
 }
 
 void
-ThreadContext::setFloatRegFlat(RegIndex reg_idx, RegVal val)
+ThreadContext::setRegFlat(const RegId &reg, const void *val)
 {
-    cpu->setArchFloatReg(reg_idx, val, thread->threadId());
-
-    conditionalSquash();
-}
-
-void
-ThreadContext::setVecRegFlat(
-        RegIndex reg_idx, const TheISA::VecRegContainer& val)
-{
-    cpu->setArchVecReg(reg_idx, val, thread->threadId());
-
-    conditionalSquash();
-}
-
-void
-ThreadContext::setVecElemFlat(RegIndex idx,
-        const ElemIndex& elemIndex, RegVal val)
-{
-    cpu->setArchVecElem(idx, elemIndex, val, thread->threadId());
-    conditionalSquash();
-}
-
-void
-ThreadContext::setVecPredRegFlat(RegIndex reg_idx,
-        const TheISA::VecPredRegContainer& val)
-{
-    cpu->setArchVecPredReg(reg_idx, val, thread->threadId());
-
-    conditionalSquash();
-}
-
-void
-ThreadContext::setCCRegFlat(RegIndex reg_idx, RegVal val)
-{
-    cpu->setArchCCReg(reg_idx, val, thread->threadId());
-
+    cpu->setArchReg(reg, val, thread->threadId());
     conditionalSquash();
 }
 
diff --git a/src/cpu/o3/thread_context.hh b/src/cpu/o3/thread_context.hh
index 14bc7f5..ae37dc9 100644
--- a/src/cpu/o3/thread_context.hh
+++ b/src/cpu/o3/thread_context.hh
@@ -107,7 +107,7 @@
     CheckerCPU *getCheckerCpuPtr() override { return NULL; }
 
     BaseISA *
-    getIsaPtr() override
+    getIsaPtr() const override
     {
         return cpu->isa[thread->threadId()];
     }
@@ -176,106 +176,6 @@
     /** Resets all architectural registers to 0. */
     void clearArchRegs() override;
 
-    /** Reads an integer register. */
-    RegVal
-    readReg(RegIndex reg_idx)
-    {
-        return readIntRegFlat(flattenRegId(RegId(IntRegClass,
-                                                 reg_idx)).index());
-    }
-    RegVal
-    readIntReg(RegIndex reg_idx) const override
-    {
-        return readIntRegFlat(flattenRegId(RegId(IntRegClass,
-                                                 reg_idx)).index());
-    }
-
-    RegVal
-    readFloatReg(RegIndex reg_idx) const override
-    {
-        return readFloatRegFlat(flattenRegId(RegId(FloatRegClass,
-                                             reg_idx)).index());
-    }
-
-    const TheISA::VecRegContainer &
-    readVecReg(const RegId& id) const override
-    {
-        return readVecRegFlat(flattenRegId(id).index());
-    }
-
-    /**
-     * Read vector register operand for modification, hierarchical indexing.
-     */
-    TheISA::VecRegContainer &
-    getWritableVecReg(const RegId& id) override
-    {
-        return getWritableVecRegFlat(flattenRegId(id).index());
-    }
-
-    RegVal
-    readVecElem(const RegId& reg) const override
-    {
-        return readVecElemFlat(flattenRegId(reg).index(), reg.elemIndex());
-    }
-
-    const TheISA::VecPredRegContainer &
-    readVecPredReg(const RegId& id) const override
-    {
-        return readVecPredRegFlat(flattenRegId(id).index());
-    }
-
-    TheISA::VecPredRegContainer&
-    getWritableVecPredReg(const RegId& id) override
-    {
-        return getWritableVecPredRegFlat(flattenRegId(id).index());
-    }
-
-    RegVal
-    readCCReg(RegIndex reg_idx) const override
-    {
-        return readCCRegFlat(flattenRegId(RegId(CCRegClass,
-                                                 reg_idx)).index());
-    }
-
-    /** Sets an integer register to a value. */
-    void
-    setIntReg(RegIndex reg_idx, RegVal val) override
-    {
-        setIntRegFlat(flattenRegId(RegId(IntRegClass, reg_idx)).index(), val);
-    }
-
-    void
-    setFloatReg(RegIndex reg_idx, RegVal val) override
-    {
-        setFloatRegFlat(flattenRegId(RegId(FloatRegClass,
-                                           reg_idx)).index(), val);
-    }
-
-    void
-    setVecReg(const RegId& reg, const TheISA::VecRegContainer& val) override
-    {
-        setVecRegFlat(flattenRegId(reg).index(), val);
-    }
-
-    void
-    setVecElem(const RegId& reg, RegVal val) override
-    {
-        setVecElemFlat(flattenRegId(reg).index(), reg.elemIndex(), val);
-    }
-
-    void
-    setVecPredReg(const RegId& reg,
-                  const TheISA::VecPredRegContainer& val) override
-    {
-        setVecPredRegFlat(flattenRegId(reg).index(), val);
-    }
-
-    void
-    setCCReg(RegIndex reg_idx, RegVal val) override
-    {
-        setCCRegFlat(flattenRegId(RegId(CCRegClass, reg_idx)).index(), val);
-    }
-
     /** Reads this thread's PC state. */
     const PCStateBase &
     pcState() const override
@@ -339,32 +239,12 @@
             cpu->squashFromTC(thread->threadId());
     }
 
-    RegVal readIntRegFlat(RegIndex idx) const override;
-    void setIntRegFlat(RegIndex idx, RegVal val) override;
+    RegVal getRegFlat(const RegId &reg) const override;
+    void getRegFlat(const RegId &reg, void *val) const override;
+    void *getWritableRegFlat(const RegId &reg) override;
 
-    RegVal readFloatRegFlat(RegIndex idx) const override;
-    void setFloatRegFlat(RegIndex idx, RegVal val) override;
-
-    const TheISA::VecRegContainer& readVecRegFlat(RegIndex idx) const override;
-    /** Read vector register operand for modification, flat indexing. */
-    TheISA::VecRegContainer& getWritableVecRegFlat(RegIndex idx) override;
-    void setVecRegFlat(RegIndex idx,
-            const TheISA::VecRegContainer& val) override;
-
-    RegVal readVecElemFlat(RegIndex idx,
-            const ElemIndex& elemIndex) const override;
-    void setVecElemFlat(RegIndex idx, const ElemIndex& elemIdx,
-                        RegVal val) override;
-
-    const TheISA::VecPredRegContainer&
-        readVecPredRegFlat(RegIndex idx) const override;
-    TheISA::VecPredRegContainer&
-        getWritableVecPredRegFlat(RegIndex idx) override;
-    void setVecPredRegFlat(RegIndex idx,
-                           const TheISA::VecPredRegContainer& val) override;
-
-    RegVal readCCRegFlat(RegIndex idx) const override;
-    void setCCRegFlat(RegIndex idx, RegVal val) override;
+    void setRegFlat(const RegId &reg, RegVal val) override;
+    void setRegFlat(const RegId &reg, const void *val) override;
 
     // hardware transactional memory
     void htmAbortTransaction(uint64_t htm_uid,
diff --git a/src/cpu/pred/SConscript b/src/cpu/pred/SConscript
index ee25b0c..0437bda 100644
--- a/src/cpu/pred/SConscript
+++ b/src/cpu/pred/SConscript
@@ -28,7 +28,7 @@
 
 Import('*')
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 SimObject('BranchPredictor.py', sim_objects=[
diff --git a/src/cpu/pred/bpred_unit.cc b/src/cpu/pred/bpred_unit.cc
index 0940ae1..ac51567 100644
--- a/src/cpu/pred/bpred_unit.cc
+++ b/src/cpu/pred/bpred_unit.cc
@@ -335,10 +335,19 @@
     while (!pred_hist.empty() &&
            pred_hist.front().seqNum > squashed_sn) {
         if (pred_hist.front().usedRAS) {
-            DPRINTF(Branch, "[tid:%i] [squash sn:%llu]"
-                    " Restoring top of RAS to: %i,"
-                    " target: %s\n", tid, squashed_sn,
-                    pred_hist.front().RASIndex, *pred_hist.front().RASTarget);
+            if (pred_hist.front().RASTarget != nullptr) {
+                DPRINTF(Branch, "[tid:%i] [squash sn:%llu]"
+                        " Restoring top of RAS to: %i,"
+                        " target: %s\n", tid, squashed_sn,
+                        pred_hist.front().RASIndex,
+                        *pred_hist.front().RASTarget);
+            }
+            else {
+                DPRINTF(Branch, "[tid:%i] [squash sn:%llu]"
+                        " Restoring top of RAS to: %i,"
+                        " target: INVALID_TARGET\n", tid, squashed_sn,
+                        pred_hist.front().RASIndex);
+            }
 
             RAS[tid].restore(pred_hist.front().RASIndex,
                              pred_hist.front().RASTarget.get());
diff --git a/src/cpu/pred/multiperspective_perceptron_tage.cc b/src/cpu/pred/multiperspective_perceptron_tage.cc
index 67c470f..6176d9c 100644
--- a/src/cpu/pred/multiperspective_perceptron_tage.cc
+++ b/src/cpu/pred/multiperspective_perceptron_tage.cc
@@ -328,11 +328,9 @@
                    int nbr, int logs, std::vector<int8_t> & w,
                    StatisticalCorrector::BranchInfo* bi)
 {
-    int percsum = 0;
     for (int i = 0; i < nbr; i++) {
         int64_t bhist = hist & ((int64_t) ((1 << length[i]) - 1));
         int64_t index = gIndex(branch_pc, bhist, logs, nbr, i);
-        percsum += (2 * tab[i][index] + 1);
         ctrUpdate(tab[i][index], taken, scCountersWidth - (i < (nbr - 1)));
     }
 }
diff --git a/src/cpu/reg_class.cc b/src/cpu/reg_class.cc
index fc39e42..444d1ff 100644
--- a/src/cpu/reg_class.cc
+++ b/src/cpu/reg_class.cc
@@ -39,17 +39,38 @@
  */
 
 #include "cpu/reg_class.hh"
+
+#include <sstream>
+
 #include "base/cprintf.hh"
+#include "sim/bufval.hh"
+#include "sim/byteswap.hh"
 
 namespace gem5
 {
 
 std::string
-DefaultRegClassOps::regName(const RegId &id) const
+RegClassOps::regName(const RegId &id) const
 {
     return csprintf("r%d", id.index());
 }
 
+std::string
+RegClassOps::valString(const void *val, size_t size) const
+{
+    // If this is just a RegVal, or could be interpreted as one, print it
+    // that way.
+    auto [reg_val_str, reg_val_success] = printUintX(val, size, HostByteOrder);
+    if (reg_val_success)
+        return reg_val_str;
+
+    // Otherwise, print it as a sequence of bytes. Use big endian order so
+    // that the most significant bytes are printed first, like digits in a
+    // regular number.
+
+    return printByteBuf(val, size, ByteOrder::big);
+}
+
 const char *RegId::regClassStrings[] = {
     "IntRegClass",
     "FloatRegClass",
diff --git a/src/cpu/reg_class.hh b/src/cpu/reg_class.hh
index febaa55..3372fce 100644
--- a/src/cpu/reg_class.hh
+++ b/src/cpu/reg_class.hh
@@ -41,13 +41,13 @@
 #ifndef __CPU__REG_CLASS_HH__
 #define __CPU__REG_CLASS_HH__
 
-#include <cassert>
 #include <cstddef>
 #include <string>
 
-#include "arch/vecregs.hh"
+#include "base/cprintf.hh"
+#include "base/debug.hh"
+#include "base/intmath.hh"
 #include "base/types.hh"
-#include "config/the_isa.hh"
 
 namespace gem5
 {
@@ -63,7 +63,8 @@
     VecElemClass,
     VecPredRegClass,
     CCRegClass,         ///< Condition-code register
-    MiscRegClass        ///< Control (misc) register
+    MiscRegClass,       ///< Control (misc) register
+    InvalidRegClass = -1
 };
 
 class RegId;
@@ -71,38 +72,50 @@
 class RegClassOps
 {
   public:
-    virtual std::string regName(const RegId &id) const = 0;
-};
-
-class DefaultRegClassOps : public RegClassOps
-{
-  public:
-    std::string regName(const RegId &id) const override;
+    /** Print the name of the register specified in id. */
+    virtual std::string regName(const RegId &id) const;
+    /** Print the value of a register pointed to by val of size size. */
+    virtual std::string valString(const void *val, size_t size) const;
 };
 
 class RegClass
 {
   private:
-    size_t _size;
-    const RegIndex _zeroReg;
+    size_t _numRegs;
+    size_t _regBytes;
+    // This is how much to shift an index by to get an offset of a register in
+    // a register file from the register index, which would otherwise need to
+    // be calculated with a multiply.
+    size_t _regShift;
 
-    static inline DefaultRegClassOps defaultOps;
+    static inline RegClassOps defaultOps;
     RegClassOps *_ops = &defaultOps;
+    const debug::Flag &debugFlag;
 
   public:
-    RegClass(size_t new_size, RegIndex new_zero=-1) :
-        _size(new_size), _zeroReg(new_zero)
+    constexpr RegClass(size_t num_regs, const debug::Flag &debug_flag,
+            size_t reg_bytes=sizeof(RegVal)) :
+        _numRegs(num_regs), _regBytes(reg_bytes),
+        _regShift(ceilLog2(reg_bytes)), debugFlag(debug_flag)
     {}
-    RegClass(size_t new_size, RegClassOps &new_ops, RegIndex new_zero=-1) :
-        RegClass(new_size, new_zero)
+    constexpr RegClass(size_t num_regs, RegClassOps &new_ops,
+            const debug::Flag &debug_flag, size_t reg_bytes=sizeof(RegVal)) :
+        RegClass(num_regs, debug_flag, reg_bytes)
     {
         _ops = &new_ops;
     }
 
-    size_t size() const { return _size; }
-    RegIndex zeroReg() const { return _zeroReg; }
+    constexpr size_t numRegs() const { return _numRegs; }
+    constexpr size_t regBytes() const { return _regBytes; }
+    constexpr size_t regShift() const { return _regShift; }
+    constexpr const debug::Flag &debug() const { return debugFlag; }
 
     std::string regName(const RegId &id) const { return _ops->regName(id); }
+    std::string
+    valString(const void *val) const
+    {
+        return _ops->valString(val, regBytes());
+    }
 };
 
 /** Register ID: describe an architectural register with its class and index.
@@ -116,96 +129,72 @@
     static const char* regClassStrings[];
     RegClassType regClass;
     RegIndex regIdx;
-    ElemIndex elemIdx;
-    static constexpr size_t Scale = TheISA::NumVecElemPerVecReg;
     int numPinnedWrites;
 
     friend struct std::hash<RegId>;
 
   public:
-    RegId() : RegId(IntRegClass, 0) {}
+    constexpr RegId() : RegId(InvalidRegClass, 0) {}
 
-    RegId(RegClassType reg_class, RegIndex reg_idx)
-        : RegId(reg_class, reg_idx, IllegalElemIndex) {}
+    constexpr RegId(RegClassType reg_class, RegIndex reg_idx)
+        : regClass(reg_class), regIdx(reg_idx), numPinnedWrites(0)
+    {}
 
-    explicit RegId(RegClassType reg_class, RegIndex reg_idx,
-            ElemIndex elem_idx)
-        : regClass(reg_class), regIdx(reg_idx), elemIdx(elem_idx),
-          numPinnedWrites(0)
+    constexpr operator RegIndex() const
     {
-        if (elemIdx == IllegalElemIndex) {
-            panic_if(regClass == VecElemClass,
-                    "Creating vector physical index w/o element index");
-        } else {
-            panic_if(regClass != VecElemClass,
-                    "Creating non-vector physical index w/ element index");
-        }
+        return index();
     }
 
-    bool
+    constexpr bool
     operator==(const RegId& that) const
     {
-        return regClass == that.classValue() && regIdx == that.index() &&
-            elemIdx == that.elemIndex();
+        return regClass == that.classValue() && regIdx == that.index();
     }
 
-    bool operator!=(const RegId& that) const { return !(*this==that); }
+    constexpr bool
+    operator!=(const RegId& that) const
+    {
+        return !(*this==that);
+    }
 
     /** Order operator.
      * The order is required to implement maps with key type RegId
      */
-    bool
+    constexpr bool
     operator<(const RegId& that) const
     {
         return regClass < that.classValue() ||
-            (regClass == that.classValue() && (
-                   regIdx < that.index() ||
-                   (regIdx == that.index() && elemIdx < that.elemIndex())));
+            (regClass == that.classValue() && (regIdx < that.index()));
     }
 
     /**
      * Return true if this register can be renamed
      */
-    bool
+    constexpr bool
     isRenameable() const
     {
-        return regClass != MiscRegClass;
+        return regClass != MiscRegClass && regClass != InvalidRegClass;
     }
 
     /** @return true if it is of the specified class. */
-    bool is(RegClassType reg_class) const { return regClass == reg_class; }
+    constexpr bool
+    is(RegClassType reg_class) const
+    {
+        return regClass == reg_class;
+    }
 
     /** Index accessors */
     /** @{ */
-    RegIndex index() const { return regIdx; }
+    constexpr RegIndex index() const { return regIdx; }
 
-    /** Index flattening.
-     * Required to be able to use a vector for the register mapping.
-     */
-    RegIndex
-    flatIndex() const
-    {
-        switch (regClass) {
-          case IntRegClass:
-          case FloatRegClass:
-          case VecRegClass:
-          case VecPredRegClass:
-          case CCRegClass:
-          case MiscRegClass:
-            return regIdx;
-          case VecElemClass:
-            return Scale * regIdx + elemIdx;
-        }
-        panic("Trying to flatten a register without class!");
-    }
-    /** @} */
-
-    /** Elem accessor */
-    RegIndex elemIndex() const { return elemIdx; }
     /** Class accessor */
-    RegClassType classValue() const { return regClass; }
+    constexpr RegClassType classValue() const { return regClass; }
     /** Return a const char* with the register class name. */
-    const char* className() const { return regClassStrings[regClass]; }
+    constexpr const char*
+    className() const
+    {
+        return regClassStrings[regClass];
+    }
 
     int getNumPinnedWrites() const { return numPinnedWrites; }
     void setNumPinnedWrites(int num_writes) { numPinnedWrites = num_writes; }
@@ -217,6 +206,38 @@
     }
 };
 
+template <typename ValueType>
+class TypedRegClassOps : public RegClassOps
+{
+  public:
+    std::string
+    valString(const void *val, size_t size) const override
+    {
+        assert(size == sizeof(ValueType));
+        return csprintf("%s", *(const ValueType *)val);
+    }
+};
+
+template <typename ValueType>
+class VecElemRegClassOps : public TypedRegClassOps<ValueType>
+{
+  protected:
+    size_t elemsPerVec;
+
+  public:
+    explicit VecElemRegClassOps(size_t elems_per_vec) :
+        elemsPerVec(elems_per_vec)
+    {}
+
+    std::string
+    regName(const RegId &id) const override
+    {
+        RegIndex reg_idx = id.index() / elemsPerVec;
+        RegIndex elem_idx = id.index() % elemsPerVec;
+        return csprintf("v%d[%d]", reg_idx, elem_idx);
+    }
+};
+
 /** Physical register ID.
  * Like a register ID but physical. The inheritance is private because the
  * only relationship between this types is functional, and it is done to
@@ -229,7 +250,7 @@
     bool pinned;
 
   public:
-    explicit PhysRegId() : RegId(IntRegClass, -1), flatIdx(-1),
+    explicit PhysRegId() : RegId(InvalidRegClass, -1), flatIdx(-1),
                            numPinnedWritesToComplete(0)
     {}
 
@@ -240,19 +261,11 @@
           numPinnedWritesToComplete(0), pinned(false)
     {}
 
-    /** Vector PhysRegId constructor (w/ elemIndex). */
-    explicit PhysRegId(RegClassType _regClass, RegIndex _regIdx,
-              ElemIndex elem_idx, RegIndex flat_idx)
-        : RegId(_regClass, _regIdx, elem_idx), flatIdx(flat_idx),
-          numPinnedWritesToComplete(0), pinned(false)
-    {}
-
     /** Visible RegId methods */
     /** @{ */
     using RegId::index;
     using RegId::classValue;
     using RegId::className;
-    using RegId::elemIndex;
     using RegId::is;
      /** @} */
     /**
@@ -288,13 +301,6 @@
     /** Flat index accessor */
     const RegIndex& flatIndex() const { return flatIdx; }
 
-    static PhysRegId
-    elemId(PhysRegId* vid, ElemIndex elem)
-    {
-        assert(vid->is(VecRegClass));
-        return PhysRegId(VecElemClass, vid->index(), elem);
-    }
-
     int getNumPinnedWrites() const { return numPinnedWrites; }
 
     void
@@ -344,7 +350,7 @@
     operator()(const gem5::RegId& reg_id) const
     {
         // Extract unique integral values for the effective fields of a RegId.
-        const size_t flat_index = static_cast<size_t>(reg_id.flatIndex());
+        const size_t index = static_cast<size_t>(reg_id.index());
         const size_t class_num = static_cast<size_t>(reg_id.regClass);
 
         const size_t shifted_class_num =
@@ -352,7 +358,7 @@
 
         // Concatenate the class_num to the end of the flat_index, in order to
         // maximize information retained.
-        const size_t concatenated_hash = flat_index | shifted_class_num;
+        const size_t concatenated_hash = index | shifted_class_num;
 
         // If RegIndex is larger than size_t, then class_num will not be
         // considered by this hash function, so we may wish to perform a
diff --git a/src/cpu/regfile.hh b/src/cpu/regfile.hh
new file mode 100644
index 0000000..a223c01
--- /dev/null
+++ b/src/cpu/regfile.hh
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __CPU_REGFILE_HH__
+#define __CPU_REGFILE_HH__
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <vector>
+
+#include "cpu/reg_class.hh"
+
+namespace gem5
+{
+
+class RegFile
+{
+  private:
+    std::vector<uint8_t> data;
+    const size_t _size;
+    const size_t _regShift;
+    const size_t _regBytes;
+
+  public:
+    const RegClass &regClass;
+
+    RegFile(const RegClass &info, const size_t new_size) :
+        data(new_size << info.regShift()), _size(new_size),
+        _regShift(info.regShift()), _regBytes(info.regBytes()),
+        regClass(info)
+    {}
+
+    RegFile(const RegClass &info) : RegFile(info, info.numRegs()) {}
+
+    size_t size() const { return _size; }
+    size_t regShift() const { return _regShift; }
+    size_t regBytes() const { return _regBytes; }
+
+    template <typename Reg=RegVal>
+    Reg &
+    reg(size_t idx)
+    {
+        assert(sizeof(Reg) == _regBytes && idx < _size);
+        return *reinterpret_cast<Reg *>(data.data() + (idx << _regShift));
+    }
+    template <typename Reg=RegVal>
+    const Reg &
+    reg(size_t idx) const
+    {
+        assert(sizeof(Reg) == _regBytes && idx < _size);
+        return *reinterpret_cast<const Reg *>(
+                data.data() + (idx << _regShift));
+    }
+
+    void *
+    ptr(size_t idx)
+    {
+        return data.data() + (idx << _regShift);
+    }
+
+    const void *
+    ptr(size_t idx) const
+    {
+        return data.data() + (idx << _regShift);
+    }
+
+    void
+    get(size_t idx, void *val) const
+    {
+        std::memcpy(val, ptr(idx), _regBytes);
+    }
+
+    void
+    set(size_t idx, const void *val)
+    {
+        std::memcpy(ptr(idx), val, _regBytes);
+    }
+
+    void clear() { std::fill(data.begin(), data.end(), 0); }
+};
+
+} // namespace gem5
+
+#endif // __CPU_REGFILE_HH__
diff --git a/src/cpu/simple/AtomicSimpleCPU.py b/src/cpu/simple/BaseAtomicSimpleCPU.py
similarity index 97%
rename from src/cpu/simple/AtomicSimpleCPU.py
rename to src/cpu/simple/BaseAtomicSimpleCPU.py
index 8dc0a5b..ba3b812 100644
--- a/src/cpu/simple/AtomicSimpleCPU.py
+++ b/src/cpu/simple/BaseAtomicSimpleCPU.py
@@ -40,12 +40,12 @@
 from m5.objects.BaseSimpleCPU import BaseSimpleCPU
 from m5.objects.SimPoint import SimPoint
 
-class AtomicSimpleCPU(BaseSimpleCPU):
+class BaseAtomicSimpleCPU(BaseSimpleCPU):
     """Simple CPU model executing a configurable number of
     instructions per cycle. This model uses the simplified 'atomic'
     memory mode."""
 
-    type = 'AtomicSimpleCPU'
+    type = 'BaseAtomicSimpleCPU'
     cxx_header = "cpu/simple/atomic.hh"
     cxx_class = 'gem5::AtomicSimpleCPU'
 
diff --git a/src/cpu/simple/NonCachingSimpleCPU.py b/src/cpu/simple/BaseNonCachingSimpleCPU.py
similarity index 94%
rename from src/cpu/simple/NonCachingSimpleCPU.py
rename to src/cpu/simple/BaseNonCachingSimpleCPU.py
index e01905a..f5cf1c7 100644
--- a/src/cpu/simple/NonCachingSimpleCPU.py
+++ b/src/cpu/simple/BaseNonCachingSimpleCPU.py
@@ -34,9 +34,9 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from m5.params import *
-from m5.objects.AtomicSimpleCPU import AtomicSimpleCPU
+from m5.objects.BaseAtomicSimpleCPU import BaseAtomicSimpleCPU
 
-class NonCachingSimpleCPU(AtomicSimpleCPU):
+class BaseNonCachingSimpleCPU(BaseAtomicSimpleCPU):
     """Simple CPU model based on the atomic CPU. Unlike the atomic CPU,
     this model causes the memory system to bypass caches and is
     therefore slightly faster in some cases. However, its main purpose
@@ -45,7 +45,7 @@
 
     """
 
-    type = 'NonCachingSimpleCPU'
+    type = 'BaseNonCachingSimpleCPU'
     cxx_header = "cpu/simple/noncaching.hh"
     cxx_class = 'gem5::NonCachingSimpleCPU'
 
diff --git a/src/cpu/simple/BaseSimpleCPU.py b/src/cpu/simple/BaseSimpleCPU.py
index 64444e4..67ba739 100644
--- a/src/cpu/simple/BaseSimpleCPU.py
+++ b/src/cpu/simple/BaseSimpleCPU.py
@@ -37,16 +37,4 @@
     cxx_header = "cpu/simple/base.hh"
     cxx_class = 'gem5::BaseSimpleCPU'
 
-    def addCheckerCpu(self):
-        if buildEnv['TARGET_ISA'] in ['arm']:
-            from m5.objects.ArmTLB import ArmMMU
-
-            self.checker = DummyChecker(workload = self.workload)
-            self.checker.mmu = ArmMMU()
-            self.checker.mmu.itb.size = self.mmu.itb.size
-            self.checker.mmu.dtb.size = self.mmu.dtb.size
-        else:
-            print("ERROR: Checker only supported under ARM ISA!")
-            exit(1)
-
     branchPred = Param.BranchPredictor(NULL, "Branch Predictor")
diff --git a/src/cpu/simple/TimingSimpleCPU.py b/src/cpu/simple/BaseTimingSimpleCPU.py
similarity index 96%
rename from src/cpu/simple/TimingSimpleCPU.py
rename to src/cpu/simple/BaseTimingSimpleCPU.py
index f670cc4..1f317a8 100644
--- a/src/cpu/simple/TimingSimpleCPU.py
+++ b/src/cpu/simple/BaseTimingSimpleCPU.py
@@ -28,8 +28,8 @@
 
 from m5.objects.BaseSimpleCPU import BaseSimpleCPU
 
-class TimingSimpleCPU(BaseSimpleCPU):
-    type = 'TimingSimpleCPU'
+class BaseTimingSimpleCPU(BaseSimpleCPU):
+    type = 'BaseTimingSimpleCPU'
     cxx_header = "cpu/simple/timing.hh"
     cxx_class = 'gem5::TimingSimpleCPU'
 
diff --git a/src/cpu/simple/SConscript b/src/cpu/simple/SConscript
index b953d1f..66e43d4 100644
--- a/src/cpu/simple/SConscript
+++ b/src/cpu/simple/SConscript
@@ -28,27 +28,21 @@
 
 Import('*')
 
-need_simple_base = False
-if 'AtomicSimpleCPU' in env['CPU_MODELS']:
-    need_simple_base = True
-    SimObject('AtomicSimpleCPU.py', sim_objects=['AtomicSimpleCPU'])
+if env['CONF']['TARGET_ISA'] != 'null':
+    SimObject('BaseAtomicSimpleCPU.py', sim_objects=['BaseAtomicSimpleCPU'])
     Source('atomic.cc')
 
     # The NonCachingSimpleCPU is really an atomic CPU in
     # disguise. It's therefore always enabled when the atomic CPU is
     # enabled.
-    SimObject('NonCachingSimpleCPU.py', sim_objects=['NonCachingSimpleCPU'])
+    SimObject('BaseNonCachingSimpleCPU.py',
+            sim_objects=['BaseNonCachingSimpleCPU'])
     Source('noncaching.cc')
 
-if 'TimingSimpleCPU' in env['CPU_MODELS']:
-    need_simple_base = True
-    SimObject('TimingSimpleCPU.py', sim_objects=['TimingSimpleCPU'])
+    SimObject('BaseTimingSimpleCPU.py', sim_objects=['BaseTimingSimpleCPU'])
     Source('timing.cc')
 
-if 'AtomicSimpleCPU' in env['CPU_MODELS'] or \
-       'TimingSimpleCPU' in env['CPU_MODELS']:
     DebugFlag('SimpleCPU')
 
-if need_simple_base:
     Source('base.cc')
     SimObject('BaseSimpleCPU.py', sim_objects=['BaseSimpleCPU'])
diff --git a/src/cpu/simple/atomic.cc b/src/cpu/simple/atomic.cc
index d9738d2..9cf7a29 100644
--- a/src/cpu/simple/atomic.cc
+++ b/src/cpu/simple/atomic.cc
@@ -52,7 +52,7 @@
 #include "mem/packet.hh"
 #include "mem/packet_access.hh"
 #include "mem/physical.hh"
-#include "params/AtomicSimpleCPU.hh"
+#include "params/BaseAtomicSimpleCPU.hh"
 #include "sim/faults.hh"
 #include "sim/full_system.hh"
 #include "sim/system.hh"
@@ -72,7 +72,7 @@
     data_amo_req->setContext(cid);
 }
 
-AtomicSimpleCPU::AtomicSimpleCPU(const AtomicSimpleCPUParams &p)
+AtomicSimpleCPU::AtomicSimpleCPU(const BaseAtomicSimpleCPUParams &p)
     : BaseSimpleCPU(p),
       tickEvent([this]{ tick(); }, "AtomicSimpleCPU tick",
                 false, Event::CPU_Tick_Pri),
@@ -406,7 +406,8 @@
             }
             dcache_access = true;
 
-            assert(!pkt.isError());
+            panic_if(pkt.isError(), "Data fetch (%s) failed: %s",
+                    pkt.getAddrRange().to_string(), pkt.print());
 
             if (req->isLLSC()) {
                 thread->getIsaPtr()->handleLockedRead(req);
@@ -508,8 +509,8 @@
                     threadSnoop(&pkt, curThread);
                 }
                 dcache_access = true;
-                assert(!pkt.isError());
-
+                panic_if(pkt.isError(), "Data write (%s) failed: %s",
+                        pkt.getAddrRange().to_string(), pkt.print());
                 if (req->isSwap()) {
                     assert(res && curr_frag_id == 0);
                     memcpy(res, pkt.getConstPtr<uint8_t>(), size);
@@ -597,7 +598,8 @@
 
         dcache_access = true;
 
-        assert(!pkt.isError());
+        panic_if(pkt.isError(), "Atomic access (%s) failed: %s",
+                pkt.getAddrRange().to_string(), pkt.print());
         assert(!req->isLLSC());
     }
 
@@ -752,7 +754,8 @@
     pkt.dataStatic(decoder->moreBytesPtr());
 
     Tick latency = sendPacket(icachePort, &pkt);
-    assert(!pkt.isError());
+    panic_if(pkt.isError(), "Instruction fetch (%s) failed: %s",
+            pkt.getAddrRange().to_string(), pkt.print());
 
     return latency;
 }
diff --git a/src/cpu/simple/atomic.hh b/src/cpu/simple/atomic.hh
index aacd0dc..6fd790e 100644
--- a/src/cpu/simple/atomic.hh
+++ b/src/cpu/simple/atomic.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013, 2015, 2018, 2020 ARM Limited
+ * Copyright (c) 2012-2013, 2015, 2018, 2020-2021 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -44,7 +44,7 @@
 #include "cpu/simple/base.hh"
 #include "cpu/simple/exec_context.hh"
 #include "mem/request.hh"
-#include "params/AtomicSimpleCPU.hh"
+#include "params/BaseAtomicSimpleCPU.hh"
 #include "sim/probe/probe.hh"
 
 namespace gem5
@@ -54,7 +54,7 @@
 {
   public:
 
-    AtomicSimpleCPU(const AtomicSimpleCPUParams &params);
+    AtomicSimpleCPU(const BaseAtomicSimpleCPUParams &params);
     virtual ~AtomicSimpleCPU();
 
     void init() override;
@@ -224,10 +224,10 @@
         override;
 
     Fault
-    initiateHtmCmd(Request::Flags flags) override
+    initiateMemMgmtCmd(Request::Flags flags) override
     {
-        panic("initiateHtmCmd() is for timing accesses, and should "
-              "never be called on AtomicSimpleCPU.\n");
+        panic("initiateMemMgmtCmd() is for timing accesses, and "
+              "should never be called on AtomicSimpleCPU.\n");
     }
 
     void
diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc
index 4cc8e1f..426aa44 100644
--- a/src/cpu/simple/base.cc
+++ b/src/cpu/simple/base.cc
@@ -85,7 +85,6 @@
     : BaseCPU(p),
       curThread(0),
       branchPred(p.branchPred),
-      zeroReg(p.isa[0]->regClasses().at(IntRegClass).zeroReg()),
       traceData(NULL),
       _status(Idle)
 {
@@ -307,9 +306,6 @@
     SimpleExecContext &t_info = *threadInfo[curThread];
     SimpleThread* thread = t_info.thread;
 
-    // maintain $r0 semantics
-    thread->setIntReg(zeroReg, 0);
-
     // resets predicates
     t_info.setPredicate(true);
     t_info.setMemAccPredicate(true);
diff --git a/src/cpu/simple/base.hh b/src/cpu/simple/base.hh
index 45fd5ea..3fdd019 100644
--- a/src/cpu/simple/base.hh
+++ b/src/cpu/simple/base.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012,2015,2018,2020 ARM Limited
+ * Copyright (c) 2011-2012,2015,2018,2020-2021 ARM Limited
  * Copyright (c) 2013 Advanced Micro Devices, Inc.
  * All rights reserved
  *
@@ -86,8 +86,6 @@
     ThreadID curThread;
     branch_prediction::BPredUnit *branchPred;
 
-    const RegIndex zeroReg;
-
     void checkPcEventQueue();
     void swapActiveThread();
 
@@ -190,11 +188,15 @@
     void serializeThread(CheckpointOut &cp, ThreadID tid) const override;
     void unserializeThread(CheckpointIn &cp, ThreadID tid) override;
 
-    /** Hardware transactional memory commands (HtmCmds), e.g. start a
-     * transaction and commit a transaction, are memory operations but are
-     * neither really (true) loads nor stores. For this reason the interface
-     * is extended and initiateHtmCmd() is used to instigate the command. */
-    virtual Fault initiateHtmCmd(Request::Flags flags) = 0;
+    /**
+     * Memory management commands such as hardware transactional memory
+     * commands or TLB invalidation commands are memory operations but are
+     * neither really (true) loads nor stores.
+     * For this reason the interface is extended,
+     * and initiateMemMgmtCmd() is used to instigate the command.
+     */
+    virtual Fault initiateMemMgmtCmd(Request::Flags flags) = 0;
+
 };
 
 } // namespace gem5
diff --git a/src/cpu/simple/exec_context.hh b/src/cpu/simple/exec_context.hh
index b0fe779..fa3c61c 100644
--- a/src/cpu/simple/exec_context.hh
+++ b/src/cpu/simple/exec_context.hh
@@ -157,7 +157,23 @@
               ADD_STAT(numBranchMispred, statistics::units::Count::get(),
                        "Number of branch mispredictions"),
               ADD_STAT(statExecutedInstType, statistics::units::Count::get(),
-                       "Class of executed instruction.")
+                       "Class of executed instruction."),
+              numRegReads{
+                  &numIntRegReads,
+                  &numFpRegReads,
+                  &numVecRegReads,
+                  &numVecRegReads,
+                  &numVecPredRegReads,
+                  &numCCRegReads
+              },
+              numRegWrites{
+                  &numIntRegWrites,
+                  &numFpRegWrites,
+                  &numVecRegWrites,
+                  &numVecRegWrites,
+                  &numVecPredRegWrites,
+                  &numCCRegWrites
+              }
         {
             numCCRegReads
                 .flags(statistics::nozero);
@@ -278,6 +294,9 @@
         // Instruction mix histogram by OpClass
         statistics::Vector statExecutedInstType;
 
+        std::array<statistics::Scalar *, CCRegClass + 1> numRegReads;
+        std::array<statistics::Scalar *, CCRegClass + 1> numRegWrites;
+
     } execContextStats;
 
   public:
@@ -288,143 +307,48 @@
         lastDcacheStall(0), execContextStats(cpu, thread)
     { }
 
-    /** Reads an integer register. */
     RegVal
-    readIntRegOperand(const StaticInst *si, int idx) override
+    getRegOperand(const StaticInst *si, int idx) override
     {
-        execContextStats.numIntRegReads++;
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(IntRegClass));
-        return thread->readIntReg(reg.index());
-    }
-
-    /** Sets an integer register to a value. */
-    void
-    setIntRegOperand(const StaticInst *si, int idx, RegVal val) override
-    {
-        execContextStats.numIntRegWrites++;
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(IntRegClass));
-        thread->setIntReg(reg.index(), val);
-    }
-
-    /** Reads a floating point register in its binary format, instead
-     * of by value. */
-    RegVal
-    readFloatRegOperandBits(const StaticInst *si, int idx) override
-    {
-        execContextStats.numFpRegReads++;
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(FloatRegClass));
-        return thread->readFloatReg(reg.index());
-    }
-
-    /** Sets the bits of a floating point register of single width
-     * to a binary value. */
-    void
-    setFloatRegOperandBits(const StaticInst *si, int idx, RegVal val) override
-    {
-        execContextStats.numFpRegWrites++;
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(FloatRegClass));
-        thread->setFloatReg(reg.index(), val);
-    }
-
-    /** Reads a vector register. */
-    const TheISA::VecRegContainer &
-    readVecRegOperand(const StaticInst *si, int idx) const override
-    {
-        execContextStats.numVecRegReads++;
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(VecRegClass));
-        return thread->readVecReg(reg);
-    }
-
-    /** Reads a vector register for modification. */
-    TheISA::VecRegContainer &
-    getWritableVecRegOperand(const StaticInst *si, int idx) override
-    {
-        execContextStats.numVecRegWrites++;
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecRegClass));
-        return thread->getWritableVecReg(reg);
-    }
-
-    /** Sets a vector register to a value. */
-    void
-    setVecRegOperand(const StaticInst *si, int idx,
-                     const TheISA::VecRegContainer& val) override
-    {
-        execContextStats.numVecRegWrites++;
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecRegClass));
-        thread->setVecReg(reg, val);
-    }
-
-    /** Reads an element of a vector register. */
-    RegVal
-    readVecElemOperand(const StaticInst *si, int idx) const override
-    {
-        execContextStats.numVecRegReads++;
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(VecElemClass));
-        return thread->readVecElem(reg);
-    }
-
-    /** Sets an element of a vector register to a value. */
-    void
-    setVecElemOperand(const StaticInst *si, int idx, RegVal val) override
-    {
-        execContextStats.numVecRegWrites++;
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecElemClass));
-        thread->setVecElem(reg, val);
-    }
-
-    const TheISA::VecPredRegContainer&
-    readVecPredRegOperand(const StaticInst *si, int idx) const override
-    {
-        execContextStats.numVecPredRegReads++;
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(VecPredRegClass));
-        return thread->readVecPredReg(reg);
-    }
-
-    TheISA::VecPredRegContainer&
-    getWritableVecPredRegOperand(const StaticInst *si, int idx) override
-    {
-        execContextStats.numVecPredRegWrites++;
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecPredRegClass));
-        return thread->getWritableVecPredReg(reg);
+        const RegId &reg = si->srcRegIdx(idx);
+        if (reg.is(InvalidRegClass))
+            return 0;
+        (*execContextStats.numRegReads[reg.classValue()])++;
+        return thread->getReg(reg);
     }
 
     void
-    setVecPredRegOperand(const StaticInst *si, int idx,
-                         const TheISA::VecPredRegContainer& val) override
+    getRegOperand(const StaticInst *si, int idx, void *val) override
     {
-        execContextStats.numVecPredRegWrites++;
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(VecPredRegClass));
-        thread->setVecPredReg(reg, val);
+        const RegId &reg = si->srcRegIdx(idx);
+        (*execContextStats.numRegReads[reg.classValue()])++;
+        thread->getReg(reg, val);
     }
 
-    RegVal
-    readCCRegOperand(const StaticInst *si, int idx) override
+    void *
+    getWritableRegOperand(const StaticInst *si, int idx) override
     {
-        execContextStats.numCCRegReads++;
-        const RegId& reg = si->srcRegIdx(idx);
-        assert(reg.is(CCRegClass));
-        return thread->readCCReg(reg.index());
+        const RegId &reg = si->destRegIdx(idx);
+        (*execContextStats.numRegWrites[reg.classValue()])++;
+        return thread->getWritableReg(reg);
     }
 
     void
-    setCCRegOperand(const StaticInst *si, int idx, RegVal val) override
+    setRegOperand(const StaticInst *si, int idx, RegVal val) override
     {
-        execContextStats.numCCRegWrites++;
-        const RegId& reg = si->destRegIdx(idx);
-        assert(reg.is(CCRegClass));
-        thread->setCCReg(reg.index(), val);
+        const RegId &reg = si->destRegIdx(idx);
+        if (reg.is(InvalidRegClass))
+            return;
+        (*execContextStats.numRegWrites[reg.classValue()])++;
+        thread->setReg(reg, val);
+    }
+
+    void
+    setRegOperand(const StaticInst *si, int idx, const void *val) override
+    {
+        const RegId &reg = si->destRegIdx(idx);
+        (*execContextStats.numRegWrites[reg.classValue()])++;
+        thread->setReg(reg, val);
     }
 
     RegVal
@@ -510,22 +434,25 @@
             byte_enable);
     }
 
-    Fault amoMem(Addr addr, uint8_t *data, unsigned int size,
-                 Request::Flags flags, AtomicOpFunctorPtr amo_op) override
+    Fault
+    amoMem(Addr addr, uint8_t *data, unsigned int size,
+           Request::Flags flags, AtomicOpFunctorPtr amo_op) override
     {
         return cpu->amoMem(addr, data, size, flags, std::move(amo_op));
     }
 
-    Fault initiateMemAMO(Addr addr, unsigned int size,
-                         Request::Flags flags,
-                         AtomicOpFunctorPtr amo_op) override
+    Fault
+    initiateMemAMO(Addr addr, unsigned int size,
+                   Request::Flags flags,
+                   AtomicOpFunctorPtr amo_op) override
     {
         return cpu->initiateMemAMO(addr, size, flags, std::move(amo_op));
     }
 
-    Fault initiateHtmCmd(Request::Flags flags) override
+    Fault
+    initiateMemMgmtCmd(Request::Flags flags) override
     {
-        return cpu->initiateHtmCmd(flags);
+        return cpu->initiateMemMgmtCmd(flags);
     }
 
     /**
diff --git a/src/cpu/simple/noncaching.cc b/src/cpu/simple/noncaching.cc
index 6458bee..424a496 100644
--- a/src/cpu/simple/noncaching.cc
+++ b/src/cpu/simple/noncaching.cc
@@ -44,7 +44,8 @@
 namespace gem5
 {
 
-NonCachingSimpleCPU::NonCachingSimpleCPU(const NonCachingSimpleCPUParams &p)
+NonCachingSimpleCPU::NonCachingSimpleCPU(
+        const BaseNonCachingSimpleCPUParams &p)
     : AtomicSimpleCPU(p)
 {
     assert(p.numThreads == 1);
diff --git a/src/cpu/simple/noncaching.hh b/src/cpu/simple/noncaching.hh
index 289f482..5dea9c6 100644
--- a/src/cpu/simple/noncaching.hh
+++ b/src/cpu/simple/noncaching.hh
@@ -41,7 +41,7 @@
 #include "base/addr_range_map.hh"
 #include "cpu/simple/atomic.hh"
 #include "mem/backdoor.hh"
-#include "params/NonCachingSimpleCPU.hh"
+#include "params/BaseNonCachingSimpleCPU.hh"
 
 namespace gem5
 {
@@ -53,7 +53,7 @@
 class NonCachingSimpleCPU : public AtomicSimpleCPU
 {
   public:
-    NonCachingSimpleCPU(const NonCachingSimpleCPUParams &p);
+    NonCachingSimpleCPU(const BaseNonCachingSimpleCPUParams &p);
 
     void verifyMemoryMode() const override;
 
diff --git a/src/cpu/simple/probes/SConscript b/src/cpu/simple/probes/SConscript
index eae6a45..8b1aa9a 100644
--- a/src/cpu/simple/probes/SConscript
+++ b/src/cpu/simple/probes/SConscript
@@ -28,6 +28,6 @@
 
 Import('*')
 
-if 'AtomicSimpleCPU' in env['CPU_MODELS']:
+if env['CONF']['TARGET_ISA'] != 'null':
     SimObject('SimPoint.py', sim_objects=['SimPoint'])
     Source('simpoint.cc')
diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc
index da3e0c0..188611f 100644
--- a/src/cpu/simple/timing.cc
+++ b/src/cpu/simple/timing.cc
@@ -1,6 +1,6 @@
 /*
  * Copyright 2014 Google, Inc.
- * Copyright (c) 2010-2013,2015,2017-2018, 2020 ARM Limited
+ * Copyright (c) 2010-2013,2015,2017-2018, 2020-2021 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -53,7 +53,7 @@
 #include "debug/SimpleCPU.hh"
 #include "mem/packet.hh"
 #include "mem/packet_access.hh"
-#include "params/TimingSimpleCPU.hh"
+#include "params/BaseTimingSimpleCPU.hh"
 #include "sim/faults.hh"
 #include "sim/full_system.hh"
 #include "sim/system.hh"
@@ -74,7 +74,7 @@
     cpu->schedule(this, t);
 }
 
-TimingSimpleCPU::TimingSimpleCPU(const TimingSimpleCPUParams &p)
+TimingSimpleCPU::TimingSimpleCPU(const BaseTimingSimpleCPUParams &p)
     : BaseSimpleCPU(p), fetchTranslation(this), icachePort(this),
       dcachePort(this), ifetch_pkt(NULL), dcache_pkt(NULL), previousCycle(0),
       fetchEvent([this]{ fetch(); }, name())
@@ -826,7 +826,8 @@
 
     // received a response from the icache: execute the received
     // instruction
-    assert(!pkt || !pkt->isError());
+    panic_if(pkt && pkt->isError(), "Instruction fetch (%s) failed: %s",
+            pkt->getAddrRange().to_string(), pkt->print());
     assert(_status == IcacheWaitResponse);
 
     _status = BaseSimpleCPU::Running;
@@ -950,7 +951,8 @@
 
     // received a response from the dcache: complete the load or store
     // instruction
-    assert(!pkt->isError());
+    panic_if(pkt->isError(), "Data access (%s) failed: %s",
+            pkt->getAddrRange().to_string(), pkt->print());
     assert(_status == DcacheWaitResponse || _status == DTBWaitResponse ||
            pkt->req->getFlags().isSet(Request::NO_ACCESS));
 
@@ -1095,14 +1097,31 @@
     }
 
     // Making it uniform across all CPUs:
-    // The CPUs need to be woken up only on an invalidation packet (when using caches)
-    // or on an incoming write packet (when not using caches)
-    // It is not necessary to wake up the processor on all incoming packets
+    // The CPUs need to be woken up only on an invalidation packet
+    // (when using caches) or on an incoming write packet (when not
+    // using caches) It is not necessary to wake up the processor on
+    // all incoming packets
     if (pkt->isInvalidate() || pkt->isWrite()) {
         for (auto &t_info : cpu->threadInfo) {
             t_info->thread->getIsaPtr()->handleLockedSnoop(pkt,
                     cacheBlockMask);
         }
+    } else if (pkt->req && pkt->req->isTlbiExtSync()) {
+        // We received a TLBI_EXT_SYNC request.
+        // In a detailed sim we would wait for memory ops to complete,
+        // but in our simple case we just respond immediately
+        auto reply_req = Request::createMemManagement(
+            Request::TLBI_EXT_SYNC_COMP,
+            cpu->dataRequestorId());
+
+        // Extra Data = the transaction ID of the Sync we're completing
+        reply_req->setExtraData(pkt->req->getExtraData());
+        PacketPtr reply_pkt = Packet::createRead(reply_req);
+
+        // TODO - reserve some credit for these responses?
+        if (!sendTimingReq(reply_pkt)) {
+            panic("Couldn't send TLBI_EXT_SYNC_COMP message");
+        }
     }
 }
 
@@ -1214,7 +1233,7 @@
 }
 
 Fault
-TimingSimpleCPU::initiateHtmCmd(Request::Flags flags)
+TimingSimpleCPU::initiateMemMgmtCmd(Request::Flags flags)
 {
     SimpleExecContext &t_info = *threadInfo[curThread];
     SimpleThread* thread = t_info.thread;
@@ -1234,7 +1253,7 @@
     req->taskId(taskId());
     req->setInstCount(t_info.numInst);
 
-    assert(req->isHTMCmd());
+    assert(req->isHTMCmd() || req->isTlbiCmd());
 
     // Use the payload as a sanity check,
     // the memory subsystem will clear allocated data
@@ -1244,14 +1263,19 @@
     memcpy (data, &rc, size);
 
     // debugging output
-    if (req->isHTMStart())
-        DPRINTF(HtmCpu, "HTMstart htmUid=%u\n", t_info.getHtmTransactionUid());
-    else if (req->isHTMCommit())
-        DPRINTF(HtmCpu, "HTMcommit htmUid=%u\n", t_info.getHtmTransactionUid());
-    else if (req->isHTMCancel())
-        DPRINTF(HtmCpu, "HTMcancel htmUid=%u\n", t_info.getHtmTransactionUid());
-    else
-        panic("initiateHtmCmd: unknown CMD");
+    if (req->isHTMCmd()) {
+        if (req->isHTMStart())
+            DPRINTF(HtmCpu, "HTMstart htmUid=%u\n",
+                t_info.getHtmTransactionUid());
+        else if (req->isHTMCommit())
+            DPRINTF(HtmCpu, "HTMcommit htmUid=%u\n",
+                t_info.getHtmTransactionUid());
+        else if (req->isHTMCancel())
+            DPRINTF(HtmCpu, "HTMcancel htmUid=%u\n",
+                t_info.getHtmTransactionUid());
+        else
+            panic("initiateMemMgmtCmd: unknown HTM CMD");
+    }
 
     sendData(req, data, nullptr, true);
 
diff --git a/src/cpu/simple/timing.hh b/src/cpu/simple/timing.hh
index b2cc4e7..ca6c0e2 100644
--- a/src/cpu/simple/timing.hh
+++ b/src/cpu/simple/timing.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013,2015,2018,2020 ARM Limited
+ * Copyright (c) 2012-2013,2015,2018,2020-2021 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -45,7 +45,7 @@
 #include "cpu/simple/base.hh"
 #include "cpu/simple/exec_context.hh"
 #include "cpu/translation.hh"
-#include "params/TimingSimpleCPU.hh"
+#include "params/BaseTimingSimpleCPU.hh"
 
 namespace gem5
 {
@@ -54,7 +54,7 @@
 {
   public:
 
-    TimingSimpleCPU(const TimingSimpleCPUParams &params);
+    TimingSimpleCPU(const BaseTimingSimpleCPUParams &params);
     virtual ~TimingSimpleCPU();
 
     void init() override;
@@ -324,8 +324,8 @@
      */
     void finishTranslation(WholeTranslationState *state);
 
-    /** hardware transactional memory **/
-    Fault initiateHtmCmd(Request::Flags flags) override;
+    /** hardware transactional memory & TLBI operations **/
+    Fault initiateMemMgmtCmd(Request::Flags flags) override;
 
     void htmSendAbortSignal(ThreadID tid, uint64_t htm_uid,
                             HtmFailureFaultCause) override;
diff --git a/src/cpu/simple_thread.cc b/src/cpu/simple_thread.cc
index 7b572ef..e9ffa21 100644
--- a/src/cpu/simple_thread.cc
+++ b/src/cpu/simple_thread.cc
@@ -70,20 +70,20 @@
                            Process *_process, BaseMMU *_mmu,
                            BaseISA *_isa, InstDecoder *_decoder)
     : ThreadState(_cpu, _thread_num, _process),
+      regFiles{{
+          {_isa->regClasses().at(IntRegClass)},
+          {_isa->regClasses().at(FloatRegClass)},
+          {_isa->regClasses().at(VecRegClass)},
+          {_isa->regClasses().at(VecElemClass)},
+          {_isa->regClasses().at(VecPredRegClass)},
+          {_isa->regClasses().at(CCRegClass)}
+      }},
       isa(dynamic_cast<TheISA::ISA *>(_isa)),
       predicate(true), memAccPredicate(true),
       comInstEventQueue("instruction-based event queue"),
       system(_sys), mmu(_mmu), decoder(_decoder),
       htmTransactionStarts(0), htmTransactionStops(0)
 {
-    assert(isa);
-    const auto &regClasses = isa->regClasses();
-    intRegs.resize(regClasses.at(IntRegClass).size());
-    floatRegs.resize(regClasses.at(FloatRegClass).size());
-    vecRegs.resize(regClasses.at(VecRegClass).size());
-    vecElemRegs.resize(regClasses.at(VecElemClass).size());
-    vecPredRegs.resize(regClasses.at(VecPredRegClass).size());
-    ccRegs.resize(regClasses.at(CCRegClass).size());
     clearArchRegs();
 }
 
diff --git a/src/cpu/simple_thread.hh b/src/cpu/simple_thread.hh
index 0f25e17..1de5b37 100644
--- a/src/cpu/simple_thread.hh
+++ b/src/cpu/simple_thread.hh
@@ -51,8 +51,10 @@
 #include "arch/generic/tlb.hh"
 #include "arch/isa.hh"
 #include "arch/vecregs.hh"
+#include "base/logging.hh"
 #include "base/types.hh"
 #include "config/the_isa.hh"
+#include "cpu/regfile.hh"
 #include "cpu/thread_context.hh"
 #include "cpu/thread_state.hh"
 #include "debug/CCRegs.hh"
@@ -96,12 +98,8 @@
     typedef ThreadContext::Status Status;
 
   protected:
-    std::vector<RegVal> floatRegs;
-    std::vector<RegVal> intRegs;
-    std::vector<TheISA::VecRegContainer> vecRegs;
-    std::vector<RegVal> vecElemRegs;
-    std::vector<TheISA::VecPredRegContainer> vecPredRegs;
-    std::vector<RegVal> ccRegs;
+    std::array<RegFile, CCRegClass + 1> regFiles;
+
     TheISA::ISA *const isa;    // one "instance" of the current ISA.
 
     std::unique_ptr<PCStateBase> _pcState;
@@ -210,7 +208,7 @@
 
     CheckerCPU *getCheckerCpuPtr() override { return NULL; }
 
-    BaseISA *getIsaPtr() override { return isa; }
+    BaseISA *getIsaPtr() const override { return isa; }
 
     InstDecoder *getDecoderPtr() override { return decoder; }
 
@@ -249,176 +247,14 @@
     clearArchRegs() override
     {
         set(_pcState, isa->newPCState());
-        std::fill(intRegs.begin(), intRegs.end(), 0);
-        std::fill(floatRegs.begin(), floatRegs.end(), 0);
-        for (auto &vec_reg: vecRegs)
-            vec_reg.zero();
-        std::fill(vecElemRegs.begin(), vecElemRegs.end(), 0);
-        for (auto &pred_reg: vecPredRegs)
-            pred_reg.reset();
-        std::fill(ccRegs.begin(), ccRegs.end(), 0);
+        for (auto &rf: regFiles)
+            rf.clear();
         isa->clear();
     }
 
     //
     // New accessors for new decoder.
     //
-    RegVal
-    readIntReg(RegIndex reg_idx) const override
-    {
-        int flatIndex = isa->flattenIntIndex(reg_idx);
-        assert(flatIndex < intRegs.size());
-        uint64_t regVal = readIntRegFlat(flatIndex);
-        DPRINTF(IntRegs, "Reading int reg %d (%d) as %#x.\n",
-                reg_idx, flatIndex, regVal);
-        return regVal;
-    }
-
-    RegVal
-    readFloatReg(RegIndex reg_idx) const override
-    {
-        int flatIndex = isa->flattenFloatIndex(reg_idx);
-        assert(flatIndex < floatRegs.size());
-        RegVal regVal = readFloatRegFlat(flatIndex);
-        DPRINTF(FloatRegs, "Reading float reg %d (%d) bits as %#x.\n",
-                reg_idx, flatIndex, regVal);
-        return regVal;
-    }
-
-    const TheISA::VecRegContainer&
-    readVecReg(const RegId& reg) const override
-    {
-        int flatIndex = isa->flattenVecIndex(reg.index());
-        assert(flatIndex < vecRegs.size());
-        const TheISA::VecRegContainer& regVal = readVecRegFlat(flatIndex);
-        DPRINTF(VecRegs, "Reading vector reg %d (%d) as %s.\n",
-                reg.index(), flatIndex, regVal);
-        return regVal;
-    }
-
-    TheISA::VecRegContainer&
-    getWritableVecReg(const RegId& reg) override
-    {
-        int flatIndex = isa->flattenVecIndex(reg.index());
-        assert(flatIndex < vecRegs.size());
-        TheISA::VecRegContainer& regVal = getWritableVecRegFlat(flatIndex);
-        DPRINTF(VecRegs, "Reading vector reg %d (%d) as %s for modify.\n",
-                reg.index(), flatIndex, regVal);
-        return regVal;
-    }
-
-    RegVal
-    readVecElem(const RegId &reg) const override
-    {
-        int flatIndex = isa->flattenVecElemIndex(reg.index());
-        assert(flatIndex < vecRegs.size());
-        RegVal regVal = readVecElemFlat(flatIndex, reg.elemIndex());
-        DPRINTF(VecRegs, "Reading element %d of vector reg %d (%d) as"
-                " %#x.\n", reg.elemIndex(), reg.index(), flatIndex, regVal);
-        return regVal;
-    }
-
-    const TheISA::VecPredRegContainer &
-    readVecPredReg(const RegId &reg) const override
-    {
-        int flatIndex = isa->flattenVecPredIndex(reg.index());
-        assert(flatIndex < vecPredRegs.size());
-        const TheISA::VecPredRegContainer& regVal =
-            readVecPredRegFlat(flatIndex);
-        DPRINTF(VecPredRegs, "Reading predicate reg %d (%d) as %s.\n",
-                reg.index(), flatIndex, regVal);
-        return regVal;
-    }
-
-    TheISA::VecPredRegContainer &
-    getWritableVecPredReg(const RegId &reg) override
-    {
-        int flatIndex = isa->flattenVecPredIndex(reg.index());
-        assert(flatIndex < vecPredRegs.size());
-        TheISA::VecPredRegContainer& regVal =
-            getWritableVecPredRegFlat(flatIndex);
-        DPRINTF(VecPredRegs,
-                "Reading predicate reg %d (%d) as %s for modify.\n",
-                reg.index(), flatIndex, regVal);
-        return regVal;
-    }
-
-    RegVal
-    readCCReg(RegIndex reg_idx) const override
-    {
-        int flatIndex = isa->flattenCCIndex(reg_idx);
-        assert(0 <= flatIndex);
-        assert(flatIndex < ccRegs.size());
-        uint64_t regVal(readCCRegFlat(flatIndex));
-        DPRINTF(CCRegs, "Reading CC reg %d (%d) as %#x.\n",
-                reg_idx, flatIndex, regVal);
-        return regVal;
-    }
-
-    void
-    setIntReg(RegIndex reg_idx, RegVal val) override
-    {
-        int flatIndex = isa->flattenIntIndex(reg_idx);
-        assert(flatIndex < intRegs.size());
-        DPRINTF(IntRegs, "Setting int reg %d (%d) to %#x.\n",
-                reg_idx, flatIndex, val);
-        setIntRegFlat(flatIndex, val);
-    }
-
-    void
-    setFloatReg(RegIndex reg_idx, RegVal val) override
-    {
-        int flatIndex = isa->flattenFloatIndex(reg_idx);
-        assert(flatIndex < floatRegs.size());
-        // XXX: Fix array out of bounds compiler error for gem5.fast
-        // when checkercpu enabled
-        if (flatIndex < floatRegs.size())
-            setFloatRegFlat(flatIndex, val);
-        DPRINTF(FloatRegs, "Setting float reg %d (%d) bits to %#x.\n",
-                reg_idx, flatIndex, val);
-    }
-
-    void
-    setVecReg(const RegId &reg, const TheISA::VecRegContainer &val) override
-    {
-        int flatIndex = isa->flattenVecIndex(reg.index());
-        assert(flatIndex < vecRegs.size());
-        setVecRegFlat(flatIndex, val);
-        DPRINTF(VecRegs, "Setting vector reg %d (%d) to %s.\n",
-                reg.index(), flatIndex, val);
-    }
-
-    void
-    setVecElem(const RegId &reg, RegVal val) override
-    {
-        int flatIndex = isa->flattenVecElemIndex(reg.index());
-        assert(flatIndex < vecRegs.size());
-        setVecElemFlat(flatIndex, reg.elemIndex(), val);
-        DPRINTF(VecRegs, "Setting element %d of vector reg %d (%d) to"
-                " %#x.\n", reg.elemIndex(), reg.index(), flatIndex, val);
-    }
-
-    void
-    setVecPredReg(const RegId &reg,
-            const TheISA::VecPredRegContainer &val) override
-    {
-        int flatIndex = isa->flattenVecPredIndex(reg.index());
-        assert(flatIndex < vecPredRegs.size());
-        setVecPredRegFlat(flatIndex, val);
-        DPRINTF(VecPredRegs, "Setting predicate reg %d (%d) to %s.\n",
-                reg.index(), flatIndex, val);
-    }
-
-    void
-    setCCReg(RegIndex reg_idx, RegVal val) override
-    {
-        int flatIndex = isa->flattenCCIndex(reg_idx);
-        assert(flatIndex < ccRegs.size());
-        DPRINTF(CCRegs, "Setting CC reg %d (%d) to %#x.\n",
-                reg_idx, flatIndex, val);
-        setCCRegFlat(flatIndex, val);
-    }
-
     const PCStateBase &pcState() const override { return *_pcState; }
     void pcState(const PCStateBase &val) override { set(_pcState, val); }
 
@@ -481,76 +317,146 @@
         storeCondFailures = sc_failures;
     }
 
-    RegVal readIntRegFlat(RegIndex idx) const override { return intRegs[idx]; }
-    void
-    setIntRegFlat(RegIndex idx, RegVal val) override
+    RegVal
+    getReg(const RegId &arch_reg) const override
     {
-        intRegs[idx] = val;
+        const RegId reg = flattenRegId(arch_reg);
+
+        const RegIndex idx = reg.index();
+
+        const auto &reg_file = regFiles[reg.classValue()];
+        const auto &reg_class = reg_file.regClass;
+
+        RegVal val = reg_file.reg(idx);
+        DPRINTFV(reg_class.debug(), "Reading %s reg %s (%d) as %#x.\n",
+                reg.className(), reg_class.regName(arch_reg), idx, val);
+        return val;
     }
 
     RegVal
-    readFloatRegFlat(RegIndex idx) const override
+    getRegFlat(const RegId &reg) const override
     {
-        return floatRegs[idx];
-    }
-    void
-    setFloatRegFlat(RegIndex idx, RegVal val) override
-    {
-        floatRegs[idx] = val;
-    }
+        const RegIndex idx = reg.index();
 
-    const TheISA::VecRegContainer &
-    readVecRegFlat(RegIndex reg) const override
-    {
-        return vecRegs[reg];
-    }
+        const auto &reg_file = regFiles[reg.classValue()];
+        const auto &reg_class = reg_file.regClass;
 
-    TheISA::VecRegContainer &
-    getWritableVecRegFlat(RegIndex reg) override
-    {
-        return vecRegs[reg];
+        RegVal val = reg_file.reg(idx);
+        DPRINTFV(reg_class.debug(), "Reading %s reg %d as %#x.\n",
+                reg.className(), idx, val);
+        return val;
     }
 
     void
-    setVecRegFlat(RegIndex reg, const TheISA::VecRegContainer &val) override
+    getReg(const RegId &arch_reg, void *val) const override
     {
-        vecRegs[reg] = val;
-    }
+        const RegId reg = flattenRegId(arch_reg);
 
-    RegVal
-    readVecElemFlat(RegIndex reg, const ElemIndex &elemIndex) const override
-    {
-        return vecElemRegs[reg * TheISA::NumVecElemPerVecReg + elemIndex];
+        const RegIndex idx = reg.index();
+
+        const auto &reg_file = regFiles[reg.classValue()];
+        const auto &reg_class = reg_file.regClass;
+
+        reg_file.get(idx, val);
+        DPRINTFV(reg_class.debug(), "Reading %s register %s (%d) as %s.\n",
+                reg.className(), reg_class.regName(arch_reg), idx,
+                reg_class.valString(val));
     }
 
     void
-    setVecElemFlat(RegIndex reg, const ElemIndex &elemIndex,
-                   RegVal val) override
+    getRegFlat(const RegId &reg, void *val) const override
     {
-        vecElemRegs[reg * TheISA::NumVecElemPerVecReg + elemIndex] = val;
+        const RegIndex idx = reg.index();
+
+        const auto &reg_file = regFiles[reg.classValue()];
+        const auto &reg_class = reg_file.regClass;
+
+        reg_file.get(idx, val);
+        DPRINTFV(reg_class.debug(), "Reading %s register %d as %s.\n",
+                reg.className(), idx, reg_class.valString(val));
     }
 
-    const TheISA::VecPredRegContainer &
-    readVecPredRegFlat(RegIndex reg) const override
+    void *
+    getWritableReg(const RegId &arch_reg) override
     {
-        return vecPredRegs[reg];
+        const RegId reg = flattenRegId(arch_reg);
+        const RegIndex idx = reg.index();
+        auto &reg_file = regFiles[reg.classValue()];
+
+        return reg_file.ptr(idx);
     }
 
-    TheISA::VecPredRegContainer &
-    getWritableVecPredRegFlat(RegIndex reg) override
+    void *
+    getWritableRegFlat(const RegId &reg) override
     {
-        return vecPredRegs[reg];
+        const RegIndex idx = reg.index();
+        auto &reg_file = regFiles[reg.classValue()];
+
+        return reg_file.ptr(idx);
     }
 
     void
-    setVecPredRegFlat(RegIndex reg,
-            const TheISA::VecPredRegContainer &val) override
+    setReg(const RegId &arch_reg, RegVal val) override
     {
-        vecPredRegs[reg] = val;
+        const RegId reg = flattenRegId(arch_reg);
+
+        if (reg.is(InvalidRegClass))
+            return;
+
+        const RegIndex idx = reg.index();
+
+        auto &reg_file = regFiles[reg.classValue()];
+        const auto &reg_class = reg_file.regClass;
+
+        DPRINTFV(reg_class.debug(), "Setting %s register %s (%d) to %#x.\n",
+                reg.className(), reg_class.regName(arch_reg), idx, val);
+        reg_file.reg(idx) = val;
     }
 
-    RegVal readCCRegFlat(RegIndex idx) const override { return ccRegs[idx]; }
-    void setCCRegFlat(RegIndex idx, RegVal val) override { ccRegs[idx] = val; }
+    void
+    setRegFlat(const RegId &reg, RegVal val) override
+    {
+        if (reg.is(InvalidRegClass))
+            return;
+
+        const RegIndex idx = reg.index();
+
+        auto &reg_file = regFiles[reg.classValue()];
+        const auto &reg_class = reg_file.regClass;
+
+        DPRINTFV(reg_class.debug(), "Setting %s register %d to %#x.\n",
+                reg.className(), idx, val);
+        reg_file.reg(idx) = val;
+    }
+
+    void
+    setReg(const RegId &arch_reg, const void *val) override
+    {
+        const RegId reg = flattenRegId(arch_reg);
+
+        const RegIndex idx = reg.index();
+
+        auto &reg_file = regFiles[reg.classValue()];
+        const auto &reg_class = reg_file.regClass;
+
+        DPRINTFV(reg_class.debug(), "Setting %s register %s (%d) to %s.\n",
+                reg.className(), reg_class.regName(arch_reg), idx,
+                reg_class.valString(val));
+        reg_file.set(idx, val);
+    }
+
+    void
+    setRegFlat(const RegId &reg, const void *val) override
+    {
+        const RegIndex idx = reg.index();
+
+        auto &reg_file = regFiles[reg.classValue()];
+        const auto &reg_class = reg_file.regClass;
+
+        DPRINTFV(reg_class.debug(), "Setting %s register %d to %s.\n",
+                reg.className(), idx, reg_class.valString(val));
+        reg_file.set(idx, val);
+    }
 
     // hardware transactional memory
     void htmAbortTransaction(uint64_t htm_uid,
diff --git a/src/cpu/static_inst.hh b/src/cpu/static_inst.hh
index 3e6357b..af5975e 100644
--- a/src/cpu/static_inst.hh
+++ b/src/cpu/static_inst.hh
@@ -42,6 +42,7 @@
 #ifndef __CPU_STATIC_INST_HH__
 #define __CPU_STATIC_INST_HH__
 
+#include <array>
 #include <bitset>
 #include <cstdint>
 #include <memory>
@@ -105,51 +106,29 @@
     OpClass _opClass;
 
     /// See numSrcRegs().
-    int8_t _numSrcRegs = 0;
+    uint8_t _numSrcRegs = 0;
 
     /// See numDestRegs().
-    int8_t _numDestRegs = 0;
+    uint8_t _numDestRegs = 0;
 
-    /// The following are used to track physical register usage
-    /// for machines with separate int & FP reg files.
-    //@{
-    int8_t _numFPDestRegs = 0;
-    int8_t _numIntDestRegs = 0;
-    int8_t _numCCDestRegs = 0;
-    //@}
-
-    /** To use in architectures with vector register file. */
-    /** @{ */
-    int8_t _numVecDestRegs = 0;
-    int8_t _numVecElemDestRegs = 0;
-    int8_t _numVecPredDestRegs = 0;
-    /** @} */
+    std::array<uint8_t, MiscRegClass + 1> _numTypedDestRegs = {};
 
   public:
 
     /// @name Register information.
-    /// The sum of numFPDestRegs(), numIntDestRegs(), numVecDestRegs(),
-    /// numVecElemDestRegs() and numVecPredDestRegs() equals numDestRegs().
-    /// The former two functions are used to track physical register usage for
-    /// machines with separate int & FP reg files, the next three are for
-    /// machines with vector and predicate register files.
+    /// The sum of the different numDestRegs([type])-s equals numDestRegs().
+    /// The per-type function is used to track physical register usage.
     //@{
     /// Number of source registers.
-    int8_t numSrcRegs()  const { return _numSrcRegs; }
+    uint8_t numSrcRegs()  const { return _numSrcRegs; }
     /// Number of destination registers.
-    int8_t numDestRegs() const { return _numDestRegs; }
-    /// Number of floating-point destination regs.
-    int8_t numFPDestRegs()  const { return _numFPDestRegs; }
-    /// Number of integer destination regs.
-    int8_t numIntDestRegs() const { return _numIntDestRegs; }
-    /// Number of vector destination regs.
-    int8_t numVecDestRegs() const { return _numVecDestRegs; }
-    /// Number of vector element destination regs.
-    int8_t numVecElemDestRegs() const { return _numVecElemDestRegs; }
-    /// Number of predicate destination regs.
-    int8_t numVecPredDestRegs() const { return _numVecPredDestRegs; }
-    /// Number of coprocesor destination regs.
-    int8_t numCCDestRegs() const { return _numCCDestRegs; }
+    uint8_t numDestRegs() const { return _numDestRegs; }
+    /// Number of destination registers of a particular type.
+    uint8_t
+    numDestRegs(RegClassType type) const
+    {
+        return _numTypedDestRegs[type];
+    }
     //@}
 
     /// @name Flag accessors.
diff --git a/src/cpu/testers/directedtest/SConscript b/src/cpu/testers/directedtest/SConscript
index e6453f9..6787648 100644
--- a/src/cpu/testers/directedtest/SConscript
+++ b/src/cpu/testers/directedtest/SConscript
@@ -35,7 +35,7 @@
 # When this dependency is removed, the ruby tester should be compiled
 # independently from Ruby
 #
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('RubyDirectedTester.py', sim_objects=[
diff --git a/src/cpu/testers/garnet_synthetic_traffic/SConscript b/src/cpu/testers/garnet_synthetic_traffic/SConscript
index b1f7c8d..14f4abd 100644
--- a/src/cpu/testers/garnet_synthetic_traffic/SConscript
+++ b/src/cpu/testers/garnet_synthetic_traffic/SConscript
@@ -28,7 +28,7 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('GarnetSyntheticTraffic.py', sim_objects=['GarnetSyntheticTraffic'])
diff --git a/src/cpu/testers/gpu_ruby_test/SConscript b/src/cpu/testers/gpu_ruby_test/SConscript
index 88215cc..0231649 100644
--- a/src/cpu/testers/gpu_ruby_test/SConscript
+++ b/src/cpu/testers/gpu_ruby_test/SConscript
@@ -31,10 +31,10 @@
 
 Import('*')
 
-if not env['BUILD_GPU']:
+if not env['CONF']['BUILD_GPU']:
     Return()
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('ProtocolTester.py', sim_objects=['ProtocolTester'])
diff --git a/src/cpu/testers/gpu_ruby_test/tester_thread.cc b/src/cpu/testers/gpu_ruby_test/tester_thread.cc
index 2dfd54a..760f8c2 100644
--- a/src/cpu/testers/gpu_ruby_test/tester_thread.cc
+++ b/src/cpu/testers/gpu_ruby_test/tester_thread.cc
@@ -152,6 +152,37 @@
     episodeHistory.push_back(curEpisode);
 }
 
+int
+TesterThread::getTokensNeeded()
+{
+    if (!tokenPort) {
+        return 0;
+    }
+
+    int tokens_needed = 0;
+    curAction = curEpisode->peekCurAction();
+
+    switch(curAction->getType()) {
+        case Episode::Action::Type::ATOMIC:
+            tokens_needed = numLanes;
+            break;
+        case Episode::Action::Type::LOAD:
+        case Episode::Action::Type::STORE:
+            for (int lane = 0; lane < numLanes; ++lane) {
+                Location loc = curAction->getLocation(lane);
+
+                if (loc != AddressManager::INVALID_LOCATION && loc >= 0) {
+                    tokens_needed++;
+                }
+            }
+            break;
+        default:
+            tokens_needed = 0;
+    }
+
+    return tokens_needed;
+}
+
 bool
 TesterThread::isNextActionReady()
 {
@@ -162,10 +193,13 @@
 
         // Only GPU wavefront threads have a token port. For all other types
         // of threads evaluate to true.
-        bool haveTokens = tokenPort ? tokenPort->haveTokens(numLanes) : true;
+        bool haveTokens = true;
 
         switch(curAction->getType()) {
             case Episode::Action::Type::ATOMIC:
+                haveTokens = tokenPort ?
+                    tokenPort->haveTokens(getTokensNeeded()) : true;
+
                 // an atomic action must wait for all previous requests
                 // to complete
                 if (pendingLdStCount == 0 &&
@@ -206,7 +240,7 @@
                 assert(pendingAtomicCount == 0);
 
                 // can't issue if there is a pending fence
-                if (pendingFenceCount > 0 || !haveTokens) {
+                if (pendingFenceCount > 0) {
                     return false;
                 }
 
@@ -215,7 +249,7 @@
                 for (int lane = 0; lane < numLanes; ++lane) {
                     Location loc = curAction->getLocation(lane);
 
-                    if (loc != AddressManager::INVALID_LOCATION) {
+                    if (loc != AddressManager::INVALID_LOCATION && loc >= 0) {
                         Addr addr = addrManager->getAddress(loc);
 
                         if (outstandingLoads.find(addr) !=
@@ -237,6 +271,12 @@
                     }
                 }
 
+                haveTokens = tokenPort ?
+                    tokenPort->haveTokens(getTokensNeeded()) : true;
+                if (!haveTokens) {
+                    return false;
+                }
+
                 return true;
             default:
                 panic("The tester got an invalid action\n");
@@ -250,7 +290,7 @@
     switch(curAction->getType()) {
         case Episode::Action::Type::ATOMIC:
             if (tokenPort) {
-                tokenPort->acquireTokens(numLanes);
+                tokenPort->acquireTokens(getTokensNeeded());
             }
             issueAtomicOps();
             break;
@@ -262,13 +302,13 @@
             break;
         case Episode::Action::Type::LOAD:
             if (tokenPort) {
-                tokenPort->acquireTokens(numLanes);
+                tokenPort->acquireTokens(getTokensNeeded());
             }
             issueLoadOps();
             break;
         case Episode::Action::Type::STORE:
             if (tokenPort) {
-                tokenPort->acquireTokens(numLanes);
+                tokenPort->acquireTokens(getTokensNeeded());
             }
             issueStoreOps();
             break;
diff --git a/src/cpu/testers/gpu_ruby_test/tester_thread.hh b/src/cpu/testers/gpu_ruby_test/tester_thread.hh
index 5612df9..9877d63 100644
--- a/src/cpu/testers/gpu_ruby_test/tester_thread.hh
+++ b/src/cpu/testers/gpu_ruby_test/tester_thread.hh
@@ -178,6 +178,7 @@
     // constraints and is ready to issue
     bool isNextActionReady();
     void issueNextAction();
+    int getTokensNeeded();
 
     // issue Ops to Ruby memory
     // must be implemented by a child class
diff --git a/src/cpu/testers/memtest/SConscript b/src/cpu/testers/memtest/SConscript
index 51e443b..766739d 100644
--- a/src/cpu/testers/memtest/SConscript
+++ b/src/cpu/testers/memtest/SConscript
@@ -28,7 +28,6 @@
 
 Import('*')
 
-#if 'O3CPU' in env['CPU_MODELS']:
 SimObject('MemTest.py', sim_objects=['MemTest'])
 
 Source('memtest.cc')
diff --git a/src/cpu/testers/memtest/memtest.cc b/src/cpu/testers/memtest/memtest.cc
index f229cab..7c256d8 100644
--- a/src/cpu/testers/memtest/memtest.cc
+++ b/src/cpu/testers/memtest/memtest.cc
@@ -88,6 +88,7 @@
       noResponseEvent([this]{ noResponse(); }, name()),
       port("port", *this),
       retryPkt(nullptr),
+      waitResponse(false),
       size(p.size),
       interval(p.interval),
       percentReads(p.percent_reads),
@@ -96,6 +97,7 @@
       requestorId(p.system->getRequestorId(this)),
       blockSize(p.system->cacheLineSize()),
       blockAddrMask(blockSize - 1),
+      sizeBlocks(size / blockSize),
       baseAddr1(p.base_addr_1),
       baseAddr2(p.base_addr_2),
       uncacheAddr(p.uncacheable_base_addr),
@@ -191,6 +193,12 @@
         reschedule(noResponseEvent, clockEdge(progressCheck));
     else if (noResponseEvent.scheduled())
         deschedule(noResponseEvent);
+
+    // schedule the next tick
+    if (waitResponse) {
+        waitResponse = false;
+        schedule(tickEvent, clockEdge(interval));
+    }
 }
 MemTest::MemTestStats::MemTestStats(statistics::Group *parent)
       : statistics::Group(parent),
@@ -205,8 +213,9 @@
 void
 MemTest::tick()
 {
-    // we should never tick if we are waiting for a retry
+    // we should never tick if we are waiting for a retry or response
     assert(!retryPkt);
+    assert(!waitResponse);
 
     // create a new request
     unsigned cmd = random_mt.random(0, 100);
@@ -216,6 +225,13 @@
     Request::Flags flags;
     Addr paddr;
 
+    // halt until we clear outstanding requests, otherwise it won't be able to
+    // find a new unique address
+    if (outstandingAddrs.size() >= sizeBlocks) {
+        waitResponse = true;
+        return;
+    }
+
     // generate a unique address
     do {
         unsigned offset = random_mt.random<unsigned>(0, size - 1);
diff --git a/src/cpu/testers/memtest/memtest.hh b/src/cpu/testers/memtest/memtest.hh
index 2e7824e..2dc1f13 100644
--- a/src/cpu/testers/memtest/memtest.hh
+++ b/src/cpu/testers/memtest/memtest.hh
@@ -120,6 +120,10 @@
 
     PacketPtr retryPkt;
 
+    // Set if reached the maximum number of outstanding requests.
+    // Won't tick until a response is received.
+    bool waitResponse;
+
     const unsigned size;
 
     const Cycles interval;
@@ -142,6 +146,8 @@
 
     const Addr blockAddrMask;
 
+    const unsigned sizeBlocks;
+
     /**
      * Get the block aligned address.
      *
diff --git a/src/cpu/testers/rubytest/SConscript b/src/cpu/testers/rubytest/SConscript
index 87c38cc..cc76d2b 100644
--- a/src/cpu/testers/rubytest/SConscript
+++ b/src/cpu/testers/rubytest/SConscript
@@ -35,7 +35,7 @@
 # When this dependency is removed, the ruby tester should be compiled
 # independently from Ruby
 #
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('RubyTester.py', sim_objects=['RubyTester'])
diff --git a/src/cpu/testers/traffic_gen/SConscript b/src/cpu/testers/traffic_gen/SConscript
index 3ac7872..098bd7b 100644
--- a/src/cpu/testers/traffic_gen/SConscript
+++ b/src/cpu/testers/traffic_gen/SConscript
@@ -65,8 +65,7 @@
 
 # Only build the traffic generator if we have support for protobuf as the
 # tracing relies on it
-if env['HAVE_PROTOBUF']:
-    SimObject('TrafficGen.py', sim_objects=['TrafficGen'])
-    Source('trace_gen.cc')
-    Source('traffic_gen.cc')
+SimObject('TrafficGen.py', sim_objects=['TrafficGen'], tags='protobuf')
+Source('trace_gen.cc', tags='protobuf')
+Source('traffic_gen.cc', tags='protobuf')
 
diff --git a/src/cpu/thread_context.cc b/src/cpu/thread_context.cc
index 40df84a..400bc16 100644
--- a/src/cpu/thread_context.cc
+++ b/src/cpu/thread_context.cc
@@ -41,6 +41,8 @@
 
 #include "cpu/thread_context.hh"
 
+#include <vector>
+
 #include "arch/generic/vec_pred_reg.hh"
 #include "base/logging.hh"
 #include "base/trace.hh"
@@ -63,7 +65,7 @@
     DPRINTF(Context, "Comparing thread contexts\n");
 
     // First loop through the integer registers.
-    for (int i = 0; i < regClasses.at(IntRegClass).size(); ++i) {
+    for (int i = 0; i < regClasses.at(IntRegClass).numRegs(); ++i) {
         RegVal t1 = one->readIntReg(i);
         RegVal t2 = two->readIntReg(i);
         if (t1 != t2)
@@ -72,7 +74,7 @@
     }
 
     // Then loop through the floating point registers.
-    for (int i = 0; i < regClasses.at(FloatRegClass).size(); ++i) {
+    for (int i = 0; i < regClasses.at(FloatRegClass).numRegs(); ++i) {
         RegVal t1 = one->readFloatReg(i);
         RegVal t2 = two->readFloatReg(i);
         if (t1 != t2)
@@ -81,26 +83,38 @@
     }
 
     // Then loop through the vector registers.
-    for (int i = 0; i < regClasses.at(VecRegClass).size(); ++i) {
+    const auto &vec_class = regClasses.at(VecRegClass);
+    std::vector<uint8_t> vec1(vec_class.regBytes());
+    std::vector<uint8_t> vec2(vec_class.regBytes());
+    for (int i = 0; i < vec_class.numRegs(); ++i) {
         RegId rid(VecRegClass, i);
-        const TheISA::VecRegContainer& t1 = one->readVecReg(rid);
-        const TheISA::VecRegContainer& t2 = two->readVecReg(rid);
-        if (t1 != t2)
+
+        one->getReg(rid, vec1.data());
+        two->getReg(rid, vec2.data());
+        if (vec1 != vec2) {
             panic("Vec reg idx %d doesn't match, one: %#x, two: %#x",
-                  i, t1, t2);
+                  i, vec_class.valString(vec1.data()),
+                  vec_class.valString(vec2.data()));
+        }
     }
 
     // Then loop through the predicate registers.
-    for (int i = 0; i < regClasses.at(VecPredRegClass).size(); ++i) {
+    const auto &vec_pred_class = regClasses.at(VecPredRegClass);
+    std::vector<uint8_t> pred1(vec_pred_class.regBytes());
+    std::vector<uint8_t> pred2(vec_pred_class.regBytes());
+    for (int i = 0; i < vec_pred_class.numRegs(); ++i) {
         RegId rid(VecPredRegClass, i);
-        const TheISA::VecPredRegContainer& t1 = one->readVecPredReg(rid);
-        const TheISA::VecPredRegContainer& t2 = two->readVecPredReg(rid);
-        if (t1 != t2)
-            panic("Pred reg idx %d doesn't match, one: %#x, two: %#x",
-                  i, t1, t2);
+
+        one->getReg(rid, pred1.data());
+        two->getReg(rid, pred2.data());
+        if (pred1 != pred2) {
+            panic("Pred reg idx %d doesn't match, one: %s, two: %s",
+                  i, vec_pred_class.valString(pred1.data()),
+                  vec_pred_class.valString(pred2.data()));
+        }
     }
 
-    for (int i = 0; i < regClasses.at(MiscRegClass).size(); ++i) {
+    for (int i = 0; i < regClasses.at(MiscRegClass).numRegs(); ++i) {
         RegVal t1 = one->readMiscRegNoEffect(i);
         RegVal t2 = two->readMiscRegNoEffect(i);
         if (t1 != t2)
@@ -109,7 +123,7 @@
     }
 
     // loop through the Condition Code registers.
-    for (int i = 0; i < regClasses.at(CCRegClass).size(); ++i) {
+    for (int i = 0; i < regClasses.at(CCRegClass).numRegs(); ++i) {
         RegVal t1 = one->readCCReg(i);
         RegVal t2 = two->readCCReg(i);
         if (t1 != t2)
@@ -153,6 +167,50 @@
     getSystemPtr()->threads.quiesceTick(contextId(), resume);
 }
 
+RegVal
+ThreadContext::getReg(const RegId &reg) const
+{
+    return getRegFlat(flattenRegId(reg));
+}
+
+void *
+ThreadContext::getWritableReg(const RegId &reg)
+{
+    return getWritableRegFlat(flattenRegId(reg));
+}
+
+void
+ThreadContext::setReg(const RegId &reg, RegVal val)
+{
+    setRegFlat(flattenRegId(reg), val);
+}
+
+void
+ThreadContext::getReg(const RegId &reg, void *val) const
+{
+    getRegFlat(flattenRegId(reg), val);
+}
+
+void
+ThreadContext::setReg(const RegId &reg, const void *val)
+{
+    setRegFlat(flattenRegId(reg), val);
+}
+
+RegVal
+ThreadContext::getRegFlat(const RegId &reg) const
+{
+    RegVal val;
+    getRegFlat(reg, &val);
+    return val;
+}
+
+void
+ThreadContext::setRegFlat(const RegId &reg, RegVal val)
+{
+    setRegFlat(reg, &val);
+}
+
 void
 serialize(const ThreadContext &tc, CheckpointOut &cp)
 {
@@ -161,7 +219,7 @@
     auto &nc_tc = const_cast<ThreadContext &>(tc);
     const auto &regClasses = nc_tc.getIsaPtr()->regClasses();
 
-    const size_t numFloats = regClasses.at(FloatRegClass).size();
+    const size_t numFloats = regClasses.at(FloatRegClass).numRegs();
     RegVal floatRegs[numFloats];
     for (int i = 0; i < numFloats; ++i)
         floatRegs[i] = tc.readFloatRegFlat(i);
@@ -169,27 +227,27 @@
     // compatibility.
     arrayParamOut(cp, "floatRegs.i", floatRegs, numFloats);
 
-    const size_t numVecs = regClasses.at(VecRegClass).size();
+    const size_t numVecs = regClasses.at(VecRegClass).numRegs();
     std::vector<TheISA::VecRegContainer> vecRegs(numVecs);
     for (int i = 0; i < numVecs; ++i) {
         vecRegs[i] = tc.readVecRegFlat(i);
     }
     SERIALIZE_CONTAINER(vecRegs);
 
-    const size_t numPreds = regClasses.at(VecPredRegClass).size();
+    const size_t numPreds = regClasses.at(VecPredRegClass).numRegs();
     std::vector<TheISA::VecPredRegContainer> vecPredRegs(numPreds);
     for (int i = 0; i < numPreds; ++i) {
-        vecPredRegs[i] = tc.readVecPredRegFlat(i);
+        tc.getRegFlat(RegId(VecPredRegClass, i), &vecPredRegs[i]);
     }
     SERIALIZE_CONTAINER(vecPredRegs);
 
-    const size_t numInts = regClasses.at(IntRegClass).size();
+    const size_t numInts = regClasses.at(IntRegClass).numRegs();
     RegVal intRegs[numInts];
     for (int i = 0; i < numInts; ++i)
         intRegs[i] = tc.readIntRegFlat(i);
     SERIALIZE_ARRAY(intRegs, numInts);
 
-    const size_t numCcs = regClasses.at(CCRegClass).size();
+    const size_t numCcs = regClasses.at(CCRegClass).numRegs();
     if (numCcs) {
         RegVal ccRegs[numCcs];
         for (int i = 0; i < numCcs; ++i)
@@ -207,7 +265,7 @@
 {
     const auto &regClasses = tc.getIsaPtr()->regClasses();
 
-    const size_t numFloats = regClasses.at(FloatRegClass).size();
+    const size_t numFloats = regClasses.at(FloatRegClass).numRegs();
     RegVal floatRegs[numFloats];
     // This is a bit ugly, but needed to maintain backwards
     // compatibility.
@@ -215,27 +273,27 @@
     for (int i = 0; i < numFloats; ++i)
         tc.setFloatRegFlat(i, floatRegs[i]);
 
-    const size_t numVecs = regClasses.at(VecRegClass).size();
+    const size_t numVecs = regClasses.at(VecRegClass).numRegs();
     std::vector<TheISA::VecRegContainer> vecRegs(numVecs);
     UNSERIALIZE_CONTAINER(vecRegs);
     for (int i = 0; i < numVecs; ++i) {
         tc.setVecRegFlat(i, vecRegs[i]);
     }
 
-    const size_t numPreds = regClasses.at(VecPredRegClass).size();
+    const size_t numPreds = regClasses.at(VecPredRegClass).numRegs();
     std::vector<TheISA::VecPredRegContainer> vecPredRegs(numPreds);
     UNSERIALIZE_CONTAINER(vecPredRegs);
     for (int i = 0; i < numPreds; ++i) {
-        tc.setVecPredRegFlat(i, vecPredRegs[i]);
+        tc.setRegFlat(RegId(VecPredRegClass, i), &vecPredRegs[i]);
     }
 
-    const size_t numInts = regClasses.at(IntRegClass).size();
+    const size_t numInts = regClasses.at(IntRegClass).numRegs();
     RegVal intRegs[numInts];
     UNSERIALIZE_ARRAY(intRegs, numInts);
     for (int i = 0; i < numInts; ++i)
         tc.setIntRegFlat(i, intRegs[i]);
 
-    const size_t numCcs = regClasses.at(CCRegClass).size();
+    const size_t numCcs = regClasses.at(CCRegClass).numRegs();
     if (numCcs) {
         RegVal ccRegs[numCcs];
         UNSERIALIZE_ARRAY(ccRegs, numCcs);
diff --git a/src/cpu/thread_context.hh b/src/cpu/thread_context.hh
index e978f37..835ac46 100644
--- a/src/cpu/thread_context.hh
+++ b/src/cpu/thread_context.hh
@@ -142,7 +142,7 @@
 
     virtual CheckerCPU *getCheckerCpuPtr() = 0;
 
-    virtual BaseISA *getIsaPtr() = 0;
+    virtual BaseISA *getIsaPtr() const = 0;
 
     virtual InstDecoder *getDecoderPtr() = 0;
 
@@ -193,36 +193,79 @@
     //
     // New accessors for new decoder.
     //
-    virtual RegVal readIntReg(RegIndex reg_idx) const = 0;
+    virtual RegVal getReg(const RegId &reg) const;
+    virtual void getReg(const RegId &reg, void *val) const;
+    virtual void *getWritableReg(const RegId &reg);
 
-    virtual RegVal readFloatReg(RegIndex reg_idx) const = 0;
+    virtual void setReg(const RegId &reg, RegVal val);
+    virtual void setReg(const RegId &reg, const void *val);
 
-    virtual const TheISA::VecRegContainer&
-        readVecReg(const RegId& reg) const = 0;
-    virtual TheISA::VecRegContainer& getWritableVecReg(const RegId& reg) = 0;
+    RegVal
+    readIntReg(RegIndex reg_idx) const
+    {
+        return getReg(RegId(IntRegClass, reg_idx));
+    }
 
-    virtual RegVal readVecElem(const RegId& reg) const = 0;
+    RegVal
+    readFloatReg(RegIndex reg_idx) const
+    {
+        return getReg(RegId(FloatRegClass, reg_idx));
+    }
 
-    virtual const TheISA::VecPredRegContainer& readVecPredReg(
-            const RegId& reg) const = 0;
-    virtual TheISA::VecPredRegContainer& getWritableVecPredReg(
-            const RegId& reg) = 0;
+    TheISA::VecRegContainer
+    readVecReg(const RegId &reg) const
+    {
+        TheISA::VecRegContainer val;
+        getReg(reg, &val);
+        return val;
+    }
+    TheISA::VecRegContainer&
+    getWritableVecReg(const RegId& reg)
+    {
+        return *(TheISA::VecRegContainer *)getWritableReg(reg);
+    }
 
-    virtual RegVal readCCReg(RegIndex reg_idx) const = 0;
+    RegVal
+    readVecElem(const RegId& reg) const
+    {
+        return getReg(reg);
+    }
 
-    virtual void setIntReg(RegIndex reg_idx, RegVal val) = 0;
+    RegVal
+    readCCReg(RegIndex reg_idx) const
+    {
+        return getReg(RegId(CCRegClass, reg_idx));
+    }
 
-    virtual void setFloatReg(RegIndex reg_idx, RegVal val) = 0;
+    void
+    setIntReg(RegIndex reg_idx, RegVal val)
+    {
+        setReg(RegId(IntRegClass, reg_idx), val);
+    }
 
-    virtual void setVecReg(const RegId& reg,
-            const TheISA::VecRegContainer& val) = 0;
+    void
+    setFloatReg(RegIndex reg_idx, RegVal val)
+    {
+        setReg(RegId(FloatRegClass, reg_idx), val);
+    }
 
-    virtual void setVecElem(const RegId& reg, RegVal val) = 0;
+    void
+    setVecReg(const RegId& reg, const TheISA::VecRegContainer &val)
+    {
+        setReg(reg, &val);
+    }
 
-    virtual void setVecPredReg(const RegId& reg,
-            const TheISA::VecPredRegContainer& val) = 0;
+    void
+    setVecElem(const RegId& reg, RegVal val)
+    {
+        setReg(reg, val);
+    }
 
-    virtual void setCCReg(RegIndex reg_idx, RegVal val) = 0;
+    void
+    setCCReg(RegIndex reg_idx, RegVal val)
+    {
+        setReg(RegId(CCRegClass, reg_idx), val);
+    }
 
     virtual const PCStateBase &pcState() const = 0;
 
@@ -272,32 +315,75 @@
      * serialization code to access all registers.
      */
 
-    virtual RegVal readIntRegFlat(RegIndex idx) const = 0;
-    virtual void setIntRegFlat(RegIndex idx, RegVal val) = 0;
+    virtual RegVal getRegFlat(const RegId &reg) const;
+    virtual void getRegFlat(const RegId &reg, void *val) const = 0;
+    virtual void *getWritableRegFlat(const RegId &reg) = 0;
 
-    virtual RegVal readFloatRegFlat(RegIndex idx) const = 0;
-    virtual void setFloatRegFlat(RegIndex idx, RegVal val) = 0;
+    virtual void setRegFlat(const RegId &reg, RegVal val);
+    virtual void setRegFlat(const RegId &reg, const void *val) = 0;
 
-    virtual const TheISA::VecRegContainer&
-        readVecRegFlat(RegIndex idx) const = 0;
-    virtual TheISA::VecRegContainer& getWritableVecRegFlat(RegIndex idx) = 0;
-    virtual void setVecRegFlat(RegIndex idx,
-            const TheISA::VecRegContainer& val) = 0;
+    RegVal
+    readIntRegFlat(RegIndex idx) const
+    {
+        return getRegFlat(RegId(IntRegClass, idx));
+    }
+    void
+    setIntRegFlat(RegIndex idx, RegVal val)
+    {
+        setRegFlat(RegId(IntRegClass, idx), val);
+    }
 
-    virtual RegVal readVecElemFlat(RegIndex idx,
-            const ElemIndex& elem_idx) const = 0;
-    virtual void setVecElemFlat(RegIndex idx, const ElemIndex& elem_idx,
-            RegVal val) = 0;
+    RegVal
+    readFloatRegFlat(RegIndex idx) const
+    {
+        return getRegFlat(RegId(FloatRegClass, idx));
+    }
+    void
+    setFloatRegFlat(RegIndex idx, RegVal val)
+    {
+        setRegFlat(RegId(FloatRegClass, idx), val);
+    }
 
-    virtual const TheISA::VecPredRegContainer &
-        readVecPredRegFlat(RegIndex idx) const = 0;
-    virtual TheISA::VecPredRegContainer& getWritableVecPredRegFlat(
-            RegIndex idx) = 0;
-    virtual void setVecPredRegFlat(RegIndex idx,
-            const TheISA::VecPredRegContainer& val) = 0;
+    TheISA::VecRegContainer
+    readVecRegFlat(RegIndex idx) const
+    {
+        TheISA::VecRegContainer val;
+        getRegFlat(RegId(VecRegClass, idx), &val);
+        return val;
+    }
+    TheISA::VecRegContainer&
+    getWritableVecRegFlat(RegIndex idx)
+    {
+        return *(TheISA::VecRegContainer *)
+            getWritableRegFlat(RegId(VecRegClass, idx));
+    }
+    void
+    setVecRegFlat(RegIndex idx, const TheISA::VecRegContainer& val)
+    {
+        setRegFlat(RegId(VecRegClass, idx), &val);
+    }
 
-    virtual RegVal readCCRegFlat(RegIndex idx) const = 0;
-    virtual void setCCRegFlat(RegIndex idx, RegVal val) = 0;
+    RegVal
+    readVecElemFlat(RegIndex idx) const
+    {
+        return getRegFlat(RegId(VecElemClass, idx));
+    }
+    void
+    setVecElemFlat(RegIndex idx, RegVal val)
+    {
+        setRegFlat(RegId(VecElemClass, idx), val);
+    }
+
+    RegVal
+    readCCRegFlat(RegIndex idx) const
+    {
+        return getRegFlat(RegId(CCRegClass, idx));
+    }
+    void
+    setCCRegFlat(RegIndex idx, RegVal val)
+    {
+        setRegFlat(RegId(CCRegClass, idx), val);
+    }
     /** @} */
 
     // hardware transactional memory
diff --git a/src/cpu/timing_expr.cc b/src/cpu/timing_expr.cc
index 6cca06a..41868a5 100644
--- a/src/cpu/timing_expr.cc
+++ b/src/cpu/timing_expr.cc
@@ -43,8 +43,7 @@
 {
 
 TimingExprEvalContext::TimingExprEvalContext(const StaticInstPtr &inst_,
-    ThreadContext *thread_,
-    TimingExprLet *let_) :
+    ThreadContext *thread_, TimingExprLet *let_) :
     inst(inst_), thread(thread_), let(let_)
 {
     /* Reserve space to hold the results of evaluating the
@@ -57,25 +56,28 @@
     }
 }
 
-uint64_t TimingExprSrcReg::eval(TimingExprEvalContext &context)
+uint64_t
+TimingExprSrcReg::eval(TimingExprEvalContext &context)
 {
     return context.inst->srcRegIdx(index).index();
 }
 
-uint64_t TimingExprReadIntReg::eval(TimingExprEvalContext &context)
+uint64_t
+TimingExprReadIntReg::eval(TimingExprEvalContext &context)
 {
     return context.thread->readIntReg(reg->eval(context));
 }
 
-uint64_t TimingExprLet::eval(TimingExprEvalContext &context)
+uint64_t
+TimingExprLet::eval(TimingExprEvalContext &context)
 {
-    TimingExprEvalContext new_context(context.inst,
-        context.thread, this);
+    TimingExprEvalContext new_context(context.inst, context.thread, this);
 
     return expr->eval(new_context);
 }
 
-uint64_t TimingExprRef::eval(TimingExprEvalContext &context)
+uint64_t
+TimingExprRef::eval(TimingExprEvalContext &context)
 {
     /* Lookup the result, evaluating if necessary.  @todo, this
      *  should have more error checking */
@@ -87,7 +89,8 @@
     return context.results[index];
 }
 
-uint64_t TimingExprUn::eval(TimingExprEvalContext &context)
+uint64_t
+TimingExprUn::eval(TimingExprEvalContext &context)
 {
     uint64_t arg_value = arg->eval(context);
     uint64_t ret = 0;
@@ -122,7 +125,8 @@
     return ret;
 }
 
-uint64_t TimingExprBin::eval(TimingExprEvalContext &context)
+uint64_t
+TimingExprBin::eval(TimingExprEvalContext &context)
 {
     uint64_t left_value = left->eval(context);
     uint64_t right_value = right->eval(context);
@@ -130,68 +134,69 @@
 
     switch (op) {
       case enums::timingExprAdd:
-          ret = left_value + right_value;
-          break;
+        ret = left_value + right_value;
+        break;
       case enums::timingExprSub:
-          ret = left_value - right_value;
-          break;
+        ret = left_value - right_value;
+        break;
       case enums::timingExprUMul:
-          ret = left_value * right_value;
-          break;
+        ret = left_value * right_value;
+        break;
       case enums::timingExprUDiv:
-          if (right_value != 0) {
-              ret = left_value / right_value;
-          }
-          break;
+        if (right_value != 0) {
+            ret = left_value / right_value;
+        }
+        break;
       case enums::timingExprUCeilDiv:
-          if (right_value != 0) {
-              ret = (left_value + (right_value - 1)) / right_value;
-          }
-          break;
+        if (right_value != 0) {
+            ret = (left_value + (right_value - 1)) / right_value;
+        }
+        break;
       case enums::timingExprSMul:
-          ret = static_cast<int64_t>(left_value) *
-              static_cast<int64_t>(right_value);
-          break;
+        ret = static_cast<int64_t>(left_value) *
+            static_cast<int64_t>(right_value);
+        break;
       case enums::timingExprSDiv:
-          if (right_value != 0) {
-              ret = static_cast<int64_t>(left_value) /
-                  static_cast<int64_t>(right_value);
-          }
-          break;
+        if (right_value != 0) {
+            ret = static_cast<int64_t>(left_value) /
+                static_cast<int64_t>(right_value);
+        }
+        break;
       case enums::timingExprEqual:
-          ret = left_value == right_value;
-          break;
+        ret = left_value == right_value;
+        break;
       case enums::timingExprNotEqual:
-          ret = left_value != right_value;
-          break;
+        ret = left_value != right_value;
+        break;
       case enums::timingExprULessThan:
-          ret = left_value < right_value;
-          break;
+        ret = left_value < right_value;
+        break;
       case enums::timingExprUGreaterThan:
-          ret = left_value > right_value;
-          break;
+        ret = left_value > right_value;
+        break;
       case enums::timingExprSLessThan:
-          ret = static_cast<int64_t>(left_value) <
-              static_cast<int64_t>(right_value);
-          break;
+        ret = static_cast<int64_t>(left_value) <
+            static_cast<int64_t>(right_value);
+        break;
       case enums::timingExprSGreaterThan:
-          ret = static_cast<int64_t>(left_value) >
-              static_cast<int64_t>(right_value);
-          break;
+        ret = static_cast<int64_t>(left_value) >
+            static_cast<int64_t>(right_value);
+        break;
       case enums::timingExprAnd:
-          ret = (left_value != 0) && (right_value != 0);
-          break;
+        ret = (left_value != 0) && (right_value != 0);
+        break;
       case enums::timingExprOr:
-          ret = (left_value != 0) || (right_value != 0);
-          break;
+        ret = (left_value != 0) || (right_value != 0);
+        break;
       default:
-          break;
+        break;
     }
 
     return ret;
 }
 
-uint64_t TimingExprIf::eval(TimingExprEvalContext &context)
+uint64_t
+TimingExprIf::eval(TimingExprEvalContext &context)
 {
     uint64_t cond_value = cond->eval(context);
 
diff --git a/src/cpu/trace/SConscript b/src/cpu/trace/SConscript
index 27d5fbe..ad77009 100644
--- a/src/cpu/trace/SConscript
+++ b/src/cpu/trace/SConscript
@@ -1,12 +1,11 @@
 Import('*')
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 # Only build TraceCPU if we have support for protobuf as TraceCPU relies on it
-if env['HAVE_PROTOBUF']:
-    SimObject('TraceCPU.py', sim_objects=['TraceCPU'])
-    Source('trace_cpu.cc')
+SimObject('TraceCPU.py', sim_objects=['TraceCPU'], tags='protobuf')
+Source('trace_cpu.cc', tags='protobuf')
 
 DebugFlag('TraceCPUData')
 DebugFlag('TraceCPUInst')
diff --git a/src/dev/IntPin.py b/src/dev/IntPin.py
index a6851cc..80618ce 100644
--- a/src/dev/IntPin.py
+++ b/src/dev/IntPin.py
@@ -35,6 +35,17 @@
     def __init__(self, desc):
         super().__init__(INT_SOURCE_ROLE, desc, is_source=True)
 
+# A vector of source pins which might represent a bank of physical pins. Unlike
+# IntSourcePin, each source pin in VectorIntSourcePin can only connect to a
+# single sink pin. VectorIntSourcePin has the same definition as IntSourcePin
+# right now, but will likely be implemented differently in the future.
+# VectorIntSourcePin is defined as its own separate type to differentiate it
+# from IntSourcePin and make it clear to the user how it should be interpreted
+# and used.
+class VectorIntSourcePin(VectorPort):
+    def __init__(self, desc):
+        super().__init__(INT_SOURCE_ROLE, desc, is_source=True)
+
 # Each "physical" pin can be driven by a single source pin since there are no
 # provisions for resolving competing signals running to the same pin.
 class IntSinkPin(Port):
diff --git a/src/cpu/simple/SConsopts b/src/dev/ResetPort.py
similarity index 62%
copy from src/cpu/simple/SConsopts
copy to src/dev/ResetPort.py
index f12fee2..d7140c5 100644
--- a/src/cpu/simple/SConsopts
+++ b/src/dev/ResetPort.py
@@ -1,7 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2022 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -26,6 +23,25 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+from m5.params import Port, VectorPort
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+RESET_REQUEST_ROLE = 'Reset Request'
+RESET_RESPONSE_ROLE = 'Reset Response'
+Port.compat(RESET_REQUEST_ROLE, RESET_RESPONSE_ROLE)
+
+# ResetRequestPort is an artifact request port for reset purpose.
+class ResetRequestPort(Port):
+    def __init__(self, desc):
+        super().__init__(RESET_REQUEST_ROLE, desc, is_source=True)
+
+# ResetResponsePort is an artifact response port for reset purpose.
+# The owner should perform whole reset when receiving a request.
+class ResetResponsePort(Port):
+    def __init__(self, desc):
+        super().__init__(RESET_RESPONSE_ROLE, desc)
+
+# VectorResetRequestPort presents a bank of artifact reset request
+# ports.
+class VectorResetRequestPort(VectorPort):
+    def __init__(self, desc):
+        super().__init__(RESET_REQUEST_ROLE, desc, is_source=True)
diff --git a/src/dev/SConscript b/src/dev/SConscript
index 755ddb5..44a7cc9 100644
--- a/src/dev/SConscript
+++ b/src/dev/SConscript
@@ -38,13 +38,16 @@
 SimObject('IntPin.py', sim_objects=[])
 Source('intpin.cc')
 
+SimObject('ResetPort.py', sim_objects=[])
+Source('reset_port.cc')
+
 DebugFlag('IsaFake')
 DebugFlag('DMA')
 
 SimObject('Platform.py', sim_objects=['Platform'])
 Source('platform.cc')
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 SimObject('BadDevice.py', sim_objects=['BadDevice'])
diff --git a/src/dev/amdgpu/AMDGPU.py b/src/dev/amdgpu/AMDGPU.py
index 9ca9b3b..6afce0f 100644
--- a/src/dev/amdgpu/AMDGPU.py
+++ b/src/dev/amdgpu/AMDGPU.py
@@ -28,8 +28,11 @@
 # POSSIBILITY OF SUCH DAMAGE.
 
 from m5.params import *
+from m5.proxy import *
 from m5.objects.PciDevice import PciDevice
 from m5.objects.PciDevice import PciMemBar, PciMemUpperBar, PciLegacyIoBar
+from m5.objects.Device import DmaDevice, DmaVirtDevice
+from m5.objects.ClockedObject import ClockedObject
 
 # PCI device model for an AMD Vega 10 based GPU. The PCI codes and BARs
 # correspond to a Vega Frontier Edition hardware device. None of the PCI
@@ -71,3 +74,49 @@
     trace_file = Param.String("MMIO trace collected on hardware")
     checkpoint_before_mmios = Param.Bool(False, "Take a checkpoint before the"
                                                 " device begins sending MMIOs")
+
+    # Specific to Vega10: Vega10 has two SDMA engines these do not have any
+    # assigned function and are referenced by ID so they are given the generic
+    # names sdma0, sdma1, ... sdmaN.
+    sdma0 = Param.SDMAEngine("SDMA Engine 0")
+    sdma1 = Param.SDMAEngine("SDMA Engine 1")
+
+    # The cp is needed here to handle certain packets the device may receive.
+    # The config script should not create a new cp here but rather assign the
+    # same cp that is assigned to the Shader SimObject.
+    cp = Param.GPUCommandProcessor(NULL, "Command Processor")
+    pm4_pkt_proc = Param.PM4PacketProcessor("PM4 Packet Processor")
+    memory_manager = Param.AMDGPUMemoryManager("GPU Memory Manager")
+    memories = VectorParam.AbstractMemory([], "All memories in the device")
+    device_ih = Param.AMDGPUInterruptHandler("GPU Interrupt handler")
+
+class SDMAEngine(DmaVirtDevice):
+    type = 'SDMAEngine'
+    cxx_header = "dev/amdgpu/sdma_engine.hh"
+    cxx_class = 'gem5::SDMAEngine'
+
+    gpu_device = Param.AMDGPUDevice(NULL, 'GPU Controller')
+    walker = Param.VegaPagetableWalker("Page table walker")
+
+class PM4PacketProcessor(DmaVirtDevice):
+    type = 'PM4PacketProcessor'
+    cxx_header = "dev/amdgpu/pm4_packet_processor.hh"
+    cxx_class = 'gem5::PM4PacketProcessor'
+
+class AMDGPUMemoryManager(ClockedObject):
+    type = 'AMDGPUMemoryManager'
+    cxx_header = 'dev/amdgpu/memory_manager.hh'
+    cxx_class = 'gem5::AMDGPUMemoryManager'
+
+    port = RequestPort('Memory Port to access VRAM (device memory)')
+    system = Param.System(Parent.any, 'System the dGPU belongs to')
+
+class AMDGPUInterruptHandler(DmaDevice):
+    type = 'AMDGPUInterruptHandler'
+    cxx_header = "dev/amdgpu/interrupt_handler.hh"
+    cxx_class = 'gem5::AMDGPUInterruptHandler'
+
+class AMDGPUSystemHub(DmaDevice):
+    type = 'AMDGPUSystemHub'
+    cxx_class = 'gem5::AMDGPUSystemHub'
+    cxx_header = "dev/amdgpu/system_hub.hh"
diff --git a/src/dev/amdgpu/SConscript b/src/dev/amdgpu/SConscript
index 87a560e..bece7c3 100644
--- a/src/dev/amdgpu/SConscript
+++ b/src/dev/amdgpu/SConscript
@@ -29,13 +29,25 @@
 
 Import('*')
 
-if not env['BUILD_GPU']:
+if not env['CONF']['BUILD_GPU']:
     Return()
 
 # Controllers
-SimObject('AMDGPU.py', sim_objects=['AMDGPUDevice'], tags='x86 isa')
+SimObject('AMDGPU.py', sim_objects=['AMDGPUDevice', 'AMDGPUInterruptHandler',
+                                    'AMDGPUMemoryManager', 'AMDGPUSystemHub',
+                                    'SDMAEngine', 'PM4PacketProcessor'],
+                                    tags='x86 isa')
 
 Source('amdgpu_device.cc', tags='x86 isa')
+Source('amdgpu_vm.cc', tags='x86 isa')
+Source('interrupt_handler.cc', tags='x86 isa')
+Source('memory_manager.cc', tags='x86 isa')
 Source('mmio_reader.cc', tags='x86 isa')
+Source('pm4_packet_processor.cc', tags='x86 isa')
+Source('sdma_engine.cc', tags='x86 isa')
+Source('system_hub.cc', tags='x86 isa')
 
 DebugFlag('AMDGPUDevice', tags='x86 isa')
+DebugFlag('AMDGPUMem', tags='x86 isa')
+DebugFlag('PM4PacketProcessor', tags='x86 isa')
+DebugFlag('SDMAEngine', tags='x86 isa')
diff --git a/src/dev/amdgpu/amdgpu_defines.hh b/src/dev/amdgpu/amdgpu_defines.hh
new file mode 100644
index 0000000..bc6377f
--- /dev/null
+++ b/src/dev/amdgpu/amdgpu_defines.hh
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_AMDGPU_AMDGPU_DEFINES_HH__
+#define __DEV_AMDGPU_AMDGPU_DEFINES_HH__
+
+#include "base/types.hh"
+
+namespace gem5
+{
+
+/* Types of queues supported by device */
+enum QueueType
+{
+    Compute,
+    Gfx,
+    SDMAGfx,
+    SDMAPage,
+    ComputeAQL,
+    InterruptHandler,
+    RLC
+};
+
+// AMD GPUs support 16 different virtual address spaces
+static constexpr int AMDGPU_VM_COUNT = 16;
+
+/* Names of BARs used by the device. */
+constexpr int FRAMEBUFFER_BAR = 0;
+constexpr int DOORBELL_BAR = 2;
+constexpr int MMIO_BAR = 5;
+
+/* By default the X86 kernel expects the vga ROM at 0xc0000. */
+constexpr uint32_t VGA_ROM_DEFAULT = 0xc0000;
+constexpr uint32_t ROM_SIZE = 0x20000;        // 128kB
+
+/* SDMA base, size, mmio offset shift. */
+static constexpr uint32_t SDMA0_BASE  = 0x4980;
+static constexpr uint32_t SDMA1_BASE  = 0x5180;
+static constexpr uint32_t SDMA_SIZE  = 0x800;
+static constexpr uint32_t SDMA_OFFSET_SHIFT  = 2;
+
+/* Interrupt handler base, size, mmio offset shift. */
+static constexpr uint32_t IH_BASE = 0x4280;
+static constexpr uint32_t IH_SIZE = 0x700;
+static constexpr uint32_t IH_OFFSET_SHIFT = 2;
+
+/* Graphics register bus manager base, size, mmio offset shift. */
+static constexpr uint32_t GRBM_BASE  = 0x8000;
+static constexpr uint32_t GRBM_SIZE  = 0x5000;
+static constexpr uint32_t GRBM_OFFSET_SHIFT  = 2;
+
+/* GFX base, size, mmio offset shift. */
+static constexpr uint32_t GFX_BASE  = 0x28000;
+static constexpr uint32_t GFX_SIZE  = 0x17000;
+static constexpr uint32_t GFX_OFFSET_SHIFT  = 2;
+
+/* MMHUB base, size, mmio offset shift. */
+static constexpr uint32_t MMHUB_BASE = 0x68000;
+static constexpr uint32_t MMHUB_SIZE = 0x2120;
+static constexpr uint32_t MMHUB_OFFSET_SHIFT = 2;
+
+/* NBIO base and size. */
+static constexpr uint32_t NBIO_BASE = 0x0;
+static constexpr uint32_t NBIO_SIZE = 0x4280;
+
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_AMDGPU_DEFINES_HH__
diff --git a/src/dev/amdgpu/amdgpu_device.cc b/src/dev/amdgpu/amdgpu_device.cc
index a4edef4..132a81a 100644
--- a/src/dev/amdgpu/amdgpu_device.cc
+++ b/src/dev/amdgpu/amdgpu_device.cc
@@ -34,6 +34,13 @@
 #include <fstream>
 
 #include "debug/AMDGPUDevice.hh"
+#include "dev/amdgpu/amdgpu_vm.hh"
+#include "dev/amdgpu/interrupt_handler.hh"
+#include "dev/amdgpu/pm4_packet_processor.hh"
+#include "dev/amdgpu/sdma_engine.hh"
+#include "dev/hsa/hw_scheduler.hh"
+#include "gpu-compute/gpu_command_processor.hh"
+#include "mem/abstract_mem.hh"
 #include "mem/packet.hh"
 #include "mem/packet_access.hh"
 #include "params/AMDGPUDevice.hh"
@@ -44,8 +51,11 @@
 {
 
 AMDGPUDevice::AMDGPUDevice(const AMDGPUDeviceParams &p)
-    : PciDevice(p), checkpoint_before_mmios(p.checkpoint_before_mmios),
-      init_interrupt_count(0)
+    : PciDevice(p), gpuMemMgr(p.memory_manager), deviceIH(p.device_ih),
+      sdma0(p.sdma0), sdma1(p.sdma1), pm4PktProc(p.pm4_pkt_proc), cp(p.cp),
+      checkpoint_before_mmios(p.checkpoint_before_mmios),
+      init_interrupt_count(0), _lastVMID(0),
+      deviceMem(name() + ".deviceMem", p.memories, false, "", false)
 {
     // Loading the rom binary dumped from hardware.
     std::ifstream romBin;
@@ -53,6 +63,16 @@
     romBin.read((char *)rom.data(), ROM_SIZE);
     romBin.close();
 
+    // System pointer needs to be explicitly set for device memory since
+    // DRAMCtrl uses it to get (1) cache line size and (2) the mem mode.
+    // Note this means the cache line size is system wide.
+    for (auto& m : p.memories) {
+        m->system(p.system);
+
+        // Add to system's device memory map.
+        p.system->addDeviceMemory(gpuMemMgr->getRequestorID(), m);
+    }
+
     if (config.expansionROM) {
         romRange = RangeSize(config.expansionROM, ROM_SIZE);
     } else {
@@ -62,6 +82,15 @@
     if (p.trace_file != "") {
         mmioReader.readMMIOTrace(p.trace_file);
     }
+
+    sdma0->setGPUDevice(this);
+    sdma0->setId(0);
+    sdma1->setGPUDevice(this);
+    sdma1->setId(1);
+    deviceIH->setGPUDevice(this);
+    pm4PktProc->setGPUDevice(this);
+    cp->hsaPacketProc().setGPUDevice(this);
+    cp->setGPUDevice(this);
 }
 
 void
@@ -150,8 +179,14 @@
 {
     DPRINTF(AMDGPUDevice, "Read framebuffer address %#lx\n", offset);
 
+    /* Try MMIO trace for frame writes first. */
     mmioReader.readFromTrace(pkt, FRAMEBUFFER_BAR, offset);
 
+    /* If the driver wrote something, use that value over the trace. */
+    if (frame_regs.find(offset) != frame_regs.end()) {
+        pkt->setUintX(frame_regs[offset], ByteOrder::little);
+    }
+
     /* Handle special counter addresses in framebuffer. */
     if (offset == 0xa28000) {
         /* Counter addresses expect the read to return previous value + 1. */
@@ -175,29 +210,134 @@
 void
 AMDGPUDevice::readMMIO(PacketPtr pkt, Addr offset)
 {
+    Addr aperture = gpuvm.getMmioAperture(offset);
+    Addr aperture_offset = offset - aperture;
+
+    // By default read from MMIO trace. Overwrite the packet for a select
+    // few more dynamic MMIOs.
     DPRINTF(AMDGPUDevice, "Read MMIO %#lx\n", offset);
     mmioReader.readFromTrace(pkt, MMIO_BAR, offset);
+
+    switch (aperture) {
+      case NBIO_BASE:
+        switch (aperture_offset) {
+          // This is a PCIe status register. At some point during driver init
+          // the driver checks that interrupts are enabled. This is only
+          // checked once, so if the MMIO trace does not exactly line up with
+          // what the driver is doing in gem5, this may still have the first
+          // bit zero causing driver to fail. Therefore, we always set this
+          // bit to one as there is no harm to do so.
+          case 0x3c: // mmPCIE_DATA2 << 2
+            uint32_t value = pkt->getLE<uint32_t>() | 0x1;
+            DPRINTF(AMDGPUDevice, "Marking interrupts enabled: %#lx\n", value);
+            pkt->setLE<uint32_t>(value);
+            break;
+        } break;
+      case GRBM_BASE:
+        gpuvm.readMMIO(pkt, aperture_offset >> GRBM_OFFSET_SHIFT);
+        break;
+      case MMHUB_BASE:
+        gpuvm.readMMIO(pkt, aperture_offset >> MMHUB_OFFSET_SHIFT);
+        break;
+      default:
+        break;
+    }
 }
 
 void
 AMDGPUDevice::writeFrame(PacketPtr pkt, Addr offset)
 {
     DPRINTF(AMDGPUDevice, "Wrote framebuffer address %#lx\n", offset);
-    mmioReader.writeFromTrace(pkt, FRAMEBUFFER_BAR, offset);
+
+    Addr aperture = gpuvm.getFrameAperture(offset);
+    Addr aperture_offset = offset - aperture;
+
+    // Record the value
+    frame_regs[offset] = pkt->getUintX(ByteOrder::little);
+    if (aperture == gpuvm.gartBase()) {
+        frame_regs[aperture_offset] = pkt->getLE<uint32_t>();
+        DPRINTF(AMDGPUDevice, "GART translation %p -> %p\n", aperture_offset,
+            bits(frame_regs[aperture_offset], 48, 12));
+        gpuvm.gartTable[aperture_offset] = pkt->getLE<uint32_t>();
+    }
 }
 
 void
 AMDGPUDevice::writeDoorbell(PacketPtr pkt, Addr offset)
 {
     DPRINTF(AMDGPUDevice, "Wrote doorbell %#lx\n", offset);
-    mmioReader.writeFromTrace(pkt, DOORBELL_BAR, offset);
+
+    if (doorbells.find(offset) != doorbells.end()) {
+        QueueType q_type = doorbells[offset];
+        DPRINTF(AMDGPUDevice, "Doorbell offset %p queue: %d\n",
+                              offset, q_type);
+        switch (q_type) {
+          case Compute:
+            pm4PktProc->process(pm4PktProc->getQueue(offset),
+                                pkt->getLE<uint64_t>());
+          break;
+          case Gfx:
+            pm4PktProc->process(pm4PktProc->getQueue(offset, true),
+                                pkt->getLE<uint64_t>());
+          break;
+          case SDMAGfx: {
+            SDMAEngine *sdmaEng = getSDMAEngine(offset);
+            sdmaEng->processGfx(pkt->getLE<uint64_t>());
+          } break;
+          case SDMAPage: {
+            SDMAEngine *sdmaEng = getSDMAEngine(offset);
+            sdmaEng->processPage(pkt->getLE<uint64_t>());
+          } break;
+          case ComputeAQL: {
+            cp->hsaPacketProc().hwScheduler()->write(offset,
+                pkt->getLE<uint64_t>() + 1);
+            pm4PktProc->updateReadIndex(offset, pkt->getLE<uint64_t>() + 1);
+          } break;
+          case InterruptHandler:
+            deviceIH->updateRptr(pkt->getLE<uint32_t>());
+            break;
+          case RLC: {
+            panic("RLC queues not yet supported. Run with the environment "
+                  "variable HSA_ENABLE_SDMA set to False");
+          } break;
+          default:
+            panic("Write to unkown queue type!");
+        }
+    } else {
+        warn("Unknown doorbell offset: %lx\n", offset);
+    }
 }
 
 void
 AMDGPUDevice::writeMMIO(PacketPtr pkt, Addr offset)
 {
+    Addr aperture = gpuvm.getMmioAperture(offset);
+    Addr aperture_offset = offset - aperture;
+
     DPRINTF(AMDGPUDevice, "Wrote MMIO %#lx\n", offset);
-    mmioReader.writeFromTrace(pkt, MMIO_BAR, offset);
+
+    switch (aperture) {
+      /* Write a register to the first System DMA. */
+      case SDMA0_BASE:
+        sdma0->writeMMIO(pkt, aperture_offset >> SDMA_OFFSET_SHIFT);
+        break;
+      /* Write a register to the second System DMA. */
+      case SDMA1_BASE:
+        sdma1->writeMMIO(pkt, aperture_offset >> SDMA_OFFSET_SHIFT);
+        break;
+      /* Write a general register to the graphics register bus manager. */
+      case GRBM_BASE:
+        gpuvm.writeMMIO(pkt, aperture_offset >> GRBM_OFFSET_SHIFT);
+        pm4PktProc->writeMMIO(pkt, aperture_offset >> GRBM_OFFSET_SHIFT);
+        break;
+      /* Write a register to the interrupt handler. */
+      case IH_BASE:
+        deviceIH->writeMMIO(pkt, aperture_offset >> IH_OFFSET_SHIFT);
+        break;
+      default:
+        DPRINTF(AMDGPUDevice, "Unknown MMIO aperture for %#x\n", offset);
+        break;
+    }
 }
 
 Tick
@@ -238,6 +378,8 @@
 
     switch (barnum) {
       case FRAMEBUFFER_BAR:
+          gpuMemMgr->writeRequest(offset, pkt->getPtr<uint8_t>(),
+                                  pkt->getSize());
           writeFrame(pkt, offset);
           break;
       case DOORBELL_BAR:
@@ -254,6 +396,9 @@
     // Reads return 0 by default.
     uint64_t data = pkt->getUintX(ByteOrder::little);
 
+    DPRINTF(AMDGPUDevice, "PCI Write to %#lx data %#lx\n",
+                            pkt->getAddr(), data);
+
     if (data || regs.find(pkt->getAddr()) != regs.end())
         regs[pkt->getAddr()] = data;
 
@@ -262,11 +407,121 @@
     return pioDelay;
 }
 
+uint32_t
+AMDGPUDevice::getRegVal(uint32_t addr)
+{
+    return regs[addr];
+}
+void
+AMDGPUDevice::setRegVal(uint32_t addr, uint32_t value)
+{
+    DPRINTF(AMDGPUDevice, "Setting register 0x%lx to %x\n",
+            addr, value);
+    regs[addr] = value;
+}
+
+void
+AMDGPUDevice::setDoorbellType(uint32_t offset, QueueType qt)
+{
+    DPRINTF(AMDGPUDevice, "Setting doorbell type for %x\n", offset);
+    doorbells[offset] = qt;
+}
+
+void
+AMDGPUDevice::setSDMAEngine(Addr offset, SDMAEngine *eng)
+{
+    sdmaEngs[offset] = eng;
+}
+
+SDMAEngine*
+AMDGPUDevice::getSDMAById(int id)
+{
+    /**
+     * PM4 packets selected SDMAs using an integer ID. This method simply maps
+     * the integer ID to a pointer to the SDMA and checks for invalid IDs.
+     */
+    switch (id) {
+        case 0:
+            return sdma0;
+            break;
+        case 1:
+            return sdma1;
+            break;
+        default:
+            panic("No SDMA with id %d\n", id);
+            break;
+    }
+
+    return nullptr;
+}
+
+SDMAEngine*
+AMDGPUDevice::getSDMAEngine(Addr offset)
+{
+    return sdmaEngs[offset];
+}
+
+void
+AMDGPUDevice::intrPost()
+{
+    PciDevice::intrPost();
+}
+
 void
 AMDGPUDevice::serialize(CheckpointOut &cp) const
 {
     // Serialize the PciDevice base class
     PciDevice::serialize(cp);
+
+    uint64_t regs_size = regs.size();
+    uint64_t doorbells_size = doorbells.size();
+    uint64_t sdma_engs_size = sdmaEngs.size();
+
+    SERIALIZE_SCALAR(regs_size);
+    SERIALIZE_SCALAR(doorbells_size);
+    SERIALIZE_SCALAR(sdma_engs_size);
+
+    // Make a c-style array of the regs to serialize
+    uint32_t reg_addrs[regs_size];
+    uint64_t reg_values[regs_size];
+    uint32_t doorbells_offset[doorbells_size];
+    QueueType doorbells_queues[doorbells_size];
+    uint32_t sdma_engs_offset[sdma_engs_size];
+    int sdma_engs[sdma_engs_size];
+
+    int idx = 0;
+    for (auto & it : regs) {
+        reg_addrs[idx] = it.first;
+        reg_values[idx] = it.second;
+        ++idx;
+    }
+
+    idx = 0;
+    for (auto & it : doorbells) {
+        doorbells_offset[idx] = it.first;
+        doorbells_queues[idx] = it.second;
+        ++idx;
+    }
+
+    idx = 0;
+    for (auto & it : sdmaEngs) {
+        sdma_engs_offset[idx] = it.first;
+        sdma_engs[idx] = it.second == sdma0 ? 0 : 1;
+        ++idx;
+    }
+
+    SERIALIZE_ARRAY(reg_addrs, sizeof(reg_addrs)/sizeof(reg_addrs[0]));
+    SERIALIZE_ARRAY(reg_values, sizeof(reg_values)/sizeof(reg_values[0]));
+    SERIALIZE_ARRAY(doorbells_offset, sizeof(doorbells_offset)/
+        sizeof(doorbells_offset[0]));
+    SERIALIZE_ARRAY(doorbells_queues, sizeof(doorbells_queues)/
+        sizeof(doorbells_queues[0]));
+    SERIALIZE_ARRAY(sdma_engs_offset, sizeof(sdma_engs_offset)/
+        sizeof(sdma_engs_offset[0]));
+    SERIALIZE_ARRAY(sdma_engs, sizeof(sdma_engs)/sizeof(sdma_engs[0]));
+
+    // Serialize the device memory
+    deviceMem.serializeSection(cp, "deviceMem");
 }
 
 void
@@ -274,6 +529,118 @@
 {
     // Unserialize the PciDevice base class
     PciDevice::unserialize(cp);
+
+    uint64_t regs_size = 0;
+    uint64_t doorbells_size = 0;
+    uint64_t sdma_engs_size = 0;
+
+    UNSERIALIZE_SCALAR(regs_size);
+    UNSERIALIZE_SCALAR(doorbells_size);
+    UNSERIALIZE_SCALAR(sdma_engs_size);
+
+    if (regs_size > 0) {
+        uint32_t reg_addrs[regs_size];
+        uint64_t reg_values[regs_size];
+
+        UNSERIALIZE_ARRAY(reg_addrs, sizeof(reg_addrs)/sizeof(reg_addrs[0]));
+        UNSERIALIZE_ARRAY(reg_values,
+                          sizeof(reg_values)/sizeof(reg_values[0]));
+
+        for (int idx = 0; idx < regs_size; ++idx) {
+            regs.insert(std::make_pair(reg_addrs[idx], reg_values[idx]));
+        }
+    }
+
+    if (doorbells_size > 0) {
+        uint32_t doorbells_offset[doorbells_size];
+        QueueType doorbells_queues[doorbells_size];
+
+        UNSERIALIZE_ARRAY(doorbells_offset, sizeof(doorbells_offset)/
+                sizeof(doorbells_offset[0]));
+        UNSERIALIZE_ARRAY(doorbells_queues, sizeof(doorbells_queues)/
+                sizeof(doorbells_queues[0]));
+
+        for (int idx = 0; idx < doorbells_size; ++idx) {
+            regs.insert(std::make_pair(doorbells_offset[idx],
+                      doorbells_queues[idx]));
+            doorbells[doorbells_offset[idx]] = doorbells_queues[idx];
+        }
+    }
+
+    if (sdma_engs_size > 0) {
+        uint32_t sdma_engs_offset[sdma_engs_size];
+        int sdma_engs[sdma_engs_size];
+
+        UNSERIALIZE_ARRAY(sdma_engs_offset, sizeof(sdma_engs_offset)/
+            sizeof(sdma_engs_offset[0]));
+        UNSERIALIZE_ARRAY(sdma_engs, sizeof(sdma_engs)/sizeof(sdma_engs[0]));
+
+        for (int idx = 0; idx < sdma_engs_size; ++idx) {
+            SDMAEngine *sdma = sdma_engs[idx] == 0 ? sdma0 : sdma1;
+            sdmaEngs.insert(std::make_pair(sdma_engs_offset[idx], sdma));
+        }
+    }
+
+    // Unserialize the device memory
+    deviceMem.unserializeSection(cp, "deviceMem");
+}
+
+uint16_t
+AMDGPUDevice::allocateVMID(uint16_t pasid)
+{
+    for (uint16_t vmid = 1; vmid < AMDGPU_VM_COUNT; vmid++) {
+        auto result = usedVMIDs.find(vmid);
+        if (result == usedVMIDs.end()) {
+            idMap.insert(std::make_pair(pasid, vmid));
+            usedVMIDs[vmid] = {};
+            _lastVMID = vmid;
+            return vmid;
+        }
+    }
+    panic("All VMIDs have been assigned");
+}
+
+void
+AMDGPUDevice::deallocateVmid(uint16_t vmid)
+{
+    usedVMIDs.erase(vmid);
+}
+
+void
+AMDGPUDevice::deallocatePasid(uint16_t pasid)
+{
+    auto result = idMap.find(pasid);
+    assert(result != idMap.end());
+    if (result == idMap.end()) return;
+    uint16_t vmid = result->second;
+
+    idMap.erase(result);
+    usedVMIDs.erase(vmid);
+}
+
+void
+AMDGPUDevice::deallocateAllQueues()
+{
+    idMap.erase(idMap.begin(), idMap.end());
+    usedVMIDs.erase(usedVMIDs.begin(), usedVMIDs.end());
+}
+
+void
+AMDGPUDevice::mapDoorbellToVMID(Addr doorbell, uint16_t vmid)
+{
+    doorbellVMIDMap[doorbell] = vmid;
+}
+
+std::unordered_map<uint16_t, std::set<int>>&
+AMDGPUDevice::getUsedVMIDs()
+{
+    return usedVMIDs;
+}
+
+void
+AMDGPUDevice::insertQId(uint16_t vmid, int id)
+{
+    usedVMIDs[vmid].insert(id);
 }
 
 } // namespace gem5
diff --git a/src/dev/amdgpu/amdgpu_device.hh b/src/dev/amdgpu/amdgpu_device.hh
index b97d682..fbb0d1c 100644
--- a/src/dev/amdgpu/amdgpu_device.hh
+++ b/src/dev/amdgpu/amdgpu_device.hh
@@ -35,6 +35,9 @@
 #include <map>
 
 #include "base/bitunion.hh"
+#include "dev/amdgpu/amdgpu_defines.hh"
+#include "dev/amdgpu/amdgpu_vm.hh"
+#include "dev/amdgpu/memory_manager.hh"
 #include "dev/amdgpu/mmio_reader.hh"
 #include "dev/io_device.hh"
 #include "dev/pci/device.hh"
@@ -43,14 +46,8 @@
 namespace gem5
 {
 
-/* Names of BARs used by the device. */
-constexpr int FRAMEBUFFER_BAR = 0;
-constexpr int DOORBELL_BAR = 2;
-constexpr int MMIO_BAR = 5;
-
-/* By default the X86 kernel expects the vga ROM at 0xc0000. */
-constexpr uint32_t VGA_ROM_DEFAULT = 0xc0000;
-constexpr uint32_t ROM_SIZE = 0x20000;        // 128kB
+class AMDGPUInterruptHandler;
+class SDMAEngine;
 
 /**
  * Device model for an AMD GPU. This models the interface between the PCI bus
@@ -85,6 +82,14 @@
     void writeMMIO(PacketPtr pkt, Addr offset);
 
     /**
+     * Structures to hold registers, doorbells, and some frame memory
+     */
+    using GPURegMap = std::unordered_map<uint32_t, uint64_t>;
+    GPURegMap frame_regs;
+    GPURegMap regs;
+    std::unordered_map<uint32_t, QueueType> doorbells;
+
+    /**
      * VGA ROM methods
      */
     AddrRange romRange;
@@ -99,13 +104,38 @@
     AMDMMIOReader mmioReader;
 
     /**
-     * Device registers - Maps register address to register value
+     * Blocks of the GPU
      */
-    std::unordered_map<uint32_t, uint64_t> regs;
+    AMDGPUMemoryManager *gpuMemMgr;
+    AMDGPUInterruptHandler *deviceIH;
+    AMDGPUVM gpuvm;
+    SDMAEngine *sdma0;
+    SDMAEngine *sdma1;
+    std::unordered_map<uint32_t, SDMAEngine *> sdmaEngs;
+    PM4PacketProcessor *pm4PktProc;
+    GPUCommandProcessor *cp;
 
+    /**
+     * Initial checkpoint support variables.
+     */
     bool checkpoint_before_mmios;
     int init_interrupt_count;
 
+    // VMIDs data structures
+    // map of pasids to vmids
+    std::unordered_map<uint16_t, uint16_t> idMap;
+    // map of doorbell offsets to vmids
+    std::unordered_map<Addr, uint16_t> doorbellVMIDMap;
+    // map of vmid to all queue ids using that vmid
+    std::unordered_map<uint16_t, std::set<int>> usedVMIDs;
+    // last vmid allocated by map_process PM4 packet
+    uint16_t _lastVMID;
+
+    /*
+     * Backing store for GPU memory / framebuffer / VRAM
+     */
+    memory::PhysicalMemory deviceMem;
+
   public:
     AMDGPUDevice(const AMDGPUDeviceParams &p);
 
@@ -127,6 +157,45 @@
      */
     void serialize(CheckpointOut &cp) const override;
     void unserialize(CheckpointIn &cp) override;
+
+    /**
+     * Get handles to GPU blocks.
+     */
+    AMDGPUInterruptHandler* getIH() { return deviceIH; }
+    SDMAEngine* getSDMAById(int id);
+    SDMAEngine* getSDMAEngine(Addr offset);
+    AMDGPUVM &getVM() { return gpuvm; }
+    AMDGPUMemoryManager* getMemMgr() { return gpuMemMgr; }
+    GPUCommandProcessor* CP() { return cp; }
+
+    /**
+     * Set handles to GPU blocks.
+     */
+    void setDoorbellType(uint32_t offset, QueueType qt);
+    void setSDMAEngine(Addr offset, SDMAEngine *eng);
+
+    /**
+     * Register value getter/setter. Used by other GPU blocks to change
+     * values from incoming driver/user packets.
+     */
+    uint32_t getRegVal(uint32_t addr);
+    void setRegVal(uint32_t addr, uint32_t value);
+
+    /**
+     * Methods related to translations and system/device memory.
+     */
+    RequestorID vramRequestorId() { return gpuMemMgr->getRequestorID(); }
+
+    /* HW context stuff */
+    uint16_t lastVMID() { return _lastVMID; }
+    uint16_t allocateVMID(uint16_t pasid);
+    void deallocateVmid(uint16_t vmid);
+    void deallocatePasid(uint16_t pasid);
+    void deallocateAllQueues();
+    void mapDoorbellToVMID(Addr doorbell, uint16_t vmid);
+    uint16_t getVMID(Addr doorbell) { return doorbellVMIDMap[doorbell]; }
+    std::unordered_map<uint16_t, std::set<int>>& getUsedVMIDs();
+    void insertQId(uint16_t vmid, int id);
 };
 
 } // namespace gem5
diff --git a/src/dev/amdgpu/amdgpu_vm.cc b/src/dev/amdgpu/amdgpu_vm.cc
new file mode 100644
index 0000000..c0c9209
--- /dev/null
+++ b/src/dev/amdgpu/amdgpu_vm.cc
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dev/amdgpu/amdgpu_vm.hh"
+
+#include "arch/amdgpu/vega/pagetable_walker.hh"
+#include "arch/amdgpu/vega/tlb.hh"
+#include "arch/generic/mmu.hh"
+#include "base/trace.hh"
+#include "debug/AMDGPUDevice.hh"
+#include "dev/amdgpu/amdgpu_defines.hh"
+#include "mem/packet_access.hh"
+
+namespace gem5
+{
+
+AMDGPUVM::AMDGPUVM()
+{
+    // Zero out contexts
+    memset(&vmContext0, 0, sizeof(AMDGPUSysVMContext));
+
+    vmContexts.resize(AMDGPU_VM_COUNT);
+    for (int i = 0; i < AMDGPU_VM_COUNT; ++i) {
+        memset(&vmContexts[0], 0, sizeof(AMDGPUVMContext));
+    }
+}
+
+Addr
+AMDGPUVM::gartBase()
+{
+    return vmContext0.ptBase;
+}
+
+Addr
+AMDGPUVM::gartSize()
+{
+    return vmContext0.ptEnd - vmContext0.ptStart;
+}
+
+void
+AMDGPUVM::readMMIO(PacketPtr pkt, Addr offset)
+{
+    uint32_t value = pkt->getLE<uint32_t>();
+
+    switch (offset) {
+      // MMHUB MMIOs
+      case mmMMHUB_VM_INVALIDATE_ENG17_SEM:
+        DPRINTF(AMDGPUDevice, "Marking invalidate ENG17 SEM acquired\n");
+        pkt->setLE<uint32_t>(1);
+        break;
+      case mmMMHUB_VM_INVALIDATE_ENG17_ACK:
+        // This is only used by driver initialization and only expects an ACK
+        // for VMID 0 which is the first bit in the response.
+        DPRINTF(AMDGPUDevice, "Telling driver invalidate ENG17 is complete\n");
+        pkt->setLE<uint32_t>(1);
+        break;
+      case mmMMHUB_VM_FB_LOCATION_BASE:
+        mmhubBase = ((Addr)bits(value, 23, 0) << 24);
+        DPRINTF(AMDGPUDevice, "MMHUB FB base set to %#x\n", mmhubBase);
+        break;
+      case mmMMHUB_VM_FB_LOCATION_TOP:
+        mmhubTop = ((Addr)bits(value, 23, 0) << 24) | 0xFFFFFFULL;
+        DPRINTF(AMDGPUDevice, "MMHUB FB top set to %#x\n", mmhubTop);
+        break;
+      // GRBM MMIOs
+      case mmVM_INVALIDATE_ENG17_ACK:
+        DPRINTF(AMDGPUDevice, "Overwritting invalidation ENG17 ACK\n");
+        pkt->setLE<uint32_t>(1);
+        break;
+      default:
+        DPRINTF(AMDGPUDevice, "GPUVM read of unknown MMIO %#x\n", offset);
+        break;
+    }
+}
+
+void
+AMDGPUVM::writeMMIO(PacketPtr pkt, Addr offset)
+{
+    switch (offset) {
+      // VMID0 MMIOs
+      case mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32:
+        vmContext0.ptBaseL = pkt->getLE<uint32_t>();
+        // Clear extra bits not part of address
+        vmContext0.ptBaseL = insertBits(vmContext0.ptBaseL, 0, 0, 0);
+        break;
+      case mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32:
+        vmContext0.ptBaseH = pkt->getLE<uint32_t>();
+        break;
+      case mmVM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32:
+        vmContext0.ptStartL = pkt->getLE<uint32_t>();
+        break;
+      case mmVM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32:
+        vmContext0.ptStartH = pkt->getLE<uint32_t>();
+        break;
+      case mmVM_CONTEXT0_PAGE_TABLE_END_ADDR_LO32:
+        vmContext0.ptEndL = pkt->getLE<uint32_t>();
+        break;
+      case mmVM_CONTEXT0_PAGE_TABLE_END_ADDR_HI32:
+        vmContext0.ptEndH = pkt->getLE<uint32_t>();
+        break;
+      case mmMC_VM_AGP_TOP: {
+        uint32_t val = pkt->getLE<uint32_t>();
+        vmContext0.agpTop = (((Addr)bits(val, 23, 0)) << 24) | 0xffffff;
+        } break;
+      case mmMC_VM_AGP_BOT: {
+        uint32_t val = pkt->getLE<uint32_t>();
+        vmContext0.agpBot = ((Addr)bits(val, 23, 0)) << 24;
+        } break;
+      case mmMC_VM_AGP_BASE: {
+        uint32_t val = pkt->getLE<uint32_t>();
+        vmContext0.agpBase = ((Addr)bits(val, 23, 0)) << 24;
+        } break;
+      case mmMC_VM_FB_LOCATION_TOP: {
+        uint32_t val = pkt->getLE<uint32_t>();
+        vmContext0.fbTop = (((Addr)bits(val, 23, 0)) << 24) | 0xffffff;
+        } break;
+      case mmMC_VM_FB_LOCATION_BASE: {
+        uint32_t val = pkt->getLE<uint32_t>();
+        vmContext0.fbBase = ((Addr)bits(val, 23, 0)) << 24;
+        } break;
+      case mmMC_VM_FB_OFFSET: {
+        uint32_t val = pkt->getLE<uint32_t>();
+        vmContext0.fbOffset = ((Addr)bits(val, 23, 0)) << 24;
+        } break;
+      case mmMC_VM_SYSTEM_APERTURE_LOW_ADDR: {
+        uint32_t val = pkt->getLE<uint32_t>();
+        vmContext0.sysAddrL = ((Addr)bits(val, 29, 0)) << 18;
+        } break;
+      case mmMC_VM_SYSTEM_APERTURE_HIGH_ADDR: {
+        uint32_t val = pkt->getLE<uint32_t>();
+        vmContext0.sysAddrH = ((Addr)bits(val, 29, 0)) << 18;
+        } break;
+      default:
+        break;
+    }
+}
+
+void
+AMDGPUVM::registerTLB(VegaISA::GpuTLB *tlb)
+{
+    DPRINTF(AMDGPUDevice, "Registered a TLB with device\n");
+    gpu_tlbs.push_back(tlb);
+}
+
+void
+AMDGPUVM::invalidateTLBs()
+{
+    DPRINTF(AMDGPUDevice, "Invalidating all TLBs\n");
+    for (auto &tlb : gpu_tlbs) {
+        tlb->invalidateAll();
+        DPRINTF(AMDGPUDevice, " ... TLB invalidated\n");
+    }
+}
+
+void
+AMDGPUVM::serialize(CheckpointOut &cp) const
+{
+    Addr vm0PTBase = vmContext0.ptBase;
+    Addr vm0PTStart = vmContext0.ptStart;
+    Addr vm0PTEnd = vmContext0.ptEnd;
+    SERIALIZE_SCALAR(vm0PTBase);
+    SERIALIZE_SCALAR(vm0PTStart);
+    SERIALIZE_SCALAR(vm0PTEnd);
+
+    SERIALIZE_SCALAR(vmContext0.agpBase);
+    SERIALIZE_SCALAR(vmContext0.agpTop);
+    SERIALIZE_SCALAR(vmContext0.agpBot);
+    SERIALIZE_SCALAR(vmContext0.fbBase);
+    SERIALIZE_SCALAR(vmContext0.fbTop);
+    SERIALIZE_SCALAR(vmContext0.fbOffset);
+    SERIALIZE_SCALAR(vmContext0.sysAddrL);
+    SERIALIZE_SCALAR(vmContext0.sysAddrH);
+
+    SERIALIZE_SCALAR(mmhubBase);
+    SERIALIZE_SCALAR(mmhubTop);
+
+    Addr ptBase[AMDGPU_VM_COUNT];
+    Addr ptStart[AMDGPU_VM_COUNT];
+    Addr ptEnd[AMDGPU_VM_COUNT];
+    for (int i = 0; i < AMDGPU_VM_COUNT; i++) {
+        ptBase[i] = vmContexts[i].ptBase;
+        ptStart[i] = vmContexts[i].ptStart;
+        ptEnd[i] = vmContexts[i].ptEnd;
+    }
+    SERIALIZE_ARRAY(ptBase, AMDGPU_VM_COUNT);
+    SERIALIZE_ARRAY(ptStart, AMDGPU_VM_COUNT);
+    SERIALIZE_ARRAY(ptEnd, AMDGPU_VM_COUNT);
+}
+
+void
+AMDGPUVM::unserialize(CheckpointIn &cp)
+{
+    // Unserialize requires fields not be packed
+    Addr vm0PTBase;
+    Addr vm0PTStart;
+    Addr vm0PTEnd;
+    UNSERIALIZE_SCALAR(vm0PTBase);
+    UNSERIALIZE_SCALAR(vm0PTStart);
+    UNSERIALIZE_SCALAR(vm0PTEnd);
+    vmContext0.ptBase = vm0PTBase;
+    vmContext0.ptStart = vm0PTStart;
+    vmContext0.ptEnd = vm0PTEnd;
+
+    UNSERIALIZE_SCALAR(vmContext0.agpBase);
+    UNSERIALIZE_SCALAR(vmContext0.agpTop);
+    UNSERIALIZE_SCALAR(vmContext0.agpBot);
+    UNSERIALIZE_SCALAR(vmContext0.fbBase);
+    UNSERIALIZE_SCALAR(vmContext0.fbTop);
+    UNSERIALIZE_SCALAR(vmContext0.fbOffset);
+    UNSERIALIZE_SCALAR(vmContext0.sysAddrL);
+    UNSERIALIZE_SCALAR(vmContext0.sysAddrH);
+
+    UNSERIALIZE_SCALAR(mmhubBase);
+    UNSERIALIZE_SCALAR(mmhubTop);
+
+    Addr ptBase[AMDGPU_VM_COUNT];
+    Addr ptStart[AMDGPU_VM_COUNT];
+    Addr ptEnd[AMDGPU_VM_COUNT];
+    UNSERIALIZE_ARRAY(ptBase, AMDGPU_VM_COUNT);
+    UNSERIALIZE_ARRAY(ptStart, AMDGPU_VM_COUNT);
+    UNSERIALIZE_ARRAY(ptEnd, AMDGPU_VM_COUNT);
+    for (int i = 0; i < AMDGPU_VM_COUNT; i++) {
+        vmContexts[i].ptBase = ptBase[i];
+        vmContexts[i].ptStart = ptStart[i];
+        vmContexts[i].ptEnd = ptEnd[i];
+    }
+}
+
+void
+AMDGPUVM::AGPTranslationGen::translate(Range &range) const
+{
+    assert(vm->inAGP(range.vaddr));
+
+    Addr next = roundUp(range.vaddr, AMDGPU_AGP_PAGE_SIZE);
+    if (next == range.vaddr)
+        next += AMDGPU_AGP_PAGE_SIZE;
+
+    range.size = std::min(range.size, next - range.vaddr);
+    range.paddr = range.vaddr - vm->getAGPBot() + vm->getAGPBase();
+
+    DPRINTF(AMDGPUDevice, "AMDGPUVM: AGP translation %#lx -> %#lx\n",
+            range.vaddr, range.paddr);
+}
+
+void
+AMDGPUVM::GARTTranslationGen::translate(Range &range) const
+{
+    Addr next = roundUp(range.vaddr, AMDGPU_GART_PAGE_SIZE);
+    if (next == range.vaddr)
+        next += AMDGPU_GART_PAGE_SIZE;
+    range.size = std::min(range.size, next - range.vaddr);
+
+    Addr gart_addr = bits(range.vaddr, 63, 12);
+
+    // This table is a bit hard to iterate over. If we cross a page, the next
+    // PTE is not necessarily the next entry but actually 7 entries away.
+    Addr lsb = bits(gart_addr, 2, 0);
+    gart_addr += lsb * 7;
+
+    // GART is a single level translation, so the value at the "virtual" addr
+    // is the PTE containing the physical address.
+    auto result = vm->gartTable.find(gart_addr);
+    if (result == vm->gartTable.end()) {
+        // There is no reason to fault as there is no recovery mechanism for
+        // invalid GART entries. Simply panic in this case
+        warn("GART translation for %p not found", range.vaddr);
+
+        // Some PM4 packets have register addresses which we ignore. In that
+        // case just return the vaddr rather than faulting.
+        range.paddr = range.vaddr;
+    } else {
+        Addr pte = result->second;
+        Addr lower_bits = bits(range.vaddr, 11, 0);
+        range.paddr = (bits(pte, 47, 12) << 12) | lower_bits;
+    }
+
+    DPRINTF(AMDGPUDevice, "AMDGPUVM: GART translation %#lx -> %#lx\n",
+            range.vaddr, range.paddr);
+}
+
+void
+AMDGPUVM::MMHUBTranslationGen::translate(Range &range) const
+{
+    assert(vm->inMMHUB(range.vaddr));
+
+    Addr next = roundUp(range.vaddr, AMDGPU_MMHUB_PAGE_SIZE);
+    if (next == range.vaddr)
+        next += AMDGPU_MMHUB_PAGE_SIZE;
+
+    range.size = std::min(range.size, next - range.vaddr);
+    range.paddr = range.vaddr - vm->getMMHUBBase();
+
+    DPRINTF(AMDGPUDevice, "AMDGPUVM: MMHUB translation %#lx -> %#lx\n",
+            range.vaddr, range.paddr);
+}
+
+void
+AMDGPUVM::UserTranslationGen::translate(Range &range) const
+{
+    // Get base address of the page table for this vmid
+    Addr base = vm->getPageTableBase(vmid);
+    Addr start = vm->getPageTableStart(vmid);
+    DPRINTF(AMDGPUDevice, "User tl base %#lx start %#lx walker %p\n",
+            base, start, walker);
+
+    bool dummy;
+    unsigned logBytes;
+    Addr paddr = range.vaddr;
+    Fault fault = walker->startFunctional(base, paddr, logBytes,
+                                          BaseMMU::Mode::Read, dummy);
+    if (fault != NoFault) {
+        fatal("User translation fault");
+    }
+
+    // GPU page size is variable. Use logBytes to determine size.
+    const Addr page_size = 1 << logBytes;
+    Addr next = roundUp(range.vaddr, page_size);
+    if (next == range.vaddr)
+        // We don't know the size of the next page, use default.
+        next += AMDGPU_USER_PAGE_SIZE;
+
+    range.size = std::min(range.size, next - range.vaddr);
+    range.paddr = paddr;
+}
+
+} // namespace gem5
diff --git a/src/dev/amdgpu/amdgpu_vm.hh b/src/dev/amdgpu/amdgpu_vm.hh
new file mode 100644
index 0000000..8df169b
--- /dev/null
+++ b/src/dev/amdgpu/amdgpu_vm.hh
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_AMDGPU_AMDGPU_VM_HH__
+#define __DEV_AMDGPU_AMDGPU_VM_HH__
+
+#include <vector>
+
+#include "arch/amdgpu/vega/pagetable_walker.hh"
+#include "base/intmath.hh"
+#include "dev/amdgpu/amdgpu_defines.hh"
+#include "mem/packet.hh"
+#include "mem/translation_gen.hh"
+#include "sim/serialize.hh"
+
+/**
+ * MMIO offsets for graphics register bus manager (GRBM). These values were
+ * taken from linux header files. The header files can be found here:
+ *
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *      drivers/gpu/drm/amd/include/ asic_reg/gc/gc_9_0_offset.h
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *      drivers/gpu/drm/amd/include/ asic_reg/mmhub/mmhub_1_0_offset.h
+ */
+
+#define mmVM_INVALIDATE_ENG17_ACK                                     0x08c6
+#define mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32                       0x08eb
+#define mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32                       0x08ec
+#define mmVM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32                      0x090b
+#define mmVM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32                      0x090c
+#define mmVM_CONTEXT0_PAGE_TABLE_END_ADDR_LO32                        0x092b
+#define mmVM_CONTEXT0_PAGE_TABLE_END_ADDR_HI32                        0x092c
+
+#define mmMC_VM_FB_OFFSET                                             0x096b
+#define mmMC_VM_FB_LOCATION_BASE                                      0x0980
+#define mmMC_VM_FB_LOCATION_TOP                                       0x0981
+#define mmMC_VM_AGP_TOP                                               0x0982
+#define mmMC_VM_AGP_BOT                                               0x0983
+#define mmMC_VM_AGP_BASE                                              0x0984
+#define mmMC_VM_SYSTEM_APERTURE_LOW_ADDR                              0x0985
+#define mmMC_VM_SYSTEM_APERTURE_HIGH_ADDR                             0x0986
+
+#define mmMMHUB_VM_INVALIDATE_ENG17_SEM                               0x06e2
+#define mmMMHUB_VM_INVALIDATE_ENG17_REQ                               0x06f4
+#define mmMMHUB_VM_INVALIDATE_ENG17_ACK                               0x0706
+#define mmMMHUB_VM_FB_LOCATION_BASE                                   0x082c
+#define mmMMHUB_VM_FB_LOCATION_TOP                                    0x082d
+
+// AMD GPUs support 16 different virtual address spaces
+static constexpr int AMDGPU_VM_COUNT = 16;
+
+// These apertures have a fixed page size
+static constexpr int AMDGPU_AGP_PAGE_SIZE = 4096;
+static constexpr int AMDGPU_GART_PAGE_SIZE = 4096;
+static constexpr int AMDGPU_MMHUB_PAGE_SIZE = 4096;
+
+// Vega page size can be any power of 2 between 4kB and 1GB.
+static constexpr int AMDGPU_USER_PAGE_SIZE = 4096;
+
+namespace gem5
+{
+
+class AMDGPUVM : public Serializable
+{
+  private:
+    typedef struct GEM5_PACKED
+    {
+        // Page table addresses: from (Base + Start) to (End)
+        union
+        {
+            struct
+            {
+                uint32_t ptBaseL;
+                uint32_t ptBaseH;
+            };
+            Addr ptBase;
+        };
+        union
+        {
+            struct
+            {
+                uint32_t ptStartL;
+                uint32_t ptStartH;
+            };
+            Addr ptStart;
+        };
+        union
+        {
+            struct
+            {
+                uint32_t ptEndL;
+                uint32_t ptEndH;
+            };
+            Addr ptEnd;
+        };
+    } AMDGPUVMContext;
+
+    typedef struct AMDGPUSysVMContext : AMDGPUVMContext
+    {
+        Addr agpBase;
+        Addr agpTop;
+        Addr agpBot;
+        Addr fbBase;
+        Addr fbTop;
+        Addr fbOffset;
+        Addr sysAddrL;
+        Addr sysAddrH;
+    } AMDGPUSysVMContext;
+
+    AMDGPUSysVMContext vmContext0;
+    std::vector<AMDGPUVMContext> vmContexts;
+
+    // MMHUB aperture. These addresses mirror the framebuffer, so addresses
+    // can be calculated by subtracting the base address.
+    uint64_t mmhubBase = 0x0;
+    uint64_t mmhubTop = 0x0;
+
+    /**
+     * List of TLBs associated with the GPU device. This is used for flushing
+     * the TLBs upon a driver request.
+     */
+    std::vector<VegaISA::GpuTLB *> gpu_tlbs;
+
+  public:
+    AMDGPUVM();
+
+    /**
+     * Return base address of GART table in framebuffer.
+     */
+    Addr gartBase();
+    /**
+     * Return size of GART in number of PTEs.
+     */
+    Addr gartSize();
+
+    /**
+     * Copy of GART table. Typically resides in device memory, however we use
+     * a copy in gem5 to simplify the interface.
+     */
+    std::unordered_map<uint64_t, uint32_t> gartTable;
+
+    void readMMIO(PacketPtr pkt, Addr offset);
+    void writeMMIO(PacketPtr pkt, Addr offset);
+
+    /**
+     * Methods for resolving apertures
+     */
+    bool
+    inAGP(Addr vaddr)
+    {
+        return ((vaddr >= vmContext0.agpBot) && (vaddr <= vmContext0.agpTop));
+    }
+
+    Addr getAGPBot() { return vmContext0.agpBot; }
+    Addr getAGPTop() { return vmContext0.agpTop; }
+    Addr getAGPBase() { return vmContext0.agpBase; }
+
+    bool
+    inMMHUB(Addr vaddr)
+    {
+        return ((vaddr >= getMMHUBBase()) && (vaddr <= getMMHUBTop()));
+    }
+
+    Addr getMMHUBBase() { return mmhubBase; }
+    Addr getMMHUBTop() { return mmhubTop; }
+
+    bool
+    inFB(Addr vaddr)
+    {
+        return ((vaddr >= vmContext0.fbBase) && (vaddr <= vmContext0.fbTop));
+    }
+
+    Addr getFBBase() { return vmContext0.fbBase; }
+    Addr getFBTop() { return vmContext0.fbTop; }
+    Addr getFBOffset() { return vmContext0.fbOffset; }
+
+    bool
+    inSys(Addr vaddr)
+    {
+        return ((vaddr >= vmContext0.sysAddrL) &&
+                (vaddr <= vmContext0.sysAddrH));
+    }
+
+    Addr getSysAddrRangeLow () { return vmContext0.sysAddrL; }
+    Addr getSysAddrRangeHigh () { return vmContext0.sysAddrH; }
+
+    Addr
+    getMmioAperture(Addr addr)
+    {
+        // Aperture ranges:
+        // NBIO               0x0     - 0x4280
+        // IH                 0x4280  - 0x4980
+        // SDMA0              0x4980  - 0x5180
+        // SDMA1              0x5180  - 0x5980
+        // GRBM               0x8000  - 0xD000
+        // GFX                0x28000 - 0x3F000
+        // MMHUB              0x68000 - 0x6a120
+
+        if (IH_BASE <= addr && addr < IH_BASE + IH_SIZE)
+            return IH_BASE;
+        else if (SDMA0_BASE <= addr && addr < SDMA0_BASE + SDMA_SIZE)
+            return SDMA0_BASE;
+        else if (SDMA1_BASE <= addr && addr < SDMA1_BASE + SDMA_SIZE)
+            return SDMA1_BASE;
+        else if (GRBM_BASE <= addr && addr < GRBM_BASE + GRBM_SIZE)
+            return GRBM_BASE;
+        else if (GFX_BASE <= addr && addr < GFX_BASE + GFX_SIZE)
+            return GFX_BASE;
+        else if (MMHUB_BASE <= addr && addr < MMHUB_BASE + MMHUB_SIZE)
+            return MMHUB_BASE;
+        else {
+            warn_once("Accessing unsupported MMIO aperture! Assuming NBIO\n");
+            return NBIO_BASE;
+        }
+
+    }
+
+    // Gettig mapped aperture base addresses
+    Addr
+    getFrameAperture(Addr addr)
+    {
+        if (addr < gartBase()) {
+            warn_once("Accessing unsupported frame apperture!\n");
+            return ~0;
+        } else if (gartBase() <= addr && addr < (gartBase() + gartSize())) {
+            return gartBase();
+        } else {
+            warn_once("Accessing unsupported frame apperture!\n");
+            return ~0;
+        }
+
+    }
+
+    /**
+     * Page table base/start accessors for user VMIDs.
+     */
+    void
+    setPageTableBase(uint16_t vmid, Addr ptBase)
+    {
+        vmContexts[vmid].ptBase = ptBase;
+    }
+
+    Addr
+    getPageTableBase(uint16_t vmid)
+    {
+        assert(vmid > 0 && vmid < vmContexts.size());
+        return vmContexts[vmid].ptBase;
+    }
+
+    Addr
+    getPageTableStart(uint16_t vmid)
+    {
+        assert(vmid > 0 && vmid < vmContexts.size());
+        return vmContexts[vmid].ptStart;
+    }
+
+    /**
+     * Control methods for TLBs associated with the GPU device.
+     */
+    void registerTLB(VegaISA::GpuTLB *tlb);
+    void invalidateTLBs();
+
+
+    void serialize(CheckpointOut &cp) const override;
+    void unserialize(CheckpointIn &cp) override;
+
+    /**
+     * Translation range generators
+     *
+     * AGP - Legacy interface to device memory. Addr range is set via MMIO
+     * GART - Legacy privledged translation table. Table in device memory
+     * MMHUB - Shadow range of VRAM
+     */
+    class AGPTranslationGen : public TranslationGen
+    {
+      private:
+        AMDGPUVM *vm;
+
+        void translate(Range &range) const override;
+
+      public:
+        AGPTranslationGen(AMDGPUVM *_vm, Addr vaddr, Addr size)
+            : TranslationGen(vaddr, size), vm(_vm)
+        {}
+    };
+
+    class GARTTranslationGen : public TranslationGen
+    {
+      private:
+        AMDGPUVM *vm;
+
+        void translate(Range &range) const override;
+
+      public:
+        GARTTranslationGen(AMDGPUVM *_vm, Addr vaddr, Addr size)
+            : TranslationGen(vaddr, size), vm(_vm)
+        {}
+    };
+
+    class MMHUBTranslationGen : public TranslationGen
+    {
+      private:
+        AMDGPUVM *vm;
+
+        void translate(Range &range) const override;
+
+      public:
+        MMHUBTranslationGen(AMDGPUVM *_vm, Addr vaddr, Addr size)
+            : TranslationGen(vaddr, size), vm(_vm)
+        {}
+    };
+
+    class UserTranslationGen : public TranslationGen
+    {
+      private:
+        AMDGPUVM *vm;
+        VegaISA::Walker *walker;
+        int vmid;
+
+        void translate(Range &range) const override;
+
+      public:
+        UserTranslationGen(AMDGPUVM *_vm, VegaISA::Walker *_walker, int _vmid,
+                           Addr vaddr, Addr size)
+            : TranslationGen(vaddr, size), vm(_vm), walker(_walker),
+              vmid(_vmid)
+        {}
+    };
+};
+
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_AMDGPU_VM_HH__
diff --git a/src/dev/amdgpu/ih_mmio.hh b/src/dev/amdgpu/ih_mmio.hh
new file mode 100644
index 0000000..b0c1e45
--- /dev/null
+++ b/src/dev/amdgpu/ih_mmio.hh
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_AMDGPU_IH_MMIO_HH__
+#define __DEV_AMDGPU_IH_MMIO_HH__
+
+/**
+ * MMIO offsets for interrupt handler. These values were taken from the linux
+ * header for IH. The header files can be found here:
+ *
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *      drivers/gpu/drm/amd/include/asic_reg/oss/osssys_4_0_offset.h
+ */
+#define mmIH_RB_CNTL                                            0x0080
+#define mmIH_RB_BASE                                            0x0081
+#define mmIH_RB_BASE_HI                                         0x0082
+#define mmIH_RB_RPTR                                            0x0083
+#define mmIH_RB_WPTR                                            0x0084
+#define mmIH_RB_WPTR_ADDR_HI                                    0x0085
+#define mmIH_RB_WPTR_ADDR_LO                                    0x0086
+#define mmIH_DOORBELL_RPTR                                      0x0087
+
+#endif // __DEV_AMDGPU_IH_MMIO_HH__
diff --git a/src/dev/amdgpu/interrupt_handler.cc b/src/dev/amdgpu/interrupt_handler.cc
new file mode 100644
index 0000000..36c9b04
--- /dev/null
+++ b/src/dev/amdgpu/interrupt_handler.cc
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "dev/amdgpu/interrupt_handler.hh"
+
+#include "debug/AMDGPUDevice.hh"
+#include "dev/amdgpu/ih_mmio.hh"
+#include "mem/packet_access.hh"
+
+// For generating interrupts, the object causing interrupt communicates with
+// the Interrupt Handler (IH), which submits a 256-bit Interrupt packet to the
+// system memory. The location where the IH submits the packet is the
+// IH Ring buffer in the system memory. The IH updates the Write Pointer
+// and the host consumes the ring buffer and once done, updates the Read
+// Pointer through the doorbell.
+
+// IH_RB_BaseAddr, IH_RB_WptrAddr (Lo/Hi), IH_RB_RptrAddr (Lo/Hi), etc. are
+// not GART addresses but system dma addresses and thus don't require
+// translations through the GART table.
+
+namespace gem5
+{
+
+AMDGPUInterruptHandler::AMDGPUInterruptHandler(
+                                       const AMDGPUInterruptHandlerParams &p)
+    : DmaDevice(p)
+{
+    memset(&regs, 0, sizeof(AMDGPUIHRegs));
+}
+
+AddrRangeList
+AMDGPUInterruptHandler::getAddrRanges() const
+{
+    AddrRangeList ranges;
+    return ranges;
+}
+
+void
+AMDGPUInterruptHandler::intrPost()
+{
+    if (gpuDevice)
+        gpuDevice->intrPost();
+}
+
+void
+AMDGPUInterruptHandler::prepareInterruptCookie(ContextID cntxt_id,
+                                                uint32_t ring_id,
+                                                uint32_t client_id,
+                                                uint32_t source_id)
+{
+    /**
+     * Setup the fields in the interrupt cookie (see header file for more
+     * detail on the fields). The timestamp here is a bogus value. It seems
+     * the driver does not really care what this value is. Additionally the
+     * model does not currently have anything to keep track of time. It is
+     * possible that tick/cycle count can be used in the future if this ends
+     * up being important. The remaining fields are passed from whichever
+     * block is sending the interrupt.
+     */
+    AMDGPUInterruptCookie *cookie = new AMDGPUInterruptCookie();
+    memset(cookie, 0, sizeof(AMDGPUInterruptCookie));
+    cookie->timestamp_Lo = 0x40;
+    cookie->clientId = client_id;
+    cookie->sourceId = source_id;
+    cookie->ringId = ring_id;
+    cookie->source_data_dw1 = cntxt_id;
+    interruptQueue.push(cookie);
+}
+
+void
+AMDGPUInterruptHandler::DmaEvent::process()
+{
+    if (data == 1) {
+        DPRINTF(AMDGPUDevice, "Completed interrupt cookie write\n");
+        deviceIh->submitWritePointer();
+    } else if (data == 2) {
+        DPRINTF(AMDGPUDevice, "Completed interrupt write pointer update\n");
+        deviceIh->intrPost();
+    } else {
+        fatal("Interrupt Handler DMA event returned bad value: %d\n", data);
+    }
+}
+
+void
+AMDGPUInterruptHandler::submitWritePointer()
+{
+    uint8_t *dataPtr = new uint8_t[sizeof(uint32_t)];
+    regs.IH_Wptr += sizeof(AMDGPUInterruptCookie);
+    Addr paddr = regs.WptrAddr;
+    std::memcpy(dataPtr, &regs.IH_Wptr, sizeof(uint32_t));
+
+    dmaEvent = new AMDGPUInterruptHandler::DmaEvent(this, 2);
+    dmaWrite(paddr, sizeof(uint32_t), dmaEvent, dataPtr);
+}
+
+void
+AMDGPUInterruptHandler::submitInterruptCookie()
+{
+    assert(!interruptQueue.empty());
+    auto cookie = interruptQueue.front();
+    size_t cookieSize = sizeof(AMDGPUInterruptCookie);
+
+    uint8_t *dataPtr = new uint8_t[cookieSize];
+    std::memcpy(dataPtr, cookie, cookieSize);
+    Addr paddr = regs.baseAddr + regs.IH_Wptr;
+
+    DPRINTF(AMDGPUDevice, "InterruptHandler rptr: 0x%x wptr: 0x%x\n",
+                          regs.IH_Rptr, regs.IH_Wptr);
+    dmaEvent = new AMDGPUInterruptHandler::DmaEvent(this, 1);
+    dmaWrite(paddr, cookieSize, dmaEvent, dataPtr);
+
+    interruptQueue.pop();
+}
+
+void
+AMDGPUInterruptHandler::writeMMIO(PacketPtr pkt, Addr mmio_offset)
+{
+    switch (mmio_offset) {
+      case mmIH_RB_CNTL:
+        setCntl(pkt->getLE<uint32_t>());
+        break;
+      case mmIH_RB_BASE:
+        setBase(pkt->getLE<uint32_t>());
+        break;
+      case mmIH_RB_BASE_HI:
+        setBaseHi(pkt->getLE<uint32_t>());
+        break;
+      case mmIH_RB_RPTR:
+        setRptr(pkt->getLE<uint32_t>());
+        break;
+      case mmIH_RB_WPTR:
+        setWptr(pkt->getLE<uint32_t>());
+        break;
+      case mmIH_RB_WPTR_ADDR_LO:
+        setWptrAddrLo(pkt->getLE<uint32_t>());
+        break;
+      case mmIH_RB_WPTR_ADDR_HI:
+        setWptrAddrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmIH_DOORBELL_RPTR:
+        setDoorbellOffset(pkt->getLE<uint32_t>());
+        if (bits(pkt->getLE<uint32_t>(), 28, 28)) {
+            gpuDevice->setDoorbellType(getDoorbellOffset() << 2,
+                                       InterruptHandler);
+        }
+        break;
+      default:
+        DPRINTF(AMDGPUDevice, "IH Unknown MMIO %#x\n", mmio_offset);
+        break;
+    }
+}
+
+void
+AMDGPUInterruptHandler::setCntl(const uint32_t &data)
+{
+    regs.IH_Cntl = data;
+}
+
+void
+AMDGPUInterruptHandler::setBase(const uint32_t &data)
+{
+    regs.IH_Base = data << 8;
+    regs.baseAddr |= regs.IH_Base;
+}
+
+void
+AMDGPUInterruptHandler::setBaseHi(const uint32_t &data)
+{
+    regs.IH_Base_Hi = data;
+    regs.baseAddr |= ((uint64_t)regs.IH_Base_Hi) << 32;
+}
+
+void
+AMDGPUInterruptHandler::setRptr(const uint32_t &data)
+{
+    regs.IH_Rptr = data;
+}
+
+void
+AMDGPUInterruptHandler::setWptr(const uint32_t &data)
+{
+    regs.IH_Wptr = data;
+}
+
+void
+AMDGPUInterruptHandler::setWptrAddrLo(const uint32_t &data)
+{
+    regs.IH_Wptr_Addr_Lo = data;
+    regs.WptrAddr |= regs.IH_Wptr_Addr_Lo;
+}
+
+void
+AMDGPUInterruptHandler::setWptrAddrHi(const uint32_t &data)
+{
+    regs.IH_Wptr_Addr_Hi = data;
+    regs.WptrAddr |= ((uint64_t)regs.IH_Wptr_Addr_Hi) << 32;
+}
+
+void
+AMDGPUInterruptHandler::setDoorbellOffset(const uint32_t &data)
+{
+    regs.IH_Doorbell = data & 0x3ffffff;
+}
+
+void
+AMDGPUInterruptHandler::updateRptr(const uint32_t &data)
+{
+    regs.IH_Rptr = data; // update ring buffer rptr offset
+}
+
+void
+AMDGPUInterruptHandler::serialize(CheckpointOut &cp) const
+{
+    uint32_t ih_cntl = regs.IH_Cntl;
+    uint32_t ih_base = regs.IH_Base;
+    uint32_t ih_base_hi = regs.IH_Base_Hi;
+    Addr ih_baseAddr = regs.baseAddr;
+    uint32_t ih_rptr = regs.IH_Rptr;
+    uint32_t ih_wptr = regs.IH_Wptr;
+    uint32_t ih_wptr_addr_lo = regs.IH_Wptr_Addr_Lo;
+    uint32_t ih_wptr_addr_hi = regs.IH_Wptr_Addr_Hi;
+    Addr ih_wptrAddr = regs.WptrAddr;
+    uint32_t ih_doorbellOffset = regs.IH_Doorbell;
+
+    SERIALIZE_SCALAR(ih_cntl);
+    SERIALIZE_SCALAR(ih_base);
+    SERIALIZE_SCALAR(ih_base_hi);
+    SERIALIZE_SCALAR(ih_baseAddr);
+    SERIALIZE_SCALAR(ih_rptr);
+    SERIALIZE_SCALAR(ih_wptr);
+    SERIALIZE_SCALAR(ih_wptr_addr_lo);
+    SERIALIZE_SCALAR(ih_wptr_addr_hi);
+    SERIALIZE_SCALAR(ih_wptrAddr);
+    SERIALIZE_SCALAR(ih_doorbellOffset);
+}
+
+void
+AMDGPUInterruptHandler::unserialize(CheckpointIn &cp)
+{
+    uint32_t ih_cntl;
+    uint32_t ih_base;
+    uint32_t ih_base_hi;
+    Addr ih_baseAddr;
+    uint32_t ih_rptr;
+    uint32_t ih_wptr;
+    uint32_t ih_wptr_addr_lo;
+    uint32_t ih_wptr_addr_hi;
+    Addr ih_wptrAddr;
+    uint32_t ih_doorbellOffset;
+
+    UNSERIALIZE_SCALAR(ih_cntl);
+    UNSERIALIZE_SCALAR(ih_base);
+    UNSERIALIZE_SCALAR(ih_base_hi);
+    UNSERIALIZE_SCALAR(ih_baseAddr);
+    UNSERIALIZE_SCALAR(ih_rptr);
+    UNSERIALIZE_SCALAR(ih_wptr);
+    UNSERIALIZE_SCALAR(ih_wptr_addr_lo);
+    UNSERIALIZE_SCALAR(ih_wptr_addr_hi);
+    UNSERIALIZE_SCALAR(ih_wptrAddr);
+    UNSERIALIZE_SCALAR(ih_doorbellOffset);
+
+    regs.IH_Cntl = ih_cntl;
+    regs.IH_Base = ih_base;
+    regs.IH_Base_Hi = ih_base_hi;
+    regs.baseAddr = ih_baseAddr;
+    regs.IH_Rptr = ih_rptr;
+    regs.IH_Wptr = ih_wptr;
+    regs.IH_Wptr_Addr_Lo = ih_wptr_addr_lo;
+    regs.IH_Wptr_Addr_Hi = ih_wptr_addr_hi;
+    regs.WptrAddr = ih_wptrAddr;
+    regs.IH_Doorbell = ih_doorbellOffset;
+}
+
+} // namespace gem5
diff --git a/src/dev/amdgpu/interrupt_handler.hh b/src/dev/amdgpu/interrupt_handler.hh
new file mode 100644
index 0000000..5e5175f
--- /dev/null
+++ b/src/dev/amdgpu/interrupt_handler.hh
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DEV_AMDGPU_INTERRUPT_HANDLER__
+#define __DEV_AMDGPU_INTERRUPT_HANDLER__
+
+#include <bitset>
+#include <iostream>
+#include <queue>
+#include <vector>
+
+#include "base/addr_range.hh"
+#include "base/flags.hh"
+#include "base/types.hh"
+#include "dev/amdgpu/amdgpu_device.hh"
+#include "dev/dma_device.hh"
+#include "params/AMDGPUInterruptHandler.hh"
+
+namespace gem5
+{
+
+/**
+ * Defines from driver code. Taken from
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *     drivers/gpu/drm/amd/include/soc15_ih_clientid.h
+ */
+enum soc15_ih_clientid
+{
+    SOC15_IH_CLIENTID_RLC       = 0x07,
+    SOC15_IH_CLIENTID_SDMA0     = 0x08,
+    SOC15_IH_CLIENTID_SDMA1     = 0x09
+};
+
+enum ihSourceId
+{
+    TRAP_ID                     = 224
+};
+
+/**
+ * MSI-style interrupts. Send a "cookie" response to clear interrupts.
+ * From [1] we know the size of the struct is 8 dwords. Then we can look at the register shift offsets in [2] to guess the rest.
+ * Or we can also look at [3].
+ *
+ * [1] https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *         drivers/gpu/drm/amd/amdkfd/kfd_device.c#L316
+ * [2] https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *         drivers/gpu/drm/amd/include/asic_reg/oss/osssys_4_0_sh_mask.h#L122
+ * [3] https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+           drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h#L46
+ *
+ */
+constexpr uint32_t INTR_COOKIE_SIZE = 32; // in bytes
+
+typedef struct
+{
+    uint32_t clientId : 8;
+    uint32_t sourceId : 8;
+    uint32_t ringId : 8;
+    uint32_t vmId : 4;
+    uint32_t reserved1 : 3;
+    uint32_t vmid_type : 1;
+    uint32_t timestamp_Lo;
+    uint32_t timestamp_Hi : 16;
+    uint32_t reserved2 : 15;
+    uint32_t timestamp_src : 1;
+    uint32_t pasid : 16;
+    uint32_t reserved3 : 15;
+    uint32_t pasid_src : 1;
+    uint32_t source_data_dw1;
+    uint32_t source_data_dw2;
+    uint32_t source_data_dw3;
+    uint32_t source_data_dw4;
+} AMDGPUInterruptCookie;
+static_assert(sizeof(AMDGPUInterruptCookie) == INTR_COOKIE_SIZE);
+
+/**
+ * Struct to contain all interrupt handler related registers.
+ */
+typedef struct
+{
+    uint32_t IH_Cntl;
+    uint32_t IH_Base;
+    uint32_t IH_Base_Hi;
+    Addr baseAddr;
+    uint32_t IH_Rptr;
+    uint32_t IH_Wptr;
+    uint32_t IH_Wptr_Addr_Lo;
+    uint32_t IH_Wptr_Addr_Hi;
+    Addr WptrAddr;
+    uint32_t IH_Doorbell;
+} AMDGPUIHRegs;
+
+class AMDGPUInterruptHandler : public DmaDevice
+{
+  public:
+    class DmaEvent : public Event
+    {
+      private:
+        AMDGPUInterruptHandler *deviceIh;
+        uint32_t data;
+
+      public:
+        DmaEvent(AMDGPUInterruptHandler *deviceIh, uint32_t data)
+            : Event(), deviceIh(deviceIh), data(data)
+        {
+            setFlags(Event::AutoDelete);
+        }
+        void process();
+        const char *description() const {
+            return "AMDGPUInterruptHandler Dma";
+        }
+
+        void setData(uint32_t _data) { data = _data; }
+        uint32_t getData() { return data; }
+    };
+
+    struct SenderState : public Packet::SenderState
+    {
+        SenderState(Packet::SenderState *sender_state, Addr addr)
+            : saved(sender_state), _addr(addr)
+        {
+        }
+        Packet::SenderState *saved;
+        Addr _addr;
+    };
+
+    AMDGPUInterruptHandler(const AMDGPUInterruptHandlerParams &p);
+
+    Tick write(PacketPtr pkt) override { return 0; }
+    Tick read(PacketPtr pkt) override { return 0; }
+    AddrRangeList getAddrRanges() const override;
+    void serialize(CheckpointOut &cp) const override;
+    void unserialize(CheckpointIn &cp) override;
+
+    void setGPUDevice(AMDGPUDevice *gpu_device) { gpuDevice = gpu_device; }
+    void prepareInterruptCookie(ContextID cntxtId, uint32_t ring_id,
+        uint32_t client_id, uint32_t source_id);
+    void submitInterruptCookie();
+    void submitWritePointer();
+    void intrPost();
+
+    /**
+     * Methods for setting the values of interrupt handler MMIO registers.
+     */
+    void writeMMIO(PacketPtr pkt, Addr mmio_offset);
+
+    uint32_t getDoorbellOffset() const { return regs.IH_Doorbell; }
+    void setCntl(const uint32_t &data);
+    void setBase(const uint32_t &data);
+    void setBaseHi(const uint32_t &data);
+    void setRptr(const uint32_t &data);
+    void setWptr(const uint32_t &data);
+    void setWptrAddrLo(const uint32_t &data);
+    void setWptrAddrHi(const uint32_t &data);
+    void setDoorbellOffset(const uint32_t &data);
+    void updateRptr(const uint32_t &data);
+
+  private:
+    AMDGPUDevice *gpuDevice;
+    AMDGPUIHRegs regs;
+    std::queue<AMDGPUInterruptCookie*> interruptQueue;
+    AMDGPUInterruptHandler::DmaEvent *dmaEvent;
+};
+
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_INTERRUPT_HANDLER__
diff --git a/src/dev/amdgpu/memory_manager.cc b/src/dev/amdgpu/memory_manager.cc
new file mode 100644
index 0000000..5698a70
--- /dev/null
+++ b/src/dev/amdgpu/memory_manager.cc
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "dev/amdgpu/memory_manager.hh"
+
+#include <memory>
+
+#include "base/chunk_generator.hh"
+#include "debug/AMDGPUMem.hh"
+#include "params/AMDGPUMemoryManager.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+
+AMDGPUMemoryManager::AMDGPUMemoryManager(const AMDGPUMemoryManagerParams &p)
+    : ClockedObject(p), _gpuMemPort(csprintf("%s-port", name()), this),
+      cacheLineSize(p.system->cacheLineSize()),
+      _requestorId(p.system->getRequestorId(this))
+{
+}
+
+void
+AMDGPUMemoryManager::writeRequest(Addr addr, uint8_t *data, int size,
+                               Request::Flags flag, Event *callback)
+{
+    assert(data);
+
+    ChunkGenerator gen(addr, size, cacheLineSize);
+    for (; !gen.done(); gen.next()) {
+        RequestPtr req = std::make_shared<Request>(gen.addr(), gen.size(),
+                                                   flag, _requestorId);
+
+        PacketPtr pkt = Packet::createWrite(req);
+        uint8_t *dataPtr = new uint8_t[gen.size()];
+        std::memcpy(dataPtr, data + (gen.complete()/sizeof(uint8_t)),
+                    gen.size());
+        pkt->dataDynamic<uint8_t>(dataPtr);
+
+        // We only want to issue the callback on the last request completing.
+        if (gen.last()) {
+            pkt->pushSenderState(new GPUMemPort::SenderState(callback, addr));
+        } else {
+            pkt->pushSenderState(new GPUMemPort::SenderState(nullptr, addr));
+        }
+
+        if (!_gpuMemPort.sendTimingReq(pkt)) {
+            DPRINTF(AMDGPUMem, "Request to %#lx needs retry\n", gen.addr());
+            _gpuMemPort.retries.push_back(pkt);
+        } else {
+            DPRINTF(AMDGPUMem, "Write request to %#lx sent\n", gen.addr());
+        }
+    }
+}
+
+bool
+AMDGPUMemoryManager::GPUMemPort::recvTimingResp(PacketPtr pkt)
+{
+    // Retrieve sender state
+    [[maybe_unused]] SenderState *sender_state =
+        safe_cast<SenderState*>(pkt->senderState);
+
+    DPRINTF(AMDGPUMem, "Recveived Response for %#x\n", sender_state->_addr);
+
+    // Check if there is a callback event and if so call it
+    if (sender_state->_callback) {
+        sender_state->_callback->process();
+        delete sender_state->_callback;
+    }
+
+    delete pkt->senderState;
+    delete pkt;
+    return true;
+}
+
+void
+AMDGPUMemoryManager::GPUMemPort::recvReqRetry()
+{
+    for (const auto &pkt : retries) {
+        if (!sendTimingReq(pkt)) {
+            break;
+        } else {
+            DPRINTF(AMDGPUMem, "Retry for %#lx sent\n", pkt->getAddr());
+            retries.pop_front();
+        }
+    }
+}
+
+} // namespace gem5
diff --git a/src/dev/amdgpu/memory_manager.hh b/src/dev/amdgpu/memory_manager.hh
new file mode 100644
index 0000000..8fb237b
--- /dev/null
+++ b/src/dev/amdgpu/memory_manager.hh
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_AMDGPU_MEMORY_MANAGER_HH__
+#define __DEV_AMDGPU_MEMORY_MANAGER_HH__
+
+#include <deque>
+
+#include "base/callback.hh"
+#include "mem/port.hh"
+#include "params/AMDGPUMemoryManager.hh"
+#include "sim/clocked_object.hh"
+
+namespace gem5
+{
+
+class AMDGPUMemoryManager : public ClockedObject
+{
+    class GPUMemPort : public MasterPort
+    {
+        public:
+        GPUMemPort(const std::string &_name, AMDGPUMemoryManager *_gpuMemMgr)
+            : MasterPort(_name, _gpuMemMgr)
+        {
+        }
+
+        bool recvTimingResp(PacketPtr pkt) override;
+        void recvReqRetry() override;
+
+        struct SenderState : public Packet::SenderState
+        {
+            SenderState(Event *callback, Addr addr)
+                : _callback(callback), _addr(addr)
+            {}
+
+            Event *_callback;
+            Addr _addr;
+        };
+
+        std::deque<PacketPtr> retries;
+    };
+
+    GPUMemPort _gpuMemPort;
+    const int cacheLineSize;
+    const RequestorID _requestorId;
+
+  public:
+    AMDGPUMemoryManager(const AMDGPUMemoryManagerParams &p);
+    ~AMDGPUMemoryManager() {};
+
+    /**
+     * Write size amount of data to device memory at addr using flags and
+     * callback.
+     *
+     * @param addr Device address to write.
+     * @param data Pointer to data to write.
+     * @param size Number of bytes to write.
+     * @param flag Additional request flags for write packets.
+     * @param callback Event callback to call after all bytes are written.
+     */
+    void writeRequest(Addr addr, uint8_t *data, int size,
+                      Request::Flags flag = 0, Event *callback = nullptr);
+
+    /**
+     * Get the requestorID for the memory manager. This ID is used for all
+     * packets which should be routed through the device network.
+     *
+     * @return requestorID of this object.
+     */
+    RequestorID getRequestorID() const { return _requestorId; }
+
+    Port &
+    getPort(const std::string &if_name, PortID idx) override
+    {
+        if (if_name == "port") {
+            return _gpuMemPort;
+        } else {
+            return ClockedObject::getPort(if_name, idx);
+        }
+    }
+};
+
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_MEMORY_MANAGER_HH__
diff --git a/src/dev/amdgpu/pm4_defines.hh b/src/dev/amdgpu/pm4_defines.hh
new file mode 100644
index 0000000..b690e54
--- /dev/null
+++ b/src/dev/amdgpu/pm4_defines.hh
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DEV_AMDGPU_PM4_DEFINES_H__
+#define __DEV_AMDGPU_PM4_DEFINES_H__
+
+#include <cstdlib>
+#include <iostream>
+#include <vector>
+
+#include "base/types.hh"
+
+namespace gem5
+{
+
+/**
+ * PM4 opcodes. Taken from linux tree from the following locations:
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *     drivers/gpu/drm/amd/amdkfd/kfd_pm4_opcodes.h
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *     drivers/gpu/drm/amd/amdgpu/soc15d.h
+ */
+enum it_opcode_type
+{
+    IT_NOP                               = 0x10,
+    IT_WRITE_DATA                        = 0x37,
+    IT_WAIT_REG_MEM                      = 0x3C,
+    IT_INDIRECT_BUFFER                   = 0x3F,
+    IT_RELEASE_MEM                       = 0x49,
+    IT_SET_UCONFIG_REG                   = 0x79,
+    IT_SWITCH_BUFFER                     = 0x8B,
+    IT_INVALIDATE_TLBS                   = 0x98,
+    IT_MAP_PROCESS                       = 0xA1,
+    IT_MAP_QUEUES                        = 0xA2,
+    IT_UNMAP_QUEUES                      = 0xA3,
+    IT_QUERY_STATUS                      = 0xA4,
+    IT_RUN_LIST                          = 0xA5,
+};
+
+/**
+ * Value from vega10/pm4_header.h.
+ */
+#define PACKET3_SET_UCONFIG_REG_START                   0x0000c000
+
+/**
+ * PM4 packets
+ */
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint16_t predicated : 1;
+            uint16_t shader : 1;
+            uint16_t reserved : 6;
+            uint16_t opcode : 8;
+            uint16_t count : 14;
+            uint16_t type : 2;
+        };
+        uint32_t ordinal;
+    };
+} PM4Header;
+static_assert(sizeof(PM4Header) == 4);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t reserved1 : 8;
+    uint32_t destSel : 4;
+    uint32_t reserved2 : 4;
+    uint32_t addrIncr : 1;
+    uint32_t reserved3 : 2;
+    uint32_t resume : 1;
+    uint32_t writeConfirm : 1;
+    uint32_t reserved4 : 4;
+    uint32_t cachePolicy : 2;
+    uint32_t reserved5 : 5;
+    union
+    {
+        struct
+        {
+            uint32_t destAddrLo;
+            uint32_t destAddrHi;
+        };
+        uint64_t destAddr;
+    };
+    uint32_t data;
+}  PM4WriteData;
+static_assert(sizeof(PM4WriteData) == 16);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t reserved1 : 4;
+    uint32_t queueSel : 2;
+    uint32_t reserved2 : 2;
+    uint32_t vmid : 4;
+    uint32_t reserved3 : 1;
+    uint32_t me : 1;
+    uint32_t pipe : 2;
+    uint32_t queueSlot : 3;
+    uint32_t reserved6 : 2;
+    uint32_t queueType : 3;
+    uint32_t allocFormat : 2;
+    uint32_t engineSel : 3;
+    uint32_t numQueues : 3;
+    uint32_t reserved4 : 1;
+    uint32_t checkDisable : 1;
+    uint32_t doorbellOffset : 26;
+    uint32_t reserved5 : 4;
+    union
+    {
+        struct
+        {
+            uint32_t mqdAddrLo : 32;
+            uint32_t mqdAddrHi : 32;
+        };
+        uint64_t mqdAddr;
+    };
+    union
+    {
+        struct
+        {
+            uint32_t wptrAddrLo : 32;
+            uint32_t wptrAddrHi : 32;
+        };
+        uint64_t wptrAddr;
+    };
+}  PM4MapQueues;
+static_assert(sizeof(PM4MapQueues) == 24);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t action : 2;
+    uint32_t reserved : 2;
+    uint32_t queueSel : 2;
+    uint32_t reserved1 : 20;
+    uint32_t engineSel : 3;
+    uint32_t numQueues : 3;
+    union
+    {
+        struct
+        {
+            uint32_t pasid : 16;
+            uint32_t reserved2 : 16;
+        };
+        struct
+        {
+            uint32_t reserved3 : 2;
+            uint32_t doorbellOffset0 : 26;
+            uint32_t reserved4 : 4;
+        };
+    };
+    uint32_t reserved5 : 2;
+    uint32_t doorbellOffset1 : 26;
+    uint32_t reserved6 : 4;
+    uint32_t reserved7 : 2;
+    uint32_t doorbellOffset2 : 26;
+    uint32_t reserved8 : 4;
+    uint32_t reserved9 : 2;
+    uint32_t doorbellOffset3 : 26;
+    uint32_t reserved10 : 4;
+}  PM4UnmapQueues;
+static_assert(sizeof(PM4UnmapQueues) == 20);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t vmidMask : 16;
+    uint32_t unmapLatency : 8;
+    uint32_t reserved : 5;
+    uint32_t queueType : 3;
+    union
+    {
+        struct
+        {
+            uint32_t queueMaskLo;
+            uint32_t queueMaskHi;
+        };
+        uint64_t queueMask;
+    };
+    union
+    {
+        struct
+        {
+            uint32_t gwsMaskLo;
+            uint32_t gwsMaskHi;
+        };
+        uint64_t gwsMask;
+    };
+    uint16_t oacMask;
+    uint16_t reserved1;
+    uint32_t gdsHeapBase : 6;
+    uint32_t reserved2 : 5;
+    uint32_t gdsHeapSize : 6;
+    uint32_t reserved3 : 15;
+}  PM4SetResources;
+static_assert(sizeof(PM4SetResources) == 28);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t pasid : 16;
+    uint32_t reserved0 : 8;
+    uint32_t diq : 1;
+    uint32_t processQuantum : 7;
+    union
+    {
+        struct
+        {
+            uint32_t ptBaseLo;
+            uint32_t ptBaseHi;
+        };
+        uint64_t ptBase;
+    };
+    uint32_t shMemBases;
+    uint32_t shMemConfig;
+    uint32_t reserved1;
+    uint32_t reserved2;
+    uint32_t reserved3;
+    uint32_t reserved4;
+    uint32_t reserved5;
+    union
+    {
+        struct
+        {
+            uint32_t gdsAddrLo;
+            uint32_t gdsAddrHi;
+        };
+        uint64_t gdsAddr;
+    };
+    uint32_t numGws : 6;
+    uint32_t reserved7 : 2;
+    uint32_t numOac : 4;
+    uint32_t reserved8 : 4;
+    uint32_t gdsSize : 6;
+    uint32_t numQueues : 10;
+    union
+    {
+        struct
+        {
+            uint32_t completionSignalLo;
+            uint32_t completionSignalHi;
+        };
+        uint64_t completionSignal;
+    };
+}  PM4MapProcess;
+static_assert(sizeof(PM4MapProcess) == 60);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t function : 4;
+    uint32_t memSpace : 2;
+    uint32_t operation : 2;
+    uint32_t reserved1 : 24;
+    union
+    {
+        struct
+        {
+            uint32_t regAddr1 : 18;
+            uint32_t reserved2 : 14;
+        };
+        uint32_t memAddrLo;
+    };
+    union
+    {
+        struct
+        {
+            uint32_t regAddr2 : 18;
+            uint32_t reserved3 : 14;
+        };
+        uint32_t memAddrHi;
+    };
+    uint32_t reference;
+    uint32_t mask;
+    uint32_t pollInterval;
+}  PM4WaitRegMem;
+static_assert(sizeof(PM4WaitRegMem) == 24);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t regOffset : 16;
+    uint32_t reserved : 16;
+    uint32_t regData;
+}  PM4SetUConfig;
+static_assert(sizeof(PM4SetUConfig) == 8);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t ibBaseLo;
+            uint32_t ibBaseHi;
+        };
+        uint64_t ibBase;
+    };
+    uint32_t ibSize : 20;
+    uint32_t chain : 1;
+    uint32_t poll : 1;
+    uint32_t reserved0 : 1;
+    uint32_t valid: 1;
+    uint32_t vmid : 4;
+    uint32_t cachePolicy : 2;
+    uint32_t reserved1 : 1;
+    uint32_t priv : 1;
+}  PM4IndirectBuf;
+static_assert(sizeof(PM4IndirectBuf) == 12);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t tmz : 1;
+            uint32_t reserved : 31;
+        };
+        uint32_t dummy;
+    };
+}  PM4SwitchBuf;
+static_assert(sizeof(PM4SwitchBuf) == 4);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t ibBaseLo;
+            uint32_t ibBaseHi;
+        };
+        uint64_t ibBase;
+    };
+    uint32_t ibSize : 20;
+    uint32_t chain : 1;
+    uint32_t ena : 1;
+    uint32_t reserved1 : 2;
+    uint32_t vmid : 4;
+    uint32_t cachePolicy : 2;
+    uint32_t preResume : 1;
+    uint32_t priv : 1;
+}  PM4IndirectBufConst;
+static_assert(sizeof(PM4IndirectBufConst) == 12);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t tmz : 1;
+    uint32_t reserved : 27;
+    uint32_t command : 4;
+}  PM4FrameCtrl;
+static_assert(sizeof(PM4FrameCtrl) == 4);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t event : 6;
+    uint32_t reserved0 : 2;
+    uint32_t eventIdx : 4;
+    uint32_t l1Volatile : 1;
+    uint32_t l2Volatile : 1;
+    uint32_t reserved1 : 1;
+    uint32_t l2WB : 1;
+    uint32_t l1Inv : 1;
+    uint32_t l2Inv : 1;
+    uint32_t reserved2 : 1;
+    uint32_t l2NC : 1;
+    uint32_t l2WC : 1;
+    uint32_t l2Meta : 1;
+    uint32_t reserved3 : 3;
+    uint32_t cachePolicy : 2;
+    uint32_t reserved4 : 1;
+    uint32_t execute : 1;
+    uint32_t reserved5 : 3;
+    uint32_t reserved6 : 16;
+    uint32_t destSelect : 2;
+    uint32_t reserved7 : 6;
+    uint32_t intSelect : 3;
+    uint32_t reserved8 : 2;
+    uint32_t dataSelect : 3;
+    union
+    {
+        struct
+        {
+            uint32_t addrLo;
+            uint32_t addrHi;
+        };
+        uint64_t addr;
+    };
+    union
+    {
+        struct
+        {
+            union
+            {
+                struct
+                {
+                    uint32_t dwOffset : 16;
+                    uint32_t numDws : 16;
+                };
+                uint32_t dataLo : 32;
+            };
+            uint32_t dataHi;
+        };
+        uint64_t data;
+    };
+    uint32_t intCtxId;
+}  PM4ReleaseMem;
+static_assert(sizeof(PM4ReleaseMem) == 28);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t offset : 16;
+    uint32_t reserved : 16;
+    uint32_t data;
+}  PM4SetUconfigReg;
+static_assert(sizeof(PM4SetUconfigReg) == 8);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t ibBaseLo;
+            uint32_t ibBaseHi;
+        };
+        uint64_t ibBase;
+    };
+    uint32_t ibSize : 20;
+    uint32_t chain : 1;
+    uint32_t offleadPolling : 1;
+    uint32_t reserved1 : 1;
+    uint32_t valid : 1;
+    uint32_t processCnt : 4;
+    uint32_t reserved2 : 4;
+}  PM4RunList;
+static_assert(sizeof(PM4RunList) == 12);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t contextId : 28;
+    uint32_t interruptSel : 2;
+    uint32_t command : 2;
+    union
+    {
+        struct
+        {
+            uint32_t pasid : 16;
+            uint32_t reserved0 : 16;
+        };
+        struct
+        {
+            uint32_t reserved1 : 2;
+            uint32_t doorbellOffset : 26;
+            uint32_t engineSel : 3;
+            uint32_t reserved2 : 1;
+        };
+    };
+    union
+    {
+        struct
+        {
+            uint32_t addrLo;
+            uint32_t addrHi;
+        };
+        uint64_t addr;
+    };
+    union
+    {
+        struct
+        {
+            uint32_t dataLo;
+            uint32_t dataHi;
+        };
+        uint64_t data;
+    };
+}  PM4QueryStatus;
+static_assert(sizeof(PM4QueryStatus) == 24);
+
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_PM4_DEFINES_HH__
diff --git a/src/dev/amdgpu/pm4_mmio.hh b/src/dev/amdgpu/pm4_mmio.hh
new file mode 100644
index 0000000..a3ce5f1
--- /dev/null
+++ b/src/dev/amdgpu/pm4_mmio.hh
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DEV_AMDGPU_PM4_MMIO_HH__
+#define __DEV_AMDGPU_PM4_MMIO_HH__
+
+namespace gem5
+{
+
+#define mmCP_RB0_BASE                                                 0x1040
+#define mmCP_RB0_CNTL                                                 0x1041
+#define mmCP_RB_WPTR_POLL_ADDR_LO                                     0x1046
+#define mmCP_RB_WPTR_POLL_ADDR_HI                                     0x1047
+#define mmCP_RB_VMID                                                  0x1051
+#define mmCP_RB0_RPTR_ADDR                                            0x1043
+#define mmCP_RB0_RPTR_ADDR_HI                                         0x1044
+#define mmCP_RB0_WPTR                                                 0x1054
+#define mmCP_RB0_WPTR_HI                                              0x1055
+#define mmCP_RB_DOORBELL_CONTROL                                      0x1059
+#define mmCP_RB_DOORBELL_RANGE_LOWER                                  0x105a
+#define mmCP_RB_DOORBELL_RANGE_UPPER                                  0x105b
+#define mmCP_RB0_BASE_HI                                              0x10b1
+
+#define mmCP_HQD_ACTIVE                                               0x1247
+#define mmCP_HQD_VMID                                                 0x1248
+#define mmCP_HQD_PQ_BASE                                              0x124d
+#define mmCP_HQD_PQ_BASE_HI                                           0x124e
+#define mmCP_HQD_PQ_DOORBELL_CONTROL                                  0x1254
+#define mmCP_HQD_PQ_RPTR                                              0x124f
+#define mmCP_HQD_PQ_RPTR_REPORT_ADDR                                  0x1250
+#define mmCP_HQD_PQ_RPTR_REPORT_ADDR_HI                               0x1251
+#define mmCP_HQD_PQ_WPTR_POLL_ADDR                                    0x1252
+#define mmCP_HQD_PQ_WPTR_POLL_ADDR_HI                                 0x1253
+#define mmCP_HQD_IB_CONTROL                                           0x125a
+#define mmCP_HQD_PQ_WPTR_LO                                           0x127b
+#define mmCP_HQD_PQ_WPTR_HI                                           0x127c
+
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_PM4_MMIO_HH__
diff --git a/src/dev/amdgpu/pm4_packet_processor.cc b/src/dev/amdgpu/pm4_packet_processor.cc
new file mode 100644
index 0000000..c70f2f2
--- /dev/null
+++ b/src/dev/amdgpu/pm4_packet_processor.cc
@@ -0,0 +1,1090 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "dev/amdgpu/pm4_packet_processor.hh"
+
+#include "debug/PM4PacketProcessor.hh"
+#include "dev/amdgpu/amdgpu_device.hh"
+#include "dev/amdgpu/interrupt_handler.hh"
+#include "dev/amdgpu/pm4_mmio.hh"
+#include "dev/amdgpu/sdma_engine.hh"
+#include "dev/hsa/hw_scheduler.hh"
+#include "enums/GfxVersion.hh"
+#include "gpu-compute/gpu_command_processor.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+
+namespace gem5
+{
+
+PM4PacketProcessor::PM4PacketProcessor(const PM4PacketProcessorParams &p)
+    : DmaVirtDevice(p)
+{
+    memset(&kiq, 0, sizeof(QueueDesc));
+    memset(&pq, 0, sizeof(QueueDesc));
+}
+
+/**
+ * AMDGPUDevice will perform DMA operations on VAs, and because
+ * page faults are not currently supported for Vega 10, we
+ * must be able to find the pages mapped for the process.
+ */
+TranslationGenPtr
+PM4PacketProcessor::translate(Addr vaddr, Addr size)
+{
+    if (gpuDevice->getVM().inAGP(vaddr)) {
+        // Use AGP translation gen
+        return TranslationGenPtr(
+            new AMDGPUVM::AGPTranslationGen(&gpuDevice->getVM(), vaddr, size));
+    }
+
+    // Assume GART otherwise as this is the only other translation aperture
+    // available to the PM4 packet processor.
+    return TranslationGenPtr(
+        new AMDGPUVM::GARTTranslationGen(&gpuDevice->getVM(), vaddr, size));
+}
+
+AddrRangeList
+PM4PacketProcessor::getAddrRanges() const
+{
+    AddrRangeList ranges;
+    return ranges;
+}
+
+void
+PM4PacketProcessor::setGPUDevice(AMDGPUDevice *gpu_device)
+{
+    gpuDevice = gpu_device;
+}
+
+Addr
+PM4PacketProcessor::getGARTAddr(Addr addr) const
+{
+    if (!gpuDevice->getVM().inAGP(addr)) {
+        Addr low_bits = bits(addr, 11, 0);
+        addr = (((addr >> 12) << 3) << 12) | low_bits;
+    }
+    return addr;
+}
+
+PM4Queue *
+PM4PacketProcessor::getQueue(Addr offset, bool gfx)
+{
+    auto result = queuesMap.find(offset);
+    if (result == queuesMap.end()) {
+        if (gfx)
+            mapPq(offset);
+        else
+            mapKiq(offset);
+        return queuesMap[offset];
+    }
+    return result->second;
+}
+
+void
+PM4PacketProcessor::mapKiq(Addr offset)
+{
+    DPRINTF(PM4PacketProcessor, "Mapping KIQ\n");
+    newQueue((QueueDesc *)&kiq, offset);
+}
+
+void
+PM4PacketProcessor::mapPq(Addr offset)
+{
+    DPRINTF(PM4PacketProcessor, "Mapping PQ\n");
+    newQueue((QueueDesc *)&pq, offset);
+}
+
+void
+PM4PacketProcessor::newQueue(QueueDesc *mqd, Addr offset,
+                             PM4MapQueues *pkt, int id)
+{
+    if (id == -1)
+        id = queues.size();
+
+    /* 256 bytes aligned address */
+    mqd->base <<= 8;
+    PM4Queue *q = new PM4Queue(id, mqd, offset, pkt);
+
+    queuesMap[offset] = q;
+    queues[id] = q;
+
+    /* we are assumming only compute queues can be map from MQDs */
+    QueueType qt;
+    qt = mqd->aql ? QueueType::ComputeAQL
+                  : QueueType::Compute;
+    gpuDevice->setDoorbellType(offset, qt);
+
+    DPRINTF(PM4PacketProcessor, "New PM4 queue %d, base: %p offset: %p\n",
+            id, q->base(), q->offset());
+}
+
+void
+PM4PacketProcessor::process(PM4Queue *q, Addr wptrOffset)
+{
+    q->wptr(wptrOffset * sizeof(uint32_t));
+
+    if (!q->processing()) {
+        q->processing(true);
+        decodeNext(q);
+    }
+}
+
+void
+PM4PacketProcessor::decodeNext(PM4Queue *q)
+{
+    DPRINTF(PM4PacketProcessor, "PM4 decode queue %d rptr %p, wptr %p\n",
+            q->id(), q->rptr(), q->wptr());
+
+    if (q->rptr() < q->wptr()) {
+        /* Additional braces here are needed due to a clang compilation bug
+           falsely throwing a "suggest braces around initialization of
+           subject" error. More info on this bug is available here:
+           https://stackoverflow.com/questions/31555584
+         */
+        PM4Header h{{{0, 0, 0, 0, 0, 0}}};
+        auto cb = new DmaVirtCallback<PM4Header>(
+            [ = ] (PM4Header header)
+                { decodeHeader(q, header); }, h);
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(uint32_t), cb,
+                    &cb->dmaBuffer);
+    } else {
+        q->processing(false);
+        if (q->ib()) {
+            q->ib(false);
+            decodeNext(q);
+        }
+    }
+}
+
+void
+PM4PacketProcessor::decodeHeader(PM4Queue *q, PM4Header header)
+{
+    DPRINTF(PM4PacketProcessor, "PM4 packet %p\n", header.opcode);
+
+    q->incRptr(sizeof(PM4Header));
+
+    DmaVirtCallback<uint64_t> *cb = nullptr;
+    void *dmaBuffer = nullptr;
+
+    switch(header.opcode) {
+      case IT_NOP: {
+        DPRINTF(PM4PacketProcessor, "PM4 nop, count %p\n", header.count);
+        DPRINTF(PM4PacketProcessor, "rptr %p wptr %p\n", q->rptr(), q->wptr());
+        if (header.count == 0x3fff) {
+            q->fastforwardRptr();
+        } else {
+            q->incRptr((header.count + 1) * sizeof(uint32_t));
+        }
+        decodeNext(q);
+        } break;
+      case IT_WRITE_DATA: {
+        dmaBuffer = new PM4WriteData();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { writeData(q, (PM4WriteData *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4WriteData), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_MAP_QUEUES: {
+        dmaBuffer = new PM4MapQueues();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { mapQueues(q, (PM4MapQueues *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4MapQueues), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_RELEASE_MEM: {
+        dmaBuffer = new PM4ReleaseMem();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { releaseMem(q, (PM4ReleaseMem *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4ReleaseMem), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_INDIRECT_BUFFER: {
+        dmaBuffer = new PM4IndirectBuf();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { indirectBuffer(q, (PM4IndirectBuf *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4IndirectBuf), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_SWITCH_BUFFER: {
+        dmaBuffer = new PM4SwitchBuf();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { switchBuffer(q, (PM4SwitchBuf *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4SwitchBuf), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_SET_UCONFIG_REG: {
+        dmaBuffer = new PM4SetUconfigReg();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { setUconfigReg(q, (PM4SetUconfigReg *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4SetUconfigReg), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_WAIT_REG_MEM: {
+        dmaBuffer = new PM4WaitRegMem();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { waitRegMem(q, (PM4WaitRegMem *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4WaitRegMem), cb,
+                    dmaBuffer);
+        } break;
+      case IT_MAP_PROCESS: {
+        dmaBuffer = new PM4MapProcess();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { mapProcess(q, (PM4MapProcess *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4MapProcess), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_UNMAP_QUEUES: {
+        dmaBuffer = new PM4UnmapQueues();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { unmapQueues(q, (PM4UnmapQueues *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4UnmapQueues), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_RUN_LIST: {
+        dmaBuffer = new PM4RunList();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { runList(q, (PM4RunList *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4RunList), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_QUERY_STATUS: {
+        dmaBuffer = new PM4QueryStatus();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { queryStatus(q, (PM4QueryStatus *)dmaBuffer); });
+        dmaReadVirt(getGARTAddr(q->rptr()), sizeof(PM4QueryStatus), cb,
+                    dmaBuffer);
+        } break;
+
+      case IT_INVALIDATE_TLBS: {
+        DPRINTF(PM4PacketProcessor, "Functionaly invalidating all TLBs\n");
+        gpuDevice->getVM().invalidateTLBs();
+        q->incRptr((header.count + 1) * sizeof(uint32_t));
+        decodeNext(q);
+        } break;
+
+      default: {
+        warn("PM4 packet opcode 0x%x not supported.\n", header.opcode);
+        DPRINTF(PM4PacketProcessor, "PM4 packet opcode 0x%x not supported.\n",
+                header.opcode);
+        q->incRptr((header.count + 1) * sizeof(uint32_t));
+        decodeNext(q);
+        } break;
+    }
+}
+
+void
+PM4PacketProcessor::writeData(PM4Queue *q, PM4WriteData *pkt)
+{
+    q->incRptr(sizeof(PM4WriteData));
+
+    Addr addr = getGARTAddr(pkt->destAddr);
+    DPRINTF(PM4PacketProcessor, "PM4 write addr: %p data: %p.\n", addr,
+            pkt->data);
+    auto cb = new DmaVirtCallback<uint32_t>(
+        [ = ](const uint32_t &) { writeDataDone(q, pkt, addr); });
+    //TODO: the specs indicate that pkt->data holds the number of dword that
+    //need to be written.
+    dmaWriteVirt(addr, sizeof(uint32_t), cb, &pkt->data);
+
+    if (!pkt->writeConfirm)
+        decodeNext(q);
+}
+
+void
+PM4PacketProcessor::writeDataDone(PM4Queue *q, PM4WriteData *pkt, Addr addr)
+{
+    DPRINTF(PM4PacketProcessor, "PM4 write completed to %p, %p.\n", addr,
+            pkt->data);
+
+    if (pkt->writeConfirm)
+        decodeNext(q);
+
+    delete pkt;
+}
+
+void
+PM4PacketProcessor::mapQueues(PM4Queue *q, PM4MapQueues *pkt)
+{
+    q->incRptr(sizeof(PM4MapQueues));
+
+    DPRINTF(PM4PacketProcessor, "MAPQueues queueSel: %d, vmid: %d, me: %d, "
+            "pipe: %d, queueSlot: %d, queueType: %d, allocFormat: %d, "
+            "engineSel: %d, numQueues: %d, checkDisable: %d, doorbellOffset:"
+            " %d, mqdAddr: %lx, wptrAddr: %lx\n", pkt->queueSel, pkt->vmid,
+            pkt->me, pkt->pipe, pkt->queueSlot, pkt->queueType,
+            pkt->allocFormat, pkt->engineSel, pkt->numQueues,
+            pkt->checkDisable, pkt->doorbellOffset, pkt->mqdAddr,
+            pkt->wptrAddr);
+
+    // Partially reading the mqd with an offset of 96 dwords
+    if (pkt->engineSel == 0 || pkt->engineSel == 1 || pkt->engineSel == 4) {
+        Addr addr = getGARTAddr(pkt->mqdAddr + 96 * sizeof(uint32_t));
+
+        DPRINTF(PM4PacketProcessor,
+                "Mapping mqd from %p %p (vmid %d - last vmid %d).\n",
+                addr, pkt->mqdAddr, pkt->vmid, gpuDevice->lastVMID());
+
+        gpuDevice->mapDoorbellToVMID(pkt->doorbellOffset,
+                                     gpuDevice->lastVMID());
+
+        QueueDesc *mqd = new QueueDesc();
+        memset(mqd, 0, sizeof(QueueDesc));
+        auto cb = new DmaVirtCallback<uint32_t>(
+            [ = ] (const uint32_t &) {
+                processMQD(pkt, q, addr, mqd, gpuDevice->lastVMID()); });
+        dmaReadVirt(addr, sizeof(QueueDesc), cb, mqd);
+    } else if (pkt->engineSel == 2 || pkt->engineSel == 3) {
+        SDMAQueueDesc *sdmaMQD = new SDMAQueueDesc();
+        memset(sdmaMQD, 0, sizeof(SDMAQueueDesc));
+
+        Addr addr = pkt->mqdAddr;
+
+        auto cb = new DmaVirtCallback<uint32_t>(
+            [ = ] (const uint32_t &) {
+                processSDMAMQD(pkt, q, addr, sdmaMQD,
+                               gpuDevice->lastVMID()); });
+        dmaReadVirt(addr, sizeof(SDMAQueueDesc), cb, sdmaMQD);
+    } else {
+        panic("Unknown engine for MQD: %d\n", pkt->engineSel);
+    }
+
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::processMQD(PM4MapQueues *pkt, PM4Queue *q, Addr addr,
+    QueueDesc *mqd, uint16_t vmid)
+{
+    DPRINTF(PM4PacketProcessor, "MQDbase: %lx, active: %d, vmid: %d, base: "
+            "%lx, rptr: %x aqlPtr: %lx\n", mqd->mqdBase, mqd->hqd_active,
+            mqd->hqd_vmid, mqd->base, mqd->rptr, mqd->aqlRptr);
+
+    Addr offset = mqd->doorbell & 0x1ffffffc;
+    newQueue(mqd, offset, pkt);
+    PM4Queue *new_q = queuesMap[offset];
+    gpuDevice->insertQId(vmid, new_q->id());
+
+    if (mqd->aql) {
+        // The queue size is encoded in the cp_hqd_pq_control field in the
+        // kernel driver in the 6 lowest bits as log2(queue_size / 4) - 1
+        // number of dwords.
+        //
+        //      https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/
+        //          roc-4.3.x/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c#L3561
+        //
+        // Queue size is then 2^(cp_hqd_pq_control[5:0] + 1) dword. Multiply
+        // by 4 to get the number of bytes as HSAPP expects.
+        int mqd_size = (1 << ((mqd->hqd_pq_control & 0x3f) + 1)) * 4;
+        auto &hsa_pp = gpuDevice->CP()->hsaPacketProc();
+        hsa_pp.setDeviceQueueDesc(mqd->aqlRptr, mqd->base, new_q->id(),
+                                  mqd_size, 8, GfxVersion::gfx900, offset,
+                                  mqd->mqdReadIndex);
+    }
+
+    DPRINTF(PM4PacketProcessor, "PM4 mqd read completed, base %p, mqd %p, "
+            "hqdAQL %d.\n", mqd->base, mqd->mqdBase, mqd->aql);
+}
+
+void
+PM4PacketProcessor::processSDMAMQD(PM4MapQueues *pkt, PM4Queue *q, Addr addr,
+    SDMAQueueDesc *mqd, uint16_t vmid)
+{
+    DPRINTF(PM4PacketProcessor, "SDMAMQD: rb base: %#lx rptr: %#x/%#x wptr: "
+            "%#x/%#x ib: %#x/%#x size: %d ctrl: %#x\n", mqd->rb_base,
+            mqd->sdmax_rlcx_rb_rptr, mqd->sdmax_rlcx_rb_rptr_hi,
+            mqd->sdmax_rlcx_rb_wptr, mqd->sdmax_rlcx_rb_wptr_hi,
+            mqd->sdmax_rlcx_ib_base_lo, mqd->sdmax_rlcx_ib_base_hi,
+            mqd->sdmax_rlcx_ib_size, mqd->sdmax_rlcx_rb_cntl);
+
+    // Engine 2 points to SDMA0 while engine 3 points to SDMA1
+    assert(pkt->engineSel == 2 || pkt->engineSel == 3);
+    SDMAEngine *sdma_eng = gpuDevice->getSDMAById(pkt->engineSel - 2);
+
+    // Register RLC queue with SDMA
+    sdma_eng->registerRLCQueue(pkt->doorbellOffset << 2,
+                               mqd->rb_base << 8);
+
+    // Register doorbell with GPU device
+    gpuDevice->setSDMAEngine(pkt->doorbellOffset << 2, sdma_eng);
+    gpuDevice->setDoorbellType(pkt->doorbellOffset << 2, RLC);
+}
+
+void
+PM4PacketProcessor::releaseMem(PM4Queue *q, PM4ReleaseMem *pkt)
+{
+    q->incRptr(sizeof(PM4ReleaseMem));
+
+    Addr addr = getGARTAddr(pkt->addr);
+    DPRINTF(PM4PacketProcessor, "PM4 release_mem event %d eventIdx %d intSel "
+            "%d destSel %d dataSel %d, address %p data %p, intCtx %p\n",
+            pkt->event, pkt->eventIdx, pkt->intSelect, pkt->destSelect,
+            pkt->dataSelect, addr, pkt->dataLo, pkt->intCtxId);
+
+    DPRINTF(PM4PacketProcessor,
+            "PM4 release_mem destSel 0 bypasses caches to MC.\n");
+
+    if (pkt->dataSelect == 1) {
+        auto cb = new DmaVirtCallback<uint32_t>(
+            [ = ](const uint32_t &) { releaseMemDone(q, pkt, addr); },
+            pkt->dataLo);
+        dmaWriteVirt(addr, sizeof(uint32_t), cb, &cb->dmaBuffer);
+    } else {
+        panic("Unimplemented PM4ReleaseMem.dataSelect");
+    }
+}
+
+void
+PM4PacketProcessor::releaseMemDone(PM4Queue *q, PM4ReleaseMem *pkt, Addr addr)
+{
+    DPRINTF(PM4PacketProcessor, "PM4 release_mem wrote %d to %p\n",
+            pkt->dataLo, addr);
+    if (pkt->intSelect == 2) {
+        DPRINTF(PM4PacketProcessor, "PM4 interrupt, ctx: %d, me: %d, pipe: "
+                "%d, queueSlot:%d\n", pkt->intCtxId, q->me(), q->pipe(),
+                q->queue());
+        // Rearranging the queue field of PM4MapQueues as the interrupt RingId
+        // format specified in PM4ReleaseMem pkt.
+        uint32_t ringId = (q->me() << 6) | (q->pipe() << 4) | q->queue();
+        gpuDevice->getIH()->prepareInterruptCookie(pkt->intCtxId, ringId,
+            SOC15_IH_CLIENTID_RLC, TRAP_ID);
+        gpuDevice->getIH()->submitInterruptCookie();
+    }
+
+    delete pkt;
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::updateReadIndex(Addr offset, uint64_t rd_idx)
+{
+    assert(queuesMap.count(offset));
+    queuesMap[offset]->getMQD()->mqdReadIndex = rd_idx;
+}
+
+void
+PM4PacketProcessor::unmapQueues(PM4Queue *q, PM4UnmapQueues *pkt)
+{
+    q->incRptr(sizeof(PM4UnmapQueues));
+
+    DPRINTF(PM4PacketProcessor, "PM4 unmap_queues queueSel: %d numQueues: %d "
+            "pasid: %p doorbellOffset0 %p \n",
+            pkt->queueSel, pkt->numQueues, pkt->pasid, pkt->doorbellOffset0);
+
+    switch (pkt->queueSel) {
+      case 0:
+        switch (pkt->numQueues) {
+          case 1:
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset0));
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset1));
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset2));
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset3));
+            break;
+          case 2:
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset1));
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset2));
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset3));
+            break;
+          case 3:
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset2));
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset3));
+            break;
+          case 4:
+            gpuDevice->deallocateVmid(
+                    gpuDevice->getVMID(pkt->doorbellOffset3));
+            break;
+          default:
+            panic("Unrecognized number of queues %d\n", pkt->numQueues);
+        }
+        break;
+      case 1:
+        gpuDevice->deallocatePasid(pkt->pasid);
+        break;
+      case 2:
+        break;
+      case 3: {
+        auto &hsa_pp = gpuDevice->CP()->hsaPacketProc();
+        for (auto iter : gpuDevice->getUsedVMIDs()) {
+            for (auto id : iter.second) {
+                assert(queues.count(id));
+
+                // Do not unmap KMD queues
+                if (queues[id]->privileged()) {
+                    continue;
+                }
+                QueueDesc *mqd = queues[id]->getMQD();
+                DPRINTF(PM4PacketProcessor, "Unmapping queue %d with read "
+                        "index %ld\n", id, mqd->mqdReadIndex);
+                // Partially writing the mqd with an offset of 96 dwords
+                Addr addr = getGARTAddr(queues[id]->mqdBase() +
+                                        96 * sizeof(uint32_t));
+                Addr mqd_base = queues[id]->mqdBase();
+                auto cb = new DmaVirtCallback<uint32_t>(
+                    [ = ] (const uint32_t &) {
+                        doneMQDWrite(mqd_base, addr);
+                    });
+                mqd->base >>= 8;
+                dmaWriteVirt(addr, sizeof(QueueDesc), cb, mqd);
+                queues.erase(id);
+                hsa_pp.unsetDeviceQueueDesc(id, 8);
+            }
+        }
+        gpuDevice->deallocateAllQueues();
+      } break;
+      default:
+        panic("Unrecognized options\n");
+        break;
+    }
+
+    delete pkt;
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::doneMQDWrite(Addr mqdAddr, Addr addr) {
+    DPRINTF(PM4PacketProcessor, "PM4 unmap_queues MQD %p wrote to addr %p\n",
+            mqdAddr, addr);
+}
+
+void
+PM4PacketProcessor::mapProcess(PM4Queue *q, PM4MapProcess *pkt)
+{
+    q->incRptr(sizeof(PM4MapProcess));
+    uint16_t vmid = gpuDevice->allocateVMID(pkt->pasid);
+
+    DPRINTF(PM4PacketProcessor, "PM4 map_process pasid: %p vmid: %d quantum: "
+            "%d pt: %p signal: %p\n", pkt->pasid, vmid, pkt->processQuantum,
+            pkt->ptBase, pkt->completionSignal);
+
+    gpuDevice->getVM().setPageTableBase(vmid, pkt->ptBase);
+
+    delete pkt;
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::runList(PM4Queue *q, PM4RunList *pkt)
+{
+    DPRINTF(PM4PacketProcessor, "PM4 run_list base: %p size: %d\n",
+            pkt->ibBase, pkt->ibSize);
+
+    q->incRptr(sizeof(PM4RunList));
+
+    q->ib(true);
+    q->ibBase(pkt->ibBase);
+    q->rptr(0);
+    q->wptr(pkt->ibSize * sizeof(uint32_t));
+
+    delete pkt;
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::indirectBuffer(PM4Queue *q, PM4IndirectBuf *pkt)
+{
+    DPRINTF(PM4PacketProcessor, "PM4 indirect buffer, base: %p.\n",
+            pkt->ibBase);
+
+    q->incRptr(sizeof(PM4IndirectBuf));
+
+    q->ib(true);
+    q->ibBase(pkt->ibBase);
+    q->wptr(pkt->ibSize * sizeof(uint32_t));
+
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::switchBuffer(PM4Queue *q, PM4SwitchBuf *pkt)
+{
+    q->incRptr(sizeof(PM4SwitchBuf));
+
+    q->ib(true);
+    DPRINTF(PM4PacketProcessor, "PM4 switching buffer, rptr: %p.\n",
+            q->wptr());
+
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::setUconfigReg(PM4Queue *q, PM4SetUconfigReg *pkt)
+{
+    q->incRptr(sizeof(PM4SetUconfigReg));
+
+    // SET_UCONFIG_REG_START and pkt->offset are dword addresses
+    uint32_t reg_addr = (PACKET3_SET_UCONFIG_REG_START + pkt->offset) * 4;
+
+    gpuDevice->setRegVal(reg_addr, pkt->data);
+
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::waitRegMem(PM4Queue *q, PM4WaitRegMem *pkt)
+{
+    q->incRptr(sizeof(PM4WaitRegMem));
+
+    DPRINTF(PM4PacketProcessor, "PM4 WAIT_REG_MEM\nfunc: %d memSpace: %d op: "
+            "%d\n", pkt->function, pkt->memSpace, pkt->operation);
+    DPRINTF(PM4PacketProcessor, "    AddrLo/Reg1: %lx\n", pkt->memAddrLo);
+    DPRINTF(PM4PacketProcessor, "    AddrHi/Reg2: %lx\n", pkt->memAddrHi);
+    DPRINTF(PM4PacketProcessor, "    Reference: %lx\n", pkt->reference);
+    DPRINTF(PM4PacketProcessor, "    Mask: %lx\n", pkt->mask);
+    DPRINTF(PM4PacketProcessor, "    Poll Interval: %lx\n", pkt->pollInterval);
+
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::queryStatus(PM4Queue *q, PM4QueryStatus *pkt)
+{
+    q->incRptr(sizeof(PM4QueryStatus));
+
+    DPRINTF(PM4PacketProcessor, "PM4 query status contextId: %d, interruptSel:"
+            " %d command: %d, pasid: %d, doorbellOffset: %d, engineSel: %d "
+            "addr: %lx, data: %lx\n", pkt->contextId, pkt->interruptSel,
+            pkt->command, pkt->pasid, pkt->doorbellOffset, pkt->engineSel,
+            pkt->addr, pkt->data);
+
+    if (pkt->interruptSel == 0 && pkt->command == 2) {
+        // Write data value to fence address
+        Addr addr = getGARTAddr(pkt->addr);
+        DPRINTF(PM4PacketProcessor, "Using GART addr %lx\n", addr);
+        auto cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &) { queryStatusDone(q, pkt); }, pkt->data);
+        dmaWriteVirt(addr, sizeof(uint64_t), cb, &cb->dmaBuffer);
+    } else {
+        // No other combinations used in amdkfd v9
+        panic("query_status with interruptSel %d command %d not supported",
+              pkt->interruptSel, pkt->command);
+    }
+}
+
+void
+PM4PacketProcessor::queryStatusDone(PM4Queue *q, PM4QueryStatus *pkt)
+{
+    DPRINTF(PM4PacketProcessor, "PM4 query status complete\n");
+
+    delete pkt;
+    decodeNext(q);
+}
+
+void
+PM4PacketProcessor::writeMMIO(PacketPtr pkt, Addr mmio_offset)
+{
+    switch (mmio_offset) {
+      /* Hardware queue descriptor (HQD) registers */
+      case mmCP_HQD_VMID:
+        setHqdVmid(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_ACTIVE:
+        setHqdActive(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_PQ_BASE:
+        setHqdPqBase(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_PQ_BASE_HI:
+        setHqdPqBaseHi(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_PQ_DOORBELL_CONTROL:
+        setHqdPqDoorbellCtrl(pkt->getLE<uint32_t>());
+        gpuDevice->setDoorbellType(getKiqDoorbellOffset(), Compute);
+        break;
+      case mmCP_HQD_PQ_RPTR:
+        setHqdPqPtr(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_PQ_WPTR_LO:
+        setHqdPqWptrLo(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_PQ_WPTR_HI:
+        setHqdPqWptrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_PQ_RPTR_REPORT_ADDR:
+        setHqdPqRptrReportAddr(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_PQ_RPTR_REPORT_ADDR_HI:
+        setHqdPqRptrReportAddrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_PQ_WPTR_POLL_ADDR:
+        setHqdPqWptrPollAddr(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_PQ_WPTR_POLL_ADDR_HI:
+        setHqdPqWptrPollAddrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_HQD_IB_CONTROL:
+        setHqdIbCtrl(pkt->getLE<uint32_t>());
+        break;
+      /* Ring buffer registers */
+      case mmCP_RB_VMID:
+        setRbVmid(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB0_CNTL:
+        setRbCntl(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB0_WPTR:
+        setRbWptrLo(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB0_WPTR_HI:
+        setRbWptrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB0_RPTR_ADDR:
+        setRbRptrAddrLo(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB0_RPTR_ADDR_HI:
+        setRbRptrAddrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB_WPTR_POLL_ADDR_LO:
+        setRbWptrPollAddrLo(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB_WPTR_POLL_ADDR_HI:
+        setRbWptrPollAddrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB0_BASE:
+        setRbBaseLo(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB0_BASE_HI:
+        setRbBaseHi(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB_DOORBELL_CONTROL:
+        setRbDoorbellCntrl(pkt->getLE<uint32_t>());
+        gpuDevice->setDoorbellType(getPqDoorbellOffset(), Gfx);
+        break;
+      case mmCP_RB_DOORBELL_RANGE_LOWER:
+        setRbDoorbellRangeLo(pkt->getLE<uint32_t>());
+        break;
+      case mmCP_RB_DOORBELL_RANGE_UPPER:
+        setRbDoorbellRangeHi(pkt->getLE<uint32_t>());
+        break;
+      default:
+        break;
+    }
+}
+
+void
+PM4PacketProcessor::setHqdVmid(uint32_t data)
+{
+    kiq.hqd_vmid = data;
+}
+
+void
+PM4PacketProcessor::setHqdActive(uint32_t data)
+{
+    kiq.hqd_active = data;
+}
+
+void
+PM4PacketProcessor::setHqdPqBase(uint32_t data)
+{
+    kiq.hqd_pq_base_lo = data;
+}
+
+void
+PM4PacketProcessor::setHqdPqBaseHi(uint32_t data)
+{
+    kiq.hqd_pq_base_hi = data;
+}
+
+void
+PM4PacketProcessor::setHqdPqDoorbellCtrl(uint32_t data)
+{
+    kiq.hqd_pq_doorbell_control = data;
+}
+
+void
+PM4PacketProcessor::setHqdPqPtr(uint32_t data)
+{
+    kiq.rptr = data;
+}
+
+void
+PM4PacketProcessor::setHqdPqWptrLo(uint32_t data)
+{
+    /* Write pointer communicated through doorbell value. */
+}
+
+void
+PM4PacketProcessor::setHqdPqWptrHi(uint32_t data)
+{
+    /* Write pointer communicated through doorbell value. */
+}
+
+void
+PM4PacketProcessor::setHqdPqRptrReportAddr(uint32_t data)
+{
+    kiq.hqd_pq_rptr_report_addr_lo = data;
+}
+
+void
+PM4PacketProcessor::setHqdPqRptrReportAddrHi(uint32_t data)
+{
+    kiq.hqd_pq_rptr_report_addr_hi = data;
+}
+
+void
+PM4PacketProcessor::setHqdPqWptrPollAddr(uint32_t data)
+{
+    kiq.hqd_pq_wptr_poll_addr_lo = data;
+}
+
+void
+PM4PacketProcessor::setHqdPqWptrPollAddrHi(uint32_t data)
+{
+    kiq.hqd_pq_wptr_poll_addr_hi = data;
+}
+
+void
+PM4PacketProcessor::setHqdIbCtrl(uint32_t data)
+{
+    kiq.hqd_ib_control = data;
+}
+
+void
+PM4PacketProcessor::setRbVmid(uint32_t data)
+{
+    pq.hqd_vmid = data;
+}
+
+void
+PM4PacketProcessor::setRbCntl(uint32_t data)
+{
+    pq.hqd_pq_control = data;
+}
+
+void
+PM4PacketProcessor::setRbWptrLo(uint32_t data)
+{
+    pq.queueWptrLo = data;
+}
+
+void
+PM4PacketProcessor::setRbWptrHi(uint32_t data)
+{
+    pq.queueWptrHi = data;
+}
+
+void
+PM4PacketProcessor::setRbRptrAddrLo(uint32_t data)
+{
+    pq.queueRptrAddrLo = data;
+}
+
+void
+PM4PacketProcessor::setRbRptrAddrHi(uint32_t data)
+{
+    pq.queueRptrAddrHi = data;
+}
+
+void
+PM4PacketProcessor::setRbWptrPollAddrLo(uint32_t data)
+{
+    pq.hqd_pq_wptr_poll_addr_lo = data;
+}
+
+void
+PM4PacketProcessor::setRbWptrPollAddrHi(uint32_t data)
+{
+    pq.hqd_pq_wptr_poll_addr_hi = data;
+}
+
+void
+PM4PacketProcessor::setRbBaseLo(uint32_t data)
+{
+    pq.hqd_pq_base_lo = data;
+}
+
+void
+PM4PacketProcessor::setRbBaseHi(uint32_t data)
+{
+    pq.hqd_pq_base_hi = data;
+}
+
+void
+PM4PacketProcessor::setRbDoorbellCntrl(uint32_t data)
+{
+    pq.hqd_pq_doorbell_control = data;
+    pq.doorbellOffset = data & 0x1ffffffc;
+}
+
+void
+PM4PacketProcessor::setRbDoorbellRangeLo(uint32_t data)
+{
+    pq.doorbellRangeLo = data;
+}
+
+void
+PM4PacketProcessor::setRbDoorbellRangeHi(uint32_t data)
+{
+    pq.doorbellRangeHi = data;
+}
+
+void
+PM4PacketProcessor::serialize(CheckpointOut &cp) const
+{
+    // Serialize the DmaVirtDevice base class
+    DmaVirtDevice::serialize(cp);
+
+    int num_queues = queues.size();
+    Addr id[num_queues];
+    Addr mqd_base[num_queues];
+    Addr base[num_queues];
+    Addr rptr[num_queues];
+    Addr wptr[num_queues];
+    Addr ib_base[num_queues];
+    Addr ib_rptr[num_queues];
+    Addr ib_wptr[num_queues];
+    Addr offset[num_queues];
+    bool processing[num_queues];
+    bool ib[num_queues];
+
+    int i = 0;
+    for (auto iter : queues) {
+        PM4Queue *q = iter.second;
+        id[i] = q->id();
+        mqd_base[i] = q->mqdBase();
+        bool cur_state = q->ib();
+        q->ib(false);
+        base[i] = q->base() >> 8;
+        rptr[i] = q->getRptr();
+        wptr[i] = q->getWptr();
+        q->ib(true);
+        ib_base[i] = q->ibBase();
+        ib_rptr[i] = q->getRptr();
+        ib_wptr[i] = q->getWptr();
+        q->ib(cur_state);
+        offset[i] = q->offset();
+        processing[i] = q->processing();
+        ib[i] = q->ib();
+        i++;
+    }
+
+    SERIALIZE_SCALAR(num_queues);
+    SERIALIZE_ARRAY(id, num_queues);
+    SERIALIZE_ARRAY(mqd_base, num_queues);
+    SERIALIZE_ARRAY(base, num_queues);
+    SERIALIZE_ARRAY(rptr, num_queues);
+    SERIALIZE_ARRAY(wptr, num_queues);
+    SERIALIZE_ARRAY(ib_base, num_queues);
+    SERIALIZE_ARRAY(ib_rptr, num_queues);
+    SERIALIZE_ARRAY(ib_wptr, num_queues);
+    SERIALIZE_ARRAY(offset, num_queues);
+    SERIALIZE_ARRAY(processing, num_queues);
+    SERIALIZE_ARRAY(ib, num_queues);
+}
+
+void
+PM4PacketProcessor::unserialize(CheckpointIn &cp)
+{
+    // Serialize the DmaVirtDevice base class
+    DmaVirtDevice::unserialize(cp);
+
+    int num_queues = 0;
+    UNSERIALIZE_SCALAR(num_queues);
+
+    Addr id[num_queues];
+    Addr mqd_base[num_queues];
+    Addr base[num_queues];
+    Addr rptr[num_queues];
+    Addr wptr[num_queues];
+    Addr ib_base[num_queues];
+    Addr ib_rptr[num_queues];
+    Addr ib_wptr[num_queues];
+    Addr offset[num_queues];
+    bool processing[num_queues];
+    bool ib[num_queues];
+
+    UNSERIALIZE_ARRAY(id, num_queues);
+    UNSERIALIZE_ARRAY(mqd_base, num_queues);
+    UNSERIALIZE_ARRAY(base, num_queues);
+    UNSERIALIZE_ARRAY(rptr, num_queues);
+    UNSERIALIZE_ARRAY(wptr, num_queues);
+    UNSERIALIZE_ARRAY(ib_base, num_queues);
+    UNSERIALIZE_ARRAY(ib_rptr, num_queues);
+    UNSERIALIZE_ARRAY(ib_wptr, num_queues);
+    UNSERIALIZE_ARRAY(offset, num_queues);
+    UNSERIALIZE_ARRAY(processing, num_queues);
+    UNSERIALIZE_ARRAY(ib, num_queues);
+
+    for (int i = 0; i < num_queues; i++) {
+        QueueDesc *mqd = new QueueDesc();
+        memset(mqd, 0, sizeof(QueueDesc));
+
+        mqd->mqdBase = mqd_base[i] >> 8;
+        mqd->base = base[i];
+        mqd->rptr = rptr[i];
+        mqd->ibBase = ib_base[i];
+        mqd->ibRptr = ib_rptr[i];
+
+        newQueue(mqd, offset[i], nullptr, id[i]);
+
+        queues[id[i]]->ib(false);
+        queues[id[i]]->wptr(wptr[i]);
+        queues[id[i]]->ib(true);
+        queues[id[i]]->wptr(ib_wptr[i]);
+        queues[id[i]]->offset(offset[i]);
+        queues[id[i]]->processing(processing[i]);
+        queues[id[i]]->ib(ib[i]);
+        DPRINTF(PM4PacketProcessor, "PM4 queue %d, rptr: %p wptr: %p\n",
+                queues[id[i]]->id(), queues[id[i]]->rptr(),
+                queues[id[i]]->wptr());
+    }
+}
+
+} // namespace gem5
diff --git a/src/dev/amdgpu/pm4_packet_processor.hh b/src/dev/amdgpu/pm4_packet_processor.hh
new file mode 100644
index 0000000..c77edd2
--- /dev/null
+++ b/src/dev/amdgpu/pm4_packet_processor.hh
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DEV_AMDGPU_PM4_PACKET_PROCESSOR__
+#define __DEV_AMDGPU_PM4_PACKET_PROCESSOR__
+
+#include <unordered_map>
+
+#include "dev/amdgpu/amdgpu_device.hh"
+#include "dev/amdgpu/pm4_defines.hh"
+#include "dev/amdgpu/pm4_queues.hh"
+#include "dev/dma_virt_device.hh"
+#include "params/PM4PacketProcessor.hh"
+
+namespace gem5
+{
+
+class AMDGPUDevice;
+
+
+
+
+class PM4PacketProcessor : public DmaVirtDevice
+{
+    AMDGPUDevice *gpuDevice;
+    /* First graphics queue */
+    PrimaryQueue pq;
+    /* First compute queue */
+    QueueDesc kiq;
+
+    /* All PM4 queues, indexed by VMID */
+    std::unordered_map<uint16_t, PM4Queue *> queues;
+    /* A map of PM4 queues based on doorbell offset */
+    std::unordered_map<uint32_t, PM4Queue *> queuesMap;
+  public:
+    PM4PacketProcessor(const PM4PacketProcessorParams &p);
+
+    void setGPUDevice(AMDGPUDevice *gpu_device);
+
+    /**
+     * Inherited methods.
+     */
+    Tick write(PacketPtr pkt) override { return 0; }
+    Tick read(PacketPtr pkt) override { return 0; }
+    AddrRangeList getAddrRanges() const override;
+    void serialize(CheckpointOut &cp) const override;
+    void unserialize(CheckpointIn &cp) override;
+
+    /**
+     * Method for functional translation.
+     */
+    TranslationGenPtr translate(Addr vaddr, Addr size) override;
+
+    uint32_t getKiqDoorbellOffset() { return kiq.doorbell & 0x1ffffffc; }
+    uint32_t getPqDoorbellOffset() { return pq.doorbellOffset; }
+
+    Addr getGARTAddr(Addr addr) const;
+
+    /**
+     * Based on an offset communicated through doorbell write, the
+     * PM4PacketProcessor identifies which queue needs processing.
+     */
+    PM4Queue* getQueue(Addr offset, bool gfx = false);
+    /**
+     * The first graphics queue, the Primary Queueu a.k.a. RB0, needs to be
+     * mapped since all queue details are communicated through MMIOs to
+     * special registers.
+     */
+    void mapPq(Addr offset);
+    /**
+     * The first compute queue, the Kernel Interface Queueu a.k.a. KIQ, needs
+     * to be mapped since all queue details are communicated through MMIOs to
+     * special registers.
+     */
+    void mapKiq(Addr offset);
+    /**
+     * This method creates a new PM4Queue based on a queue descriptor and an
+     * offset.
+     */
+    void newQueue(QueueDesc *q, Addr offset, PM4MapQueues *pkt = nullptr,
+                  int id = -1);
+
+    /**
+     * This method start processing a PM4Queue from the current read pointer
+     * to the newly communicated write pointer (i.e., wptrOffset).
+     */
+    void process(PM4Queue *q, Addr wptrOffset);
+
+    /**
+     * Update read index on doorbell rings. We use write index, however read
+     * index == write index when the queue is empty. This allows us to save
+     * previous read index when a queue is remapped. The remapped queue will
+     * read from the previous read index rather than reset to zero.
+     */
+    void updateReadIndex(Addr offset, uint64_t rd_idx);
+
+    /**
+     * This method decodes the next packet in a PM4Queue.
+     */
+    void decodeNext(PM4Queue *q);
+    /**
+     * This method calls other PM4 packet processing methods based on the
+     * header of a PM4 packet.
+     */
+    void decodeHeader(PM4Queue *q, PM4Header header);
+
+    /* Methods that implement PM4 packets */
+    void writeData(PM4Queue *q, PM4WriteData *pkt);
+    void writeDataDone(PM4Queue *q, PM4WriteData *pkt, Addr addr);
+    void mapQueues(PM4Queue *q, PM4MapQueues *pkt);
+    void unmapQueues(PM4Queue *q, PM4UnmapQueues *pkt);
+    void doneMQDWrite(Addr mqdAddr, Addr addr);
+    void mapProcess(PM4Queue *q, PM4MapProcess *pkt);
+    void processMQD(PM4MapQueues *pkt, PM4Queue *q, Addr addr, QueueDesc *mqd,
+                    uint16_t vmid);
+    void processSDMAMQD(PM4MapQueues *pkt, PM4Queue *q, Addr addr,
+                        SDMAQueueDesc *mqd, uint16_t vmid);
+    void releaseMem(PM4Queue *q, PM4ReleaseMem *pkt);
+    void releaseMemDone(PM4Queue *q, PM4ReleaseMem *pkt, Addr addr);
+    void runList(PM4Queue *q, PM4RunList *pkt);
+    void indirectBuffer(PM4Queue *q, PM4IndirectBuf *pkt);
+    void switchBuffer(PM4Queue *q, PM4SwitchBuf *pkt);
+    void setUconfigReg(PM4Queue *q, PM4SetUconfigReg *pkt);
+    void waitRegMem(PM4Queue *q, PM4WaitRegMem *pkt);
+    void queryStatus(PM4Queue *q, PM4QueryStatus *pkt);
+    void queryStatusDone(PM4Queue *q, PM4QueryStatus *pkt);
+
+    /* Methods that implement MMIO regs */
+    void writeMMIO(PacketPtr pkt, Addr mmio_offset);
+
+    void setHqdVmid(uint32_t data);
+    void setHqdActive(uint32_t data);
+    void setHqdPqBase(uint32_t data);
+    void setHqdPqBaseHi(uint32_t data);
+    void setHqdPqDoorbellCtrl(uint32_t data);
+    void setHqdPqPtr(uint32_t data);
+    void setHqdPqWptrLo(uint32_t data);
+    void setHqdPqWptrHi(uint32_t data);
+    void setHqdPqRptrReportAddr(uint32_t data);
+    void setHqdPqRptrReportAddrHi(uint32_t data);
+    void setHqdPqWptrPollAddr(uint32_t data);
+    void setHqdPqWptrPollAddrHi(uint32_t data);
+    void setHqdIbCtrl(uint32_t data);
+    void setRbVmid(uint32_t data);
+    void setRbCntl(uint32_t data);
+    void setRbWptrLo(uint32_t data);
+    void setRbWptrHi(uint32_t data);
+    void setRbRptrAddrLo(uint32_t data);
+    void setRbRptrAddrHi(uint32_t data);
+    void setRbWptrPollAddrLo(uint32_t data);
+    void setRbWptrPollAddrHi(uint32_t data);
+    void setRbBaseLo(uint32_t data);
+    void setRbBaseHi(uint32_t data);
+    void setRbDoorbellCntrl(uint32_t data);
+    void setRbDoorbellRangeLo(uint32_t data);
+    void setRbDoorbellRangeHi(uint32_t data);
+};
+
+} // namespace gem5
+
+#endif //__DEV_AMDGPU_PM4_PACKET_PROCESSOR__
diff --git a/src/dev/amdgpu/pm4_queues.hh b/src/dev/amdgpu/pm4_queues.hh
new file mode 100644
index 0000000..4e8638b
--- /dev/null
+++ b/src/dev/amdgpu/pm4_queues.hh
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DEV_AMDGPU_PM4_QUEUES_HH__
+#define __DEV_AMDGPU_PM4_QUEUES_HH__
+
+namespace gem5
+{
+
+/**
+ * Queue descriptor with relevant MQD attributes. Taken from
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *     drivers/gpu/drm/amd/include/v9_structs.h
+ */
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t cp_mqd_readindex_lo;
+            uint32_t cp_mqd_readindex_hi;
+        };
+        uint64_t mqdReadIndex;
+    };
+    uint32_t cp_mqd_save_start_time_lo;
+    uint32_t cp_mqd_save_start_time_hi;
+    uint32_t cp_mqd_save_end_time_lo;
+    uint32_t cp_mqd_save_end_time_hi;
+    uint32_t cp_mqd_restore_start_time_lo;
+    uint32_t cp_mqd_restore_start_time_hi;
+    uint32_t cp_mqd_restore_end_time_lo;
+    uint32_t cp_mqd_restore_end_time_hi;
+    uint32_t disable_queue;
+    uint32_t reserved_107;
+    uint32_t gds_cs_ctxsw_cnt0;
+    uint32_t gds_cs_ctxsw_cnt1;
+    uint32_t gds_cs_ctxsw_cnt2;
+    uint32_t gds_cs_ctxsw_cnt3;
+    uint32_t reserved_112;
+    uint32_t reserved_113;
+    uint32_t cp_pq_exe_status_lo;
+    uint32_t cp_pq_exe_status_hi;
+    uint32_t cp_packet_id_lo;
+    uint32_t cp_packet_id_hi;
+    uint32_t cp_packet_exe_status_lo;
+    uint32_t cp_packet_exe_status_hi;
+    uint32_t gds_save_base_addr_lo;
+    uint32_t gds_save_base_addr_hi;
+    uint32_t gds_save_mask_lo;
+    uint32_t gds_save_mask_hi;
+    uint32_t ctx_save_base_addr_lo;
+    uint32_t ctx_save_base_addr_hi;
+    uint32_t dynamic_cu_mask_addr_lo;
+    uint32_t dynamic_cu_mask_addr_hi;
+    union
+    {
+        struct
+        {
+            uint32_t mqd_base_addr_lo;
+            uint32_t mqd_base_addr_hi;
+        };
+        uint64_t mqdBase;
+    };
+    uint32_t hqd_active;
+    uint32_t hqd_vmid;
+    uint32_t hqd_persistent_state;
+    uint32_t hqd_pipe_priority;
+    uint32_t hqd_queue_priority;
+    uint32_t hqd_quantum;
+    union
+    {
+        struct
+        {
+            uint32_t hqd_pq_base_lo;
+            uint32_t hqd_pq_base_hi;
+        };
+        uint64_t base;
+    };
+    union
+    {
+        uint32_t hqd_pq_rptr;
+        uint32_t rptr;
+    };
+    union
+    {
+        struct
+        {
+            uint32_t hqd_pq_rptr_report_addr_lo;
+            uint32_t hqd_pq_rptr_report_addr_hi;
+        };
+        uint64_t aqlRptr;
+    };
+    uint32_t hqd_pq_wptr_poll_addr_lo;
+    uint32_t hqd_pq_wptr_poll_addr_hi;
+    union
+    {
+        uint32_t hqd_pq_doorbell_control;
+        uint32_t doorbell;
+    };
+    uint32_t reserved_144;
+    uint32_t hqd_pq_control;
+    union
+    {
+        struct
+        {
+            uint32_t hqd_ib_base_addr_lo;
+            uint32_t hqd_ib_base_addr_hi;
+        };
+        Addr ibBase;
+    };
+    union
+    {
+        uint32_t hqd_ib_rptr;
+        uint32_t ibRptr;
+    };
+    uint32_t hqd_ib_control;
+    uint32_t hqd_iq_timer;
+    uint32_t hqd_iq_rptr;
+    uint32_t cp_hqd_dequeue_request;
+    uint32_t cp_hqd_dma_offload;
+    uint32_t cp_hqd_sema_cmd;
+    uint32_t cp_hqd_msg_type;
+    uint32_t cp_hqd_atomic0_preop_lo;
+    uint32_t cp_hqd_atomic0_preop_hi;
+    uint32_t cp_hqd_atomic1_preop_lo;
+    uint32_t cp_hqd_atomic1_preop_hi;
+    uint32_t cp_hqd_hq_status0;
+    uint32_t cp_hqd_hq_control0;
+    uint32_t cp_mqd_control;
+    uint32_t cp_hqd_hq_status1;
+    uint32_t cp_hqd_hq_control1;
+    uint32_t cp_hqd_eop_base_addr_lo;
+    uint32_t cp_hqd_eop_base_addr_hi;
+    uint32_t cp_hqd_eop_control;
+    uint32_t cp_hqd_eop_rptr;
+    uint32_t cp_hqd_eop_wptr;
+    uint32_t cp_hqd_eop_done_events;
+    uint32_t cp_hqd_ctx_save_base_addr_lo;
+    uint32_t cp_hqd_ctx_save_base_addr_hi;
+    uint32_t cp_hqd_ctx_save_control;
+    uint32_t cp_hqd_cntl_stack_offset;
+    uint32_t cp_hqd_cntl_stack_size;
+    uint32_t cp_hqd_wg_state_offset;
+    uint32_t cp_hqd_ctx_save_size;
+    uint32_t cp_hqd_gds_resource_state;
+    uint32_t cp_hqd_error;
+    uint32_t cp_hqd_eop_wptr_mem;
+    union
+    {
+        uint32_t cp_hqd_aql_control;
+        uint32_t aql;
+    };
+    uint32_t cp_hqd_pq_wptr_lo;
+    uint32_t cp_hqd_pq_wptr_hi;
+} QueueDesc;
+
+/**
+ * Queue descriptor for SDMA-based user queues (RLC queues). Taken from
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *     drivers/gpu/drm/amd/include/v9_structs.h
+ */
+typedef struct GEM5_PACKED
+{
+    uint32_t sdmax_rlcx_rb_cntl;
+    union
+    {
+        struct
+        {
+            uint32_t sdmax_rlcx_rb_base;
+            uint32_t sdmax_rlcx_rb_base_hi;
+        };
+        uint64_t rb_base;
+    };
+    uint32_t sdmax_rlcx_rb_rptr;
+    uint32_t sdmax_rlcx_rb_rptr_hi;
+    uint32_t sdmax_rlcx_rb_wptr;
+    uint32_t sdmax_rlcx_rb_wptr_hi;
+    uint32_t sdmax_rlcx_rb_wptr_poll_cntl;
+    uint32_t sdmax_rlcx_rb_rptr_addr_hi;
+    uint32_t sdmax_rlcx_rb_rptr_addr_lo;
+    uint32_t sdmax_rlcx_ib_cntl;
+    uint32_t sdmax_rlcx_ib_rptr;
+    uint32_t sdmax_rlcx_ib_offset;
+    uint32_t sdmax_rlcx_ib_base_lo;
+    uint32_t sdmax_rlcx_ib_base_hi;
+    uint32_t sdmax_rlcx_ib_size;
+    uint32_t sdmax_rlcx_skip_cntl;
+    uint32_t sdmax_rlcx_context_status;
+    uint32_t sdmax_rlcx_doorbell;
+    uint32_t sdmax_rlcx_status;
+    uint32_t sdmax_rlcx_doorbell_log;
+    uint32_t sdmax_rlcx_watermark;
+    uint32_t sdmax_rlcx_doorbell_offset;
+    uint32_t sdmax_rlcx_csa_addr_lo;
+    uint32_t sdmax_rlcx_csa_addr_hi;
+    uint32_t sdmax_rlcx_ib_sub_remain;
+    uint32_t sdmax_rlcx_preempt;
+    uint32_t sdmax_rlcx_dummy_reg;
+    uint32_t sdmax_rlcx_rb_wptr_poll_addr_hi;
+    uint32_t sdmax_rlcx_rb_wptr_poll_addr_lo;
+    uint32_t sdmax_rlcx_rb_aql_cntl;
+    uint32_t sdmax_rlcx_minor_ptr_update;
+    uint32_t sdmax_rlcx_midcmd_data0;
+    uint32_t sdmax_rlcx_midcmd_data1;
+    uint32_t sdmax_rlcx_midcmd_data2;
+    uint32_t sdmax_rlcx_midcmd_data3;
+    uint32_t sdmax_rlcx_midcmd_data4;
+    uint32_t sdmax_rlcx_midcmd_data5;
+    uint32_t sdmax_rlcx_midcmd_data6;
+    uint32_t sdmax_rlcx_midcmd_data7;
+    uint32_t sdmax_rlcx_midcmd_data8;
+    uint32_t sdmax_rlcx_midcmd_cntl;
+    uint32_t reserved_42;
+    uint32_t reserved_43;
+    uint32_t reserved_44;
+    uint32_t reserved_45;
+    uint32_t reserved_46;
+    uint32_t reserved_47;
+    uint32_t reserved_48;
+    uint32_t reserved_49;
+    uint32_t reserved_50;
+    uint32_t reserved_51;
+    uint32_t reserved_52;
+    uint32_t reserved_53;
+    uint32_t reserved_54;
+    uint32_t reserved_55;
+    uint32_t reserved_56;
+    uint32_t reserved_57;
+    uint32_t reserved_58;
+    uint32_t reserved_59;
+    uint32_t reserved_60;
+    uint32_t reserved_61;
+    uint32_t reserved_62;
+    uint32_t reserved_63;
+    uint32_t reserved_64;
+    uint32_t reserved_65;
+    uint32_t reserved_66;
+    uint32_t reserved_67;
+    uint32_t reserved_68;
+    uint32_t reserved_69;
+    uint32_t reserved_70;
+    uint32_t reserved_71;
+    uint32_t reserved_72;
+    uint32_t reserved_73;
+    uint32_t reserved_74;
+    uint32_t reserved_75;
+    uint32_t reserved_76;
+    uint32_t reserved_77;
+    uint32_t reserved_78;
+    uint32_t reserved_79;
+    uint32_t reserved_80;
+    uint32_t reserved_81;
+    uint32_t reserved_82;
+    uint32_t reserved_83;
+    uint32_t reserved_84;
+    uint32_t reserved_85;
+    uint32_t reserved_86;
+    uint32_t reserved_87;
+    uint32_t reserved_88;
+    uint32_t reserved_89;
+    uint32_t reserved_90;
+    uint32_t reserved_91;
+    uint32_t reserved_92;
+    uint32_t reserved_93;
+    uint32_t reserved_94;
+    uint32_t reserved_95;
+    uint32_t reserved_96;
+    uint32_t reserved_97;
+    uint32_t reserved_98;
+    uint32_t reserved_99;
+    uint32_t reserved_100;
+    uint32_t reserved_101;
+    uint32_t reserved_102;
+    uint32_t reserved_103;
+    uint32_t reserved_104;
+    uint32_t reserved_105;
+    uint32_t reserved_106;
+    uint32_t reserved_107;
+    uint32_t reserved_108;
+    uint32_t reserved_109;
+    uint32_t reserved_110;
+    uint32_t reserved_111;
+    uint32_t reserved_112;
+    uint32_t reserved_113;
+    uint32_t reserved_114;
+    uint32_t reserved_115;
+    uint32_t reserved_116;
+    uint32_t reserved_117;
+    uint32_t reserved_118;
+    uint32_t reserved_119;
+    uint32_t reserved_120;
+    uint32_t reserved_121;
+    uint32_t reserved_122;
+    uint32_t reserved_123;
+    uint32_t reserved_124;
+    uint32_t reserved_125;
+    /* reserved_126,127: repurposed for driver-internal use */
+    uint32_t sdma_engine_id;
+    uint32_t sdma_queue_id;
+} SDMAQueueDesc;
+
+/* The Primary Queue has extra attributes, which will be stored separately. */
+typedef struct PrimaryQueue : QueueDesc
+{
+    union
+    {
+        struct
+        {
+            uint32_t queueRptrAddrLo;
+            uint32_t queueRptrAddrHi;
+        };
+        Addr queueRptrAddr;
+    };
+    union
+    {
+        struct
+        {
+            uint32_t queueWptrLo;
+            uint32_t queueWptrHi;
+        };
+        Addr queueWptr;
+    };
+    uint32_t doorbellOffset;
+    uint32_t doorbellRangeLo;
+    uint32_t doorbellRangeHi;
+} PrimaryQueue;
+
+/**
+ * Class defining a PM4 queue.
+ */
+class PM4Queue
+{
+    int _id;
+
+    /* Queue descriptor read from the system memory of the simulated system. */
+    QueueDesc *q;
+
+    /**
+     * Most important fields of a PM4 queue are stored in the queue descriptor
+     * (i.e., QueueDesc). However, since the write pointers are communicated
+     * through the doorbell value, we will add separate atributes for them.
+     */
+    Addr _wptr;
+    Addr _ibWptr;
+    Addr _offset;
+    bool _processing;
+    bool _ib;
+    PM4MapQueues *_pkt;
+  public:
+    PM4Queue() : _id(0), q(nullptr), _wptr(0), _offset(0), _processing(false),
+        _ib(false), _pkt(nullptr) {}
+    PM4Queue(int id, QueueDesc *queue, Addr offset) :
+        _id(id), q(queue), _wptr(queue->rptr), _ibWptr(0), _offset(offset),
+        _processing(false), _ib(false), _pkt(nullptr) {}
+    PM4Queue(int id, QueueDesc *queue, Addr offset, PM4MapQueues *pkt) :
+        _id(id), q(queue), _wptr(queue->rptr), _ibWptr(0), _offset(offset),
+        _processing(false), _ib(false), _pkt(pkt) {}
+
+    QueueDesc *getMQD() { return q; }
+    int id() { return _id; }
+    Addr mqdBase() { return q->mqdBase; }
+    Addr base() { return q->base; }
+    Addr ibBase() { return q->ibBase; }
+
+    Addr
+    rptr()
+    {
+        if (ib()) return q->ibBase + q->ibRptr;
+        else return q->base + q->rptr;
+    }
+
+    Addr
+    wptr()
+    {
+        if (ib()) return q->ibBase + _ibWptr;
+        else return q->base + _wptr;
+    }
+
+    Addr
+    getRptr()
+    {
+        if (ib()) return q->ibRptr;
+        else return q->rptr;
+    }
+
+    Addr
+    getWptr()
+    {
+        if (ib()) return _ibWptr;
+        else return _wptr;
+    }
+
+    Addr offset() { return _offset; }
+    bool processing() { return _processing; }
+    bool ib() { return _ib; }
+
+    void id(int value) { _id = value; }
+    void base(Addr value) { q->base = value; }
+    void ibBase(Addr value) { q->ibBase = value; }
+
+    /**
+     * It seems that PM4 nop packets with count 0x3fff, not only do not
+     * consider the count value, they also fast forward the read pointer.
+     * Without proper sync packets this can potentially be dangerous, since
+     * more useful packets can be enqueued in the time between nop enqueu and
+     * nop processing.
+     */
+    void
+    fastforwardRptr()
+    {
+        if (ib()) q->ibRptr = _ibWptr;
+        else q->rptr = _wptr;
+    }
+
+    void
+    incRptr(Addr value)
+    {
+        if (ib()) q->ibRptr += value;
+        else q->rptr += value;
+    }
+
+    void
+    rptr(Addr value)
+    {
+        if (ib()) q->ibRptr = value;
+        else q->rptr = value;
+    }
+
+    void
+    wptr(Addr value)
+    {
+        if (ib()) _ibWptr = value;
+        else _wptr = value;
+    }
+
+    void offset(Addr value) { _offset = value; }
+    void processing(bool value) { _processing = value; }
+    void ib(bool value) { _ib = value; }
+    uint32_t me() { if (_pkt) return _pkt->me; else return 0; }
+    uint32_t pipe() { if (_pkt) return _pkt->pipe; else return 0; }
+    uint32_t queue() { if (_pkt) return _pkt->queueSlot; else return 0; }
+    bool privileged() { assert(_pkt); return _pkt->queueSel == 0 ? 1 : 0; }
+};
+
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_PM4_QUEUES_HH__
diff --git a/src/dev/amdgpu/sdma_commands.hh b/src/dev/amdgpu/sdma_commands.hh
new file mode 100644
index 0000000..50f942b
--- /dev/null
+++ b/src/dev/amdgpu/sdma_commands.hh
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_AMDGPU_SDMA_COMMANDS_HH__
+#define __DEV_AMDGPU_SDMA_COMMANDS_HH__
+
+/**
+ * Commands for the SDMA engine. The header files can be found here:
+ *
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *      drivers/gpu/drm/amd/amdgpu/vega10_sdma_pkt_open.h
+ */
+#define SDMA_OP_NOP  0
+#define SDMA_OP_COPY  1
+#define SDMA_OP_WRITE  2
+#define SDMA_OP_INDIRECT  4
+#define SDMA_OP_FENCE  5
+#define SDMA_OP_TRAP  6
+#define SDMA_OP_SEM  7
+#define SDMA_OP_POLL_REGMEM  8
+#define SDMA_OP_COND_EXE  9
+#define SDMA_OP_ATOMIC  10
+#define SDMA_OP_CONST_FILL  11
+#define SDMA_OP_PTEPDE  12
+#define SDMA_OP_TIMESTAMP  13
+#define SDMA_OP_SRBM_WRITE  14
+#define SDMA_OP_PRE_EXE  15
+#define SDMA_OP_DUMMY_TRAP  16
+#define SDMA_SUBOP_TIMESTAMP_SET  0
+#define SDMA_SUBOP_TIMESTAMP_GET  1
+#define SDMA_SUBOP_TIMESTAMP_GET_GLOBAL  2
+#define SDMA_SUBOP_COPY_LINEAR  0
+#define SDMA_SUBOP_COPY_LINEAR_SUB_WIND  4
+#define SDMA_SUBOP_COPY_TILED  1
+#define SDMA_SUBOP_COPY_TILED_SUB_WIND  5
+#define SDMA_SUBOP_COPY_T2T_SUB_WIND  6
+#define SDMA_SUBOP_COPY_SOA  3
+#define SDMA_SUBOP_COPY_DIRTY_PAGE  7
+#define SDMA_SUBOP_COPY_LINEAR_PHY  8
+#define SDMA_SUBOP_WRITE_LINEAR  0
+#define SDMA_SUBOP_WRITE_TILED  1
+#define SDMA_SUBOP_PTEPDE_GEN  0
+#define SDMA_SUBOP_PTEPDE_COPY  1
+#define SDMA_SUBOP_PTEPDE_RMW  2
+#define SDMA_SUBOP_PTEPDE_COPY_BACKWARDS  3
+#define SDMA_SUBOP_DATA_FILL_MULTI  1
+#define SDMA_SUBOP_POLL_REG_WRITE_MEM  1
+#define SDMA_SUBOP_POLL_DBIT_WRITE_MEM  2
+#define SDMA_SUBOP_POLL_MEM_VERIFY  3
+#define HEADER_AGENT_DISPATCH  4
+#define HEADER_BARRIER  5
+#define SDMA_OP_AQL_COPY  0
+#define SDMA_OP_AQL_BARRIER_OR  0
+
+#endif // __DEV_AMDGPU_SDMA_COMMANDS_HH__
diff --git a/src/dev/amdgpu/sdma_engine.cc b/src/dev/amdgpu/sdma_engine.cc
new file mode 100644
index 0000000..df08e32
--- /dev/null
+++ b/src/dev/amdgpu/sdma_engine.cc
@@ -0,0 +1,1158 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dev/amdgpu/sdma_engine.hh"
+
+#include "arch/amdgpu/vega/pagetable_walker.hh"
+#include "arch/generic/mmu.hh"
+#include "dev/amdgpu/interrupt_handler.hh"
+#include "dev/amdgpu/sdma_commands.hh"
+#include "dev/amdgpu/sdma_mmio.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+#include "params/SDMAEngine.hh"
+
+namespace gem5
+{
+
+SDMAEngine::SDMAEngine(const SDMAEngineParams &p)
+    : DmaVirtDevice(p), id(0), gfxBase(0), gfxRptr(0),
+      gfxDoorbell(0), gfxDoorbellOffset(0), gfxWptr(0), pageBase(0),
+      pageRptr(0), pageDoorbell(0), pageDoorbellOffset(0),
+      pageWptr(0), gpuDevice(nullptr), walker(p.walker)
+{
+    gfx.ib(&gfxIb);
+    gfxIb.parent(&gfx);
+    gfx.valid(true);
+    gfxIb.valid(true);
+
+    page.ib(&pageIb);
+    pageIb.parent(&page);
+    page.valid(true);
+    pageIb.valid(true);
+
+    rlc0.ib(&rlc0Ib);
+    rlc0Ib.parent(&rlc0);
+
+    rlc1.ib(&rlc1Ib);
+    rlc1Ib.parent(&rlc1);
+}
+
+void
+SDMAEngine::setGPUDevice(AMDGPUDevice *gpu_device)
+{
+    gpuDevice = gpu_device;
+    walker->setDevRequestor(gpuDevice->vramRequestorId());
+}
+
+int
+SDMAEngine::getIHClientId()
+{
+    switch (id) {
+      case 0:
+        return SOC15_IH_CLIENTID_SDMA0;
+      case 1:
+        return SOC15_IH_CLIENTID_SDMA1;
+      default:
+        panic("Unknown SDMA id");
+    }
+}
+
+Addr
+SDMAEngine::getGARTAddr(Addr addr) const
+{
+    if (!gpuDevice->getVM().inAGP(addr)) {
+        Addr low_bits = bits(addr, 11, 0);
+        addr = (((addr >> 12) << 3) << 12) | low_bits;
+    }
+    return addr;
+}
+
+/**
+ * GPUController will perform DMA operations on VAs, and because
+ * page faults are not currently supported for GPUController, we
+ * must be able to find the pages mapped for the process.
+ */
+TranslationGenPtr
+SDMAEngine::translate(Addr vaddr, Addr size)
+{
+    if (gpuDevice->getVM().inAGP(vaddr)) {
+        // Use AGP translation gen
+        return TranslationGenPtr(
+            new AMDGPUVM::AGPTranslationGen(&gpuDevice->getVM(), vaddr, size));
+    } else if (gpuDevice->getVM().inMMHUB(vaddr)) {
+        // Use MMHUB translation gen
+        return TranslationGenPtr(new AMDGPUVM::MMHUBTranslationGen(
+                                            &gpuDevice->getVM(), vaddr, size));
+    }
+
+    // Assume GART otherwise as this is the only other translation aperture
+    // available to the SDMA engine processor.
+    return TranslationGenPtr(
+        new AMDGPUVM::GARTTranslationGen(&gpuDevice->getVM(), vaddr, size));
+}
+
+void
+SDMAEngine::registerRLCQueue(Addr doorbell, Addr rb_base)
+{
+    // Get first free RLC
+    if (!rlc0.valid()) {
+        DPRINTF(SDMAEngine, "Doorbell %lx mapped to RLC0\n", doorbell);
+        rlcMap.insert(std::make_pair(doorbell, 0));
+        rlc0.valid(true);
+        rlc0.base(rb_base);
+        rlc0.rptr(0);
+        rlc0.wptr(0);
+        rlc0.processing(false);
+        // TODO: size - I think pull from MQD 2^rb_cntrl[6:1]-1
+        rlc0.size(1024*1024);
+    } else if (!rlc1.valid()) {
+        DPRINTF(SDMAEngine, "Doorbell %lx mapped to RLC1\n", doorbell);
+        rlcMap.insert(std::make_pair(doorbell, 1));
+        rlc1.valid(true);
+        rlc1.base(rb_base);
+        rlc1.rptr(1);
+        rlc1.wptr(1);
+        rlc1.processing(false);
+        // TODO: size - I think pull from MQD 2^rb_cntrl[6:1]-1
+        rlc1.size(1024*1024);
+    } else {
+        panic("No free RLCs. Check they are properly unmapped.");
+    }
+}
+
+void
+SDMAEngine::unregisterRLCQueue(Addr doorbell)
+{
+    assert(rlcMap.find(doorbell) != rlcMap.end());
+
+    if (rlcMap[doorbell] == 0) {
+        rlc0.valid(false);
+        rlcMap.erase(doorbell);
+    } else if (rlcMap[doorbell] == 1) {
+        rlc1.valid(false);
+        rlcMap.erase(doorbell);
+    } else {
+        panic("Cannot unregister unknown RLC queue: %d\n", rlcMap[doorbell]);
+    }
+}
+
+/* Start decoding packets from the Gfx queue. */
+void
+SDMAEngine::processGfx(Addr wptrOffset)
+{
+    gfx.setWptr(wptrOffset);
+    if (!gfx.processing()) {
+        gfx.processing(true);
+        decodeNext(&gfx);
+    }
+}
+
+/* Start decoding packets from the Page queue. */
+void
+SDMAEngine::processPage(Addr wptrOffset)
+{
+    page.setWptr(wptrOffset);
+    if (!page.processing()) {
+        page.processing(true);
+        decodeNext(&page);
+    }
+}
+
+/* Process RLC queue at given doorbell. */
+void
+SDMAEngine::processRLC(Addr doorbellOffset, Addr wptrOffset)
+{
+    assert(rlcMap.find(doorbellOffset) != rlcMap.end());
+
+    if (rlcMap[doorbellOffset] == 0) {
+        processRLC0(wptrOffset);
+    } else if (rlcMap[doorbellOffset] == 1) {
+        processRLC1(wptrOffset);
+    } else {
+        panic("Cannot process unknown RLC queue: %d\n",
+               rlcMap[doorbellOffset]);
+    }
+}
+
+/* Start decoding packets from the RLC0 queue. */
+void
+SDMAEngine::processRLC0(Addr wptrOffset)
+{
+    assert(rlc0.valid());
+
+    rlc0.setWptr(wptrOffset);
+    if (!rlc0.processing()) {
+        cur_vmid = 1;
+        rlc0.processing(true);
+        decodeNext(&rlc0);
+    }
+}
+
+/* Start decoding packets from the RLC1 queue. */
+void
+SDMAEngine::processRLC1(Addr wptrOffset)
+{
+    assert(rlc1.valid());
+
+    rlc1.setWptr(wptrOffset);
+    if (!rlc1.processing()) {
+        cur_vmid = 1;
+        rlc1.processing(true);
+        decodeNext(&rlc1);
+    }
+}
+
+/* Decoding next packet in the queue. */
+void
+SDMAEngine::decodeNext(SDMAQueue *q)
+{
+    DPRINTF(SDMAEngine, "SDMA decode rptr %p wptr %p\n", q->rptr(), q->wptr());
+
+    if (q->rptr() != q->wptr()) {
+        // We are using lambda functions passed to the DmaVirtCallback objects
+        // which will call the actuall callback method (e.g., decodeHeader).
+        // The dmaBuffer member of the DmaVirtCallback is passed to the lambda
+        // function as header in this case.
+        auto cb = new DmaVirtCallback<uint32_t>(
+            [ = ] (const uint32_t &header)
+                { decodeHeader(q, header); });
+        dmaReadVirt(q->rptr(), sizeof(uint32_t), cb, &cb->dmaBuffer);
+    } else {
+        q->processing(false);
+        if (q->parent()) {
+            DPRINTF(SDMAEngine, "SDMA switching queues\n");
+            decodeNext(q->parent());
+        }
+        cur_vmid = 0;
+    }
+}
+
+/* Decoding the header of a packet. */
+void
+SDMAEngine::decodeHeader(SDMAQueue *q, uint32_t header)
+{
+    q->incRptr(sizeof(header));
+    int opcode = bits(header, 7, 0);
+    int sub_opcode = bits(header, 15, 8);
+
+    DmaVirtCallback<uint64_t> *cb = nullptr;
+    void *dmaBuffer = nullptr;
+
+    DPRINTF(SDMAEngine, "SDMA opcode %p sub-opcode %p\n", opcode, sub_opcode);
+
+    switch(opcode) {
+      case SDMA_OP_NOP: {
+        uint32_t NOP_count = (header >> 16) & 0x3FFF;
+        DPRINTF(SDMAEngine, "SDMA NOP packet with count %d\n", NOP_count);
+        if (NOP_count > 0) q->incRptr(NOP_count * 4);
+        decodeNext(q);
+        } break;
+      case SDMA_OP_COPY: {
+        DPRINTF(SDMAEngine, "SDMA Copy packet\n");
+        switch (sub_opcode) {
+          case SDMA_SUBOP_COPY_LINEAR: {
+            dmaBuffer = new sdmaCopy();
+            cb = new DmaVirtCallback<uint64_t>(
+                [ = ] (const uint64_t &)
+                    { copy(q, (sdmaCopy *)dmaBuffer); });
+            dmaReadVirt(q->rptr(), sizeof(sdmaCopy), cb, dmaBuffer);
+            } break;
+          case SDMA_SUBOP_COPY_LINEAR_SUB_WIND: {
+            panic("SDMA_SUBOP_COPY_LINEAR_SUB_WIND not implemented");
+            } break;
+          case SDMA_SUBOP_COPY_TILED: {
+            panic("SDMA_SUBOP_COPY_TILED not implemented");
+            } break;
+          case SDMA_SUBOP_COPY_TILED_SUB_WIND: {
+            panic("SDMA_SUBOP_COPY_TILED_SUB_WIND not implemented");
+            } break;
+          case SDMA_SUBOP_COPY_T2T_SUB_WIND: {
+            panic("SDMA_SUBOP_COPY_T2T_SUB_WIND not implemented");
+            } break;
+          case SDMA_SUBOP_COPY_SOA: {
+            panic("SDMA_SUBOP_COPY_SOA not implemented");
+            } break;
+          case SDMA_SUBOP_COPY_DIRTY_PAGE: {
+            panic("SDMA_SUBOP_COPY_DIRTY_PAGE not implemented");
+            } break;
+          case SDMA_SUBOP_COPY_LINEAR_PHY: {
+            panic("SDMA_SUBOP_COPY_LINEAR_PHY  not implemented");
+            } break;
+          default: {
+            panic("SDMA unknown copy sub-opcode.");
+            } break;
+        }
+        } break;
+      case SDMA_OP_WRITE: {
+        DPRINTF(SDMAEngine, "SDMA Write packet\n");
+        switch (sub_opcode) {
+          case SDMA_SUBOP_WRITE_LINEAR: {
+            dmaBuffer = new sdmaWrite();
+            cb = new DmaVirtCallback<uint64_t>(
+                [ = ] (const uint64_t &)
+                    { write(q, (sdmaWrite *)dmaBuffer); });
+            dmaReadVirt(q->rptr(), sizeof(sdmaWrite), cb, dmaBuffer);
+            } break;
+          case SDMA_SUBOP_WRITE_TILED: {
+            panic("SDMA_SUBOP_WRITE_TILED not implemented.\n");
+            } break;
+          default:
+            break;
+        }
+        } break;
+      case SDMA_OP_INDIRECT: {
+        DPRINTF(SDMAEngine, "SDMA IndirectBuffer packet\n");
+        dmaBuffer = new sdmaIndirectBuffer();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { indirectBuffer(q, (sdmaIndirectBuffer *)dmaBuffer); });
+        dmaReadVirt(q->rptr(), sizeof(sdmaIndirectBuffer), cb, dmaBuffer);
+        } break;
+      case SDMA_OP_FENCE: {
+        DPRINTF(SDMAEngine, "SDMA Fence packet\n");
+        dmaBuffer = new sdmaFence();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { fence(q, (sdmaFence *)dmaBuffer); });
+        dmaReadVirt(q->rptr(), sizeof(sdmaFence), cb, dmaBuffer);
+        } break;
+      case SDMA_OP_TRAP: {
+        DPRINTF(SDMAEngine, "SDMA Trap packet\n");
+        dmaBuffer = new sdmaTrap();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { trap(q, (sdmaTrap *)dmaBuffer); });
+        dmaReadVirt(q->rptr(), sizeof(sdmaTrap), cb, dmaBuffer);
+        } break;
+      case SDMA_OP_SEM: {
+        q->incRptr(sizeof(sdmaSemaphore));
+        warn("SDMA_OP_SEM not implemented");
+        decodeNext(q);
+        } break;
+      case SDMA_OP_POLL_REGMEM: {
+        DPRINTF(SDMAEngine, "SDMA PollRegMem packet\n");
+        sdmaPollRegMemHeader *h = new sdmaPollRegMemHeader();
+        *h = *(sdmaPollRegMemHeader *)&header;
+        dmaBuffer = new sdmaPollRegMem();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { pollRegMem(q, h, (sdmaPollRegMem *)dmaBuffer); });
+        dmaReadVirt(q->rptr(), sizeof(sdmaPollRegMem), cb, dmaBuffer);
+        switch (sub_opcode) {
+          case SDMA_SUBOP_POLL_REG_WRITE_MEM: {
+            panic("SDMA_SUBOP_POLL_REG_WRITE_MEM not implemented");
+            } break;
+          case SDMA_SUBOP_POLL_DBIT_WRITE_MEM: {
+            panic("SDMA_SUBOP_POLL_DBIT_WRITE_MEM not implemented");
+            } break;
+          case SDMA_SUBOP_POLL_MEM_VERIFY: {
+            panic("SDMA_SUBOP_POLL_MEM_VERIFY not implemented");
+            } break;
+          default:
+            break;
+        }
+        } break;
+      case SDMA_OP_COND_EXE: {
+        q->incRptr(sizeof(sdmaCondExec));
+        warn("SDMA_OP_SEM not implemented");
+        decodeNext(q);
+        } break;
+      case SDMA_OP_ATOMIC: {
+        q->incRptr(sizeof(sdmaAtomic));
+        warn("SDMA_OP_ATOMIC not implemented");
+        decodeNext(q);
+        } break;
+      case SDMA_OP_CONST_FILL: {
+        q->incRptr(sizeof(sdmaConstFill));
+        warn("SDMA_OP_CONST_FILL not implemented");
+        decodeNext(q);
+        } break;
+      case SDMA_OP_PTEPDE: {
+        DPRINTF(SDMAEngine, "SDMA PTEPDE packet\n");
+        switch (sub_opcode) {
+          case SDMA_SUBOP_PTEPDE_GEN:
+            DPRINTF(SDMAEngine, "SDMA PTEPDE_GEN sub-opcode\n");
+            dmaBuffer = new sdmaPtePde();
+            cb = new DmaVirtCallback<uint64_t>(
+                [ = ] (const uint64_t &)
+                    { ptePde(q, (sdmaPtePde *)dmaBuffer); });
+            dmaReadVirt(q->rptr(), sizeof(sdmaPtePde), cb, dmaBuffer);
+            break;
+          case SDMA_SUBOP_PTEPDE_COPY:
+            panic("SDMA_SUBOP_PTEPDE_COPY not implemented");
+            break;
+          case SDMA_SUBOP_PTEPDE_COPY_BACKWARDS:
+            panic("SDMA_SUBOP_PTEPDE_COPY not implemented");
+            break;
+          case SDMA_SUBOP_PTEPDE_RMW: {
+            panic("SDMA_SUBOP_PTEPDE_RMW not implemented");
+            } break;
+          default:
+            DPRINTF(SDMAEngine, "Unsupported PTEPDE sub-opcode %d\n",
+                    sub_opcode);
+            decodeNext(q);
+          break;
+        }
+        } break;
+      case SDMA_OP_TIMESTAMP: {
+        q->incRptr(sizeof(sdmaTimestamp));
+        switch (sub_opcode) {
+          case SDMA_SUBOP_TIMESTAMP_SET: {
+            } break;
+          case SDMA_SUBOP_TIMESTAMP_GET: {
+            } break;
+          case SDMA_SUBOP_TIMESTAMP_GET_GLOBAL: {
+            } break;
+          default:
+            break;
+        }
+        warn("SDMA_OP_TIMESTAMP not implemented");
+        decodeNext(q);
+        } break;
+      case SDMA_OP_SRBM_WRITE: {
+        DPRINTF(SDMAEngine, "SDMA SRBMWrite packet\n");
+        sdmaSRBMWriteHeader *header = new sdmaSRBMWriteHeader();
+        *header = *(sdmaSRBMWriteHeader *)&header;
+        dmaBuffer = new sdmaSRBMWrite();
+        cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &)
+                { srbmWrite(q, header, (sdmaSRBMWrite *)dmaBuffer); });
+        dmaReadVirt(q->rptr(), sizeof(sdmaSRBMWrite), cb, dmaBuffer);
+        } break;
+      case SDMA_OP_PRE_EXE: {
+        q->incRptr(sizeof(sdmaPredExec));
+        warn("SDMA_OP_PRE_EXE not implemented");
+        decodeNext(q);
+        } break;
+      case SDMA_OP_DUMMY_TRAP: {
+        q->incRptr(sizeof(sdmaDummyTrap));
+        warn("SDMA_OP_DUMMY_TRAP not implemented");
+        decodeNext(q);
+        } break;
+      default: {
+        panic("Invalid SDMA packet.\n");
+        } break;
+    }
+}
+
+/* Implements a write packet. */
+void
+SDMAEngine::write(SDMAQueue *q, sdmaWrite *pkt)
+{
+    q->incRptr(sizeof(sdmaWrite));
+    // count represents the number of dwords - 1 to write
+    pkt->count++;
+    DPRINTF(SDMAEngine, "Write %d dwords to %lx\n", pkt->count, pkt->dest);
+
+    // first we have to read needed data from the SDMA queue
+    uint32_t *dmaBuffer = new uint32_t[pkt->count];
+    auto cb = new DmaVirtCallback<uint64_t>(
+        [ = ] (const uint64_t &) { writeReadData(q, pkt, dmaBuffer); });
+    dmaReadVirt(q->rptr(), sizeof(uint32_t) * pkt->count, cb,
+                (void *)dmaBuffer);
+}
+
+/* Completion of data reading for a write packet. */
+void
+SDMAEngine::writeReadData(SDMAQueue *q, sdmaWrite *pkt, uint32_t *dmaBuffer)
+{
+    int bufferSize = sizeof(uint32_t) * pkt->count;
+    q->incRptr(bufferSize);
+
+    DPRINTF(SDMAEngine, "Write packet data:\n");
+    for (int i = 0; i < pkt->count; ++i) {
+        DPRINTF(SDMAEngine, "%08x\n", dmaBuffer[i]);
+    }
+
+    // lastly we write read data to the destination address
+    if (gpuDevice->getVM().inMMHUB(pkt->dest)) {
+        Addr mmhubAddr = pkt->dest - gpuDevice->getVM().getMMHUBBase();
+        gpuDevice->getMemMgr()->writeRequest(mmhubAddr, (uint8_t *)dmaBuffer,
+                                           bufferSize);
+
+        delete []dmaBuffer;
+        delete pkt;
+        decodeNext(q);
+    } else {
+        // TODO: getGARTAddr?
+        pkt->dest = getGARTAddr(pkt->dest);
+        auto cb = new DmaVirtCallback<uint32_t>(
+            [ = ] (const uint64_t &) { writeDone(q, pkt, dmaBuffer); });
+        dmaWriteVirt(pkt->dest, bufferSize, cb, (void *)dmaBuffer);
+    }
+}
+
+/* Completion of a write packet. */
+void
+SDMAEngine::writeDone(SDMAQueue *q, sdmaWrite *pkt, uint32_t *dmaBuffer)
+{
+    DPRINTF(SDMAEngine, "Write packet completed to %p, %d dwords\n",
+            pkt->dest, pkt->count);
+    delete []dmaBuffer;
+    delete pkt;
+    decodeNext(q);
+}
+
+/* Implements a copy packet. */
+void
+SDMAEngine::copy(SDMAQueue *q, sdmaCopy *pkt)
+{
+    DPRINTF(SDMAEngine, "Copy src: %lx -> dest: %lx count %d\n",
+            pkt->source, pkt->dest, pkt->count);
+    q->incRptr(sizeof(sdmaCopy));
+    // count represents the number of bytes - 1 to be copied
+    pkt->count++;
+    DPRINTF(SDMAEngine, "Getting GART addr for %lx\n", pkt->source);
+    pkt->source = getGARTAddr(pkt->source);
+    DPRINTF(SDMAEngine, "GART addr %lx\n", pkt->source);
+
+    // first we have to read needed data from the source address
+    uint8_t *dmaBuffer = new uint8_t[pkt->count];
+    auto cb = new DmaVirtCallback<uint64_t>(
+        [ = ] (const uint64_t &) { copyReadData(q, pkt, dmaBuffer); });
+    dmaReadVirt(pkt->source, pkt->count, cb, (void *)dmaBuffer);
+}
+
+/* Completion of data reading for a copy packet. */
+void
+SDMAEngine::copyReadData(SDMAQueue *q, sdmaCopy *pkt, uint8_t *dmaBuffer)
+{
+    // lastly we write read data to the destination address
+    DPRINTF(SDMAEngine, "Copy packet data:\n");
+    uint64_t *dmaBuffer64 = new uint64_t[pkt->count/8];
+    memcpy(dmaBuffer64, dmaBuffer, pkt->count);
+    for (int i = 0; i < pkt->count/8; ++i) {
+        DPRINTF(SDMAEngine, "%016lx\n", dmaBuffer64[i]);
+    }
+    delete [] dmaBuffer64;
+
+    // Aperture is unknown until translating. Do a dummy translation.
+    auto tgen = translate(pkt->dest, 64);
+    auto addr_range = *(tgen->begin());
+    Addr tmp_addr = addr_range.paddr;
+    DPRINTF(SDMAEngine, "Tmp addr %#lx -> %#lx\n", pkt->dest, tmp_addr);
+
+    // Writing generated data to the destination address.
+    if ((gpuDevice->getVM().inMMHUB(pkt->dest) && cur_vmid == 0) ||
+        (gpuDevice->getVM().inMMHUB(tmp_addr) && cur_vmid != 0)) {
+        Addr mmhubAddr = 0;
+        if (cur_vmid == 0) {
+            mmhubAddr = pkt->dest - gpuDevice->getVM().getMMHUBBase();
+        } else {
+            mmhubAddr = tmp_addr - gpuDevice->getVM().getMMHUBBase();
+        }
+        DPRINTF(SDMAEngine, "Copying to MMHUB address %#lx\n", mmhubAddr);
+        gpuDevice->getMemMgr()->writeRequest(mmhubAddr, dmaBuffer, pkt->count);
+
+        delete pkt;
+        decodeNext(q);
+    } else {
+        auto cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &) { copyDone(q, pkt, dmaBuffer); });
+        dmaWriteVirt(pkt->dest, pkt->count, cb, (void *)dmaBuffer);
+    }
+}
+
+/* Completion of a copy packet. */
+void
+SDMAEngine::copyDone(SDMAQueue *q, sdmaCopy *pkt, uint8_t *dmaBuffer)
+{
+    DPRINTF(SDMAEngine, "Copy completed to %p, %d dwords\n",
+            pkt->dest, pkt->count);
+    delete []dmaBuffer;
+    delete pkt;
+    decodeNext(q);
+}
+
+/* Implements an indirect buffer packet. */
+void
+SDMAEngine::indirectBuffer(SDMAQueue *q, sdmaIndirectBuffer *pkt)
+{
+    q->ib()->base(getGARTAddr(pkt->base));
+    q->ib()->rptr(0);
+    q->ib()->size(pkt->size * sizeof(uint32_t) + 1);
+    q->ib()->setWptr(pkt->size * sizeof(uint32_t));
+
+    q->incRptr(sizeof(sdmaIndirectBuffer));
+
+    delete pkt;
+    decodeNext(q->ib());
+}
+
+/* Implements a fence packet. */
+void
+SDMAEngine::fence(SDMAQueue *q, sdmaFence *pkt)
+{
+    q->incRptr(sizeof(sdmaFence));
+    pkt->dest = getGARTAddr(pkt->dest);
+
+    // Writing the data from the fence packet to the destination address.
+    auto cb = new DmaVirtCallback<uint32_t>(
+        [ = ] (const uint32_t &) { fenceDone(q, pkt); }, pkt->data);
+    dmaWriteVirt(pkt->dest, sizeof(pkt->data), cb, &cb->dmaBuffer);
+}
+
+/* Completion of a fence packet. */
+void
+SDMAEngine::fenceDone(SDMAQueue *q, sdmaFence *pkt)
+{
+    DPRINTF(SDMAEngine, "Fence completed to %p, data 0x%x\n",
+            pkt->dest, pkt->data);
+    delete pkt;
+    decodeNext(q);
+}
+
+/* Implements a trap packet. */
+void
+SDMAEngine::trap(SDMAQueue *q, sdmaTrap *pkt)
+{
+    q->incRptr(sizeof(sdmaTrap));
+
+    DPRINTF(SDMAEngine, "Trap contextId: %p rbRptr: %p ibOffset: %p\n",
+            pkt->contextId, pkt->rbRptr, pkt->ibOffset);
+
+    gpuDevice->getIH()->prepareInterruptCookie(pkt->contextId, 0,
+            getIHClientId(), TRAP_ID);
+    gpuDevice->getIH()->submitInterruptCookie();
+
+    delete pkt;
+    decodeNext(q);
+}
+
+/* Implements a write SRBM packet. */
+void
+SDMAEngine::srbmWrite(SDMAQueue *q, sdmaSRBMWriteHeader *header,
+                      sdmaSRBMWrite *pkt)
+{
+    q->incRptr(sizeof(sdmaSRBMWrite));
+
+    [[maybe_unused]] uint32_t reg_addr = pkt->regAddr << 2;
+    uint32_t reg_mask = 0x00000000;
+
+    if (header->byteEnable & 0x8) reg_mask |= 0xFF000000;
+    if (header->byteEnable & 0x4) reg_mask |= 0x00FF0000;
+    if (header->byteEnable & 0x2) reg_mask |= 0x0000FF00;
+    if (header->byteEnable & 0x1) reg_mask |= 0x000000FF;
+    pkt->data &= reg_mask;
+
+    DPRINTF(SDMAEngine, "SRBM write to %#x with data %#x\n",
+            reg_addr, pkt->data);
+
+    warn_once("SRBM write not performed, no SRBM model. This needs to be fixed"
+              " if correct system simulation is relying on SRBM registers.");
+
+    delete header;
+    delete pkt;
+    decodeNext(q);
+}
+
+/**
+ * Implements a poll reg/mem packet that polls an SRBM register or a memory
+ * location, compares the retrieved value with a reference value and if
+ * unsuccessfull it retries indefinitely or for a limited number of times.
+ */
+void
+SDMAEngine::pollRegMem(SDMAQueue *q, sdmaPollRegMemHeader *header,
+                       sdmaPollRegMem *pkt)
+{
+    q->incRptr(sizeof(sdmaPollRegMem));
+
+    DPRINTF(SDMAEngine, "POLL_REGMEM: M=%d, func=%d, op=%d, addr=%p, ref=%d, "
+            "mask=%p, retry=%d, pinterval=%d\n", header->mode, header->func,
+            header->op, pkt->address, pkt->ref, pkt->mask, pkt->retryCount,
+            pkt->pollInt);
+
+    bool skip = false;
+
+    if (header->mode == 1) {
+        // polling on a memory location
+        if (header->op == 0) {
+            auto cb = new DmaVirtCallback<uint32_t>(
+                [ = ] (const uint32_t &dma_buffer) {
+                    pollRegMemRead(q, header, pkt, dma_buffer, 0); });
+            dmaReadVirt(pkt->address >> 3, sizeof(uint32_t), cb,
+                        (void *)&cb->dmaBuffer);
+        } else {
+            panic("SDMA poll mem operation not implemented.");
+            skip = true;
+        }
+    } else {
+        warn_once("SDMA poll reg is not implemented. If this is required for "
+                  "correctness, an SRBM model needs to be implemented.");
+        skip = true;
+    }
+
+    if (skip) {
+        delete header;
+        delete pkt;
+        decodeNext(q);
+    }
+}
+
+void
+SDMAEngine::pollRegMemRead(SDMAQueue *q, sdmaPollRegMemHeader *header,
+                           sdmaPollRegMem *pkt, uint32_t dma_buffer, int count)
+{
+    assert(header->mode == 1 && header->op == 0);
+
+    if (!pollRegMemFunc(dma_buffer, pkt->ref, header->func) &&
+        ((count < (pkt->retryCount + 1) && pkt->retryCount != 0xfff) ||
+         pkt->retryCount == 0xfff)) {
+
+        // continue polling on a memory location until reference value is met,
+        // retryCount is met or indefinitelly if retryCount is 0xfff
+        DPRINTF(SDMAEngine, "SDMA polling mem addr %p, val %d ref %d.\n",
+                pkt->address, dma_buffer, pkt->ref);
+
+        auto cb = new DmaVirtCallback<uint32_t>(
+            [ = ] (const uint32_t &dma_buffer) {
+                pollRegMemRead(q, header, pkt, dma_buffer, count + 1); });
+        dmaReadVirt(pkt->address, sizeof(uint32_t), cb,
+                    (void *)&cb->dmaBuffer);
+    } else {
+        DPRINTF(SDMAEngine, "SDMA polling mem addr %p, val %d ref %d done.\n",
+                pkt->address, dma_buffer, pkt->ref);
+
+        delete header;
+        delete pkt;
+        decodeNext(q);
+    }
+}
+
+bool
+SDMAEngine::pollRegMemFunc(uint32_t value, uint32_t reference, uint32_t func)
+{
+    switch (func) {
+      case 0:
+        return true;
+      break;
+      case 1:
+        return value < reference;
+      break;
+      case 2:
+        return value <= reference;
+      break;
+      case 3:
+        return value == reference;
+      break;
+      case 4:
+        return value != reference;
+      break;
+      case 5:
+        return value >= reference;
+      break;
+      case 6:
+        return value > reference;
+      break;
+      default:
+        panic("SDMA POLL_REGMEM unknown comparison function.");
+      break;
+    }
+}
+
+/* Implements a PTE PDE generation packet. */
+void
+SDMAEngine::ptePde(SDMAQueue *q, sdmaPtePde *pkt)
+{
+    q->incRptr(sizeof(sdmaPtePde));
+    pkt->count++;
+
+    DPRINTF(SDMAEngine, "PTEPDE init: %d inc: %d count: %d\n",
+            pkt->initValue, pkt->increment, pkt->count);
+
+    // Generating pkt->count double dwords using the initial value, increment
+    // and a mask.
+    uint64_t *dmaBuffer = new uint64_t[pkt->count];
+    for (int i = 0; i < pkt->count; i++) {
+        dmaBuffer[i] = (pkt->mask | (pkt->initValue + (i * pkt->increment)));
+    }
+
+    // Writing generated data to the destination address.
+    if (gpuDevice->getVM().inMMHUB(pkt->dest)) {
+        Addr mmhubAddr = pkt->dest - gpuDevice->getVM().getMMHUBBase();
+        gpuDevice->getMemMgr()->writeRequest(mmhubAddr, (uint8_t *)dmaBuffer,
+                                           sizeof(uint64_t) * pkt->count);
+
+        decodeNext(q);
+    } else {
+        auto cb = new DmaVirtCallback<uint64_t>(
+            [ = ] (const uint64_t &) { ptePdeDone(q, pkt, dmaBuffer); });
+        dmaWriteVirt(pkt->dest, sizeof(uint64_t) * pkt->count, cb,
+            (void *)dmaBuffer);
+    }
+}
+
+/* Completion of a PTE PDE generation packet. */
+void
+SDMAEngine::ptePdeDone(SDMAQueue *q, sdmaPtePde *pkt, uint64_t *dmaBuffer)
+{
+    DPRINTF(SDMAEngine, "PtePde packet completed to %p, %d 2dwords\n",
+            pkt->dest, pkt->count);
+
+    delete []dmaBuffer;
+    delete pkt;
+    decodeNext(q);
+}
+
+AddrRangeList
+SDMAEngine::getAddrRanges() const
+{
+    AddrRangeList ranges;
+    return ranges;
+}
+
+void
+SDMAEngine::serialize(CheckpointOut &cp) const
+{
+    // Serialize the DmaVirtDevice base class
+    DmaVirtDevice::serialize(cp);
+
+    SERIALIZE_SCALAR(gfxBase);
+    SERIALIZE_SCALAR(gfxRptr);
+    SERIALIZE_SCALAR(gfxDoorbell);
+    SERIALIZE_SCALAR(gfxDoorbellOffset);
+    SERIALIZE_SCALAR(gfxWptr);
+    SERIALIZE_SCALAR(pageBase);
+    SERIALIZE_SCALAR(pageRptr);
+    SERIALIZE_SCALAR(pageDoorbell);
+    SERIALIZE_SCALAR(pageDoorbellOffset);
+    SERIALIZE_SCALAR(pageWptr);
+
+    int num_queues = 4;
+
+    std::vector<SDMAQueue *> queues;
+    queues.push_back((SDMAQueue *)&gfx);
+    queues.push_back((SDMAQueue *)&page);
+    queues.push_back((SDMAQueue *)&gfxIb);
+    queues.push_back((SDMAQueue *)&pageIb);
+
+    Addr base[num_queues];
+    Addr rptr[num_queues];
+    Addr wptr[num_queues];
+    Addr size[num_queues];
+    bool processing[num_queues];
+
+    for (int i = 0; i < num_queues; i++) {
+        base[i] = queues[i]->base();
+        rptr[i] = queues[i]->getRptr();
+        wptr[i] = queues[i]->getWptr();
+        size[i] = queues[i]->size();
+        processing[i] = queues[i]->processing();
+    }
+
+    SERIALIZE_ARRAY(base, num_queues);
+    SERIALIZE_ARRAY(rptr, num_queues);
+    SERIALIZE_ARRAY(wptr, num_queues);
+    SERIALIZE_ARRAY(size, num_queues);
+    SERIALIZE_ARRAY(processing, num_queues);
+}
+
+void
+SDMAEngine::unserialize(CheckpointIn &cp)
+{
+    // Serialize the DmaVirtDevice base class
+    DmaVirtDevice::unserialize(cp);
+
+    UNSERIALIZE_SCALAR(gfxBase);
+    UNSERIALIZE_SCALAR(gfxRptr);
+    UNSERIALIZE_SCALAR(gfxDoorbell);
+    UNSERIALIZE_SCALAR(gfxDoorbellOffset);
+    UNSERIALIZE_SCALAR(gfxWptr);
+    UNSERIALIZE_SCALAR(pageBase);
+    UNSERIALIZE_SCALAR(pageRptr);
+    UNSERIALIZE_SCALAR(pageDoorbell);
+    UNSERIALIZE_SCALAR(pageDoorbellOffset);
+    UNSERIALIZE_SCALAR(pageWptr);
+
+    int num_queues = 4;
+    Addr base[num_queues];
+    Addr rptr[num_queues];
+    Addr wptr[num_queues];
+    Addr size[num_queues];
+    bool processing[num_queues];
+
+    UNSERIALIZE_ARRAY(base, num_queues);
+    UNSERIALIZE_ARRAY(rptr, num_queues);
+    UNSERIALIZE_ARRAY(wptr, num_queues);
+    UNSERIALIZE_ARRAY(size, num_queues);
+    UNSERIALIZE_ARRAY(processing, num_queues);
+
+    std::vector<SDMAQueue *> queues;
+    queues.push_back((SDMAQueue *)&gfx);
+    queues.push_back((SDMAQueue *)&page);
+    queues.push_back((SDMAQueue *)&gfxIb);
+    queues.push_back((SDMAQueue *)&pageIb);
+
+    for (int i = 0; i < num_queues; i++) {
+        queues[i]->base(base[i]);
+        queues[i]->rptr(rptr[i]);
+        queues[i]->wptr(wptr[i]);
+        queues[i]->size(size[i]);
+        queues[i]->processing(processing[i]);
+    }
+}
+
+void
+SDMAEngine::writeMMIO(PacketPtr pkt, Addr mmio_offset)
+{
+    DPRINTF(SDMAEngine, "Writing offset %#x with data %x\n", mmio_offset,
+            pkt->getLE<uint32_t>());
+
+    // In Vega10 headers, the offsets are the same for both SDMAs
+    switch (mmio_offset) {
+      case mmSDMA_GFX_RB_BASE:
+        setGfxBaseLo(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_GFX_RB_BASE_HI:
+        setGfxBaseHi(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_GFX_RB_RPTR_ADDR_LO:
+        setGfxRptrLo(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_GFX_RB_RPTR_ADDR_HI:
+        setGfxRptrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_GFX_DOORBELL:
+        setGfxDoorbellLo(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_GFX_DOORBELL_OFFSET:
+        setGfxDoorbellOffsetLo(pkt->getLE<uint32_t>());
+        // Bit 28 of doorbell indicates that doorbell is enabled.
+        if (bits(getGfxDoorbell(), 28, 28)) {
+            gpuDevice->setDoorbellType(getGfxDoorbellOffset(),
+                                       QueueType::SDMAGfx);
+            gpuDevice->setSDMAEngine(getGfxDoorbellOffset(), this);
+        }
+        break;
+      case mmSDMA_GFX_RB_CNTL: {
+        uint32_t rb_size = bits(pkt->getLE<uint32_t>(), 6, 1);
+        assert(rb_size >= 6 && rb_size <= 62);
+        setGfxSize(1 << (rb_size + 2));
+      } break;
+      case mmSDMA_GFX_RB_WPTR_POLL_ADDR_LO:
+        setGfxWptrLo(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_GFX_RB_WPTR_POLL_ADDR_HI:
+        setGfxWptrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_PAGE_RB_BASE:
+        setPageBaseLo(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_PAGE_RB_RPTR_ADDR_LO:
+        setPageRptrLo(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_PAGE_RB_RPTR_ADDR_HI:
+        setPageRptrHi(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_PAGE_DOORBELL:
+        setPageDoorbellLo(pkt->getLE<uint32_t>());
+        break;
+      case mmSDMA_PAGE_DOORBELL_OFFSET:
+        setPageDoorbellOffsetLo(pkt->getLE<uint32_t>());
+        // Bit 28 of doorbell indicates that doorbell is enabled.
+        if (bits(getPageDoorbell(), 28, 28)) {
+            gpuDevice->setDoorbellType(getPageDoorbellOffset(),
+                                       QueueType::SDMAPage);
+            gpuDevice->setSDMAEngine(getPageDoorbellOffset(), this);
+        }
+        break;
+      case mmSDMA_PAGE_RB_CNTL: {
+        uint32_t rb_size = bits(pkt->getLE<uint32_t>(), 6, 1);
+        assert(rb_size >= 6 && rb_size <= 62);
+        setPageSize(1 << (rb_size + 2));
+      } break;
+      case mmSDMA_PAGE_RB_WPTR_POLL_ADDR_LO:
+        setPageWptrLo(pkt->getLE<uint32_t>());
+        break;
+      default:
+        DPRINTF(SDMAEngine, "Unknown SDMA MMIO %#x\n", mmio_offset);
+        break;
+    }
+}
+
+void
+SDMAEngine::setGfxBaseLo(uint32_t data)
+{
+    gfxBase = insertBits(gfxBase, 31, 0, 0);
+    gfxBase |= data;
+    gfx.base((gfxBase >> 1) << 12);
+}
+
+void
+SDMAEngine::setGfxBaseHi(uint32_t data)
+{
+    gfxBase = insertBits(gfxBase, 63, 32, 0);
+    gfxBase |= ((uint64_t)data) << 32;
+    gfx.base((gfxBase >> 1) << 12);
+}
+
+void
+SDMAEngine::setGfxRptrLo(uint32_t data)
+{
+    gfxRptr = insertBits(gfxRptr, 31, 0, 0);
+    gfxRptr |= data;
+}
+
+void
+SDMAEngine::setGfxRptrHi(uint32_t data)
+{
+    gfxRptr = insertBits(gfxRptr, 63, 32, 0);
+    gfxRptr |= ((uint64_t)data) << 32;
+}
+
+void
+SDMAEngine::setGfxDoorbellLo(uint32_t data)
+{
+    gfxDoorbell = insertBits(gfxDoorbell, 31, 0, 0);
+    gfxDoorbell |= data;
+}
+
+void
+SDMAEngine::setGfxDoorbellHi(uint32_t data)
+{
+    gfxDoorbell = insertBits(gfxDoorbell, 63, 32, 0);
+    gfxDoorbell |= ((uint64_t)data) << 32;
+}
+
+void
+SDMAEngine::setGfxDoorbellOffsetLo(uint32_t data)
+{
+    gfxDoorbellOffset = insertBits(gfxDoorbellOffset, 31, 0, 0);
+    gfxDoorbellOffset |= data;
+}
+
+void
+SDMAEngine::setGfxDoorbellOffsetHi(uint32_t data)
+{
+    gfxDoorbellOffset = insertBits(gfxDoorbellOffset, 63, 32, 0);
+    gfxDoorbellOffset |= ((uint64_t)data) << 32;
+}
+
+void
+SDMAEngine::setGfxSize(uint64_t data)
+{
+    gfx.size(data);
+}
+
+void
+SDMAEngine::setGfxWptrLo(uint32_t data)
+{
+    gfxWptr = insertBits(gfxWptr, 31, 0, 0);
+    gfxWptr |= data;
+}
+
+void
+SDMAEngine::setGfxWptrHi(uint32_t data)
+{
+    gfxWptr = insertBits(gfxWptr, 31, 0, 0);
+    gfxWptr |= ((uint64_t)data) << 32;
+}
+
+void
+SDMAEngine::setPageBaseLo(uint32_t data)
+{
+    pageBase = insertBits(pageBase, 31, 0, 0);
+    pageBase |= data;
+    page.base((pageBase >> 1) << 12);
+}
+
+void
+SDMAEngine::setPageBaseHi(uint32_t data)
+{
+    pageBase = insertBits(pageBase, 63, 32, 0);
+    pageBase |= ((uint64_t)data) << 32;
+    page.base((pageBase >> 1) << 12);
+}
+
+void
+SDMAEngine::setPageRptrLo(uint32_t data)
+{
+    pageRptr = insertBits(pageRptr, 31, 0, 0);
+    pageRptr |= data;
+}
+
+void
+SDMAEngine::setPageRptrHi(uint32_t data)
+{
+    pageRptr = insertBits(pageRptr, 63, 32, 0);
+    pageRptr |= ((uint64_t)data) << 32;
+}
+
+void
+SDMAEngine::setPageDoorbellLo(uint32_t data)
+{
+    pageDoorbell = insertBits(pageDoorbell, 31, 0, 0);
+    pageDoorbell |= data;
+}
+
+void
+SDMAEngine::setPageDoorbellHi(uint32_t data)
+{
+    pageDoorbell = insertBits(pageDoorbell, 63, 32, 0);
+    pageDoorbell |= ((uint64_t)data) << 32;
+}
+
+void
+SDMAEngine::setPageDoorbellOffsetLo(uint32_t data)
+{
+    pageDoorbellOffset = insertBits(pageDoorbellOffset, 31, 0, 0);
+    pageDoorbellOffset |= data;
+}
+
+void
+SDMAEngine::setPageDoorbellOffsetHi(uint32_t data)
+{
+    pageDoorbellOffset = insertBits(pageDoorbellOffset, 63, 32, 0);
+    pageDoorbellOffset |= ((uint64_t)data) << 32;
+}
+
+void
+SDMAEngine::setPageSize(uint64_t data)
+{
+    page.size(data);
+}
+
+void
+SDMAEngine::setPageWptrLo(uint32_t data)
+{
+    pageWptr = insertBits(pageWptr, 31, 0, 0);
+    pageWptr |= data;
+}
+
+void
+SDMAEngine::setPageWptrHi(uint32_t data)
+{
+    pageWptr = insertBits(pageWptr, 63, 32, 0);
+    pageWptr |= ((uint64_t)data) << 32;
+}
+
+} // namespace gem5
diff --git a/src/dev/amdgpu/sdma_engine.hh b/src/dev/amdgpu/sdma_engine.hh
new file mode 100644
index 0000000..90d8e5b
--- /dev/null
+++ b/src/dev/amdgpu/sdma_engine.hh
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_AMDGPU_SDMA_ENGINE_HH__
+#define __DEV_AMDGPU_SDMA_ENGINE_HH__
+
+#include "base/bitunion.hh"
+#include "debug/SDMAEngine.hh"
+#include "dev/amdgpu/amdgpu_device.hh"
+#include "dev/amdgpu/sdma_packets.hh"
+#include "dev/dma_virt_device.hh"
+#include "params/SDMAEngine.hh"
+
+namespace gem5
+{
+
+/**
+ * System DMA Engine class for AMD dGPU.
+ */
+class SDMAEngine : public DmaVirtDevice
+{
+    enum SDMAType
+    {
+        SDMAGfx,
+        SDMAPage
+    };
+
+    class SDMAQueue
+    {
+        Addr _base;
+        Addr _rptr;
+        Addr _wptr;
+        Addr _size;
+        bool _valid;
+        bool _processing;
+        SDMAQueue *_parent;
+        SDMAQueue *_ib;
+      public:
+        SDMAQueue() : _rptr(0), _wptr(0), _valid(false), _processing(false),
+            _parent(nullptr), _ib(nullptr) {}
+
+        Addr base() { return _base; }
+        Addr rptr() { return _base + _rptr; }
+        Addr getRptr() { return _rptr; }
+        Addr wptr() { return _base + _wptr; }
+        Addr getWptr() { return _wptr; }
+        Addr size() { return _size; }
+        bool valid() { return _valid; }
+        bool processing() { return _processing; }
+        SDMAQueue* parent() { return _parent; }
+        SDMAQueue* ib() { return _ib; }
+
+        void base(Addr value) { _base = value; }
+
+        void
+        incRptr(uint32_t value)
+        {
+            //assert((_rptr + value) <= (_size << 1));
+            _rptr = (_rptr + value) % _size;
+        }
+
+        void rptr(Addr value) { _rptr = value; }
+
+        void
+        setWptr(Addr value)
+        {
+            //assert(value <= (_size << 1));
+            _wptr = value % _size;
+        }
+
+        void wptr(Addr value) { _wptr = value; }
+
+        void size(Addr value) { _size = value; }
+        void valid(bool v) { _valid = v; }
+        void processing(bool value) { _processing = value; }
+        void parent(SDMAQueue* q) { _parent = q; }
+        void ib(SDMAQueue* ib) { _ib = ib; }
+    };
+
+    /* SDMA Engine ID */
+    int id;
+    /**
+     * Each SDMAEngine processes four queues: paging, gfx, rlc0, and rlc1,
+     * where RLC stands for Run List Controller. Each one of these
+     * can have one indirect buffer associated at any particular time.
+     * The switching order between queues is supposed to be page -> gfx ->
+     * rlc0 -> page -> gfx -> rlc1, skipping empty queues.
+     */
+    SDMAQueue gfx, page, gfxIb, pageIb;
+    SDMAQueue rlc0, rlc0Ib, rlc1, rlc1Ib;
+
+    /* Gfx ring buffer registers */
+    uint64_t gfxBase;
+    uint64_t gfxRptr;
+    uint64_t gfxDoorbell;
+    uint64_t gfxDoorbellOffset;
+    uint64_t gfxWptr;
+    /* Page ring buffer registers */
+    uint64_t pageBase;
+    uint64_t pageRptr;
+    uint64_t pageDoorbell;
+    uint64_t pageDoorbellOffset;
+    uint64_t pageWptr;
+
+    AMDGPUDevice *gpuDevice;
+    VegaISA::Walker *walker;
+
+    /* processRLC will select the correct queue for the doorbell */
+    std::unordered_map<Addr, int> rlcMap;
+    void processRLC0(Addr wptrOffset);
+    void processRLC1(Addr wptrOffset);
+
+  public:
+    SDMAEngine(const SDMAEngineParams &p);
+
+    void setGPUDevice(AMDGPUDevice *gpu_device);
+
+    void setId(int _id) { id = _id; }
+    /**
+     * Returns the client id for the Interrupt Handler.
+     */
+    int getIHClientId();
+
+    /**
+     * Methods for translation.
+     */
+    Addr getGARTAddr(Addr addr) const;
+    TranslationGenPtr translate(Addr vaddr, Addr size) override;
+
+    /**
+     * Inherited methods.
+     */
+    Tick write(PacketPtr pkt) override { return 0; }
+    Tick read(PacketPtr pkt) override { return 0; }
+    AddrRangeList getAddrRanges() const override;
+    void serialize(CheckpointOut &cp) const override;
+    void unserialize(CheckpointIn &cp) override;
+
+    /**
+     * Given a new write ptr offset, communicated to the GPU through a doorbell
+     * write, the SDMA engine processes the page, gfx, rlc0, or rlc1 queue.
+     */
+    void processGfx(Addr wptrOffset);
+    void processPage(Addr wptrOffset);
+    void processRLC(Addr doorbellOffset, Addr wptrOffset);
+
+    /**
+     * This method checks read and write pointers and starts decoding
+     * packets if the read pointer is less than the write pointer.
+     * It also marks a queue a being currently processing, in case the
+     * doorbell is rung again, the newly enqueued packets will be decoded once
+     * the currently processing once are finished. This is achieved by calling
+     * decodeNext once an entire SDMA packet has been processed.
+     */
+    void decodeNext(SDMAQueue *q);
+
+    /**
+     * Reads the first DW (32 bits) (i.e., header) of an SDMA packet, which
+     * encodes the opcode and sub-opcode of the packet. It also creates an
+     * SDMA packet object and calls the associated processing function.
+     */
+    void decodeHeader(SDMAQueue *q, uint32_t data);
+
+    /**
+     * Methods that implement processing of SDMA packets
+     */
+    void write(SDMAQueue *q, sdmaWrite *pkt);
+    void writeReadData(SDMAQueue *q, sdmaWrite *pkt, uint32_t *dmaBuffer);
+    void writeDone(SDMAQueue *q, sdmaWrite *pkt, uint32_t *dmaBuffer);
+    void copy(SDMAQueue *q, sdmaCopy *pkt);
+    void copyReadData(SDMAQueue *q, sdmaCopy *pkt, uint8_t *dmaBuffer);
+    void copyDone(SDMAQueue *q, sdmaCopy *pkt, uint8_t *dmaBuffer);
+    void indirectBuffer(SDMAQueue *q, sdmaIndirectBuffer *pkt);
+    void fence(SDMAQueue *q, sdmaFence *pkt);
+    void fenceDone(SDMAQueue *q, sdmaFence *pkt);
+    void trap(SDMAQueue *q, sdmaTrap *pkt);
+    void srbmWrite(SDMAQueue *q, sdmaSRBMWriteHeader *header,
+                    sdmaSRBMWrite *pkt);
+    void pollRegMem(SDMAQueue *q, sdmaPollRegMemHeader *header,
+                    sdmaPollRegMem *pkt);
+    void pollRegMemRead(SDMAQueue *q, sdmaPollRegMemHeader *header,
+                        sdmaPollRegMem *pkt, uint32_t dma_buffer, int count);
+    bool pollRegMemFunc(uint32_t value, uint32_t reference, uint32_t func);
+    void ptePde(SDMAQueue *q, sdmaPtePde *pkt);
+    void ptePdeDone(SDMAQueue *q, sdmaPtePde *pkt, uint64_t *dmaBuffer);
+
+    /**
+     * Methods for getting the values of SDMA MMIO registers.
+     */
+    uint64_t getGfxBase() { return gfxBase; }
+    uint64_t getGfxRptr() { return gfxRptr; }
+    uint64_t getGfxDoorbell() { return gfxDoorbell; }
+    uint64_t getGfxDoorbellOffset() { return gfxDoorbellOffset; }
+    uint64_t getGfxWptr() { return gfxWptr; }
+    uint64_t getPageBase() { return pageBase; }
+    uint64_t getPageRptr() { return pageRptr; }
+    uint64_t getPageDoorbell() { return pageDoorbell; }
+    uint64_t getPageDoorbellOffset() { return pageDoorbellOffset; }
+    uint64_t getPageWptr() { return pageWptr; }
+
+    /**
+     * Methods for setting the values of SDMA MMIO registers.
+     */
+    void writeMMIO(PacketPtr pkt, Addr mmio_offset);
+
+    void setGfxBaseLo(uint32_t data);
+    void setGfxBaseHi(uint32_t data);
+    void setGfxRptrLo(uint32_t data);
+    void setGfxRptrHi(uint32_t data);
+    void setGfxDoorbellLo(uint32_t data);
+    void setGfxDoorbellHi(uint32_t data);
+    void setGfxDoorbellOffsetLo(uint32_t data);
+    void setGfxDoorbellOffsetHi(uint32_t data);
+    void setGfxSize(uint64_t data);
+    void setGfxWptrLo(uint32_t data);
+    void setGfxWptrHi(uint32_t data);
+    void setPageBaseLo(uint32_t data);
+    void setPageBaseHi(uint32_t data);
+    void setPageRptrLo(uint32_t data);
+    void setPageRptrHi(uint32_t data);
+    void setPageDoorbellLo(uint32_t data);
+    void setPageDoorbellHi(uint32_t data);
+    void setPageDoorbellOffsetLo(uint32_t data);
+    void setPageDoorbellOffsetHi(uint32_t data);
+    void setPageSize(uint64_t data);
+    void setPageWptrLo(uint32_t data);
+    void setPageWptrHi(uint32_t data);
+
+    /**
+     * Methods for RLC queues
+     */
+    void registerRLCQueue(Addr doorbell, Addr rb_base);
+    void unregisterRLCQueue(Addr doorbell);
+
+    int cur_vmid = 0;
+};
+
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_SDMA_ENGINE_HH__
diff --git a/src/dev/amdgpu/sdma_mmio.hh b/src/dev/amdgpu/sdma_mmio.hh
new file mode 100644
index 0000000..a87bfb8
--- /dev/null
+++ b/src/dev/amdgpu/sdma_mmio.hh
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_AMDGPU_SDMA_MMIO_HH__
+#define __DEV_AMDGPU_SDMA_MMIO_HH__
+
+/**
+ * MMIO offsets for SDMA engine. These values were taken from the linux header
+ * for SDMA. The header files can be found here:
+ *
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *      drivers/gpu/drm/amd/include/asic_reg/sdma0/sdma0_4_0_offset.h
+ * https://github.com/RadeonOpenCompute/ROCK-Kernel-Driver/blob/roc-4.3.x/
+ *     drivers/gpu/drm/amd/include/asic_reg/sdma1/sdma1_4_0_offset.h
+ */
+#define mmSDMA_GFX_RB_CNTL                                0x0080
+#define mmSDMA_GFX_RB_BASE                                0x0081
+#define mmSDMA_GFX_RB_BASE_HI                             0x0082
+#define mmSDMA_GFX_RB_RPTR_ADDR_HI                        0x0088
+#define mmSDMA_GFX_RB_RPTR_ADDR_LO                        0x0089
+#define mmSDMA_GFX_DOORBELL                               0x0092
+#define mmSDMA_GFX_DOORBELL_OFFSET                        0x00ab
+#define mmSDMA_GFX_RB_WPTR_POLL_ADDR_HI                   0x00b2
+#define mmSDMA_GFX_RB_WPTR_POLL_ADDR_LO                   0x00b3
+#define mmSDMA_PAGE_RB_CNTL                               0x00e0
+#define mmSDMA_PAGE_RB_BASE                               0x00e1
+#define mmSDMA_PAGE_RB_RPTR_ADDR_HI                       0x00e8
+#define mmSDMA_PAGE_RB_RPTR_ADDR_LO                       0x00e9
+#define mmSDMA_PAGE_DOORBELL                              0x00f2
+#define mmSDMA_PAGE_DOORBELL_OFFSET                       0x010b
+#define mmSDMA_PAGE_RB_WPTR_POLL_ADDR_LO                  0x0113
+
+#endif // __DEV_AMDGPU_SDMA_MMIO_HH__
diff --git a/src/dev/amdgpu/sdma_packets.hh b/src/dev/amdgpu/sdma_packets.hh
new file mode 100644
index 0000000..c490756
--- /dev/null
+++ b/src/dev/amdgpu/sdma_packets.hh
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DEV_AMDGPU_SDMA_PACKETS_HH__
+#define __DEV_AMDGPU_SDMA_PACKETS_HH__
+
+namespace gem5
+{
+
+/**
+ * SDMA packets
+ */
+typedef struct GEM5_PACKED
+{
+    uint32_t count : 30;
+    uint32_t res0 : 2;
+    uint32_t res1 : 16;
+    uint32_t sdw: 2;
+    uint32_t res2 : 6;
+    uint32_t ddw: 2;
+    uint32_t res3 : 6;
+    uint64_t source;
+    uint64_t dest;
+}  sdmaCopy;
+static_assert(sizeof(sdmaCopy) == 24);
+
+typedef struct GEM5_PACKED
+{
+    uint64_t dest;
+    uint32_t count : 20;
+    uint32_t reserved0 : 4;
+    uint32_t sw : 2;
+    uint32_t reserved1 : 6;
+}  sdmaWrite;
+static_assert(sizeof(sdmaWrite) == 12);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t addrLo;
+            uint32_t addrHi;
+        };
+        Addr addr;
+    };
+    uint32_t srcData;
+    uint32_t unused : 10;
+    uint32_t count : 22;
+}  sdmaConstFill;
+static_assert(sizeof(sdmaConstFill) == 16);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t key0;
+    uint32_t key1;
+    uint32_t key2;
+    uint32_t key3;
+    uint32_t count0;
+    uint32_t count1;
+    uint32_t count2;
+    uint32_t count3;
+}  sdmaAESKey;
+static_assert(sizeof(sdmaAESKey) == 32);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t countData0;
+    uint32_t countData1;
+    uint32_t countData2;
+    uint32_t countData3;
+}  sdmaAESCounter;
+static_assert(sizeof(sdmaAESCounter) == 16);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t countKey0;
+    uint32_t countKey1;
+    uint32_t countKey2;
+    uint32_t countKey3;
+}  sdmaAESLoad;
+static_assert(sizeof(sdmaAESLoad) == 16);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t reserved : 6;
+    uint32_t offset : 26;
+}  sdmaAESOffset;
+static_assert(sizeof(sdmaAESOffset) == 4);
+
+typedef struct GEM5_PACKED
+{
+    uint64_t base;
+    uint32_t size : 20;
+    uint32_t reserved : 12;
+    uint64_t csaAddr;
+}  sdmaIndirectBuffer;
+static_assert(sizeof(sdmaIndirectBuffer) == 20);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t priv : 1;
+    uint32_t reserved1 : 11;
+    uint32_t vmid : 4;
+    uint32_t reserved2 : 16;
+}  sdmaIndirectBufferHeader;
+static_assert(sizeof(sdmaIndirectBufferHeader) == 4);
+
+typedef struct GEM5_PACKED
+{
+    uint64_t dest;
+    uint32_t data;
+}  sdmaFence;
+static_assert(sizeof(sdmaFence) == 12);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t contextId : 3;
+            uint32_t rbRptr: 13;
+            uint32_t ibOffset : 12;
+            uint32_t reserved : 4;
+        };
+        uint32_t intrContext;
+    };
+}  sdmaTrap;
+static_assert(sizeof(sdmaTrap) == 4);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t reserved : 3;
+            uint32_t addrLo : 29;
+            uint32_t addrHi;
+        };
+        Addr addr;
+    };
+}  sdmaSemaphore;
+static_assert(sizeof(sdmaSemaphore) == 8);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t reserved : 3;
+            uint32_t addrLo : 29;
+            uint32_t addrHi;
+        };
+        Addr addr;
+    };
+}  sdmaMemInc;
+static_assert(sizeof(sdmaMemInc) == 8);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t regAddr : 18;
+    uint32_t reserved : 2;
+    uint32_t apertureId : 12;
+    uint32_t data;
+}  sdmaSRBMWrite;
+static_assert(sizeof(sdmaSRBMWrite) == 8);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t reserved : 28;
+    uint32_t byteEnable : 4;
+}  sdmaSRBMWriteHeader;
+static_assert(sizeof(sdmaSRBMWriteHeader) == 4);
+
+typedef struct GEM5_PACKED
+{
+    uint64_t address;
+    uint32_t ref;
+    uint32_t mask;
+    uint32_t pollInt : 16;
+    uint32_t retryCount : 12;
+    uint32_t reserved1 : 4;
+}  sdmaPollRegMem;
+static_assert(sizeof(sdmaPollRegMem) == 20);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t reserved : 26;
+    uint32_t op : 2;            // Operation
+    uint32_t func : 3;          // Comparison function
+    uint32_t mode : 1;          // Mode: register or memory polling
+}  sdmaPollRegMemHeader;
+static_assert(sizeof(sdmaPollRegMemHeader) == 4);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t addrLo;
+            uint32_t addrHi;
+        };
+        Addr addr;
+    };
+    uint32_t reference;
+    union
+    {
+        struct
+        {
+            uint32_t execCount : 14;
+            uint32_t unused : 18;
+        };
+        uint32_t ordinal;
+    };
+}  sdmaCondExec;
+static_assert(sizeof(sdmaCondExec) == 16);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t addrLo;
+            uint32_t addrHi;
+        };
+        Addr addr;
+    };
+    union
+    {
+        struct
+        {
+            uint32_t srcDataLo;
+            uint32_t srdDataHi;
+        };
+        uint64_t srcData;
+    };
+    union
+    {
+        struct
+        {
+            uint32_t cmpDataLo;
+            uint32_t cmpDataHi;
+        };
+        uint64_t cmpData;
+    };
+    uint32_t loopInt : 13;
+    uint32_t reserved : 19;
+}  sdmaAtomic;
+static_assert(sizeof(sdmaAtomic) == 28);
+
+typedef struct GEM5_PACKED
+{
+    uint64_t dest;
+    uint64_t mask;
+    uint64_t initValue;
+    uint64_t increment;
+    uint32_t count: 19;
+    uint32_t reserved : 13;
+}  sdmaPtePde;
+static_assert(sizeof(sdmaPtePde) == 36);
+
+typedef struct GEM5_PACKED
+{
+    union
+    {
+        struct
+        {
+            uint32_t initDataLo;
+            uint32_t initDataHi;
+        };
+        uint64_t initData;
+    };
+}  sdmaTimestamp;
+static_assert(sizeof(sdmaTimestamp) == 8);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t execCount : 14;
+    uint32_t reserved : 18;
+}  sdmaPredExec;
+static_assert(sizeof(sdmaPredExec) == 4);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t opcode : 8;
+    uint32_t subOpcode : 8;
+    uint32_t device : 8;
+    uint32_t unused : 8;
+}  sdmaPredExecHeader;
+static_assert(sizeof(sdmaPredExecHeader) == 4);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t contextId : 3;
+    uint32_t rbRptr: 13;
+    uint32_t ibOffset : 12;
+    uint32_t reserved : 4;
+}  sdmaDummyTrap;
+static_assert(sizeof(sdmaDummyTrap) == 4);
+
+typedef struct GEM5_PACKED
+{
+    uint32_t byteStride;
+    uint32_t dmaCount;
+    union
+    {
+        struct
+        {
+            uint32_t destLo;
+            uint32_t destHi;
+        };
+        uint64_t dest;
+    };
+    uint32_t byteCount : 26;
+}  sdmaDataFillMulti;
+static_assert(sizeof(sdmaDataFillMulti) == 20);
+
+typedef struct GEM5_PACKED
+{
+    uint16_t format : 8;
+    uint16_t barrier : 1;
+    uint16_t acqFenceScope : 2;
+    uint16_t relFenceScope : 2;
+    uint16_t reserved : 3;
+}  sdmaHeaderAgentDisp;
+static_assert(sizeof(sdmaHeaderAgentDisp) == 2);
+
+typedef struct GEM5_PACKED
+{
+    sdmaHeaderAgentDisp header;
+    uint16_t res0;
+    uint32_t res1;
+    union
+    {
+        struct
+        {
+            uint32_t retLo;
+            uint32_t retHi;
+        };
+        Addr ret;
+    };
+    uint32_t count : 22;
+    uint32_t res2 : 10;
+    uint32_t res3 : 16;
+    uint32_t swDest : 2;
+    uint32_t res4 : 6;
+    uint32_t swSrc : 2;
+    uint32_t unused : 6;
+    union
+    {
+        struct
+        {
+            uint32_t srcLo;
+            uint32_t srcHi;
+        };
+        Addr src;
+    };
+    union
+    {
+        struct
+        {
+            uint32_t destLo;
+            uint32_t destHi;
+        };
+        Addr dest;
+    };
+    uint64_t res5;
+    uint64_t res6;
+    union
+    {
+        struct
+        {
+            uint32_t compSignalLo;
+            uint32_t compSignalHi;
+        };
+        Addr compSignal;
+    };
+}  sdmaAQLCopy;
+static_assert(sizeof(sdmaAQLCopy) == 64);
+
+typedef struct GEM5_PACKED
+{
+    sdmaHeaderAgentDisp header;
+    uint16_t res0;
+    uint32_t res1;
+    Addr depSignal0;
+    Addr depSignal1;
+    Addr depSignal2;
+    Addr depSignal3;
+    Addr depSignal4;
+    uint64_t res2;
+    union
+    {
+        struct
+        {
+            uint32_t compSignalLo;
+            uint32_t compSignalHi;
+        };
+        Addr compSignal;
+    };
+}  sdmaAQLBarrierOr;
+static_assert(sizeof(sdmaAQLBarrierOr) == 64);
+
+} // namespace gem5
+
+#endif // __DEV_AMDGPU_SDMA_PACKETS_HH__
diff --git a/src/dev/amdgpu/system_hub.cc b/src/dev/amdgpu/system_hub.cc
new file mode 100644
index 0000000..7a252ea
--- /dev/null
+++ b/src/dev/amdgpu/system_hub.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dev/amdgpu/system_hub.hh"
+
+#include "mem/port.hh"
+
+namespace gem5
+{
+
+void
+AMDGPUSystemHub::sendRequest(PacketPtr pkt, Event *callback)
+{
+    ResponseEvent *dmaRespEvent = new ResponseEvent(callback);
+    Tick delay = 0;
+
+    if (pkt->isWrite()) {
+        dmaWrite(pkt->getAddr(), pkt->getSize(), dmaRespEvent,
+                 pkt->getPtr<uint8_t>(), 0, 0, delay);
+    } else {
+        assert(pkt->isRead());
+        dmaRead(pkt->getAddr(), pkt->getSize(), dmaRespEvent,
+                pkt->getPtr<uint8_t>(), 0, 0, delay);
+    }
+}
+
+void
+AMDGPUSystemHub::dmaResponse(PacketPtr pkt)
+{
+}
+
+AMDGPUSystemHub::ResponseEvent::ResponseEvent(Event *_callback)
+    : callback(_callback)
+{
+    // Delete this event after process is called
+    setFlags(Event::AutoDelete);
+}
+
+void
+AMDGPUSystemHub::ResponseEvent::process()
+{
+    callback->process();
+}
+
+AddrRangeList
+AMDGPUSystemHub::getAddrRanges() const
+{
+    AddrRangeList ranges;
+    return ranges;
+}
+
+} // namespace gem5
diff --git a/src/dev/amdgpu/system_hub.hh b/src/dev/amdgpu/system_hub.hh
new file mode 100644
index 0000000..0b48c3b
--- /dev/null
+++ b/src/dev/amdgpu/system_hub.hh
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2021 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_GPU_SYSTEM_HUB_HH__
+#define __DEV_GPU_SYSTEM_HUB_HH__
+
+#include "dev/dma_device.hh"
+#include "params/AMDGPUSystemHub.hh"
+
+namespace gem5
+{
+
+class RequestPort;
+
+/**
+ * This class handles reads from the system/host memory space from the shader.
+ * It is meant to handle requests to memory which translation to system
+ * addresses. This can occur in fetch, scalar read/write, or vector memory
+ * read/write. It is a very basic interface to convert read packets to DMA
+ * requests and respond to the caller using an event.
+ */
+class AMDGPUSystemHub : public DmaDevice
+{
+  public:
+    AMDGPUSystemHub(const AMDGPUSystemHubParams &p) : DmaDevice(p) { }
+
+    void sendRequest(PacketPtr pkt, Event *callback);
+    void dmaResponse(PacketPtr pkt);
+
+    /**
+     * Inherited methods.
+     */
+    Tick write(PacketPtr pkt) override { return 0; }
+    Tick read(PacketPtr pkt) override { return 0; }
+    AddrRangeList getAddrRanges() const override;
+
+  private:
+
+    class ResponseEvent : public Event
+    {
+       Event *callback;
+
+       public:
+        ResponseEvent(Event *_callback);
+
+        void process();
+
+    };
+};
+
+} // namespace gem5
+
+#endif /* __DEV_GPU_SYSTEM_HUB_HH__ */
diff --git a/src/dev/arm/GenericTimer.py b/src/dev/arm/GenericTimer.py
index da686c1..af6d5ec 100644
--- a/src/dev/arm/GenericTimer.py
+++ b/src/dev/arm/GenericTimer.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2020 ARM Limited
+# Copyright (c) 2009-2020, 2022 Arm Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -86,10 +86,13 @@
 
     counter = Param.SystemCounter(Parent.any, "Global system counter")
 
-    int_phys_s = Param.ArmPPI("Physical (S) timer interrupt")
-    int_phys_ns = Param.ArmPPI("Physical (NS) timer interrupt")
-    int_virt = Param.ArmPPI("Virtual timer interrupt")
-    int_hyp = Param.ArmPPI("Hypervisor timer interrupt")
+    int_el1_phys = Param.ArmPPI("EL1 physical timer interrupt")
+    int_el1_virt = Param.ArmPPI("EL1 virtual timer interrupt")
+    int_el2_ns_phys = Param.ArmPPI("EL2 Non-secure physical timer interrupt")
+    int_el2_ns_virt = Param.ArmPPI("EL2 Non-secure virtual timer interrupt")
+    int_el2_s_phys = Param.ArmPPI("EL2 Secure physical timer interrupt")
+    int_el2_s_virt = Param.ArmPPI("EL2 Secure virtual timer interrupt")
+    int_el3_phys = Param.ArmPPI("EL3 physical timer interrupt")
 
     # This value should be in theory initialized by the highest
     # priviledged software. We do this in gem5 to avoid KVM
@@ -109,10 +112,11 @@
 
         gic = self._parent.unproxy(self).gic
         node.append(FdtPropertyWords("interrupts",
-            self.int_phys_s.generateFdtProperty(gic) +
-            self.int_phys_ns.generateFdtProperty(gic) +
-            self.int_virt.generateFdtProperty(gic) +
-            self.int_hyp.generateFdtProperty(gic)))
+            self.int_el3_phys.generateFdtProperty(gic) +
+            self.int_el1_phys.generateFdtProperty(gic) +
+            self.int_el1_virt.generateFdtProperty(gic) +
+            self.int_el2_ns_phys.generateFdtProperty(gic) +
+            self.int_el2_ns_virt.generateFdtProperty(gic)))
 
         if self._freq_in_dtb:
             node.append(self.counter.unproxy(self).generateDtb())
diff --git a/src/dev/arm/Gic.py b/src/dev/arm/Gic.py
index 953ccb4..ffbdbac 100644
--- a/src/dev/arm/Gic.py
+++ b/src/dev/arm/Gic.py
@@ -283,7 +283,7 @@
         "Maximum number of PE. This is affecting the maximum number of "
         "redistributors")
 
-    gicv4 = Param.Bool(True, "GICv4 extension available")
+    gicv4 = Param.Bool(False, "GIC is GICv4 compatible")
 
     def interruptCells(self, int_type, int_num, int_trigger, partition=None):
         """
diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py
index 2b86227..6645b39 100644
--- a/src/dev/arm/RealView.py
+++ b/src/dev/arm/RealView.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2009-2021 ARM Limited
+# Copyright (c) 2009-2022 Arm Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -73,12 +73,14 @@
 # emulation. Use a GIC model that automatically switches between
 # gem5's GIC model and KVM's GIC model if KVM is available.
 try:
-    from m5.objects.KvmGic import MuxingKvmGic
-    kvm_gicv2_class = MuxingKvmGic
+    from m5.objects.KvmGic import MuxingKvmGicV2, MuxingKvmGicV3
+    kvm_gicv2_class = MuxingKvmGicV2
+    kvm_gicv3_class = MuxingKvmGicV3
 except ImportError:
     # KVM support wasn't compiled into gem5. Fallback to a
     # software-only GIC.
     kvm_gicv2_class = Gic400
+    kvm_gicv3_class = Gicv3
     pass
 
 class AmbaPioDevice(BasicPioDevice):
@@ -750,6 +752,8 @@
         cur_sys.workload.boot_loader = boot_loader
         cur_sys.workload.load_addr_offset = load_offset
         cur_sys.workload.dtb_addr = load_offset + dtb_addr
+        # Use 0x200000 as this is the maximum size allowed for a DTB
+        cur_sys.workload.initrd_addr = cur_sys.workload.dtb_addr + 0x200000
         cur_sys.workload.cpu_release_addr = cur_sys.workload.dtb_addr - 8
 
     def generateDeviceTree(self, state):
@@ -828,10 +832,13 @@
 
     sys_counter = SystemCounter()
     generic_timer = GenericTimer(
-        int_phys_s=ArmPPI(num=29, int_type='IRQ_TYPE_LEVEL_LOW'),
-        int_phys_ns=ArmPPI(num=30, int_type='IRQ_TYPE_LEVEL_LOW'),
-        int_virt=ArmPPI(num=27, int_type='IRQ_TYPE_LEVEL_LOW'),
-        int_hyp=ArmPPI(num=26, int_type='IRQ_TYPE_LEVEL_LOW'))
+        int_el3_phys=ArmPPI(num=29, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el1_phys=ArmPPI(num=30, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el1_virt=ArmPPI(num=27, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el2_ns_phys=ArmPPI(num=26, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el2_ns_virt=ArmPPI(num=28, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el2_s_phys=ArmPPI(num=20, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el2_s_virt=ArmPPI(num=19, int_type='IRQ_TYPE_LEVEL_LOW'))
 
     timer0 = Sp804(int0=ArmSPI(num=34), int1=ArmSPI(num=34),
                    pio_addr=0x1C110000, clock0='1MHz', clock1='1MHz')
@@ -971,8 +978,8 @@
                                         memory map
 
     Interrupts:
-        Arm CoreTile Express A15x2 (V2P-CA15) - ARM DUI 0604E
-        Section 2.8.2 - Test chip interrupts
+        Armv8-A Foundation Platform - User Guide - Version 11.8
+        Document ID: 100961_1180_00_en
 
 Memory map:
    0x00000000-0x03ffffff: Boot memory (CS0)
@@ -1040,12 +1047,14 @@
 Interrupts:
       0- 15: Software generated interrupts (SGIs)
      16- 31: On-chip private peripherals (PPIs)
+        19   : generic_timer (virt sec EL2)
+        20   : generic_timer (phys sec EL2)
         25   : vgic
-        26   : generic_timer (hyp)
-        27   : generic_timer (virt)
-        28   : Reserved (Legacy FIQ)
-        29   : generic_timer (phys, sec)
-        30   : generic_timer (phys, non-sec)
+        26   : generic_timer (phys non-sec EL2)
+        27   : generic_timer (virt EL1)
+        28   : generic_timer (virt non-sec EL2)
+        29   : generic_timer (phys EL3)
+        30   : generic_timer (phys EL1)
         31   : Reserved (Legacy IRQ)
     32- 95: Mother board peripherals (SPIs)
         32   : Watchdog (SP805)
@@ -1121,10 +1130,13 @@
 
     sys_counter = SystemCounter()
     generic_timer = GenericTimer(
-        int_phys_s=ArmPPI(num=29, int_type='IRQ_TYPE_LEVEL_LOW'),
-        int_phys_ns=ArmPPI(num=30, int_type='IRQ_TYPE_LEVEL_LOW'),
-        int_virt=ArmPPI(num=27, int_type='IRQ_TYPE_LEVEL_LOW'),
-        int_hyp=ArmPPI(num=26, int_type='IRQ_TYPE_LEVEL_LOW'))
+        int_el3_phys=ArmPPI(num=29, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el1_phys=ArmPPI(num=30, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el1_virt=ArmPPI(num=27, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el2_ns_phys=ArmPPI(num=26, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el2_ns_virt=ArmPPI(num=28, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el2_s_phys=ArmPPI(num=20, int_type='IRQ_TYPE_LEVEL_LOW'),
+        int_el2_s_virt=ArmPPI(num=19, int_type='IRQ_TYPE_LEVEL_LOW'))
     generic_timer_mem = GenericTimerMem(cnt_control_base=0x2a430000,
                                         cnt_read_base=0x2a800000,
                                         cnt_ctl_base=0x2a810000,
@@ -1366,6 +1378,7 @@
 class VExpress_GEM5_V2_Base(VExpress_GEM5_Base):
     gic = Gicv3(dist_addr=0x2c000000, redist_addr=0x2c010000,
                 maint_int=ArmPPI(num=25),
+                gicv4=True,
                 its=Gicv3Its(pio_addr=0x2e010000))
 
     # Limiting to 128 since it will otherwise overlap with PCI space
@@ -1418,9 +1431,9 @@
 
     clcd = Pl111(pio_addr=0x1c1f0000, interrupt=ArmSPI(num=46))
 
-    gic = Gicv3(dist_addr=0x2f000000, redist_addr=0x2f100000,
-                maint_int=ArmPPI(num=25), gicv4=False,
-                its=NULL)
+    gic = kvm_gicv3_class(dist_addr=0x2f000000, redist_addr=0x2f100000,
+                          maint_int=ArmPPI(num=25), gicv4=False,
+                          its=NULL)
 
     pci_host = GenericArmPciHost(
         conf_base=0x40000000, conf_size='256MiB', conf_device_bits=12,
@@ -1436,5 +1449,5 @@
 
     def setupBootLoader(self, cur_sys, loc, boot_loader=None):
         if boot_loader is None:
-            boot_loader = [ loc('boot_v2.arm64') ]
+            boot_loader = [ loc('boot_foundation.arm64') ]
         super().setupBootLoader(cur_sys, boot_loader)
diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript
index e74b190..f7e9c76 100644
--- a/src/dev/arm/SConscript
+++ b/src/dev/arm/SConscript
@@ -63,8 +63,8 @@
 SimObject('NoMali.py', sim_objects=['NoMaliGpu', 'CustomNoMaliGpu'],
     enums=['NoMaliGpuType'], tags='arm isa')
 SimObject('VirtIOMMIO.py', sim_objects=['MmioVirtIO'], tags='arm isa')
-if env['USE_ARM_FASTMODEL']:
-    SimObject('VExpressFastmodel.py', tags='arm isa')
+if env['CONF']['USE_ARM_FASTMODEL']:
+    SimObject('VExpressFastmodel.py', sim_objects=[], tags='arm isa')
 
 Source('a9scu.cc', tags='arm isa')
 Source('amba_device.cc', tags='arm isa')
diff --git a/src/dev/arm/base_gic.cc b/src/dev/arm/base_gic.cc
index 4685ce4..5694c37 100644
--- a/src/dev/arm/base_gic.cc
+++ b/src/dev/arm/base_gic.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2017-2018 ARM Limited
+ * Copyright (c) 2012, 2017-2018, 2021 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -39,6 +39,7 @@
 
 #include "cpu/thread_context.hh"
 #include "dev/arm/realview.hh"
+#include "debug/GIC.hh"
 #include "params/ArmInterruptPin.hh"
 #include "params/ArmPPI.hh"
 #include "params/ArmSigInterruptPin.hh"
diff --git a/src/dev/arm/base_gic.hh b/src/dev/arm/base_gic.hh
index 72fe9f4..1ab8c2a 100644
--- a/src/dev/arm/base_gic.hh
+++ b/src/dev/arm/base_gic.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013, 2017-2018 ARM Limited
+ * Copyright (c) 2012-2013, 2017-2018, 2021 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -119,22 +119,21 @@
     /** Check if version supported */
     virtual bool supportsVersion(GicVersion version) = 0;
 
+  protected: // GIC state transfer
+    /**
+     * When trasferring the state between two GICs (essentially
+     * writing architectural registers) an interrupt might be posted
+     * by the model. We don't want this to happen as the GIC might
+     * be in an inconsistent state. We therefore disable side effects
+     * by relying on the blockIntUpdate method.
+     */
+    virtual bool blockIntUpdate() const { return false; }
+
   protected:
     /** Platform this GIC belongs to. */
     Platform *platform;
 };
 
-class BaseGicRegisters
-{
-  public:
-    virtual uint32_t readDistributor(ContextID ctx, Addr daddr) = 0;
-    virtual uint32_t readCpu(ContextID ctx, Addr daddr) = 0;
-
-    virtual void writeDistributor(ContextID ctx, Addr daddr,
-                                  uint32_t data) = 0;
-    virtual void writeCpu(ContextID ctx, Addr daddr, uint32_t data) = 0;
-};
-
 /**
  * This SimObject is instantiated in the python world and
  * serves as an ArmInterruptPin generator. In this way it
diff --git a/src/dev/arm/generic_timer.cc b/src/dev/arm/generic_timer.cc
index 20aaf82..ac25c05 100644
--- a/src/dev/arm/generic_timer.cc
+++ b/src/dev/arm/generic_timer.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, 2017-2018,2020 ARM Limited
+ * Copyright (c) 2013, 2015, 2017-2018,2020,2022 Arm Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -38,13 +38,17 @@
 #include "dev/arm/generic_timer.hh"
 
 #include <cmath>
+#include <string_view>
 
 #include "arch/arm/page_size.hh"
 #include "arch/arm/system.hh"
 #include "arch/arm/utility.hh"
 #include "base/logging.hh"
 #include "base/trace.hh"
+#include "config/kvm_isa.hh"
+#include "config/use_kvm.hh"
 #include "cpu/base.hh"
+#include "cpu/kvm/vm.hh"
 #include "debug/Timer.hh"
 #include "dev/arm/base_gic.hh"
 #include "mem/packet_access.hh"
@@ -403,6 +407,18 @@
     updateCounter();
 }
 
+bool
+ArchTimerKvm::scheduleEvents()
+{
+    if constexpr (USE_KVM &&
+            std::string_view(KVM_ISA) == std::string_view("arm")) {
+        auto *vm = system.getKvmVM();
+        return !vm || !vm->validEnvironment();
+    } else {
+        return true;
+    }
+}
+
 GenericTimer::GenericTimer(const GenericTimerParams &p)
     : SimObject(p),
       systemCounter(*p.counter),
@@ -479,10 +495,13 @@
 
         timers[i].reset(
             new CoreTimers(*this, system, i,
-                           p.int_phys_s->get(tc),
-                           p.int_phys_ns->get(tc),
-                           p.int_virt->get(tc),
-                           p.int_hyp->get(tc)));
+                           p.int_el3_phys->get(tc),
+                           p.int_el1_phys->get(tc),
+                           p.int_el1_virt->get(tc),
+                           p.int_el2_ns_phys->get(tc),
+                           p.int_el2_ns_virt->get(tc),
+                           p.int_el2_s_phys->get(tc),
+                           p.int_el2_s_virt->get(tc)));
     }
 }
 
@@ -516,7 +535,6 @@
 GenericTimer::setMiscReg(int reg, unsigned cpu, RegVal val)
 {
     CoreTimers &core(getTimers(cpu));
-    ThreadContext *tc = system.threads[cpu];
 
     switch (reg) {
       case MISCREG_CNTFRQ:
@@ -528,14 +546,10 @@
       case MISCREG_CNTKCTL:
       case MISCREG_CNTKCTL_EL1:
       {
-        if (ELIsInHost(tc, currEL(tc))) {
-            tc->setMiscReg(MISCREG_CNTHCTL_EL2, val);
-            return;
-        }
         RegVal old_cnt_ctl = core.cntkctl;
         core.cntkctl = val;
 
-        ArchTimer *timer = &core.virt;
+        ArchTimer *timer = &core.virtEL1;
         CoreTimers::EventStream *ev_stream = &core.virtEvStream;
 
         handleStream(ev_stream, timer, old_cnt_ctl, val);
@@ -547,26 +561,26 @@
         RegVal old_cnt_ctl = core.cnthctl;
         core.cnthctl = val;
 
-        ArchTimer *timer = &core.physNS;
+        ArchTimer *timer = &core.physEL1;
         CoreTimers::EventStream *ev_stream = &core.physEvStream;
 
         handleStream(ev_stream, timer, old_cnt_ctl, val);
         return;
       }
-      // Physical timer (NS)
+      // EL1 physical timer
       case MISCREG_CNTP_CVAL_NS:
       case MISCREG_CNTP_CVAL_EL0:
-        core.physNS.setCompareValue(val);
+        core.physEL1.setCompareValue(val);
         return;
 
       case MISCREG_CNTP_TVAL_NS:
       case MISCREG_CNTP_TVAL_EL0:
-        core.physNS.setTimerValue(val);
+        core.physEL1.setTimerValue(val);
         return;
 
       case MISCREG_CNTP_CTL_NS:
       case MISCREG_CNTP_CTL_EL0:
-        core.physNS.setControl(val);
+        core.physEL1.setControl(val);
         return;
 
       // Count registers
@@ -578,57 +592,96 @@
              miscRegName[reg]);
         return;
 
-      // Virtual timer
+      // EL1 virtual timer
       case MISCREG_CNTVOFF:
       case MISCREG_CNTVOFF_EL2:
-        core.virt.setOffset(val);
+        core.virtEL1.setOffset(val);
         return;
 
       case MISCREG_CNTV_CVAL:
       case MISCREG_CNTV_CVAL_EL0:
-        core.virt.setCompareValue(val);
+        core.virtEL1.setCompareValue(val);
         return;
 
       case MISCREG_CNTV_TVAL:
       case MISCREG_CNTV_TVAL_EL0:
-        core.virt.setTimerValue(val);
+        core.virtEL1.setTimerValue(val);
         return;
 
       case MISCREG_CNTV_CTL:
       case MISCREG_CNTV_CTL_EL0:
-        core.virt.setControl(val);
+        core.virtEL1.setControl(val);
         return;
 
-      // Physical timer (S)
+      // EL3 physical timer
       case MISCREG_CNTP_CTL_S:
       case MISCREG_CNTPS_CTL_EL1:
-        core.physS.setControl(val);
+        core.physEL3.setControl(val);
         return;
 
       case MISCREG_CNTP_CVAL_S:
       case MISCREG_CNTPS_CVAL_EL1:
-        core.physS.setCompareValue(val);
+        core.physEL3.setCompareValue(val);
         return;
 
       case MISCREG_CNTP_TVAL_S:
       case MISCREG_CNTPS_TVAL_EL1:
-        core.physS.setTimerValue(val);
+        core.physEL3.setTimerValue(val);
         return;
 
-      // Hyp phys. timer, non-secure
+      // EL2 Non-secure physical timer
       case MISCREG_CNTHP_CTL:
       case MISCREG_CNTHP_CTL_EL2:
-        core.hyp.setControl(val);
+        core.physNsEL2.setControl(val);
         return;
 
       case MISCREG_CNTHP_CVAL:
       case MISCREG_CNTHP_CVAL_EL2:
-        core.hyp.setCompareValue(val);
+        core.physNsEL2.setCompareValue(val);
         return;
 
       case MISCREG_CNTHP_TVAL:
       case MISCREG_CNTHP_TVAL_EL2:
-        core.hyp.setTimerValue(val);
+        core.physNsEL2.setTimerValue(val);
+        return;
+
+      // EL2 Non-secure virtual timer
+      case MISCREG_CNTHV_CTL_EL2:
+        core.virtNsEL2.setControl(val);
+        return;
+
+      case MISCREG_CNTHV_CVAL_EL2:
+        core.virtNsEL2.setCompareValue(val);
+        return;
+
+      case MISCREG_CNTHV_TVAL_EL2:
+        core.virtNsEL2.setTimerValue(val);
+        return;
+
+      // EL2 Secure physical timer
+      case MISCREG_CNTHPS_CTL_EL2:
+        core.physSEL2.setControl(val);
+        return;
+
+      case MISCREG_CNTHPS_CVAL_EL2:
+        core.physSEL2.setCompareValue(val);
+        return;
+
+      case MISCREG_CNTHPS_TVAL_EL2:
+        core.physSEL2.setTimerValue(val);
+        return;
+
+      // EL2 Secure virtual timer
+      case MISCREG_CNTHVS_CTL_EL2:
+        core.virtSEL2.setControl(val);
+        return;
+
+      case MISCREG_CNTHVS_CVAL_EL2:
+        core.virtSEL2.setCompareValue(val);
+        return;
+
+      case MISCREG_CNTHVS_TVAL_EL2:
+        core.virtSEL2.setTimerValue(val);
         return;
 
       default:
@@ -653,70 +706,100 @@
       case MISCREG_CNTHCTL:
       case MISCREG_CNTHCTL_EL2:
         return core.cnthctl & 0x00000000ffffffff;
-      // Physical timer
+      // EL1 physical timer
       case MISCREG_CNTP_CVAL_NS:
       case MISCREG_CNTP_CVAL_EL0:
-        return core.physNS.compareValue();
+        return core.physEL1.compareValue();
 
       case MISCREG_CNTP_TVAL_NS:
       case MISCREG_CNTP_TVAL_EL0:
-        return core.physNS.timerValue();
+        return core.physEL1.timerValue();
 
       case MISCREG_CNTP_CTL_EL0:
       case MISCREG_CNTP_CTL_NS:
-        return core.physNS.control();
+        return core.physEL1.control();
 
       case MISCREG_CNTPCT:
       case MISCREG_CNTPCT_EL0:
-        return core.physNS.value();
+        return core.physEL1.value();
 
 
-      // Virtual timer
+      // EL1 virtual timer
       case MISCREG_CNTVCT:
       case MISCREG_CNTVCT_EL0:
-        return core.virt.value();
+        return core.virtEL1.value();
 
       case MISCREG_CNTVOFF:
       case MISCREG_CNTVOFF_EL2:
-        return core.virt.offset();
+        return core.virtEL1.offset();
 
       case MISCREG_CNTV_CVAL:
       case MISCREG_CNTV_CVAL_EL0:
-        return core.virt.compareValue();
+        return core.virtEL1.compareValue();
 
       case MISCREG_CNTV_TVAL:
       case MISCREG_CNTV_TVAL_EL0:
-        return core.virt.timerValue();
+        return core.virtEL1.timerValue();
 
       case MISCREG_CNTV_CTL:
       case MISCREG_CNTV_CTL_EL0:
-        return core.virt.control();
+        return core.virtEL1.control();
 
-      // PL1 phys. timer, secure
+      // EL3 physical timer
       case MISCREG_CNTP_CTL_S:
       case MISCREG_CNTPS_CTL_EL1:
-        return core.physS.control();
+        return core.physEL3.control();
 
       case MISCREG_CNTP_CVAL_S:
       case MISCREG_CNTPS_CVAL_EL1:
-        return core.physS.compareValue();
+        return core.physEL3.compareValue();
 
       case MISCREG_CNTP_TVAL_S:
       case MISCREG_CNTPS_TVAL_EL1:
-        return core.physS.timerValue();
+        return core.physEL3.timerValue();
 
-      // HYP phys. timer (NS)
+      // EL2 Non-secure physical timer
       case MISCREG_CNTHP_CTL:
       case MISCREG_CNTHP_CTL_EL2:
-        return core.hyp.control();
+        return core.physNsEL2.control();
 
       case MISCREG_CNTHP_CVAL:
       case MISCREG_CNTHP_CVAL_EL2:
-        return core.hyp.compareValue();
+        return core.physNsEL2.compareValue();
 
       case MISCREG_CNTHP_TVAL:
       case MISCREG_CNTHP_TVAL_EL2:
-        return core.hyp.timerValue();
+        return core.physNsEL2.timerValue();
+
+      // EL2 Non-secure virtual timer
+      case MISCREG_CNTHV_CTL_EL2:
+        return core.virtNsEL2.control();
+
+      case MISCREG_CNTHV_CVAL_EL2:
+        return core.virtNsEL2.compareValue();
+
+      case MISCREG_CNTHV_TVAL_EL2:
+        return core.virtNsEL2.timerValue();
+
+      // EL2 Secure physical timer
+      case MISCREG_CNTHPS_CTL_EL2:
+        return core.physSEL2.control();
+
+      case MISCREG_CNTHPS_CVAL_EL2:
+        return core.physSEL2.compareValue();
+
+      case MISCREG_CNTHPS_TVAL_EL2:
+        return core.physSEL2.timerValue();
+
+      // EL2 Secure virtual timer
+      case MISCREG_CNTHVS_CTL_EL2:
+        return core.virtSEL2.control();
+
+      case MISCREG_CNTHVS_CVAL_EL2:
+        return core.virtSEL2.compareValue();
+
+      case MISCREG_CNTHVS_TVAL_EL2:
+        return core.virtSEL2.timerValue();
 
       default:
         warn("Reading from unknown register: %s\n", miscRegName[reg]);
@@ -726,30 +809,42 @@
 
 GenericTimer::CoreTimers::CoreTimers(GenericTimer &_parent,
     ArmSystem &system, unsigned cpu,
-    ArmInterruptPin *_irqPhysS, ArmInterruptPin *_irqPhysNS,
-    ArmInterruptPin *_irqVirt, ArmInterruptPin *_irqHyp)
+    ArmInterruptPin *irq_el3_phys, ArmInterruptPin *irq_el1_phys,
+    ArmInterruptPin *irq_el1_virt, ArmInterruptPin *irq_el2_ns_phys,
+    ArmInterruptPin *irq_el2_ns_virt, ArmInterruptPin *irq_el2_s_phys,
+    ArmInterruptPin *irq_el2_s_virt)
       : parent(_parent),
         cntfrq(parent.params().cntfrq),
         cntkctl(0), cnthctl(0),
         threadContext(system.threads[cpu]),
-        irqPhysS(_irqPhysS),
-        irqPhysNS(_irqPhysNS),
-        irqVirt(_irqVirt),
-        irqHyp(_irqHyp),
-        physS(csprintf("%s.phys_s_timer%d", parent.name(), cpu),
-              system, parent, parent.systemCounter,
-              _irqPhysS),
-        // This should really be phys_timerN, but we are stuck with
-        // arch_timer for backwards compatibility.
-        physNS(csprintf("%s.arch_timer%d", parent.name(), cpu),
-             system, parent, parent.systemCounter,
-             _irqPhysNS),
-        virt(csprintf("%s.virt_timer%d", parent.name(), cpu),
-           system, parent, parent.systemCounter,
-           _irqVirt),
-        hyp(csprintf("%s.hyp_timer%d", parent.name(), cpu),
-           system, parent, parent.systemCounter,
-           _irqHyp),
+        irqPhysEL3(irq_el3_phys),
+        irqPhysEL1(irq_el1_phys),
+        irqVirtEL1(irq_el1_virt),
+        irqPhysNsEL2(irq_el2_ns_phys),
+        irqVirtNsEL2(irq_el2_ns_virt),
+        irqPhysSEL2(irq_el2_s_phys),
+        irqVirtSEL2(irq_el2_s_virt),
+        physEL3(csprintf("%s.el3_phys_timer%d", parent.name(), cpu),
+                system, parent, parent.systemCounter,
+                irq_el3_phys),
+        physEL1(csprintf("%s.el1_phys_timer%d", parent.name(), cpu),
+                system, parent, parent.systemCounter,
+                irq_el1_phys),
+        virtEL1(csprintf("%s.el1_virt_timer%d", parent.name(), cpu),
+                system, parent, parent.systemCounter,
+                irq_el1_virt),
+        physNsEL2(csprintf("%s.el2_ns_phys_timer%d", parent.name(), cpu),
+                  system, parent, parent.systemCounter,
+                  irq_el2_ns_phys),
+        virtNsEL2(csprintf("%s.el2_ns_virt_timer%d", parent.name(), cpu),
+                  system, parent, parent.systemCounter,
+                  irq_el2_ns_virt),
+        physSEL2(csprintf("%s.el2_s_phys_timer%d", parent.name(), cpu),
+                 system, parent, parent.systemCounter,
+                 irq_el2_s_phys),
+        virtSEL2(csprintf("%s.el2_s_virt_timer%d", parent.name(), cpu),
+                 system, parent, parent.systemCounter,
+                 irq_el2_s_virt),
         physEvStream{
            EventFunctionWrapper([this]{ physEventStreamCallback(); },
            csprintf("%s.phys_event_gen%d", parent.name(), cpu)), 0, 0
@@ -765,14 +860,14 @@
 GenericTimer::CoreTimers::physEventStreamCallback()
 {
     eventStreamCallback();
-    schedNextEvent(physEvStream, physNS);
+    schedNextEvent(physEvStream, physEL1);
 }
 
 void
 GenericTimer::CoreTimers::virtEventStreamCallback()
 {
     eventStreamCallback();
-    schedNextEvent(virtEvStream, virt);
+    schedNextEvent(virtEvStream, virtEL1);
 }
 
 void
@@ -793,8 +888,8 @@
 void
 GenericTimer::CoreTimers::notify()
 {
-    schedNextEvent(virtEvStream, virt);
-    schedNextEvent(physEvStream, physNS);
+    schedNextEvent(virtEvStream, virtEL1);
+    schedNextEvent(physEvStream, physEL1);
 }
 
 void
@@ -822,10 +917,13 @@
     SERIALIZE_SCALAR(virtEvStream.transitionTo);
     SERIALIZE_SCALAR(virtEvStream.transitionBit);
 
-    physS.serializeSection(cp, "phys_s_timer");
-    physNS.serializeSection(cp, "phys_ns_timer");
-    virt.serializeSection(cp, "virt_timer");
-    hyp.serializeSection(cp, "hyp_timer");
+    physEL3.serializeSection(cp, "phys_el3_timer");
+    physEL1.serializeSection(cp, "phys_el1_timer");
+    virtEL1.serializeSection(cp, "virt_el1_timer");
+    physNsEL2.serializeSection(cp, "phys_ns_el2_timer");
+    virtNsEL2.serializeSection(cp, "virt_ns_el2_timer");
+    physSEL2.serializeSection(cp, "phys_s_el2_timer");
+    virtSEL2.serializeSection(cp, "virt_s_el2_timer");
 }
 
 void
@@ -855,10 +953,13 @@
     UNSERIALIZE_SCALAR(virtEvStream.transitionTo);
     UNSERIALIZE_SCALAR(virtEvStream.transitionBit);
 
-    physS.unserializeSection(cp, "phys_s_timer");
-    physNS.unserializeSection(cp, "phys_ns_timer");
-    virt.unserializeSection(cp, "virt_timer");
-    hyp.unserializeSection(cp, "hyp_timer");
+    physEL3.unserializeSection(cp, "phys_el3_timer");
+    physEL1.unserializeSection(cp, "phys_el1_timer");
+    virtEL1.unserializeSection(cp, "virt_el1_timer");
+    physNsEL2.unserializeSection(cp, "phys_ns_el2_timer");
+    virtNsEL2.unserializeSection(cp, "virt_ns_el2_timer");
+    physSEL2.unserializeSection(cp, "phys_s_el2_timer");
+    virtSEL2.unserializeSection(cp, "virt_s_el2_timer");
 }
 
 void
diff --git a/src/dev/arm/generic_timer.hh b/src/dev/arm/generic_timer.hh
index 9cccef6..50324c8 100644
--- a/src/dev/arm/generic_timer.hh
+++ b/src/dev/arm/generic_timer.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, 2017-2018,2020 ARM Limited
+ * Copyright (c) 2013, 2015, 2017-2018,2020,2022 Arm Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -280,9 +280,7 @@
     // For ArchTimer's in a GenericTimerISA with Kvm execution about
     // to begin, skip rescheduling the event.
     // Otherwise, we should reschedule the event (if necessary).
-    bool scheduleEvents() override {
-        return !system.validKvmEnvironment();
-    }
+    bool scheduleEvents() override;
 };
 
 class GenericTimer : public SimObject
@@ -304,8 +302,13 @@
     {
       public:
         CoreTimers(GenericTimer &_parent, ArmSystem &system, unsigned cpu,
-                   ArmInterruptPin *_irqPhysS, ArmInterruptPin *_irqPhysNS,
-                   ArmInterruptPin *_irqVirt, ArmInterruptPin *_irqHyp);
+                   ArmInterruptPin *irq_el3_phys,
+                   ArmInterruptPin *irq_el1_phys,
+                   ArmInterruptPin *irq_el1_virt,
+                   ArmInterruptPin *irq_el2_ns_phys,
+                   ArmInterruptPin *irq_el2_ns_virt,
+                   ArmInterruptPin *irq_el2_s_phys,
+                   ArmInterruptPin *irq_el2_s_virt);
 
         /// Generic Timer parent reference
         GenericTimer &parent;
@@ -322,15 +325,21 @@
         /// Thread (HW) context associated to this PE implementation
         ThreadContext *threadContext;
 
-        ArmInterruptPin const *irqPhysS;
-        ArmInterruptPin const *irqPhysNS;
-        ArmInterruptPin const *irqVirt;
-        ArmInterruptPin const *irqHyp;
+        ArmInterruptPin const *irqPhysEL3;
+        ArmInterruptPin const *irqPhysEL1;
+        ArmInterruptPin const *irqVirtEL1;
+        ArmInterruptPin const *irqPhysNsEL2;
+        ArmInterruptPin const *irqVirtNsEL2;
+        ArmInterruptPin const *irqPhysSEL2;
+        ArmInterruptPin const *irqVirtSEL2;
 
-        ArchTimerKvm physS;
-        ArchTimerKvm physNS;
-        ArchTimerKvm virt;
-        ArchTimerKvm hyp;
+        ArchTimerKvm physEL3;
+        ArchTimerKvm physEL1;
+        ArchTimerKvm virtEL1;
+        ArchTimerKvm physNsEL2;
+        ArchTimerKvm virtNsEL2;
+        ArchTimerKvm physSEL2;
+        ArchTimerKvm virtSEL2;
 
         // Event Stream. Events are generated based on a configurable
         // transitionBit over the counter value. transitionTo indicates
diff --git a/src/dev/arm/gic_v2.cc b/src/dev/arm/gic_v2.cc
index bceccf8..7dc001e 100644
--- a/src/dev/arm/gic_v2.cc
+++ b/src/dev/arm/gic_v2.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, 2015-2018, 2020 ARM Limited
+ * Copyright (c) 2010, 2013, 2015-2018, 2020-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -64,6 +64,61 @@
 const AddrRange GicV2::GICD_ITARGETSR (0x800, 0xc00);
 const AddrRange GicV2::GICD_ICFGR     (0xc00, 0xd00);
 
+void
+GicV2Registers::copyDistRegister(GicV2Registers* from,
+                                 GicV2Registers* to,
+                                 ContextID ctx, Addr daddr)
+{
+    auto val = from->readDistributor(ctx, daddr);
+    DPRINTF(GIC, "copy dist 0x%x 0x%08x\n", daddr, val);
+    to->writeDistributor(ctx, daddr, val);
+}
+
+void
+GicV2Registers::copyCpuRegister(GicV2Registers* from,
+                                GicV2Registers* to,
+                                ContextID ctx, Addr daddr)
+{
+    auto val = from->readCpu(ctx, daddr);
+    DPRINTF(GIC, "copy cpu  0x%x 0x%08x\n", daddr, val);
+    to->writeCpu(ctx, daddr, val);
+}
+
+void
+GicV2Registers::copyBankedDistRange(System *sys, GicV2Registers* from,
+                                    GicV2Registers* to,
+                                    Addr daddr, size_t size)
+{
+    for (int ctx = 0; ctx < sys->threads.size(); ++ctx)
+        for (auto a = daddr; a < daddr + size; a += 4)
+            copyDistRegister(from, to, ctx, a);
+}
+
+void
+GicV2Registers::clearBankedDistRange(System *sys, GicV2Registers* to,
+                                     Addr daddr, size_t size)
+{
+    for (int ctx = 0; ctx < sys->threads.size(); ++ctx)
+        for (auto a = daddr; a < daddr + size; a += 4)
+            to->writeDistributor(ctx, a, 0xFFFFFFFF);
+}
+
+void
+GicV2Registers::copyDistRange(GicV2Registers* from,
+                              GicV2Registers* to,
+                              Addr daddr, size_t size)
+{
+    for (auto a = daddr; a < daddr + size; a += 4)
+        copyDistRegister(from, to, 0, a);
+}
+
+void
+GicV2Registers::clearDistRange(GicV2Registers* to, Addr daddr, size_t size)
+{
+    for (auto a = daddr; a < daddr + size; a += 4)
+        to->writeDistributor(0, a, 0xFFFFFFFF);
+}
+
 GicV2::GicV2(const Params &p)
     : BaseGic(p),
       gicdPIDR(p.gicd_pidr),
@@ -738,6 +793,9 @@
 void
 GicV2::updateIntState(int hint)
 {
+    if (blockIntUpdate())
+        return;
+
     for (int cpu = 0; cpu < sys->threads.size(); cpu++) {
         if (!cpuEnabled(cpu))
             continue;
@@ -995,6 +1053,78 @@
 }
 
 void
+GicV2::copyGicState(GicV2Registers* from, GicV2Registers* to)
+{
+    Addr set, clear;
+    size_t size;
+
+    /// CPU state (GICC_*)
+    // Copy CPU Interface Control Register (CTLR),
+    //      Interrupt Priority Mask Register (PMR), and
+    //      Binary Point Register (BPR)
+    for (int ctx = 0; ctx < sys->threads.size(); ++ctx) {
+        copyCpuRegister(from, to, ctx, GICC_CTLR);
+        copyCpuRegister(from, to, ctx, GICC_PMR);
+        copyCpuRegister(from, to, ctx, GICC_BPR);
+    }
+
+    /// Distributor state (GICD_*)
+    // Copy Distributor Control Register (CTLR)
+    copyDistRegister(from, to, 0, GICD_CTLR);
+
+    // Copy interrupt-enabled statuses (I[CS]ENABLERn; R0 is per-CPU banked)
+    set   = GICD_ISENABLER.start();
+    clear = GICD_ICENABLER.start();
+    size  = itLines / 8;
+    clearBankedDistRange(sys, to, clear, 4);
+    copyBankedDistRange(sys, from, to, set, 4);
+
+    set += 4, clear += 4, size -= 4;
+    clearDistRange(to, clear, size);
+    copyDistRange(from, to, set, size);
+
+    // Copy pending interrupts (I[CS]PENDRn; R0 is per-CPU banked)
+    set   = GICD_ISPENDR.start();
+    clear = GICD_ICPENDR.start();
+    size  = itLines / 8;
+    clearBankedDistRange(sys, to, clear, 4);
+    copyBankedDistRange(sys, from, to, set, 4);
+
+    set += 4, clear += 4, size -= 4;
+    clearDistRange(to, clear, size);
+    copyDistRange(from, to, set, size);
+
+    // Copy active interrupts (I[CS]ACTIVERn; R0 is per-CPU banked)
+    set   = GICD_ISACTIVER.start();
+    clear = GICD_ICACTIVER.start();
+    size  = itLines / 8;
+    clearBankedDistRange(sys, to, clear, 4);
+    copyBankedDistRange(sys, from, to, set, 4);
+
+    set += 4, clear += 4, size -= 4;
+    clearDistRange(to, clear, size);
+    copyDistRange(from, to, set, size);
+
+    // Copy interrupt priorities (IPRIORITYRn; R0-7 are per-CPU banked)
+    set   = GICD_IPRIORITYR.start();
+    copyBankedDistRange(sys, from, to, set, 32);
+
+    set += 32;
+    size = itLines - 32;
+    copyDistRange(from, to, set, size);
+
+    // Copy interrupt processor target regs (ITARGETRn; R0-7 are read-only)
+    set = GICD_ITARGETSR.start() + 32;
+    size = itLines - 32;
+    copyDistRange(from, to, set, size);
+
+    // Copy interrupt configuration registers (ICFGRn)
+    set = GICD_ICFGR.start();
+    size = itLines / 4;
+    copyDistRange(from, to, set, size);
+}
+
+void
 GicV2::serialize(CheckpointOut &cp) const
 {
     DPRINTF(Checkpoint, "Serializing Arm GIC\n");
diff --git a/src/dev/arm/gic_v2.hh b/src/dev/arm/gic_v2.hh
index 9e68745..a0eec93 100644
--- a/src/dev/arm/gic_v2.hh
+++ b/src/dev/arm/gic_v2.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, 2015-2020 ARM Limited
+ * Copyright (c) 2010, 2013, 2015-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -59,7 +59,36 @@
 namespace gem5
 {
 
-class GicV2 : public BaseGic, public BaseGicRegisters
+class GicV2Registers
+{
+  public:
+    virtual uint32_t readDistributor(ContextID ctx, Addr daddr) = 0;
+    virtual uint32_t readCpu(ContextID ctx, Addr daddr) = 0;
+
+    virtual void writeDistributor(ContextID ctx, Addr daddr,
+                                  uint32_t data) = 0;
+    virtual void writeCpu(ContextID ctx, Addr daddr, uint32_t data) = 0;
+
+  protected:
+    static void copyDistRegister(GicV2Registers* from,
+                                 GicV2Registers* to,
+                                 ContextID ctx, Addr daddr);
+    static void copyCpuRegister(GicV2Registers* from,
+                                GicV2Registers* to,
+                                ContextID ctx, Addr daddr);
+    static void copyBankedDistRange(System *sys,
+                                    GicV2Registers* from,
+                                    GicV2Registers* to,
+                                    Addr daddr, size_t size);
+    static void clearBankedDistRange(System *sys, GicV2Registers* to,
+                                     Addr daddr, size_t size);
+    static void copyDistRange(GicV2Registers* from,
+                              GicV2Registers* to,
+                              Addr daddr, size_t size);
+    static void clearDistRange(GicV2Registers* to, Addr daddr, size_t size);
+};
+
+class GicV2 : public BaseGic, public GicV2Registers
 {
   protected:
     // distributor memory addresses
@@ -448,7 +477,7 @@
     /** See if some processor interrupt flags need to be enabled/disabled
      * @param hint which set of interrupts needs to be checked
      */
-    virtual void updateIntState(int hint);
+    void updateIntState(int hint);
 
     /** Update the register that records priority of the highest priority
      *  active interrupt*/
@@ -512,7 +541,9 @@
 
     bool supportsVersion(GicVersion version) override;
 
-  protected:
+  protected: /** GIC state transfer */
+    void copyGicState(GicV2Registers* from, GicV2Registers* to);
+
     /** Handle a read to the distributor portion of the GIC
      * @param pkt packet to respond to
      */
diff --git a/src/dev/arm/gic_v3.cc b/src/dev/arm/gic_v3.cc
index 223e86a..dde3818 100644
--- a/src/dev/arm/gic_v3.cc
+++ b/src/dev/arm/gic_v3.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2020 ARM Limited
+ * Copyright (c) 2019-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -54,6 +54,80 @@
 namespace gem5
 {
 
+void
+Gicv3Registers::copyDistRegister(Gicv3Registers* from,
+                                 Gicv3Registers* to,
+                                 Addr daddr)
+{
+    auto val = from->readDistributor(daddr);
+    DPRINTF(GIC, "copy dist 0x%x 0x%08x\n", daddr, val);
+    to->writeDistributor(daddr, val);
+}
+
+void
+Gicv3Registers::copyRedistRegister(Gicv3Registers* from,
+                                   Gicv3Registers* to,
+                                   const ArmISA::Affinity &aff, Addr daddr)
+{
+    auto val = from->readRedistributor(aff, daddr);
+    DPRINTF(GIC,
+            "copy redist (aff3: %d, aff2: %d, aff1: %d, aff0: %d) "
+            "0x%x 0x%08x\n",
+            aff.aff3, aff.aff2, aff.aff1, aff.aff0, daddr, val);
+
+    to->writeRedistributor(aff, daddr, val);
+}
+
+void
+Gicv3Registers::copyCpuRegister(Gicv3Registers* from,
+                                Gicv3Registers* to,
+                                const ArmISA::Affinity &aff,
+                                ArmISA::MiscRegIndex misc_reg)
+{
+    auto val = from->readCpu(aff, misc_reg);
+    DPRINTF(GIC,
+            "copy cpu (aff3: %d, aff2: %d, aff1: %d, aff0: %d) "
+            "%s 0x%08x\n",
+            aff.aff3, aff.aff2, aff.aff1, aff.aff0,
+            ArmISA::miscRegName[misc_reg], val);
+
+    to->writeCpu(aff, misc_reg, val);
+}
+
+void
+Gicv3Registers::clearRedistRegister(Gicv3Registers* to,
+                                    const ArmISA::Affinity &aff, Addr daddr)
+{
+    to->writeRedistributor(aff, daddr, 0xFFFFFFFF);
+}
+
+void
+Gicv3Registers::copyRedistRange(Gicv3Registers* from,
+                                Gicv3Registers* to,
+                                const ArmISA::Affinity &aff,
+                                Addr daddr, size_t size)
+{
+    for (auto a = daddr; a < daddr + size; a += 4)
+        copyRedistRegister(from, to, aff, a);
+}
+
+void
+Gicv3Registers::copyDistRange(Gicv3Registers *from,
+                              Gicv3Registers *to,
+                              Addr daddr, size_t size)
+{
+    for (auto a = daddr; a < daddr + size; a += 4)
+        copyDistRegister(from, to, a);
+}
+
+void
+Gicv3Registers::clearDistRange(Gicv3Registers *to, Addr daddr, size_t size)
+{
+    for (auto a = daddr; a < daddr + size; a += 4)
+        to->writeDistributor(a, 0xFFFFFFFF);
+}
+
+
 Gicv3::Gicv3(const Params &p)
     : BaseGic(p)
 {
@@ -240,11 +314,17 @@
     return tc->getCpuPtr()->checkInterrupts(tc->threadId());
 }
 
+Gicv3CPUInterface *
+Gicv3::getCPUInterfaceByAffinity(const ArmISA::Affinity &aff) const
+{
+    return getRedistributorByAffinity(aff)->getCPUInterface();
+}
+
 Gicv3Redistributor *
-Gicv3::getRedistributorByAffinity(uint32_t affinity) const
+Gicv3::getRedistributorByAffinity(const ArmISA::Affinity &aff) const
 {
     for (auto & redistributor : redistributors) {
-        if (redistributor->getAffinity() == affinity) {
+        if (redistributor->getAffinity() == aff) {
             return redistributor;
         }
     }
@@ -268,6 +348,63 @@
     return redistributors[redistributor_id];
 }
 
+uint32_t
+Gicv3::readDistributor(Addr daddr)
+{
+    return distributor->read(daddr, 4, false);
+}
+
+uint32_t
+Gicv3::readRedistributor(const ArmISA::Affinity &aff, Addr daddr)
+{
+    auto redistributor = getRedistributorByAffinity(aff);
+    assert(redistributor);
+    return redistributor->read(daddr, 4, false);
+}
+
+RegVal
+Gicv3::readCpu(const ArmISA::Affinity &aff, ArmISA::MiscRegIndex misc_reg)
+{
+    auto cpu_interface = getCPUInterfaceByAffinity(aff);
+    assert(cpu_interface);
+    return cpu_interface->readMiscReg(misc_reg);
+}
+
+void
+Gicv3::writeDistributor(Addr daddr, uint32_t data)
+{
+    distributor->write(daddr, data, sizeof(data), false);
+}
+
+void
+Gicv3::writeRedistributor(const ArmISA::Affinity &aff, Addr daddr, uint32_t data)
+{
+    auto redistributor = getRedistributorByAffinity(aff);
+    assert(redistributor);
+    redistributor->write(daddr, data, sizeof(data), false);
+}
+
+void
+Gicv3::writeCpu(const ArmISA::Affinity &aff, ArmISA::MiscRegIndex misc_reg,
+                RegVal data)
+{
+    auto cpu_interface = getCPUInterfaceByAffinity(aff);
+    assert(cpu_interface);
+    cpu_interface->setMiscReg(misc_reg, data);
+}
+
+void
+Gicv3::copyGicState(Gicv3Registers* from, Gicv3Registers* to)
+{
+    distributor->copy(from, to);
+    for (auto& redistributor : redistributors) {
+        redistributor->copy(from, to);
+    }
+    for (auto& cpu_interface : cpuInterfaces) {
+        cpu_interface->copy(from, to);
+    }
+}
+
 void
 Gicv3::serialize(CheckpointOut & cp) const
 {
diff --git a/src/dev/arm/gic_v3.hh b/src/dev/arm/gic_v3.hh
index f4aaea5..120b039 100644
--- a/src/dev/arm/gic_v3.hh
+++ b/src/dev/arm/gic_v3.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 ARM Limited
+ * Copyright (c) 2019, 2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -42,6 +42,7 @@
 #define __DEV_ARM_GICV3_H__
 
 #include "arch/arm/interrupts.hh"
+#include "arch/arm/types.hh"
 #include "dev/arm/base_gic.hh"
 #include "params/Gicv3.hh"
 
@@ -53,11 +54,50 @@
 class Gicv3Redistributor;
 class Gicv3Its;
 
-class Gicv3 : public BaseGic
+class Gicv3Registers
+{
+  public:
+    virtual uint32_t readDistributor(Addr daddr) = 0;
+    virtual uint32_t readRedistributor(const ArmISA::Affinity &aff,
+                                       Addr daddr) = 0;
+    virtual RegVal readCpu(const ArmISA::Affinity &aff,
+                           ArmISA::MiscRegIndex misc_reg) = 0;
+
+    virtual void writeDistributor(Addr daddr, uint32_t data) = 0;
+    virtual void writeRedistributor(const ArmISA::Affinity &aff,
+                                    Addr daddr, uint32_t data) = 0;
+    virtual void writeCpu(const ArmISA::Affinity &aff,
+                          ArmISA::MiscRegIndex misc_reg, RegVal data) = 0;
+
+  protected:
+    static void copyDistRegister(Gicv3Registers* from,
+                                 Gicv3Registers* to,
+                                 Addr daddr);
+    static void copyRedistRegister(Gicv3Registers* from,
+                                   Gicv3Registers* to,
+                                   const ArmISA::Affinity &aff, Addr daddr);
+    static void copyCpuRegister(Gicv3Registers* from,
+                                Gicv3Registers* to,
+                                const ArmISA::Affinity &aff,
+                                ArmISA::MiscRegIndex misc_reg);
+    static void clearRedistRegister(Gicv3Registers* to,
+                                    const ArmISA::Affinity &aff, Addr daddr);
+    static void copyRedistRange(Gicv3Registers* from,
+                                Gicv3Registers* to,
+                                const ArmISA::Affinity &aff,
+                                Addr daddr, size_t size);
+    static void copyDistRange(Gicv3Registers* from,
+                              Gicv3Registers* to,
+                              Addr daddr, size_t size);
+    static void clearDistRange(Gicv3Registers* to, Addr daddr, size_t size);
+};
+
+class Gicv3 : public BaseGic, public Gicv3Registers
 {
   protected:
     friend class Gicv3CPUInterface;
     friend class Gicv3Redistributor;
+    friend class Gicv3Distributor;
 
     Gicv3Distributor * distributor;
     std::vector<Gicv3Redistributor *> redistributors;
@@ -155,13 +195,32 @@
         return redistributors[context_id];
     }
 
+    Gicv3CPUInterface *
+    getCPUInterfaceByAffinity(const ArmISA::Affinity &aff) const;
+
     Gicv3Redistributor *
-    getRedistributorByAffinity(uint32_t affinity) const;
+    getRedistributorByAffinity(const ArmISA::Affinity &aff) const;
 
     Gicv3Redistributor *
     getRedistributorByAddr(Addr address) const;
 
     void postInt(uint32_t cpu, ArmISA::InterruptTypes int_type);
+
+  protected: // GIC state transfer
+    void copyGicState(Gicv3Registers* from, Gicv3Registers* to);
+
+  public: // Gicv3Registers
+    uint32_t readDistributor(Addr daddr) override;
+    uint32_t readRedistributor(const ArmISA::Affinity &aff,
+                               Addr daddr) override;
+    RegVal readCpu(const ArmISA::Affinity &aff,
+                   ArmISA::MiscRegIndex misc_reg) override;
+
+    void writeDistributor(Addr daddr, uint32_t data) override;
+    void writeRedistributor(const ArmISA::Affinity &aff,
+                            Addr daddr, uint32_t data) override;
+    void writeCpu(const ArmISA::Affinity &aff,
+                  ArmISA::MiscRegIndex misc_reg, RegVal data) override;
 };
 
 } // namespace gem5
diff --git a/src/dev/arm/gic_v3_cpu_interface.cc b/src/dev/arm/gic_v3_cpu_interface.cc
index 6093e86..b089ba0 100644
--- a/src/dev/arm/gic_v3_cpu_interface.cc
+++ b/src/dev/arm/gic_v3_cpu_interface.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 ARM Limited
+ * Copyright (c) 2019, 2021-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -2032,6 +2032,9 @@
 void
 Gicv3CPUInterface::update()
 {
+    if (gic->blockIntUpdate())
+        return;
+
     bool signal_IRQ = false;
     bool signal_FIQ = false;
 
@@ -2066,6 +2069,9 @@
 void
 Gicv3CPUInterface::virtualUpdate()
 {
+    if (gic->blockIntUpdate())
+        return;
+
     bool signal_IRQ = false;
     bool signal_FIQ = false;
     int lr_idx = getHPPVILR();
@@ -2587,6 +2593,29 @@
 }
 
 void
+Gicv3CPUInterface::copy(Gicv3Registers *from, Gicv3Registers *to)
+{
+    const auto affinity = redistributor->getAffinity();
+    gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_PMR_EL1);
+
+    gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_AP1R0_EL1);
+    if (PRIORITY_BITS >= 6) {
+        gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_AP1R1_EL1);
+    }
+
+    if (PRIORITY_BITS >= 7) {
+        gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_AP1R2_EL1);
+        gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_AP1R3_EL1);
+    }
+
+    gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_BPR1_EL1);
+    gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_CTLR_EL1);
+    gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_SRE_EL1);
+    gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_IGRPEN0_EL1);
+    gic->copyCpuRegister(from, to, affinity, MISCREG_ICC_IGRPEN1_EL1);
+}
+
+void
 Gicv3CPUInterface::serialize(CheckpointOut & cp) const
 {
     SERIALIZE_SCALAR(hppi.intid);
diff --git a/src/dev/arm/gic_v3_cpu_interface.hh b/src/dev/arm/gic_v3_cpu_interface.hh
index eb16602..5bcfba5 100644
--- a/src/dev/arm/gic_v3_cpu_interface.hh
+++ b/src/dev/arm/gic_v3_cpu_interface.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 ARM Limited
+ * Copyright (c) 2019, 2022 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -360,6 +360,9 @@
 
     void init();
 
+  public:
+    void copy(Gicv3Registers *from, Gicv3Registers *to);
+
   public: // BaseISADevice
     RegVal readMiscReg(int misc_reg) override;
     void setMiscReg(int misc_reg, RegVal val) override;
diff --git a/src/dev/arm/gic_v3_distributor.cc b/src/dev/arm/gic_v3_distributor.cc
index 75c3df5..820f8bc 100644
--- a/src/dev/arm/gic_v3_distributor.cc
+++ b/src/dev/arm/gic_v3_distributor.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2020 ARM Limited
+ * Copyright (c) 2019-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -88,7 +88,7 @@
       gicdTyper(0),
       gicdPidr0(0x92),
       gicdPidr1(0xb4),
-      gicdPidr2(0x3b),
+      gicdPidr2(gic->params().gicv4 ? 0x4b : 0x3b),
       gicdPidr3(0),
       gicdPidr4(0x44)
 {
@@ -1085,6 +1085,9 @@
 void
 Gicv3Distributor::update()
 {
+    if (gic->blockIntUpdate())
+        return;
+
     // Find the highest priority pending SPI
     for (int int_id = Gicv3::SGI_MAX + Gicv3::PPI_MAX; int_id < itLines;
          int_id++) {
@@ -1179,6 +1182,29 @@
 }
 
 void
+Gicv3Distributor::copy(Gicv3Registers *from, Gicv3Registers *to)
+{
+    const size_t size = itLines / 8;
+
+    gic->copyDistRegister(from, to, GICD_CTLR);
+
+    gic->clearDistRange(to, GICD_ICENABLER.start(), size);
+    gic->clearDistRange(to, GICD_ICPENDR.start(), size);
+    gic->clearDistRange(to, GICD_ICACTIVER.start(), size);
+
+    gic->copyDistRange(from, to, GICD_IGROUPR.start(), size);
+    gic->copyDistRange(from, to, GICD_ISENABLER.start(), size);
+    gic->copyDistRange(from, to, GICD_ISPENDR.start(), size);
+    gic->copyDistRange(from, to, GICD_ISACTIVER.start(), size);
+    gic->copyDistRange(from, to, GICD_IPRIORITYR.start(), size);
+    gic->copyDistRange(from, to, GICD_ITARGETSR.start(), size);
+    gic->copyDistRange(from, to, GICD_ICFGR.start(), size);
+    gic->copyDistRange(from, to, GICD_IGRPMODR.start(), size);
+    gic->copyDistRange(from, to, GICD_NSACR.start(), size);
+    gic->copyDistRange(from, to, GICD_IROUTER.start(), size);
+}
+
+void
 Gicv3Distributor::serialize(CheckpointOut & cp) const
 {
     SERIALIZE_SCALAR(ARE);
diff --git a/src/dev/arm/gic_v3_distributor.hh b/src/dev/arm/gic_v3_distributor.hh
index 22c5ad7..9960e91 100644
--- a/src/dev/arm/gic_v3_distributor.hh
+++ b/src/dev/arm/gic_v3_distributor.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2020 ARM Limited
+ * Copyright (c) 2019-2022 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -272,6 +272,8 @@
     uint64_t read(Addr addr, size_t size, bool is_secure_access);
     void write(Addr addr, uint64_t data, size_t size,
                bool is_secure_access);
+
+    void copy(Gicv3Registers *from, Gicv3Registers *to);
 };
 
 } // namespace gem5
diff --git a/src/dev/arm/gic_v3_redistributor.cc b/src/dev/arm/gic_v3_redistributor.cc
index 8d4c29b..e4380ef 100644
--- a/src/dev/arm/gic_v3_redistributor.cc
+++ b/src/dev/arm/gic_v3_redistributor.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2020 ARM Limited
+ * Copyright (c) 2019-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -193,10 +193,7 @@
       }
 
       case GICR_PIDR2: { // Peripheral ID2 Register
-          uint8_t arch_rev = 0x3; // 0x3 GICv3
-          uint8_t jedec = 0x1; // JEP code
-          uint8_t des_1 = 0x3; // JEP106 identification code, bits[6:4]
-          return (arch_rev << 4) | (jedec << 3) | (des_1 << 0);
+          return gic->getDistributor()->gicdPidr2;
       }
 
       case GICR_PIDR3: // Peripheral ID3 Register
@@ -804,6 +801,9 @@
 void
 Gicv3Redistributor::update()
 {
+    if (gic->blockIntUpdate())
+        return;
+
     for (int int_id = 0; int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX; int_id++) {
         Gicv3::GroupId int_group = getIntGroup(int_id);
         bool group_enabled = distributor->groupEnabled(int_group);
@@ -1014,17 +1014,7 @@
 Gicv3Redistributor::getAffinity() const
 {
     ThreadContext *tc = gic->getSystem()->threads[cpuId];
-    uint64_t mpidr = getMPIDR(gic->getSystem(), tc);
-    /*
-     * Aff3 = MPIDR[39:32]
-     * (Note getMPIDR() returns uint32_t so Aff3 is always 0...)
-     * Aff2 = MPIDR[23:16]
-     * Aff1 = MPIDR[15:8]
-     * Aff0 = MPIDR[7:0]
-     * affinity = Aff3.Aff2.Aff1.Aff0
-     */
-    uint64_t affinity = ((mpidr & 0xff00000000) >> 8) | (mpidr & (0xffffff));
-    return affinity;
+    return gem5::ArmISA::getAffinity(gic->getSystem(), tc);
 }
 
 bool
@@ -1054,6 +1044,34 @@
 }
 
 void
+Gicv3Redistributor::copy(Gicv3Registers *from, Gicv3Registers *to)
+{
+    const auto affinity = getAffinity();
+    // SGI_Base regs
+    gic->copyRedistRegister(from, to, affinity, GICR_CTLR);
+    gic->copyRedistRegister(from, to, affinity, GICR_WAKER);
+
+    gic->clearRedistRegister(to, affinity, GICR_ICENABLER0);
+    gic->clearRedistRegister(to, affinity, GICR_ICPENDR0);
+    gic->clearRedistRegister(to, affinity, GICR_ICACTIVER0);
+
+    gic->copyRedistRegister(from, to, affinity, GICR_ISENABLER0);
+    gic->copyRedistRegister(from, to, affinity, GICR_ISPENDR0);
+    gic->copyRedistRegister(from, to, affinity, GICR_ISACTIVER0);
+    gic->copyRedistRegister(from, to, affinity, GICR_ICFGR0);
+    gic->copyRedistRegister(from, to, affinity, GICR_ICFGR1);
+    gic->copyRedistRegister(from, to, affinity, GICR_IGRPMODR0);
+    gic->copyRedistRegister(from, to, affinity, GICR_NSACR);
+
+    gic->copyRedistRange(from, to, affinity,
+        GICR_IPRIORITYR.start(), GICR_IPRIORITYR.size());
+
+    // RD_Base regs
+    gic->copyRedistRegister(from, to, affinity, GICR_PROPBASER);
+    gic->copyRedistRegister(from, to, affinity, GICR_PENDBASER);
+}
+
+void
 Gicv3Redistributor::serialize(CheckpointOut & cp) const
 {
     SERIALIZE_SCALAR(peInLowPowerState);
diff --git a/src/dev/arm/gic_v3_redistributor.hh b/src/dev/arm/gic_v3_redistributor.hh
index 4d4bc99..2709ff6 100644
--- a/src/dev/arm/gic_v3_redistributor.hh
+++ b/src/dev/arm/gic_v3_redistributor.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2020 ARM Limited
+ * Copyright (c) 2019-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -260,6 +260,8 @@
     void sendPPInt(uint32_t int_id);
     void clearPPInt(uint32_t int_id);
     void write(Addr addr, uint64_t data, size_t size, bool is_secure_access);
+
+    void copy(Gicv3Registers *from, Gicv3Registers *to);
 };
 
 } // namespace gem5
diff --git a/src/dev/hsa/HSADevice.py b/src/dev/hsa/HSADevice.py
index 00a3dd7..73d5911 100644
--- a/src/dev/hsa/HSADevice.py
+++ b/src/dev/hsa/HSADevice.py
@@ -31,6 +31,7 @@
 from m5.params import *
 from m5.proxy import *
 from m5.objects.Device import DmaVirtDevice
+from m5.objects.VegaGPUTLB import VegaPagetableWalker
 
 class HSAPacketProcessor(DmaVirtDevice):
     type = 'HSAPacketProcessor'
@@ -48,3 +49,5 @@
     # See: https://github.com/RadeonOpenCompute/atmi/tree/master/examples/
     #      runtime/kps
     pktProcessDelay = Param.Tick(4400000, "Packet processing delay")
+    walker = Param.VegaPagetableWalker(VegaPagetableWalker(),
+            "Page table walker")
diff --git a/src/dev/hsa/SConscript b/src/dev/hsa/SConscript
index c1d3ed4..c53ddd3 100644
--- a/src/dev/hsa/SConscript
+++ b/src/dev/hsa/SConscript
@@ -31,7 +31,7 @@
 
 Import('*')
 
-if not env['BUILD_GPU']:
+if not env['CONF']['BUILD_GPU']:
     Return()
 
 SimObject('HSADevice.py', sim_objects=['HSAPacketProcessor'])
diff --git a/src/dev/hsa/hsa_packet_processor.cc b/src/dev/hsa/hsa_packet_processor.cc
index 91dd732..1236256 100644
--- a/src/dev/hsa/hsa_packet_processor.cc
+++ b/src/dev/hsa/hsa_packet_processor.cc
@@ -39,6 +39,7 @@
 #include "base/logging.hh"
 #include "base/trace.hh"
 #include "debug/HSAPacketProcessor.hh"
+#include "dev/amdgpu/amdgpu_device.hh"
 #include "dev/dma_device.hh"
 #include "dev/hsa/hsa_packet.hh"
 #include "dev/hsa/hw_scheduler.hh"
@@ -46,6 +47,7 @@
 #include "gpu-compute/gpu_command_processor.hh"
 #include "mem/packet_access.hh"
 #include "mem/page_table.hh"
+#include "sim/full_system.hh"
 #include "sim/process.hh"
 #include "sim/proxy_ptr.hh"
 #include "sim/system.hh"
@@ -58,12 +60,13 @@
   }
 
 #define PKT_TYPE(PKT) ((hsa_packet_type_t)(((PKT->header) >> \
-            HSA_PACKET_HEADER_TYPE) & (HSA_PACKET_HEADER_WIDTH_TYPE - 1)))
+            HSA_PACKET_HEADER_TYPE) & mask(HSA_PACKET_HEADER_WIDTH_TYPE)))
 
 // checks if the barrier bit is set in the header -- shift the barrier bit
 // to LSB, then bitwise "and" to mask off all other bits
 #define IS_BARRIER(PKT) ((hsa_packet_header_t)(((PKT->header) >> \
-            HSA_PACKET_HEADER_BARRIER) & HSA_PACKET_HEADER_WIDTH_BARRIER))
+            HSA_PACKET_HEADER_BARRIER) & \
+            mask(HSA_PACKET_HEADER_WIDTH_BARRIER)))
 
 namespace gem5
 {
@@ -71,7 +74,8 @@
 HSAPP_EVENT_DESCRIPTION_GENERATOR(QueueProcessEvent)
 
 HSAPacketProcessor::HSAPacketProcessor(const Params &p)
-    : DmaVirtDevice(p), numHWQueues(p.numHWQueues), pioAddr(p.pioAddr),
+    : DmaVirtDevice(p), walker(p.walker),
+      numHWQueues(p.numHWQueues), pioAddr(p.pioAddr),
       pioSize(PAGE_SIZE), pioDelay(10), pktProcessDelay(p.pktProcessDelay)
 {
     DPRINTF(HSAPacketProcessor, "%s:\n", __FUNCTION__);
@@ -90,6 +94,15 @@
 }
 
 void
+HSAPacketProcessor::setGPUDevice(AMDGPUDevice *gpu_device)
+{
+    gpuDevice = gpu_device;
+
+    assert(walker);
+    walker->setDevRequestor(gpuDevice->vramRequestorId());
+}
+
+void
 HSAPacketProcessor::unsetDeviceQueueDesc(uint64_t queue_id, int doorbellSize)
 {
     hwSchdlr->unregisterQueue(queue_id, doorbellSize);
@@ -100,14 +113,15 @@
                                        uint64_t basePointer,
                                        uint64_t queue_id,
                                        uint32_t size, int doorbellSize,
-                                       GfxVersion gfxVersion)
+                                       GfxVersion gfxVersion,
+                                       Addr offset, uint64_t rd_idx)
 {
     DPRINTF(HSAPacketProcessor,
              "%s:base = %p, qID = %d, ze = %d\n", __FUNCTION__,
              (void *)basePointer, queue_id, size);
     hwSchdlr->registerNewQueue(hostReadIndexPointer,
                                basePointer, queue_id, size, doorbellSize,
-                               gfxVersion);
+                               gfxVersion, offset, rd_idx);
 }
 
 AddrRangeList
@@ -163,12 +177,20 @@
 TranslationGenPtr
 HSAPacketProcessor::translate(Addr vaddr, Addr size)
 {
-    // Grab the process and try to translate the virtual address with it; with
-    // new extensions, it will likely be wrong to just arbitrarily grab context
-    // zero.
-    auto process = sys->threads[0]->getProcessPtr();
+    if (!FullSystem) {
+        // Grab the process and try to translate the virtual address with it;
+        // with new extensions, it will likely be wrong to just arbitrarily
+        // grab context zero.
+        auto process = sys->threads[0]->getProcessPtr();
 
-    return process->pTable->translateRange(vaddr, size);
+        return process->pTable->translateRange(vaddr, size);
+    }
+
+    // In full system use the page tables setup by the kernel driver rather
+    // than the CPU page tables.
+    return TranslationGenPtr(
+        new AMDGPUVM::UserTranslationGen(&gpuDevice->getVM(), walker,
+                                         1 /* vmid */, vaddr, size));
 }
 
 /**
@@ -582,6 +604,20 @@
     std::fill(_aqlComplete.begin(), _aqlComplete.end(), false);
 }
 
+void
+AQLRingBuffer::setRdIdx(uint64_t value)
+{
+    _rdIdx = value;
+
+    // Mark entries below the previous doorbell value as complete. This will
+    // cause the next call to freeEntry on the queue to increment the read
+    // index to the next value which will be written to the doorbell.
+    for (int i = 0; i <= value; ++i) {
+        _aqlComplete[i] = true;
+        DPRINTF(HSAPacketProcessor, "Marking _aqlComplete[%d] true\n", i);
+    }
+}
+
 bool
 AQLRingBuffer::freeEntry(void *pkt)
 {
diff --git a/src/dev/hsa/hsa_packet_processor.hh b/src/dev/hsa/hsa_packet_processor.hh
index 29d6889..65d1b44 100644
--- a/src/dev/hsa/hsa_packet_processor.hh
+++ b/src/dev/hsa/hsa_packet_processor.hh
@@ -55,6 +55,8 @@
 namespace gem5
 {
 
+class AMDGPUDevice;
+
 // Ideally, each queue should store this status and
 // the processPkt() should make decisions based on that
 // status variable.
@@ -232,6 +234,7 @@
      void incWrIdx(uint64_t value) { _wrIdx += value; }
      void incDispIdx(uint64_t value) { _dispIdx += value; }
      uint64_t compltnPending() { return (_dispIdx - _rdIdx); }
+     void setRdIdx(uint64_t value);
 };
 
 struct QCntxt
@@ -253,6 +256,8 @@
     typedef void (DmaDevice::*DmaFnPtr)(Addr, int, Event*, uint8_t*, Tick);
     GPUCommandProcessor *gpu_device;
     HWScheduler *hwSchdlr;
+    AMDGPUDevice *gpuDevice;
+    VegaISA::Walker *walker;
 
     // Structure to store the read values of dependency signals
     // from shared memory. Also used for tracking the status of
@@ -351,11 +356,14 @@
                             uint64_t basePointer,
                             uint64_t queue_id,
                             uint32_t size, int doorbellSize,
-                            GfxVersion gfxVersion);
+                            GfxVersion gfxVersion,
+                            Addr offset = 0, uint64_t rd_idx = 0);
     void unsetDeviceQueueDesc(uint64_t queue_id, int doorbellSize);
     void setDevice(GPUCommandProcessor * dev);
+    void setGPUDevice(AMDGPUDevice *gpu_device);
     void updateReadIndex(int, uint32_t);
     void getCommandsFromHost(int pid, uint32_t rl_idx);
+    HWScheduler *hwScheduler() { return hwSchdlr; }
 
     // PIO interface
     virtual Tick read(Packet*) override;
diff --git a/src/dev/hsa/hw_scheduler.cc b/src/dev/hsa/hw_scheduler.cc
index bd02b30..a0f1e87 100644
--- a/src/dev/hsa/hw_scheduler.cc
+++ b/src/dev/hsa/hw_scheduler.cc
@@ -86,19 +86,23 @@
                               uint64_t basePointer,
                               uint64_t queue_id,
                               uint32_t size, int doorbellSize,
-                              GfxVersion gfxVersion)
+                              GfxVersion gfxVersion,
+                              Addr offset, uint64_t rd_idx)
 {
     assert(queue_id < MAX_ACTIVE_QUEUES);
     // Map queue ID to doorbell.
     // We are only using offset to pio base address as doorbell
     // We use the same mapping function used by hsa runtime to do this mapping
-    Addr db_offset = queue_id * doorbellSize;
-    if (dbMap.find(db_offset) != dbMap.end()) {
+    if (!offset) {
+        offset = queue_id * doorbellSize;
+    }
+    if (dbMap.find(offset) != dbMap.end()) {
         panic("Creating an already existing queue (queueID %d)", queue_id);
     }
 
     // Populate doorbell map
-    dbMap[db_offset] = queue_id;
+    dbMap[offset] = queue_id;
+    qidMap[queue_id] = offset;
 
     if (queue_id >= MAX_ACTIVE_QUEUES) {
         panic("Attempting to create a queue (queueID %d)" \
@@ -106,12 +110,18 @@
     }
 
     HSAQueueDescriptor* q_desc =
-       new HSAQueueDescriptor(basePointer, db_offset,
+       new HSAQueueDescriptor(basePointer, offset,
                               hostReadIndexPointer, size, gfxVersion);
     AQLRingBuffer* aql_buf =
         new AQLRingBuffer(NUM_DMA_BUFS, hsaPP->name());
+    if (rd_idx > 0) {
+        aql_buf->setRdIdx(rd_idx);
+    }
+    DPRINTF(HSAPacketProcessor, "Setting read index for %#lx to %ld\n",
+                                offset, rd_idx);
+
     QCntxt q_cntxt(q_desc, aql_buf);
-    activeList[dbMap[db_offset]] = q_cntxt;
+    activeList[dbMap[offset]] = q_cntxt;
 
     // Check if this newly created queue can be directly mapped
     // to registered queue list
@@ -120,7 +130,7 @@
     schedWakeup();
     DPRINTF(HSAPacketProcessor,
              "%s: offset = %p, qID = %d, is_regd = %s, AL size %d\n",
-             __FUNCTION__, db_offset, queue_id,
+             __FUNCTION__, offset, queue_id,
              (register_q) ? "true" : "false", dbMap.size());
 }
 
@@ -191,7 +201,7 @@
 HWScheduler::contextSwitchQ()
 {
     DPRINTF(HSAPacketProcessor,
-            "Trying to map next queue, @ %s", __FUNCTION__);
+            "Trying to map next queue, @ %s\n", __FUNCTION__);
     // Identify the next queue, if there is nothing to
     // map, return false
     if (!findNextActiveALQ()) {
@@ -325,6 +335,13 @@
     uint32_t al_idx = dbMap[db_addr];
     // Modify the write pointer
     activeList[al_idx].qDesc->writeIndex = doorbell_reg;
+    // If a queue is unmapped and remapped (common in full system) the qDesc
+    // gets reused. Keep the readIndex up to date so that when the HSA packet
+    // processor gets commands from host, the correct entry is read after
+    // remapping.
+    activeList[al_idx].qDesc->readIndex = doorbell_reg - 1;
+    DPRINTF(HSAPacketProcessor, "queue %d qDesc->writeIndex %d\n",
+            al_idx, activeList[al_idx].qDesc->writeIndex);
     // If this queue is mapped, then start DMA to fetch the
     // AQL packet
     if (regdListMap.find(al_idx) != regdListMap.end()) {
@@ -335,7 +352,8 @@
 void
 HWScheduler::unregisterQueue(uint64_t queue_id, int doorbellSize)
 {
-    Addr db_offset = queue_id * doorbellSize;
+    assert(qidMap.count(queue_id));
+    Addr db_offset = qidMap[queue_id];
     auto dbmap_iter = dbMap.find(db_offset);
     if (dbmap_iter == dbMap.end()) {
         panic("Destroying a non-existing queue (db_offset %x)",
diff --git a/src/dev/hsa/hw_scheduler.hh b/src/dev/hsa/hw_scheduler.hh
index e7d1a61..5d6ddcc 100644
--- a/src/dev/hsa/hw_scheduler.hh
+++ b/src/dev/hsa/hw_scheduler.hh
@@ -59,7 +59,8 @@
                           uint64_t basePointer,
                           uint64_t queue_id,
                           uint32_t size, int doorbellSize,
-                          GfxVersion gfxVersion);
+                          GfxVersion gfxVersion,
+                          Addr offset = 0, uint64_t rd_idx = 0);
     void unregisterQueue(uint64_t queue_id, int doorbellSize);
     void wakeup();
     void schedWakeup();
@@ -90,8 +91,10 @@
     // Active list keeps track of all queues created
     std::map<uint32_t, QCntxt> activeList;
     //TODO: Modify this to support multi-process in the future.
-    // doorbell map, maps doorbells to active list entry
+    // doorbell map, maps doorbell offsets to queue ID
     std::map<Addr, uint32_t> dbMap;
+    // Reverse of doorbell map, maps queue ID to doorbell offset
+    std::map<uint64_t, Addr> qidMap;
     // regdListMap keeps track of the mapping of queues to
     // registered list. regdListMap is indexed with active
     // list index (which is same as queue ID)
diff --git a/src/dev/i2c/SConscript b/src/dev/i2c/SConscript
index 71f7fce..808caa9 100644
--- a/src/dev/i2c/SConscript
+++ b/src/dev/i2c/SConscript
@@ -37,7 +37,7 @@
 
 Import('*')
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 SimObject('I2C.py', sim_objects=['I2CDevice', 'I2CBus'])
diff --git a/src/dev/intel_8254_timer.cc b/src/dev/intel_8254_timer.cc
index c0fc773..df9b4b3 100644
--- a/src/dev/intel_8254_timer.cc
+++ b/src/dev/intel_8254_timer.cc
@@ -36,37 +36,40 @@
 namespace gem5
 {
 
-Intel8254Timer::Intel8254Timer(EventManager *em, const std::string &name,
-    Counter *counter0, Counter *counter1, Counter *counter2) :
-    EventManager(em), _name(name)
-{
-    counter[0] = counter0;
-    counter[1] = counter1;
-    counter[2] = counter2;
-}
-
 Intel8254Timer::Intel8254Timer(EventManager *em, const std::string &name) :
-    EventManager(em), _name(name)
-{
-    counter[0] = new Counter(this, name + ".counter0", 0);
-    counter[1] = new Counter(this, name + ".counter1", 1);
-    counter[2] = new Counter(this, name + ".counter2", 2);
-}
+    EventManager(em), _name(name), counters{{
+            {this, name + ".counter0", 0},
+            {this, name + ".counter1", 1},
+            {this, name + ".counter2", 2}
+        }}
+{}
 
 void
 Intel8254Timer::writeControl(const CtrlReg data)
 {
     int sel = data.sel;
 
-    if (sel == ReadBackCommand)
-       panic("PITimer Read-Back Command is not implemented.\n");
+    if (sel == ReadBackCommand) {
+        ReadBackCommandVal rb_val = static_cast<uint8_t>(data);
 
-    if (data.rw == LatchCommand)
-        counter[sel]->latchCount();
-    else {
-        counter[sel]->setRW(data.rw);
-        counter[sel]->setMode(data.mode);
-        counter[sel]->setBCD(data.bcd);
+        panic_if(!rb_val.status,
+                "Latching the PIT status byte is not implemented.");
+
+        if (!rb_val.count) {
+            for (auto &counter: counters) {
+                if (bits((uint8_t)rb_val.select, counter.index()))
+                    counter.latchCount();
+            }
+        }
+        return;
+    }
+
+    if (data.rw == LatchCommand) {
+        counters[sel].latchCount();
+    } else {
+        counters[sel].setRW(data.rw);
+        counters[sel].setMode(data.mode);
+        counters[sel].setBCD(data.bcd);
     }
 }
 
@@ -74,26 +77,26 @@
 Intel8254Timer::serialize(const std::string &base, CheckpointOut &cp) const
 {
     // serialize the counters
-    counter[0]->serialize(base + ".counter0", cp);
-    counter[1]->serialize(base + ".counter1", cp);
-    counter[2]->serialize(base + ".counter2", cp);
+    counters[0].serialize(base + ".counter0", cp);
+    counters[1].serialize(base + ".counter1", cp);
+    counters[2].serialize(base + ".counter2", cp);
 }
 
 void
 Intel8254Timer::unserialize(const std::string &base, CheckpointIn &cp)
 {
     // unserialze the counters
-    counter[0]->unserialize(base + ".counter0", cp);
-    counter[1]->unserialize(base + ".counter1", cp);
-    counter[2]->unserialize(base + ".counter2", cp);
+    counters[0].unserialize(base + ".counter0", cp);
+    counters[1].unserialize(base + ".counter1", cp);
+    counters[2].unserialize(base + ".counter2", cp);
 }
 
 void
 Intel8254Timer::startup()
 {
-    counter[0]->startup();
-    counter[1]->startup();
-    counter[2]->startup();
+    counters[0].startup();
+    counters[1].startup();
+    counters[2].startup();
 }
 
 Intel8254Timer::Counter::Counter(Intel8254Timer *p,
diff --git a/src/dev/intel_8254_timer.hh b/src/dev/intel_8254_timer.hh
index 9876d13..7aec065 100644
--- a/src/dev/intel_8254_timer.hh
+++ b/src/dev/intel_8254_timer.hh
@@ -29,6 +29,7 @@
 #ifndef __DEV_8254_HH__
 #define __DEV_8254_HH__
 
+#include <array>
 #include <iostream>
 #include <string>
 
@@ -53,6 +54,16 @@
         Bitfield<0> bcd;
     EndBitUnion(CtrlReg)
 
+    BitUnion8(ReadBackCommandVal)
+        Bitfield<4> status; // Active low.
+        Bitfield<5> count; // Active low.
+        SubBitUnion(select, 3, 1)
+            Bitfield<3> cnt2;
+            Bitfield<2> cnt1;
+            Bitfield<1> cnt0;
+        EndSubBitUnion(select)
+    EndBitUnion(ReadBackCommandVal)
+
     enum SelectVal
     {
         SelectCounter0,
@@ -152,6 +163,8 @@
       public:
         Counter(Intel8254Timer *p, const std::string &name, unsigned int num);
 
+        unsigned int index() const { return num; }
+
         /** Latch the current count (if one is not already latched) */
         void latchCount();
 
@@ -200,7 +213,7 @@
     const std::string &name() const { return _name; }
 
     /** PIT has three seperate counters */
-    Counter *counter[3];
+    std::array<Counter, 3> counters;
 
     virtual void
     counterInterrupt(unsigned int num)
@@ -214,9 +227,6 @@
     ~Intel8254Timer()
     {}
 
-    Intel8254Timer(EventManager *em, const std::string &name,
-            Counter *counter0, Counter *counter1, Counter *counter2);
-
     Intel8254Timer(EventManager *em, const std::string &name);
 
     /** Write control word */
@@ -226,21 +236,21 @@
     readCounter(unsigned int num)
     {
         assert(num < 3);
-        return counter[num]->read();
+        return counters[num].read();
     }
 
     void
     writeCounter(unsigned int num, const uint8_t data)
     {
         assert(num < 3);
-        counter[num]->write(data);
+        counters[num].write(data);
     }
 
     bool
     outputHigh(unsigned int num)
     {
         assert(num < 3);
-        return counter[num]->outputHigh();
+        return counters[num].outputHigh();
     }
 
     /**
diff --git a/src/dev/net/SConscript b/src/dev/net/SConscript
index 4ba68cc..cd28510 100644
--- a/src/dev/net/SConscript
+++ b/src/dev/net/SConscript
@@ -44,7 +44,7 @@
     'EtherLink', 'DistEtherLink', 'EtherBus', 'EtherSwitch', 'EtherTapBase',
     'EtherTapStub', 'EtherDump', 'EtherDevice', 'IGbE', 'EtherDevBase',
     'NSGigE', 'Sinic'] +
-    (['EtherTap'] if env['HAVE_TUNTAP'] else []))
+    (['EtherTap'] if env['CONF']['HAVE_TUNTAP'] else []))
 
 # Basic Ethernet infrastructure
 Source('etherbus.cc')
diff --git a/src/dev/net/SConsopts b/src/dev/net/SConsopts
index 874c06e..2af5ceb 100644
--- a/src/dev/net/SConsopts
+++ b/src/dev/net/SConsopts
@@ -29,9 +29,7 @@
 
 with gem5_scons.Configure(main) as conf:
     # Check if the TUN/TAP driver is available.
-    conf.env['HAVE_TUNTAP'] = conf.CheckHeader('linux/if_tun.h', '<>')
+    conf.env['CONF']['HAVE_TUNTAP'] = conf.CheckHeader('linux/if_tun.h', '<>')
 
-if not main['HAVE_TUNTAP']:
+if not main['CONF']['HAVE_TUNTAP']:
     print("Info: Compatible header file <linux/if_tun.h> not found.")
-
-export_vars.append('HAVE_TUNTAP')
diff --git a/src/dev/ps2/SConscript b/src/dev/ps2/SConscript
index 303d25f..aa9d1fd 100644
--- a/src/dev/ps2/SConscript
+++ b/src/dev/ps2/SConscript
@@ -37,7 +37,7 @@
 
 Import('*')
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 SimObject('PS2.py', sim_objects=[
diff --git a/src/dev/ps2/keyboard.cc b/src/dev/ps2/keyboard.cc
index fb63d48..e3b13ac 100644
--- a/src/dev/ps2/keyboard.cc
+++ b/src/dev/ps2/keyboard.cc
@@ -128,9 +128,26 @@
             return true;
         }
       case keyboard::DiagnosticEcho:
-        panic("Keyboard diagnostic echo unimplemented.\n");
+        send(keyboard::DiagnosticEcho);
+        return true;
       case keyboard::AlternateScanCodes:
-        panic("Accessing alternate scan codes unimplemented.\n");
+        if (data.size() == 1) {
+            DPRINTF(PS2, "Got scan code set command.\n");
+            sendAck();
+            return false;
+        } else {
+            sendAck();
+            uint8_t scan_code = data[1];
+            if (scan_code == 0) {
+                DPRINTF(PS2, "Sending hard coded current scan code set 2.\n");
+                send(0x2);
+            } else {
+                DPRINTF(PS2, "Setting scan code set to %d.\n", scan_code);
+                panic_if(scan_code != 0x2,
+                        "PS/2 scan code set %d not supported.", scan_code);
+            }
+        }
+        return true;
       case keyboard::TypematicInfo:
         if (data.size() == 1) {
             DPRINTF(PS2, "Setting typematic info.\n");
diff --git a/src/dev/qemu/QemuFwCfg.py b/src/dev/qemu/QemuFwCfg.py
new file mode 100644
index 0000000..0851ef0
--- /dev/null
+++ b/src/dev/qemu/QemuFwCfg.py
@@ -0,0 +1,95 @@
+# Copyright 2022 Google, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from m5.params import *
+from m5.objects.SimObject import SimObject
+from m5.objects.Device import PioDevice
+
+class QemuFwCfgItem(SimObject):
+    type = 'QemuFwCfgItem'
+    cxx_class = 'gem5::qemu::FwCfgItemFactoryBase'
+    cxx_header = 'dev/qemu/fw_cfg.hh'
+    abstract = True
+
+    # The path this item will be listed under in the firmware config directory.
+    arch_specific = Param.Bool(False, 'if this item is archiecture specific')
+    index = Param.Unsigned(0, 'Fixed index, or 0 for automatic')
+    path = Param.String('Path to item in the firmware config directory')
+
+class QemuFwCfgItemFile(QemuFwCfgItem):
+    type = 'QemuFwCfgItemFile'
+    cxx_class = 'gem5::qemu::FwCfgItemFactory<gem5::qemu::FwCfgItemFile>'
+    cxx_template_params = ['class ItemType']
+    cxx_header = 'dev/qemu/fw_cfg.hh'
+
+    # The path to the file that will be used to populate this item.
+    file = Param.String('Path to file to export')
+
+class QemuFwCfgItemString(QemuFwCfgItem):
+    type = 'QemuFwCfgItemString'
+    cxx_class = 'gem5::qemu::FwCfgItemFactory<gem5::qemu::FwCfgItemString>'
+    cxx_template_params = ['class ItemType']
+    cxx_header = 'dev/qemu/fw_cfg.hh'
+
+    # The string which directly populates this item.
+    string = Param.String('String to export')
+
+class QemuFwCfgItemBytes(QemuFwCfgItem):
+    type = 'QemuFwCfgItemBytes'
+    cxx_class = 'gem5::qemu::FwCfgItemFactory<gem5::qemu::FwCfgItemBytes>'
+    cxx_template_params = ['class ItemType']
+    cxx_header = 'dev/qemu/fw_cfg.hh'
+
+    data = VectorParam.UInt8('Bytes to export')
+
+class QemuFwCfg(PioDevice):
+    type = 'QemuFwCfg'
+    cxx_class = 'gem5::qemu::FwCfg'
+    cxx_header = 'dev/qemu/fw_cfg.hh'
+    abstract = True
+
+    items = VectorParam.QemuFwCfgItem([],
+            'Items exported by the firmware config device')
+
+class QemuFwCfgIo(QemuFwCfg):
+    type = 'QemuFwCfgIo'
+    cxx_class = 'gem5::qemu::FwCfgIo'
+    cxx_header = 'dev/qemu/fw_cfg.hh'
+
+    # The selector register is 16 bits wide, and little endian. The data
+    # register must be one port ahead of the selector.
+    selector_addr = Param.Addr('IO port for the selector register')
+
+class QemuFwCfgMmio(QemuFwCfg):
+    type = 'QemuFwCfgMmio'
+    cxx_class = 'gem5::qemu::FwCfgMmio'
+    cxx_header = 'dev/qemu/fw_cfg.hh'
+
+    # The selector register is 16 bits wide, and big endian.
+    selector_addr = Param.Addr('Memory address for the selector register')
+
+    # The data register is 8, 16, 32 or 64 bits wide.
+    data_addr_range = \
+            Param.AddrRange('Memory address range for the data register')
diff --git a/src/cpu/simple/SConsopts b/src/dev/qemu/SConscript
similarity index 83%
copy from src/cpu/simple/SConsopts
copy to src/dev/qemu/SConscript
index f12fee2..59a50cf 100644
--- a/src/cpu/simple/SConsopts
+++ b/src/dev/qemu/SConscript
@@ -1,7 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2022 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -28,4 +25,11 @@
 
 Import('*')
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+SimObject('QemuFwCfg.py', sim_objects=[
+    'QemuFwCfgItem',
+    'QemuFwCfgItemBytes', 'QemuFwCfgItemFile', 'QemuFwCfgItemString',
+    'QemuFwCfg', 'QemuFwCfgIo', 'QemuFwCfgMmio'])
+Source('fw_cfg.cc')
+
+DebugFlag('QemuFwCfg')
+DebugFlag('QemuFwCfgVerbose')
diff --git a/src/dev/qemu/fw_cfg.cc b/src/dev/qemu/fw_cfg.cc
new file mode 100644
index 0000000..09f0efa
--- /dev/null
+++ b/src/dev/qemu/fw_cfg.cc
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dev/qemu/fw_cfg.hh"
+
+#include <cstring>
+#include <sstream>
+#include <string>
+
+#include "base/compiler.hh"
+#include "base/cprintf.hh"
+#include "base/logging.hh"
+#include "base/trace.hh"
+#include "debug/QemuFwCfg.hh"
+#include "debug/QemuFwCfgVerbose.hh"
+#include "mem/packet_access.hh"
+#include "sim/byteswap.hh"
+
+namespace gem5
+{
+
+namespace qemu
+{
+
+void
+FwCfgItemFixed::read(void *buf, uint64_t offset, uint32_t to_read)
+{
+    // Get access to the data we need to fill this buffer.
+    const void *data = bytes();
+    const uint64_t total_length = length();
+
+    if (offset > total_length) {
+        // We're completely off the end, so return only zeroes.
+        std::memset(buf, 0, to_read);
+        return;
+    }
+
+    if (offset + to_read > total_length) {
+        // We're partially off the end, truncate this read and zero fill.
+
+        // Figure out how far past the end we're attempting to read.
+        uint64_t overflow = offset + to_read - total_length;
+
+        // Reduce the requested read size to what we can actually fill.
+        to_read -= overflow;
+
+        // Zero out the part we won't read data into.
+        std::memset((uint8_t *)buf + to_read, 0, overflow);
+    }
+
+    // Do the read.
+    std::memcpy(buf, (uint8_t *)data + offset, to_read);
+}
+
+FwCfg::FwCfg(const Params &p, const AddrRangeList &addr_ranges) :
+    PioDevice(p),
+    signature(".[FW_CFG_SIGNATURE]", false, "QEMU CFG", 0),
+    // The ID says we support the traditional interface but not DMA. To enable
+    // DMA, this should be equal to 3.
+    id(".[FW_CFG_ID]", false, "\x1", 1),
+    addrRanges(addr_ranges)
+{
+    // Add the unnamed, fixed items.
+    addItem(&signature);
+    addItem(&id);
+
+    for (auto factory: p.items) {
+        // Process named items and add them to the index.
+        auto &item = factory->item();
+
+        uint32_t &next_index =
+            item.archSpecific() ? nextArchIndex : nextGenericIndex;
+        const uint32_t &max_index =
+            item.archSpecific() ? MaxArchIndex : MaxGenericIndex;
+
+        // Automatically assign an ID if a fixed one wasn't specified.
+        if (!item.index())
+            item.index(next_index++);
+
+        panic_if(item.index() >= max_index,
+                "Firmware config device out of %s indexes.",
+                item.archSpecific() ? "arch" : "generic");
+
+        addItem(&item);
+    }
+
+    directory.update(names, numbers);
+    addItem(&directory);
+};
+
+void
+FwCfg::addItem(FwCfgItem *item)
+{
+    const auto [kit, ksuccess] =
+        numbers.insert(std::make_pair(item->index(), item));
+
+    panic_if(!ksuccess, "Duplicate firmware config item key %#x, "
+            "paths %s and %s.",
+            item->index(), item->path(), kit->second->path());
+
+    const std::string &path = item->path();
+    if (path.empty() || path[0] != '.') {
+        const auto res =
+            names.insert(std::make_pair(item->path(), item->index()));
+
+        panic_if(!res.second, "Duplicate firmware config item path %s.",
+                item->path());
+    }
+}
+
+void
+FwCfg::select(uint16_t key)
+{
+    DPRINTF(QemuFwCfg, "Selecting item with key %#x.\n", key);
+
+    // Clear any previous selection.
+    offset = 0;
+    current = nullptr;
+
+    auto iter = numbers.find(key);
+    if (iter == numbers.end()) {
+        warn("Firmware config failed to select item with key %#x.", key);
+        return;
+    }
+
+    auto item = iter->second;
+
+    current = item;
+    if (current)
+        DPRINTF(QemuFwCfg, "Selected item with path %s.\n", item->path());
+    else
+        DPRINTF(QemuFwCfg, "No item is currently selected.\n");
+}
+
+void
+FwCfg::readItem(void *buf, uint32_t length)
+{
+    if (!current) {
+        DPRINTF(QemuFwCfgVerbose,
+                "Tried to read while nothing was selected.\n");
+        std::memset(buf, 0, length);
+        return;
+    }
+
+    current->read(buf, offset, length);
+
+    if (gem5::debug::QemuFwCfgVerbose) {
+        std::stringstream data_str;
+        for (int idx = 0; idx < length; idx++)
+            ccprintf(data_str, " %02x", ((uint8_t *)buf)[idx]);
+
+        DPRINTF(QemuFwCfgVerbose, "Read [%#x-%#x) =>%s.\n",
+                offset, offset + length, data_str.str());
+    }
+
+    offset += length;
+}
+
+FwCfg::Directory::Directory() :
+    FwCfgItemFixed(".[FW_CFG_FILE_DIR]", false, 0x19)
+{}
+
+void
+FwCfg::Directory::update(
+        const std::map<std::string, uint16_t> &names,
+        const std::map<uint16_t, FwCfgItem *> &numbers)
+{
+    uint32_t count = names.size();
+
+    struct GEM5_PACKED File
+    {
+        uint32_t size;
+        uint16_t select;
+        uint16_t reserved;
+        char name[56];
+    };
+
+    uint64_t bytes = sizeof(count) + sizeof(File) * count;
+    data.resize(bytes);
+
+    uint8_t *ptr = data.data();
+
+    uint32_t be_count = htobe(count);
+    std::memcpy(ptr, &be_count, sizeof(be_count));
+    ptr += sizeof(be_count);
+
+    for (auto &[name, index]: names) {
+        // Fill in the entry.
+        File file{(uint32_t)numbers.at(index)->length(), index, 0, {}};
+        std::memset(file.name, 0, sizeof(file.name));
+        std::strncpy(file.name, name.c_str(), sizeof(file.name) - 1);
+
+        // Fix endianness.
+        file.size = htobe(file.size);
+        file.select = htobe(file.select);
+
+        // Copy it to the buffer and update ptr.
+        std::memcpy(ptr, &file, sizeof(file));
+        ptr += sizeof(file);
+    }
+}
+
+FwCfgIo::FwCfgIo(const Params &p) : FwCfg(p, {
+        // This covers both the 16 bit selector, and the 8 bit data reg which
+        // overlaps it.
+        {p.selector_addr, p.selector_addr + 2}}),
+    selectorAddr(p.selector_addr), dataAddr(p.selector_addr + 1)
+{}
+
+Tick
+FwCfgIo::read(PacketPtr pkt)
+{
+    const Addr addr = pkt->getAddr();
+    const auto size = pkt->getSize();
+
+    pkt->makeResponse();
+    // The default response is all zeroes.
+    std::memset(pkt->getPtr<uint8_t>(), 0, size);
+
+    if (addr == selectorAddr) {
+        warn("Read from firmware config selector register not supported.");
+    } else if (addr == dataAddr) {
+        if (size == 1) {
+            readItem(pkt->getPtr<void>(), size);
+        } else {
+            warn("Read from firmware config data register with width %d not "
+                    "supported.", size);
+        }
+    } else {
+        panic("Unregognized firmware config read [%#x-%#x).",
+                addr, addr + size);
+    }
+
+    return 0;
+}
+
+Tick
+FwCfgIo::write(PacketPtr pkt)
+{
+    const Addr addr = pkt->getAddr();
+    const auto size = pkt->getSize();
+
+    pkt->makeResponse();
+
+    if (addr == selectorAddr) {
+        if (size != 2) {
+            warn("Write to firmware config selector register with width %d "
+                    "not supported.", size);
+        } else {
+            auto key = pkt->getLE<uint16_t>();
+            select(key);
+        }
+    } else if (addr == dataAddr) {
+        // Writes to the firmware config data can only be done through the
+        // DMA interface.
+        warn("Write to firmware config data register not supported.");
+    } else {
+        panic("Unrecognized firmware config write [%#x-%#x).",
+                addr, addr + size);
+    }
+
+    return 0;
+}
+
+FwCfgMmio::FwCfgMmio(const Params &p) : FwCfg(p, {
+        {p.selector_addr, p.selector_addr + 2},
+        {p.data_addr_range}}),
+    selectorAddr(p.selector_addr),
+    dataAddr(p.data_addr_range.start()), dataSize(p.data_addr_range.size())
+{}
+
+Tick
+FwCfgMmio::read(PacketPtr pkt)
+{
+    const Addr addr = pkt->getAddr();
+    const auto size = pkt->getSize();
+
+    pkt->makeResponse();
+    // The default response is all zeroes.
+    std::memset(pkt->getPtr<uint8_t>(), 0, size);
+
+    if (addr == selectorAddr) {
+        warn("Read from firmware config selector register not supported.");
+    } else if (addr == dataAddr) {
+        if (size == dataSize) {
+            readItem(pkt->getPtr<void>(), size);
+        } else {
+            warn("Read from firmware config data register with width %d not "
+                    "supported.", size);
+        }
+    } else {
+        panic("Unregognized firmware config read [%#x-%#x).",
+                addr, addr + size);
+    }
+
+    return 0;
+}
+
+Tick
+FwCfgMmio::write(PacketPtr pkt)
+{
+    const Addr addr = pkt->getAddr();
+    const auto size = pkt->getSize();
+
+    pkt->makeResponse();
+
+    if (addr == selectorAddr) {
+        if (size != 2) {
+            warn("Write to firmware config selector register with width %d "
+                    "not supported.", size);
+        } else {
+            auto key = pkt->getBE<uint16_t>();
+            select(key);
+        }
+    } else if (addr == dataAddr) {
+        // Writes to the firmware config data can only be done through the
+        // DMA interface.
+        warn("Write to firmware config data register not supported.");
+    } else {
+        panic("Unrecognized firmware config write [%#x-%#x).",
+                addr, addr + size);
+    }
+
+    return 0;
+}
+
+} // namespace qemu
+} // namespace gem5
diff --git a/src/dev/qemu/fw_cfg.hh b/src/dev/qemu/fw_cfg.hh
new file mode 100644
index 0000000..c196eb6
--- /dev/null
+++ b/src/dev/qemu/fw_cfg.hh
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_QEMU_FW_CFG_HH__
+#define __DEV_QEMU_FW_CFG_HH__
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "base/loader/image_file_data.hh"
+#include "base/types.hh"
+#include "dev/io_device.hh"
+#include "params/QemuFwCfg.hh"
+#include "params/QemuFwCfgIo.hh"
+#include "params/QemuFwCfgItem.hh"
+#include "params/QemuFwCfgItemBytes.hh"
+#include "params/QemuFwCfgItemFile.hh"
+#include "params/QemuFwCfgItemString.hh"
+#include "params/QemuFwCfgMmio.hh"
+
+namespace gem5
+{
+
+namespace qemu
+{
+
+/*
+ * Items which can be reported by the firmware config device.
+ */
+
+class FwCfgItem
+{
+  protected:
+    uint16_t _index;
+    const std::string _path;
+    bool _archSpecific;
+
+    FwCfgItem(const std::string &new_path, bool arch_specific,
+            uint16_t new_index=0) :
+        _index(new_index), _path(new_path), _archSpecific(arch_specific)
+    {}
+
+  public:
+    uint16_t index() const { return _index; }
+    void index(uint16_t new_index) { _index = new_index; }
+
+    const std::string &path() const { return _path; }
+    bool archSpecific() const { return _archSpecific; }
+
+    virtual uint64_t length() const = 0;
+
+    virtual void read(void *buf, uint64_t offset, uint32_t to_read) = 0;
+};
+
+// Read only items with precomputed data.
+class FwCfgItemFixed : public FwCfgItem
+{
+  protected:
+    virtual const void *bytes() const = 0;
+
+  public:
+    using FwCfgItem::FwCfgItem;
+
+    void read(void *buf, uint64_t offset, uint32_t to_read) override;
+};
+
+// An item who's value comes from a file.
+class FwCfgItemFile : public FwCfgItemFixed
+{
+  private:
+    const gem5::loader::ImageFileData data;
+
+  public:
+    FwCfgItemFile(const std::string &new_path, bool arch_specific,
+            const std::string path, uint16_t new_index=0) :
+        FwCfgItemFixed(new_path, arch_specific, new_index), data(path)
+    {}
+
+    FwCfgItemFile(const QemuFwCfgItemFileParams &p) :
+        FwCfgItemFile(p.path, p.arch_specific, p.file, p.index)
+    {}
+
+    const void *bytes() const override { return data.data(); }
+    uint64_t length() const override { return data.len(); }
+};
+
+// An item who's value comes from a string.
+class FwCfgItemString : public FwCfgItemFixed
+{
+  private:
+    std::string str;
+
+  public:
+    FwCfgItemString(const std::string &new_path, bool arch_specific,
+            const std::string _str, uint16_t new_index=0) :
+        FwCfgItemFixed(new_path, arch_specific, new_index), str(_str)
+    {}
+
+    FwCfgItemString(const QemuFwCfgItemStringParams &p) :
+        FwCfgItemString(p.path, p.arch_specific, p.string, p.index)
+    {}
+
+    const void *bytes() const override { return (void *)str.data(); }
+    uint64_t
+    length() const override
+    {
+        return sizeof(std::string::value_type) * str.length();
+    }
+};
+
+// An item who's value comes from an array of bytes.
+class FwCfgItemBytes : public FwCfgItemFixed
+{
+  private:
+    std::vector<uint8_t> data;
+
+  public:
+    FwCfgItemBytes(const std::string &new_path, bool arch_specific,
+            const std::vector<uint8_t> &_data, uint16_t new_index=0) :
+        FwCfgItemFixed(new_path, arch_specific, new_index), data(_data)
+    {}
+
+    FwCfgItemBytes(const QemuFwCfgItemBytesParams &p) :
+        FwCfgItemBytes(p.path, p.arch_specific, p.data, p.index)
+    {}
+
+    const void *bytes() const override { return (void *)data.data(); }
+    uint64_t length() const override { return data.size(); }
+};
+
+/*
+ * Base and template classes for creating SimObject wrappers for item types.
+ */
+
+class FwCfgItemFactoryBase : public SimObject
+{
+  public:
+    PARAMS(QemuFwCfgItem);
+    FwCfgItemFactoryBase(const Params &p) : SimObject(p) {}
+
+    virtual FwCfgItem &item() = 0;
+};
+
+template <class ItemType>
+class FwCfgItemFactory : public FwCfgItemFactoryBase
+{
+  private:
+    ItemType _item;
+
+  public:
+    template <class PType, class = typename std::enable_if_t<
+        std::is_base_of_v<SimObjectParams, PType>>>
+    FwCfgItemFactory(const PType &p) : FwCfgItemFactoryBase(p), _item(p) {}
+
+    FwCfgItem &item() override { return _item; }
+};
+
+/*
+ * The actual firmware config device itself.
+ */
+
+// The base class.
+class FwCfg : public PioDevice
+{
+  private:
+    std::map<std::string, uint16_t> names;
+    std::map<uint16_t, FwCfgItem *> numbers;
+
+    uint32_t nextGenericIndex = 0x20;
+    static inline const uint32_t MaxGenericIndex = 0x3fff;
+
+    uint32_t nextArchIndex = 0x8020;
+    static inline const uint32_t MaxArchIndex = 0xbfff;
+
+    uint64_t offset = 0;
+    FwCfgItem *current = nullptr;
+
+    class Directory : public FwCfgItemFixed
+    {
+      private:
+        std::vector<uint8_t> data;
+
+      public:
+        Directory();
+
+        void update(const std::map<std::string, uint16_t> &names,
+                const std::map<uint16_t, FwCfgItem *> &numbers);
+
+        const void *bytes() const override { return (void *)data.data(); }
+        uint64_t length() const override { return data.size(); }
+    };
+
+    FwCfgItemString signature;
+    FwCfgItemString id;
+    Directory directory;
+
+    void addItem(FwCfgItem *item);
+
+  protected:
+    const AddrRangeList addrRanges;
+
+    void select(uint16_t key);
+
+  public:
+    PARAMS(QemuFwCfg);
+    FwCfg(const Params &p, const AddrRangeList &addr_ranges);
+
+    AddrRangeList getAddrRanges() const override { return addrRanges; }
+
+    void readItem(void *buf, uint32_t length);
+};
+
+// A version which uses IO ports.
+class FwCfgIo : public FwCfg
+{
+  private:
+    const Addr selectorAddr;
+    const Addr dataAddr;
+
+  public:
+    PARAMS(QemuFwCfgIo);
+    FwCfgIo(const Params &p);
+
+    Tick read(PacketPtr pkt) override;
+    Tick write(PacketPtr pkt) override;
+};
+
+// A version which uses memory mapped IO.
+class FwCfgMmio : public FwCfg
+{
+  private:
+    const Addr selectorAddr;
+    const Addr dataAddr;
+    const Addr dataSize;
+
+  public:
+    PARAMS(QemuFwCfgMmio);
+    FwCfgMmio(const Params &p);
+
+    Tick read(PacketPtr pkt) override;
+    Tick write(PacketPtr pkt) override;
+};
+
+} // namespace qemu
+} // namespace gem5
+
+#endif //__DEV_QEMU_FW_CFG_HH__
diff --git a/src/dev/reset_port.cc b/src/dev/reset_port.cc
new file mode 100644
index 0000000..8d32c7d
--- /dev/null
+++ b/src/dev/reset_port.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dev/reset_port.hh"
+
+#include "base/logging.hh"
+
+namespace gem5
+{
+
+void
+ResetRequestPort::bind(Port &p)
+{
+    peer = dynamic_cast<ResetResponsePortBase*>(&p);
+    fatal_if(peer == nullptr, "Attempt to bind reset request port %s to "
+            "incompatible port %s.", name(), p.name());
+    Port::bind(p);
+}
+
+void
+ResetRequestPort::unbind()
+{
+    peer = nullptr;
+    Port::unbind();
+}
+
+void
+ResetRequestPort::requestReset()
+{
+    peer->requestReset();
+}
+
+} // namespace gem5
diff --git a/src/dev/reset_port.hh b/src/dev/reset_port.hh
new file mode 100644
index 0000000..a08db1c
--- /dev/null
+++ b/src/dev/reset_port.hh
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_RESET_PORT_HH__
+#define __DEV_RESET_PORT_HH__
+
+#include "sim/port.hh"
+
+#include <string>
+
+namespace gem5
+{
+
+class ResetResponsePortBase : public Port
+{
+  public:
+    using Port::Port;
+    virtual void requestReset() = 0;
+};
+
+template <class Device>
+class ResetResponsePort : public ResetResponsePortBase
+{
+  public:
+    ResetResponsePort(const std::string &name, PortID id, Device *dev) :
+        ResetResponsePortBase(name, id), device(dev) {}
+    void requestReset() override { device->requestReset(); }
+
+  private:
+    Device *device = nullptr;
+};
+
+class ResetRequestPort : public Port
+{
+  public:
+    ResetRequestPort(const std::string &_name, PortID _id)
+        : Port(_name, _id) {}
+    void bind(Port &p) override;
+    void unbind() override;
+    void requestReset();
+
+  private:
+    ResetResponsePortBase *peer = nullptr;
+};
+
+} // namespace gem5
+
+#endif // __DEV_RESET_PORT_HH__
diff --git a/src/dev/riscv/HiFive.py b/src/dev/riscv/HiFive.py
index a76e456..08ef943 100755
--- a/src/dev/riscv/HiFive.py
+++ b/src/dev/riscv/HiFive.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2021 Huawei International
+# Copyright (c) 2022 EXAscale Performance SYStems (EXAPSYS)
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -44,6 +45,20 @@
 from m5.proxy import *
 from m5.util.fdthelper import *
 
+from m5.objects.PciHost import GenericPciHost
+
+class GenericRiscvPciHost(GenericPciHost):
+    type = 'GenericRiscvPciHost'
+    cxx_header = "dev/riscv/pci_host.hh"
+    cxx_class = 'gem5::GenericRiscvPciHost'
+    int_base = Param.Int(0x10,
+                        "Base number used as interrupt line and PLIC source.")
+    int_count = Param.Unsigned(4,
+                        "Maximum number of interrupts used by this host")
+    # This python parameter can be used in configuration scripts to turn
+    # on/off the fdt dma-coherent flag when doing dtb autogeneration
+    _dma_coherent = True
+
 class HiFive(Platform):
     """HiFive Platform
 
@@ -105,6 +120,10 @@
     # PLIC
     plic = Param.Plic(Plic(pio_addr=0xc000000), "PLIC")
 
+    #PCI
+    pci_host = GenericRiscvPciHost(conf_base=0x30000000, conf_size='256MB',
+        conf_device_bits=12, pci_pio_base=0x2f000000, pci_mem_base=0x40000000)
+
     # Uart
     uart = RiscvUart8250(pio_addr=0x10000000)
     # Int source ID to redirect console interrupts to
@@ -151,7 +170,8 @@
     def attachPlic(self):
         """Count number of PLIC interrupt sources
         """
-        plic_srcs = [self.uart_int_id]
+        plic_srcs = [self.uart_int_id, self.pci_host.int_base
+                     + self.pci_host.int_count]
         for device in self._off_chip_devices():
             if hasattr(device, "interrupt_id"):
                 plic_srcs.append(device.interrupt_id)
@@ -159,14 +179,14 @@
 
     def attachOnChipIO(self, bus):
         """Attach on-chip IO devices, needs modification
-            to support DMA and PCI
+            to support DMA
         """
         for device in self._on_chip_devices():
             device.pio = bus.mem_side_ports
 
     def attachOffChipIO(self, bus):
         """Attach off-chip IO devices, needs modification
-            to support DMA and PCI
+            to support DMA
         """
         for device in self._off_chip_devices():
             device.pio = bus.mem_side_ports
diff --git a/src/dev/riscv/SConscript b/src/dev/riscv/SConscript
index d0ef5eb..af0b96b 100755
--- a/src/dev/riscv/SConscript
+++ b/src/dev/riscv/SConscript
@@ -1,6 +1,7 @@
 # -*- mode:python -*-
 
 # Copyright (c) 2021 Huawei International
+# Copyright (c) 2022 EXAscale Performance SYStems (EXAPSYS)
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -28,7 +29,8 @@
 
 Import('*')
 
-SimObject('HiFive.py', sim_objects=['HiFive'], tags='riscv isa')
+SimObject('HiFive.py', sim_objects=['HiFive', 'GenericRiscvPciHost'],
+          tags='riscv isa')
 SimObject('LupV.py', sim_objects=['LupV'], tags='riscv isa')
 SimObject('Clint.py', sim_objects=['Clint'], tags='riscv isa')
 SimObject('PlicDevice.py', sim_objects=['PlicIntDevice'], tags='riscv isa')
@@ -41,6 +43,8 @@
 DebugFlag('Plic', tags='riscv isa')
 DebugFlag('VirtIOMMIO', tags='riscv isa')
 
+Source('pci_host.cc', tags='riscv isa')
+
 Source('hifive.cc', tags='riscv isa')
 Source('lupv.cc', tags='riscv isa')
 Source('clint.cc', tags='riscv isa')
diff --git a/src/dev/riscv/pci_host.cc b/src/dev/riscv/pci_host.cc
new file mode 100755
index 0000000..e4248a9
--- /dev/null
+++ b/src/dev/riscv/pci_host.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2022 EXAscale Performance SYStems (EXAPSYS)
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dev/riscv/pci_host.hh"
+#include "params/GenericRiscvPciHost.hh"
+
+namespace gem5
+{
+
+GenericRiscvPciHost::GenericRiscvPciHost(const GenericRiscvPciHostParams &p)
+    : GenericPciHost(p), intBase(p.int_base), intCount(p.int_count)
+{
+}
+
+uint32_t
+GenericRiscvPciHost::mapPciInterrupt(
+    const PciBusAddr &addr, PciIntPin pin) const
+{
+    fatal_if(pin == PciIntPin::NO_INT,
+             "%02x:%02x.%i: Interrupt from a device without interrupts\n",
+             addr.bus, addr.dev, addr.func);
+
+    return intBase + (addr.dev % intCount);
+}
+
+}
diff --git a/src/dev/riscv/pci_host.hh b/src/dev/riscv/pci_host.hh
new file mode 100755
index 0000000..db4d152
--- /dev/null
+++ b/src/dev/riscv/pci_host.hh
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2022 EXAscale Performance SYStems (EXAPSYS)
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __DEV_RISCV_PCI_HOST_HH__
+#define __DEV_RISCV_PCI_HOST_HH__
+
+#include "dev/pci/host.hh"
+#include "params/GenericRiscvPciHost.hh"
+
+namespace gem5
+{
+
+class GenericRiscvPciHost : public GenericPciHost
+{
+  private:
+    const uint32_t intBase;
+    const uint32_t intCount;
+
+  public:
+    PARAMS(GenericRiscvPciHost);
+    GenericRiscvPciHost(const GenericRiscvPciHostParams &p);
+
+  protected:
+    uint32_t mapPciInterrupt(const PciBusAddr &addr,
+                             PciIntPin pin) const override;
+};
+
+}
+
+#endif // __DEV_RISCV_PCI_HOST_HH__
diff --git a/src/dev/serial/SConscript b/src/dev/serial/SConscript
index 1f5823d..a862def 100644
--- a/src/dev/serial/SConscript
+++ b/src/dev/serial/SConscript
@@ -40,7 +40,7 @@
 
 Import('*')
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 SimObject('Serial.py', sim_objects=['SerialDevice', 'SerialNullDevice'])
diff --git a/src/dev/storage/SConscript b/src/dev/storage/SConscript
index 27abf2d..615e1bf 100644
--- a/src/dev/storage/SConscript
+++ b/src/dev/storage/SConscript
@@ -40,7 +40,7 @@
 
 Import('*')
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 # Controllers
diff --git a/src/dev/storage/ide_ctrl.cc b/src/dev/storage/ide_ctrl.cc
index 7d4ecac..45e6242 100644
--- a/src/dev/storage/ide_ctrl.cc
+++ b/src/dev/storage/ide_ctrl.cc
@@ -50,10 +50,6 @@
 #include "params/IdeController.hh"
 #include "sim/byteswap.hh"
 
-// clang complains about std::set being overloaded with Packet::set if
-// we open up the entire namespace std
-using std::string;
-
 namespace gem5
 {
 
@@ -65,7 +61,9 @@
     BMIDescTablePtr = 0x4
 };
 
-IdeController::Channel::Channel(string newName) : _name(newName)
+IdeController::Channel::Channel(std::string new_name, IdeController *new_ctrl,
+        bool new_primary) :
+    Named(new_name), ctrl(new_ctrl), primary(new_primary)
 {
     bmiRegs.reset();
     bmiRegs.status.dmaCap0 = 1;
@@ -74,34 +72,28 @@
 
 IdeController::IdeController(const Params &p)
     : PciDevice(p), configSpaceRegs(name() + ".config_space_regs"),
-    primary(name() + ".primary"),
-    secondary(name() + ".secondary"),
+    primary(name() + ".primary", this, true),
+    secondary(name() + ".secondary", this, false),
     ioShift(p.io_shift), ctrlOffset(p.ctrl_offset)
 {
+    panic_if(params().disks.size() > 3,
+            "IDE controllers support a maximum of 4 devices attached!");
 
     // Assign the disks to channels
     for (int i = 0; i < params().disks.size(); i++) {
-        if (!params().disks[i])
+        auto *disk = params().disks[i];
+        auto &channel = (i < 2) ? primary : secondary;
+
+        if (!disk)
             continue;
-        switch (i) {
-          case 0:
-            primary.device0 = params().disks[0];
-            break;
-          case 1:
-            primary.device1 = params().disks[1];
-            break;
-          case 2:
-            secondary.device0 = params().disks[2];
-            break;
-          case 3:
-            secondary.device1 = params().disks[3];
-            break;
-          default:
-            panic("IDE controllers support a maximum "
-                  "of 4 devices attached!\n");
-        }
+
+        if (i % 2 == 0)
+            channel.setDevice0(disk);
+        else
+            channel.setDevice1(disk);
+
         // Arbitrarily set the chunk size to 4K.
-        params().disks[i]->setController(this, 4 * 1024);
+        disk->setChannel(&channel, 4 * 1024);
     }
 
     primary.select(false);
@@ -128,34 +120,38 @@
     UNSERIALIZE_SCALAR(udmaTiming);
 }
 
-bool
-IdeController::isDiskSelected(IdeDisk *diskPtr)
+void
+IdeController::Channel::postInterrupt()
 {
-    return (primary.selected == diskPtr || secondary.selected == diskPtr);
+    bmiRegs.status.intStatus = 1;
+    _pendingInterrupt = true;
+    ctrl->postInterrupt(isPrimary());
 }
 
 void
-IdeController::intrPost()
+IdeController::Channel::clearInterrupt()
 {
-    primary.bmiRegs.status.intStatus = 1;
-    PciDevice::intrPost();
+    bmiRegs.status.intStatus = 0;
+    _pendingInterrupt = false;
+    ctrl->clearInterrupt(isPrimary());
 }
 
 void
-IdeController::setDmaComplete(IdeDisk *disk)
+IdeController::postInterrupt(bool is_primary)
 {
-    Channel *channel;
-    if (disk == primary.device0 || disk == primary.device1) {
-        channel = &primary;
-    } else if (disk == secondary.device0 || disk == secondary.device1) {
-        channel = &secondary;
-    } else {
-        panic("Unable to find disk based on pointer %#x\n", disk);
-    }
+    auto &other = is_primary ? secondary : primary;
+    // If an interrupt isn't already posted for the other channel...
+    if (!other.pendingInterrupt())
+        PciDevice::intrPost();
+}
 
-    channel->bmiRegs.command.startStop = 0;
-    channel->bmiRegs.status.active = 0;
-    channel->bmiRegs.status.intStatus = 1;
+void
+IdeController::clearInterrupt(bool is_primary)
+{
+    auto &other = is_primary ? secondary : primary;
+    // If the interrupt isn't still needed by the other channel...
+    if (!other.pendingInterrupt())
+        PciDevice::intrClear();
 }
 
 Tick
@@ -206,13 +202,13 @@
     if (!read && offset == SelectOffset)
         select(*data & SelectDevBit);
 
-    if (selected == NULL) {
+    if (selected() == NULL) {
         assert(size == sizeof(uint8_t));
         *data = 0;
     } else if (read) {
-        selected->readCommand(offset, size, data);
+        selected()->readCommand(offset, size, data);
     } else {
-        selected->writeCommand(offset, size, data);
+        selected()->writeCommand(offset, size, data);
     }
 }
 
@@ -220,13 +216,13 @@
 IdeController::Channel::accessControl(Addr offset,
         int size, uint8_t *data, bool read)
 {
-    if (selected == NULL) {
+    if (selected() == NULL) {
         assert(size == sizeof(uint8_t));
         *data = 0;
     } else if (read) {
-        selected->readControl(offset, size, data);
+        selected()->readControl(offset, size, data);
     } else {
-        selected->writeControl(offset, size, data);
+        selected()->writeControl(offset, size, data);
     }
 }
 
@@ -252,19 +248,19 @@
                     oldVal.rw = newVal.rw;
 
                 if (oldVal.startStop != newVal.startStop) {
-                    if (selected == NULL)
+                    if (selected() == NULL)
                         panic("DMA start for disk which does not exist\n");
 
                     if (oldVal.startStop) {
                         DPRINTF(IdeCtrl, "Stopping DMA transfer\n");
                         bmiRegs.status.active = 0;
 
-                        selected->abortDma();
+                        selected()->abortDma();
                     } else {
                         DPRINTF(IdeCtrl, "Starting DMA transfer\n");
                         bmiRegs.status.active = 1;
 
-                        selected->startDma(letoh(bmiRegs.bmidtp));
+                        selected()->startDma(letoh(bmiRegs.bmidtp));
                     }
                 }
 
@@ -287,8 +283,8 @@
                     newVal.intStatus = 0; // clear the interrupt?
                 } else {
                     // Assigning two bitunion fields to each other does not
-                    // work as intended, so we need to use this temporary variable
-                    // to get around the bug.
+                    // work as intended, so we need to use this temporary
+                    // variable to get around the bug.
                     uint8_t tmp = oldVal.intStatus;
                     newVal.intStatus = tmp;
                 }
@@ -309,8 +305,9 @@
             break;
           default:
             if (size != sizeof(uint8_t) && size != sizeof(uint16_t) &&
-                    size != sizeof(uint32_t))
-                panic("IDE controller write of invalid write size: %x\n", size);
+                    size != sizeof(uint32_t)) {
+                panic("IDE controller write of invalid size: %x\n", size);
+            }
             memcpy((uint8_t *)&bmiRegs + offset, data, size);
         }
     }
@@ -378,6 +375,14 @@
     pkt->makeAtomicResponse();
 }
 
+void
+IdeController::Channel::setDmaComplete()
+{
+    bmiRegs.command.startStop = 0;
+    bmiRegs.status.active = 0;
+    bmiRegs.status.intStatus = 1;
+}
+
 Tick
 IdeController::read(PacketPtr pkt)
 {
diff --git a/src/dev/storage/ide_ctrl.hh b/src/dev/storage/ide_ctrl.hh
index cf6a58b..38b97bf 100644
--- a/src/dev/storage/ide_ctrl.hh
+++ b/src/dev/storage/ide_ctrl.hh
@@ -104,14 +104,48 @@
 
     ConfigSpaceRegs configSpaceRegs;
 
-    struct Channel
+  public:
+    class Channel : public Named
     {
-        std::string _name;
+      private:
+        IdeController *ctrl;
 
-        const std::string
-        name()
+        /** IDE disks connected to this controller
+         * For more details about device0 and device1 see:
+         * https://en.wikipedia.org/wiki/Parallel_ATA
+         * #Multiple_devices_on_a_cable
+         *
+        */
+        IdeDisk *device0 = nullptr, *device1 = nullptr;
+
+        /** Currently selected disk */
+        IdeDisk *_selected = nullptr;
+
+        bool selectBit = false;
+        bool primary;
+
+        bool _pendingInterrupt = false;
+
+      public:
+        bool isPrimary() const { return primary; }
+
+        bool pendingInterrupt() const { return _pendingInterrupt; }
+
+        IdeDisk *selected() const { return _selected; }
+        IdeController *controller() const { return ctrl; }
+
+        void
+        setDevice0(IdeDisk *disk)
         {
-            return _name;
+            assert(!device0 && disk);
+            device0 = disk;
+        }
+
+        void
+        setDevice1(IdeDisk *disk)
+        {
+            assert(!device1 && disk);
+            device1 = disk;
         }
 
         /** Registers used for bus master interface */
@@ -130,36 +164,30 @@
             uint32_t bmidtp;
         } bmiRegs;
 
-        /** IDE disks connected to this controller
-         * For more details about device0 and device1 see:
-         * https://en.wikipedia.org/wiki/Parallel_ATA
-         * #Multiple_devices_on_a_cable
-         *
-        */
-        IdeDisk *device0 = nullptr, *device1 = nullptr;
-
-        /** Currently selected disk */
-        IdeDisk *selected = nullptr;
-
-        bool selectBit = false;
-
         void
         select(bool select_device_1)
         {
             selectBit = select_device_1;
-            selected = selectBit ? device1 : device0;
+            _selected = selectBit ? device1 : device0;
         }
 
         void accessCommand(Addr offset, int size, uint8_t *data, bool read);
         void accessControl(Addr offset, int size, uint8_t *data, bool read);
         void accessBMI(Addr offset, int size, uint8_t *data, bool read);
 
-        Channel(std::string newName);
+        void setDmaComplete();
+
+        void postInterrupt();
+        void clearInterrupt();
+
+        Channel(std::string new_name, IdeController *new_ctrl,
+                bool new_primary);
 
         void serialize(const std::string &base, std::ostream &os) const;
         void unserialize(const std::string &base, CheckpointIn &cp);
     };
 
+  private:
     Channel primary;
     Channel secondary;
 
@@ -171,16 +199,12 @@
     PARAMS(IdeController);
     IdeController(const Params &p);
 
-    /** See if a disk is selected based on its pointer */
-    bool isDiskSelected(IdeDisk *diskPtr);
-
-    void intrPost();
+    virtual void postInterrupt(bool is_primary);
+    virtual void clearInterrupt(bool is_primary);
 
     Tick writeConfig(PacketPtr pkt) override;
     Tick readConfig(PacketPtr pkt) override;
 
-    void setDmaComplete(IdeDisk *disk);
-
     Tick read(PacketPtr pkt) override;
     Tick write(PacketPtr pkt) override;
 
diff --git a/src/dev/storage/ide_disk.cc b/src/dev/storage/ide_disk.cc
index a7185e4..e43437f 100644
--- a/src/dev/storage/ide_disk.cc
+++ b/src/dev/storage/ide_disk.cc
@@ -49,6 +49,7 @@
 #include <deque>
 #include <string>
 
+#include "base/bitfield.hh"
 #include "base/chunk_generator.hh"
 #include "base/compiler.hh"
 #include "base/cprintf.hh" // csprintf
@@ -63,12 +64,9 @@
 {
 
 IdeDisk::IdeDisk(const Params &p)
-    : SimObject(p), ctrl(NULL), image(p.image), diskDelay(p.delay),
-      ideDiskStats(this),
+    : SimObject(p), image(p.image), diskDelay(p.delay), ideDiskStats(this),
       dmaTransferEvent([this]{ doDmaTransfer(); }, name()),
-      dmaReadCG(NULL),
       dmaReadWaitEvent([this]{ doDmaRead(); }, name()),
-      dmaWriteCG(NULL),
       dmaWriteWaitEvent([this]{ doDmaWrite(); }, name()),
       dmaPrdReadEvent([this]{ dmaPrdReadDone(); }, name()),
       dmaReadEvent([this]{ dmaReadDone(); }, name()),
@@ -159,7 +157,7 @@
     cmdBytesLeft = 0;
     drqBytesLeft = 0;
     dmaRead = false;
-    intrPending = false;
+    pendingInterrupt = false;
     dmaAborted = false;
 
     // set the device state to idle
@@ -190,16 +188,14 @@
 bool
 IdeDisk::isDEVSelect()
 {
-    return ctrl->isDiskSelected(this);
+    return channel->selected() == this;
 }
 
 Addr
 IdeDisk::pciToDma(Addr pciAddr)
 {
-    if (ctrl)
-        return ctrl->pciToDma(pciAddr);
-    else
-        panic("Access to unset controller!\n");
+    panic_if(!ctrl, "Access to unset controller!");
+    return ctrl->pciToDma(pciAddr);
 }
 
 ////
@@ -343,16 +339,18 @@
         return;
     }
 
-    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
+    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma) {
         panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
               dmaState, devState);
+    }
 
     if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
         schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD);
         return;
-    } else
+    } else {
         ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent,
                 (uint8_t*)&curPrd.entry);
+    }
 }
 
 void
@@ -538,8 +536,9 @@
 void
 IdeDisk::dmaWriteDone()
 {
-    DPRINTF(IdeDisk, "doWriteDone: curPrd byte count %d, eot %#x cmd bytes left:%d\n",
-                curPrd.getByteCount(), curPrd.getEOT(), cmdBytesLeft);
+    DPRINTF(IdeDisk,
+            "doWriteDone: curPrd byte count %d, eot %#x cmd bytes left:%d\n",
+            curPrd.getByteCount(), curPrd.getEOT(), cmdBytesLeft);
     // check for the EOT
     if (curPrd.getEOT()) {
         assert(cmdBytesLeft == 0);
@@ -559,9 +558,9 @@
 {
     uint32_t bytesRead = image->read(data, sector);
 
-    if (bytesRead != SectorSize)
-        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
-              name(), bytesRead, SectorSize, errno);
+    panic_if(bytesRead != SectorSize,
+            "Can't read from %s. Only %d of %d read. errno=%d",
+            name(), bytesRead, SectorSize, errno);
 }
 
 void
@@ -569,9 +568,9 @@
 {
     uint32_t bytesWritten = image->write(data, sector);
 
-    if (bytesWritten != SectorSize)
-        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
-              name(), bytesWritten, SectorSize, errno);
+    panic_if(bytesWritten != SectorSize,
+            "Can't write to %s. Only %d of %d written. errno=%d",
+            name(), bytesWritten, SectorSize, errno);
 }
 
 ////
@@ -581,11 +580,11 @@
 void
 IdeDisk::startDma(const uint32_t &prdTableBase)
 {
-    if (dmaState != Dma_Start)
-        panic("Inconsistent DMA state, should be in Dma_Start!\n");
+    panic_if(dmaState != Dma_Start,
+            "Inconsistent DMA state, should be in Dma_Start!");
 
-    if (devState != Transfer_Data_Dma)
-        panic("Inconsistent device state for DMA start!\n");
+    panic_if(devState != Transfer_Data_Dma,
+            "Inconsistent device state for DMA start!");
 
     // PRD base address is given by bits 31:2
     curPrdAddr = pciToDma((Addr)(prdTableBase & ~0x3ULL));
@@ -599,11 +598,11 @@
 void
 IdeDisk::abortDma()
 {
-    if (dmaState == Dma_Idle)
-        panic("Inconsistent DMA state, should be Start or Transfer!");
+    panic_if(dmaState == Dma_Idle,
+            "Inconsistent DMA state, should be Start or Transfer!");
 
-    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
-        panic("Inconsistent device state, should be Transfer or Prepare!\n");
+    panic_if(devState != Transfer_Data_Dma && devState != Prepare_Data_Dma,
+            "Inconsistent device state, should be Transfer or Prepare!");
 
     updateState(ACT_CMD_ERROR);
 }
@@ -615,6 +614,10 @@
     uint32_t size = 0;
     dmaRead = false;
 
+    // Clear any existing errors.
+    replaceBits(status, 0, 0);
+    replaceBits(cmdReg.error, 2, 0);
+
     // Decode commands
     switch (cmdReg.command) {
         // Supported non-data commands
@@ -644,16 +647,23 @@
 
         // Supported PIO data-in commands
       case WDCC_IDENTIFY:
-      case ATAPI_IDENTIFY_DEVICE:
         cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
         devState = Prepare_Data_In;
         action = ACT_DATA_READY;
         break;
 
+      case ATAPI_IDENTIFY_DEVICE:
+        // We're not an ATAPI device, so this command isn't implemented.
+        devState = Command_Execution;
+        action = ACT_CMD_ERROR;
+        replaceBits(cmdReg.error, 2, 1);
+        replaceBits(status, 0, 1);
+        break;
+
       case WDCC_READMULTI:
       case WDCC_READ:
-        if (!(cmdReg.drive & DRIVE_LBA_BIT))
-            panic("Attempt to perform CHS access, only supports LBA\n");
+        panic_if(!(cmdReg.drive & DRIVE_LBA_BIT),
+                "Attempt to perform CHS access, only supports LBA");
 
         if (cmdReg.sec_count == 0)
             cmdBytes = cmdBytesLeft = (256 * SectorSize);
@@ -670,8 +680,8 @@
         // Supported PIO data-out commands
       case WDCC_WRITEMULTI:
       case WDCC_WRITE:
-        if (!(cmdReg.drive & DRIVE_LBA_BIT))
-            panic("Attempt to perform CHS access, only supports LBA\n");
+        panic_if(!(cmdReg.drive & DRIVE_LBA_BIT),
+                "Attempt to perform CHS access, only supports LBA");
 
         if (cmdReg.sec_count == 0)
             cmdBytes = cmdBytesLeft = (256 * SectorSize);
@@ -689,14 +699,15 @@
         dmaRead = true;  // a write to the disk is a DMA read from memory
         [[fallthrough]];
       case WDCC_READDMA:
-        if (!(cmdReg.drive & DRIVE_LBA_BIT))
-            panic("Attempt to perform CHS access, only supports LBA\n");
+        panic_if(!(cmdReg.drive & DRIVE_LBA_BIT),
+                "Attempt to perform CHS access, only supports LBA");
 
         if (cmdReg.sec_count == 0)
             cmdBytes = cmdBytesLeft = (256 * SectorSize);
         else
             cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
-        DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d in readdma\n", cmdBytesLeft);
+        DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d in readdma\n",
+                cmdBytesLeft);
 
         curSector = getLBABase();
 
@@ -725,32 +736,28 @@
 ////
 
 void
-IdeDisk::intrPost()
+IdeDisk::postInterrupt()
 {
     DPRINTF(IdeDisk, "Posting Interrupt\n");
-    if (intrPending)
-        panic("Attempt to post an interrupt with one pending\n");
+    panic_if(pendingInterrupt,
+            "Attempt to post an interrupt with one pending");
 
-    intrPending = true;
+    pendingInterrupt = true;
 
-    // talk to controller to set interrupt
-    if (ctrl) {
-        ctrl->intrPost();
-    }
+    assert(channel);
+    channel->postInterrupt();
 }
 
 void
-IdeDisk::intrClear()
+IdeDisk::clearInterrupt()
 {
     DPRINTF(IdeDisk, "Clearing Interrupt\n");
-    if (!intrPending)
-        panic("Attempt to clear a non-pending interrupt\n");
+    panic_if(!pendingInterrupt, "Attempt to clear a non-pending interrupt");
 
-    intrPending = false;
+    pendingInterrupt = false;
 
-    // talk to controller to clear interrupt
-    if (ctrl)
-        ctrl->intrClear();
+    assert(channel);
+    channel->clearInterrupt();
 }
 
 ////
@@ -786,12 +793,12 @@
       case Device_Idle_SI:
         if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
             devState = Device_Idle_NS;
-            intrClear();
+            clearInterrupt();
         } else if (action == ACT_STAT_READ || isIENSet()) {
             devState = Device_Idle_S;
-            intrClear();
+            clearInterrupt();
         } else if (action == ACT_CMD_WRITE) {
-            intrClear();
+            clearInterrupt();
             startCommand();
         }
 
@@ -799,24 +806,24 @@
 
       case Device_Idle_NS:
         if (action == ACT_SELECT_WRITE && isDEVSelect()) {
-            if (!isIENSet() && intrPending) {
+            if (!isIENSet() && pendingInterrupt) {
                 devState = Device_Idle_SI;
-                intrPost();
+                postInterrupt();
             }
-            if (isIENSet() || !intrPending) {
+            if (isIENSet() || !pendingInterrupt) {
                 devState = Device_Idle_S;
             }
         }
         break;
 
       case Command_Execution:
-        if (action == ACT_CMD_COMPLETE) {
+        if (action == ACT_CMD_ERROR || action == ACT_CMD_COMPLETE) {
             // clear the BSY bit
             setComplete();
 
             if (!isIENSet()) {
                 devState = Device_Idle_SI;
-                intrPost();
+                postInterrupt();
             } else {
                 devState = Device_Idle_S;
             }
@@ -830,7 +837,7 @@
 
             if (!isIENSet()) {
                 devState = Device_Idle_SI;
-                intrPost();
+                postInterrupt();
             } else {
                 devState = Device_Idle_S;
             }
@@ -861,7 +868,7 @@
 
             if (!isIENSet()) {
                 devState = Data_Ready_INTRQ_In;
-                intrPost();
+                postInterrupt();
             } else {
                 devState = Transfer_Data_In;
             }
@@ -871,7 +878,7 @@
       case Data_Ready_INTRQ_In:
         if (action == ACT_STAT_READ) {
             devState = Transfer_Data_In;
-            intrClear();
+            clearInterrupt();
         }
         break;
 
@@ -917,7 +924,7 @@
 
             if (!isIENSet()) {
                 devState = Device_Idle_SI;
-                intrPost();
+                postInterrupt();
             } else {
                 devState = Device_Idle_S;
             }
@@ -937,7 +944,7 @@
                 devState = Transfer_Data_Out;
             } else {
                 devState = Data_Ready_INTRQ_Out;
-                intrPost();
+                postInterrupt();
             }
         }
         break;
@@ -945,7 +952,7 @@
       case Data_Ready_INTRQ_Out:
         if (action == ACT_STAT_READ) {
             devState = Transfer_Data_Out;
-            intrClear();
+            clearInterrupt();
         }
         break;
 
@@ -992,7 +999,7 @@
 
             if (!isIENSet()) {
                 devState = Device_Idle_SI;
-                intrPost();
+                postInterrupt();
             } else {
                 devState = Device_Idle_S;
             }
@@ -1022,11 +1029,11 @@
             // set the seek bit
             status |= STATUS_SEEK_BIT;
             // clear the controller state for DMA transfer
-            ctrl->setDmaComplete(this);
+            channel->setDmaComplete();
 
             if (!isIENSet()) {
                 devState = Device_Idle_SI;
-                intrPost();
+                postInterrupt();
             } else {
                 devState = Device_Idle_S;
             }
@@ -1037,13 +1044,13 @@
         if (action == ACT_CMD_ERROR) {
             setComplete();
             status |= STATUS_SEEK_BIT;
-            ctrl->setDmaComplete(this);
+            channel->setDmaComplete();
             dmaAborted = false;
             dmaState = Dma_Idle;
 
             if (!isIENSet()) {
                 devState = Device_Idle_SI;
-                intrPost();
+                postInterrupt();
             } else {
                 devState = Device_Idle_S;
             }
@@ -1128,7 +1135,7 @@
     SERIALIZE_SCALAR(drqBytesLeft);
     SERIALIZE_SCALAR(curSector);
     SERIALIZE_SCALAR(dmaRead);
-    SERIALIZE_SCALAR(intrPending);
+    paramOut(cp, "intrPending", pendingInterrupt);
     SERIALIZE_SCALAR(dmaAborted);
     SERIALIZE_ENUM(devState);
     SERIALIZE_ENUM(dmaState);
@@ -1181,7 +1188,7 @@
     UNSERIALIZE_SCALAR(drqBytesLeft);
     UNSERIALIZE_SCALAR(curSector);
     UNSERIALIZE_SCALAR(dmaRead);
-    UNSERIALIZE_SCALAR(intrPending);
+    paramIn(cp, "intrPending", pendingInterrupt);
     UNSERIALIZE_SCALAR(dmaAborted);
     UNSERIALIZE_ENUM(devState);
     UNSERIALIZE_ENUM(dmaState);
diff --git a/src/dev/storage/ide_disk.hh b/src/dev/storage/ide_disk.hh
index 95eec24..5eeb3d1 100644
--- a/src/dev/storage/ide_disk.hh
+++ b/src/dev/storage/ide_disk.hh
@@ -217,7 +217,9 @@
 {
   protected:
     /** The IDE controller for this disk. */
-    IdeController *ctrl;
+    IdeController *ctrl = nullptr;
+    /** The channel this disk is connected to. */
+    IdeController::Channel *channel = nullptr;
     /** The image that contains the data of this disk. */
     DiskImage *image;
 
@@ -259,7 +261,7 @@
     /** Device ID (device0=0/device1=1) */
     int devID;
     /** Interrupt pending */
-    bool intrPending;
+    bool pendingInterrupt;
     /** DMA Aborted */
     bool dmaAborted;
 
@@ -294,10 +296,11 @@
      * @param c The IDE controller
      */
     void
-    setController(IdeController *c, Addr chunk_bytes)
+    setChannel(IdeController::Channel *_channel, Addr chunk_bytes)
     {
-        panic_if(ctrl, "Cannot change the controller once set!\n");
-        ctrl = c;
+        panic_if(channel, "Cannot change the channel once set!");
+        channel = _channel;
+        ctrl = channel->controller();
         chunkBytes = chunk_bytes;
     }
 
@@ -315,8 +318,8 @@
     void startCommand();
 
     // Interrupt management
-    void intrPost();
-    void intrClear();
+    void postInterrupt();
+    void clearInterrupt();
 
     // DMA stuff
     void doDmaTransfer();
@@ -325,13 +328,13 @@
     void doDmaDataRead();
 
     void doDmaRead();
-    ChunkGenerator *dmaReadCG;
+    ChunkGenerator *dmaReadCG = nullptr;
     EventFunctionWrapper dmaReadWaitEvent;
 
     void doDmaDataWrite();
 
     void doDmaWrite();
-    ChunkGenerator *dmaWriteCG;
+    ChunkGenerator *dmaWriteCG = nullptr;
     EventFunctionWrapper dmaWriteWaitEvent;
 
     void dmaPrdReadDone();
@@ -355,7 +358,8 @@
     bool isIENSet() { return nIENBit; }
     bool isDEVSelect();
 
-    void setComplete()
+    void
+    setComplete()
     {
         // clear out the status byte
         status = 0;
@@ -365,10 +369,13 @@
         status |= STATUS_SEEK_BIT;
     }
 
-    uint32_t getLBABase()
+    uint32_t
+    getLBABase()
     {
-        return  (Addr)(((cmdReg.head & 0xf) << 24) | (cmdReg.cyl_high << 16) |
-                       (cmdReg.cyl_low << 8) | (cmdReg.sec_num));
+        return ((cmdReg.head & 0xf) << 24) |
+               (cmdReg.cyl_high << 16) |
+               (cmdReg.cyl_low << 8) |
+               (cmdReg.sec_num);
     }
 
     inline Addr pciToDma(Addr pciAddr);
diff --git a/src/dev/virtio/SConscript b/src/dev/virtio/SConscript
index b00679c..e004da6 100644
--- a/src/dev/virtio/SConscript
+++ b/src/dev/virtio/SConscript
@@ -37,7 +37,7 @@
 
 Import('*')
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 SimObject('VirtIO.py', sim_objects=[
diff --git a/src/cpu/minor/SConsopts b/src/dev/virtio/VirtIORng 2.py
similarity index 83%
copy from src/cpu/minor/SConsopts
copy to src/dev/virtio/VirtIORng 2.py
index 16ff599..13df059 100644
--- a/src/cpu/minor/SConsopts
+++ b/src/dev/virtio/VirtIORng 2.py
@@ -1,6 +1,7 @@
 # -*- mode:python -*-
 
-# Copyright (c) 2012-2014 ARM Limited
+# Copyright (c) 2022  Institute of Computing Technology, Chinese
+#                     Academy of Sciences
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -35,6 +36,13 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+from m5.params import *
+from m5.proxy import *
+from m5.objects.VirtIO import VirtIODeviceBase
 
-main.Append(ALL_CPU_MODELS=['MinorCPU'])
+class VirtIORng(VirtIODeviceBase):
+    type = 'VirtIORng'
+    cxx_header = 'dev/virtio/rng.hh'
+    cxx_class = 'gem5::VirtIORng'
+
+    qSize = Param.Unsigned(16, "Request queue size")
diff --git a/src/dev/virtio/VirtIORng.py b/src/dev/virtio/VirtIORng.py
index 54848ee..13df059 100644
--- a/src/dev/virtio/VirtIORng.py
+++ b/src/dev/virtio/VirtIORng.py
@@ -46,5 +46,3 @@
     cxx_class = 'gem5::VirtIORng'
 
     qSize = Param.Unsigned(16, "Request queue size")
-
-    entropy_source = Param.String("/dev/random", "The source of entropy")
diff --git a/src/dev/virtio/rng 2.cc b/src/dev/virtio/rng 2.cc
new file mode 100644
index 0000000..c26568e
--- /dev/null
+++ b/src/dev/virtio/rng 2.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2022  Institute of Computing Technology, Chinese Academy
+ *                     of Sciences
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dev/virtio/rng.hh"
+
+#include "base/random.hh"
+#include "debug/VIORng.hh"
+#include "params/VirtIORng.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+
+VirtIORng::VirtIORng(const Params &params)
+    : VirtIODeviceBase(params, ID_RNG, 0, 0),
+      qReq(params.system->physProxy, byteOrder, params.qSize, *this)
+{
+    registerQueue(qReq);
+}
+
+VirtIORng::~VirtIORng()
+{
+}
+
+VirtIORng::RngQueue::RngQueue(PortProxy &proxy, ByteOrder bo, uint16_t size,
+    VirtIORng &_parent)
+    : VirtQueue(proxy, bo, size), parent(_parent)
+{
+}
+
+void
+VirtIORng::readConfig(PacketPtr pkt, Addr cfgOffset)
+{
+    // There are no configuration for RNG device
+    pkt->makeResponse();
+}
+
+void
+VirtIORng::RngQueue::trySend()
+{
+    DPRINTF(VIORng, "try send\n");
+
+    VirtDescriptor *d;
+    while ((d = consumeDescriptor())) {
+        DPRINTF(VIORng, "Got descriptor (len: %i)\n", d->size());
+        size_t len = 0;
+        while (len < d->size()) {
+            uint8_t byte = gem5::random_mt.random<uint8_t>();
+            d->chainWrite(len, &byte, sizeof(uint8_t));
+            ++len;
+        }
+
+        // Tell the guest that we are done with this descriptor.
+        produceDescriptor(d, len);
+        parent.kick();
+    }
+}
+
+} // namespace gem5
diff --git a/src/dev/virtio/rng 2.hh b/src/dev/virtio/rng 2.hh
new file mode 100644
index 0000000..7be2354
--- /dev/null
+++ b/src/dev/virtio/rng 2.hh
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2022  Institute of Computing Technology, Chinese Academy
+ *                     of Sciences
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_VIRTIO_RNG_HH__
+#define __DEV_VIRTIO_RNG_HH__
+
+#include "base/compiler.hh"
+#include "dev/virtio/base.hh"
+
+namespace gem5
+{
+
+struct VirtIORngParams;
+
+/**
+ * VirtIO Rng
+ *
+ * @see https://github.com/rustyrussell/virtio-spec
+ * @see http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
+ */
+class VirtIORng : public VirtIODeviceBase
+{
+  public:
+    typedef VirtIORngParams Params;
+    VirtIORng(const Params &params);
+    virtual ~VirtIORng();
+
+    void readConfig(PacketPtr pkt, Addr cfgOffset);
+
+  protected:
+    /** VirtIO device ID */
+    static const DeviceId ID_RNG = 0x04;
+
+  protected:
+    /**
+     * Virtqueue for data going from the host to the guest.
+     */
+    class RngQueue
+        : public VirtQueue
+    {
+      public:
+        RngQueue(PortProxy &proxy, ByteOrder bo, uint16_t size,
+                 VirtIORng &_parent);
+        virtual ~RngQueue() {}
+
+        void onNotify() { trySend(); }
+
+        /** Try to send data pending data from the terminal. */
+        void trySend();
+
+        std::string name() const { return parent.name() + ".qRecv"; }
+
+      protected:
+        VirtIORng &parent;
+    };
+    /** Receive queue for port 0 */
+    RngQueue qReq;
+};
+
+} // namespace gem5
+
+#endif // __DEV_VIRTIO_RNG_HH__
diff --git a/src/dev/virtio/rng.cc b/src/dev/virtio/rng.cc
index 50a747c..c26568e 100644
--- a/src/dev/virtio/rng.cc
+++ b/src/dev/virtio/rng.cc
@@ -38,9 +38,7 @@
 
 #include "dev/virtio/rng.hh"
 
-#include <fcntl.h>
-#include <unistd.h>
-
+#include "base/random.hh"
 #include "debug/VIORng.hh"
 #include "params/VirtIORng.hh"
 #include "sim/system.hh"
@@ -50,8 +48,7 @@
 
 VirtIORng::VirtIORng(const Params &params)
     : VirtIODeviceBase(params, ID_RNG, 0, 0),
-      qReq(params.system->physProxy, byteOrder, params.qSize,
-           params.entropy_source, *this)
+      qReq(params.system->physProxy, byteOrder, params.qSize, *this)
 {
     registerQueue(qReq);
 }
@@ -60,16 +57,10 @@
 {
 }
 
-VirtIORng::RngQueue::RngQueue(PortProxy &proxy, ByteOrder bo,
-    uint16_t size, const std::string &rng_file_path,
+VirtIORng::RngQueue::RngQueue(PortProxy &proxy, ByteOrder bo, uint16_t size,
     VirtIORng &_parent)
-    : VirtQueue(proxy, bo, size), parent(_parent), dist(0,255)
+    : VirtQueue(proxy, bo, size), parent(_parent)
 {
-    rng_fd = open(rng_file_path.c_str(), O_RDONLY);
-    if (rng_fd < 0) {
-        DPRINTF(VIORng, "error when open entropy source: %s\n",
-                rng_file_path.c_str());
-    }
 }
 
 void
@@ -89,16 +80,7 @@
         DPRINTF(VIORng, "Got descriptor (len: %i)\n", d->size());
         size_t len = 0;
         while (len < d->size()) {
-            uint8_t byte = 0;
-            bool rng_read_success = false;
-            if (rng_fd >= 0) {
-                ssize_t result = read(rng_fd, &byte, sizeof(uint8_t));
-                rng_read_success = (result > 0);
-            }
-            if (!rng_read_success) {
-                // fallback to C++ std rng generator
-                byte = dist(rd_device);
-            }
+            uint8_t byte = gem5::random_mt.random<uint8_t>();
             d->chainWrite(len, &byte, sizeof(uint8_t));
             ++len;
         }
diff --git a/src/dev/virtio/rng.hh b/src/dev/virtio/rng.hh
index 50a3723..7be2354 100644
--- a/src/dev/virtio/rng.hh
+++ b/src/dev/virtio/rng.hh
@@ -39,8 +39,6 @@
 #ifndef __DEV_VIRTIO_RNG_HH__
 #define __DEV_VIRTIO_RNG_HH__
 
-#include <random>
-
 #include "base/compiler.hh"
 #include "dev/virtio/base.hh"
 
@@ -76,9 +74,8 @@
         : public VirtQueue
     {
       public:
-        RngQueue(PortProxy &proxy, ByteOrder bo,
-                uint16_t size, const std::string &rng_file_path,
-                VirtIORng &_parent);
+        RngQueue(PortProxy &proxy, ByteOrder bo, uint16_t size,
+                 VirtIORng &_parent);
         virtual ~RngQueue() {}
 
         void onNotify() { trySend(); }
@@ -90,12 +87,6 @@
 
       protected:
         VirtIORng &parent;
-      private:
-        // system's special file for generating random number
-        int rng_fd;
-        // fallback random number generator
-        std::random_device rd_device;
-        std::uniform_int_distribution<int> dist;
     };
     /** Receive queue for port 0 */
     RngQueue qReq;
diff --git a/src/dev/x86/I8042.py b/src/dev/x86/I8042.py
index d2d9a17..956a1bf 100644
--- a/src/dev/x86/I8042.py
+++ b/src/dev/x86/I8042.py
@@ -26,17 +26,16 @@
 
 from m5.params import *
 from m5.proxy import *
-from m5.objects.Device import BasicPioDevice
+from m5.objects.Device import PioDevice
 from m5.objects.IntPin import IntSourcePin
 from m5.objects.PS2 import *
 
-class I8042(BasicPioDevice):
+class I8042(PioDevice):
     type = 'I8042'
     cxx_class = 'gem5::X86ISA::I8042'
     cxx_header = "dev/x86/i8042.hh"
 
-    # This isn't actually used for anything here.
-    pio_addr = 0x0
+    pio_latency = Param.Latency('100ns', "Programmed IO latency")
     data_port = Param.Addr('Data port address')
     command_port = Param.Addr('Command/status port address')
     mouse_int_pin = IntSourcePin('Pin to signal the mouse has data')
diff --git a/src/dev/x86/I82094AA.py b/src/dev/x86/I82094AA.py
index 212bca3..591c8d1 100644
--- a/src/dev/x86/I82094AA.py
+++ b/src/dev/x86/I82094AA.py
@@ -38,6 +38,5 @@
     int_requestor = RequestPort("Port for sending interrupt messages")
     int_latency = Param.Latency('1ns', \
             "Latency for an interrupt to propagate through this device.")
-    external_int_pic = Param.I8259(NULL, "External PIC, if any")
 
     inputs = VectorIntSinkPin('The pins that drive this IO APIC')
diff --git a/src/dev/x86/SConscript b/src/dev/x86/SConscript
index 5a1c0ec..a58c22e 100644
--- a/src/dev/x86/SConscript
+++ b/src/dev/x86/SConscript
@@ -55,6 +55,9 @@
 Source('i8042.cc', tags='x86 isa')
 DebugFlag('I8042', 'The I8042 keyboard controller', tags='x86 isa');
 
+SimObject('X86Ide.py', sim_objects=['X86IdeController'], tags='x86 isa');
+Source('ide_ctrl.cc', tags='x86 isa')
+
 SimObject('PcSpeaker.py', sim_objects=['PcSpeaker'], tags='x86 isa')
 Source('speaker.cc', tags='x86 isa')
 DebugFlag('PcSpeaker', tags='x86 isa')
@@ -62,3 +65,6 @@
 SimObject('I82094AA.py', sim_objects=['I82094AA'], tags='x86 isa')
 Source('i82094aa.cc', tags='x86 isa')
 DebugFlag('I82094AA', tags='x86 isa')
+
+SimObject('X86QemuFwCfg.py', sim_objects=['QemuFwCfgItemE820'], tags='x86 isa')
+Source('qemu_fw_cfg.cc', tags='x86 isa')
diff --git a/src/dev/x86/SouthBridge.py b/src/dev/x86/SouthBridge.py
index 9e0a88f..35866a7 100644
--- a/src/dev/x86/SouthBridge.py
+++ b/src/dev/x86/SouthBridge.py
@@ -32,9 +32,9 @@
 from m5.objects.I8237 import I8237
 from m5.objects.I8254 import I8254
 from m5.objects.I8259 import I8259
-from m5.objects.Ide import IdeController
 from m5.objects.PciDevice import PciLegacyIoBar, PciIoBar
 from m5.objects.PcSpeaker import PcSpeaker
+from m5.objects.X86Ide import X86IdeController
 from m5.SimObject import SimObject
 
 def x86IOAddress(port):
@@ -63,15 +63,7 @@
     io_apic = Param.I82094AA(I82094AA(pio_addr=0xFEC00000), "I/O APIC")
 
     # IDE controller
-    ide = IdeController(disks=[], pci_func=0, pci_dev=4, pci_bus=0)
-    ide.BAR0 = PciLegacyIoBar(addr=0x1f0, size='8B')
-    ide.BAR1 = PciLegacyIoBar(addr=0x3f4, size='3B')
-    ide.BAR2 = PciLegacyIoBar(addr=0x170, size='8B')
-    ide.BAR3 = PciLegacyIoBar(addr=0x374, size='3B')
-    ide.Command = 0
-    ide.ProgIF = 0x80
-    ide.InterruptLine = 14
-    ide.InterruptPin = 1
+    ide = X86IdeController(disks=[], pci_func=0, pci_dev=4, pci_bus=0)
 
     def attachIO(self, bus, dma_ports):
         # Route interrupt signals
@@ -82,10 +74,13 @@
         self.pit.int_pin = self.io_apic.inputs[2]
         self.keyboard.keyboard_int_pin = self.io_apic.inputs[1]
         self.keyboard.mouse_int_pin = self.io_apic.inputs[12]
+        self.ide.int_primary = self.pic2.inputs[6]
+        self.ide.int_primary = self.io_apic.inputs[14]
+        self.ide.int_secondary = self.pic2.inputs[7]
+        self.ide.int_secondary = self.io_apic.inputs[15]
         # Tell the devices about each other
         self.pic1.slave = self.pic2
         self.speaker.i8254 = self.pit
-        self.io_apic.external_int_pic = self.pic1
         # Connect to the bus
         self.cmos.pio = bus.mem_side_ports
         self.dma1.pio = bus.mem_side_ports
diff --git a/src/cpu/o3/O3Checker.py b/src/dev/x86/X86Ide.py
similarity index 64%
copy from src/cpu/o3/O3Checker.py
copy to src/dev/x86/X86Ide.py
index c343cd6..99aa853 100644
--- a/src/cpu/o3/O3Checker.py
+++ b/src/dev/x86/X86Ide.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2007 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2022 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,10 +23,27 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.SimObject import SimObject
 from m5.params import *
-from m5.objects.CheckerCPU import CheckerCPU
+from m5.objects.Ide import IdeController
+from m5.objects.IntPin import IntSourcePin
+from m5.objects.PciDevice import PciLegacyIoBar
 
-class O3Checker(CheckerCPU):
-    type = 'O3Checker'
-    cxx_class = 'gem5::o3::Checker'
-    cxx_header = 'cpu/o3/checker.hh'
+class X86IdeController(IdeController):
+    type = 'X86IdeController'
+    cxx_header = "dev/x86/ide_ctrl.hh"
+    cxx_class = 'gem5::X86IdeController'
+
+    VendorID = 0x8086
+    DeviceID = 0x7111
+    ProgIF = 0x80
+    InterruptLine = 0xff
+    InterruptPin = 0x01
+
+    BAR0 = PciLegacyIoBar(addr=0x1f0, size='8B')
+    BAR1 = PciLegacyIoBar(addr=0x3f4, size='3B')
+    BAR2 = PciLegacyIoBar(addr=0x170, size='8B')
+    BAR3 = PciLegacyIoBar(addr=0x374, size='3B')
+
+    int_primary = IntSourcePin('Interrupt for the primary channel')
+    int_secondary = IntSourcePin('Interrupt for the secondary channel')
diff --git a/src/cpu/o3/O3Checker.py b/src/dev/x86/X86QemuFwCfg.py
similarity index 66%
copy from src/cpu/o3/O3Checker.py
copy to src/dev/x86/X86QemuFwCfg.py
index c343cd6..6af2b79 100644
--- a/src/cpu/o3/O3Checker.py
+++ b/src/dev/x86/X86QemuFwCfg.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2007 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2022 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -25,9 +24,25 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from m5.params import *
-from m5.objects.CheckerCPU import CheckerCPU
+from m5.objects.E820 import X86E820Entry
+from m5.objects.QemuFwCfg import QemuFwCfgIo, QemuFwCfgItem
 
-class O3Checker(CheckerCPU):
-    type = 'O3Checker'
-    cxx_class = 'gem5::o3::Checker'
-    cxx_header = 'cpu/o3/checker.hh'
+def x86IOAddress(port):
+    IO_address_space_base = 0x8000000000000000
+    return IO_address_space_base + port;
+
+class X86QemuFwCfg(QemuFwCfgIo):
+    selector_addr = x86IOAddress(0x510)
+
+class QemuFwCfgItemE820(QemuFwCfgItem):
+    type = 'QemuFwCfgItemE820'
+    cxx_class = 'gem5::qemu::FwCfgItemFactory<gem5::qemu::FwCfgItemE820>'
+    cxx_template_params = ['class ItemType']
+    cxx_header = 'dev/x86/qemu_fw_cfg.hh'
+
+    # There is a fixed index for this file.
+    index = 0x8003
+    arch_specific = True
+    path = "etc/e820"
+
+    entries = VectorParam.X86E820Entry('entries for the e820 table')
diff --git a/src/dev/x86/cmos.cc b/src/dev/x86/cmos.cc
index 19fab68..d141922 100644
--- a/src/dev/x86/cmos.cc
+++ b/src/dev/x86/cmos.cc
@@ -56,7 +56,7 @@
         pkt->setLE(address);
         break;
       case 0x1:
-        pkt->setLE(readRegister(address));
+        pkt->setLE(readRegister(address.regNum));
         break;
       default:
         panic("Read from undefined CMOS port.\n");
@@ -75,7 +75,8 @@
         address = pkt->getLE<uint8_t>();
         break;
       case 0x1:
-        writeRegister(address, pkt->getLE<uint8_t>());
+        // Ignore the NMI mask bit since we never try to generate one anyway.
+        writeRegister(address.regNum, pkt->getLE<uint8_t>());
         break;
       default:
         panic("Write to undefined CMOS port.\n");
diff --git a/src/dev/x86/cmos.hh b/src/dev/x86/cmos.hh
index 72937af..de64d5a 100644
--- a/src/dev/x86/cmos.hh
+++ b/src/dev/x86/cmos.hh
@@ -29,6 +29,7 @@
 #ifndef __DEV_X86_CMOS_HH__
 #define __DEV_X86_CMOS_HH__
 
+#include "base/bitunion.hh"
 #include "dev/intpin.hh"
 #include "dev/io_device.hh"
 #include "dev/mc146818.hh"
@@ -45,7 +46,12 @@
   protected:
     Tick latency;
 
-    uint8_t address;
+    BitUnion8(CmosAddress)
+        Bitfield<6, 0> regNum;
+        Bitfield<7> nmiMask;
+    EndBitUnion(CmosAddress)
+
+    CmosAddress address;
 
     static const int numRegs = 128;
 
diff --git a/src/dev/x86/i8042.cc b/src/dev/x86/i8042.cc
index 723affe..9fab694 100644
--- a/src/dev/x86/i8042.cc
+++ b/src/dev/x86/i8042.cc
@@ -48,22 +48,18 @@
 
 
 X86ISA::I8042::I8042(const Params &p)
-    : BasicPioDevice(p, 0), // pioSize arg is dummy value... not used
-      latency(p.pio_latency),
+    : PioDevice(p), latency(p.pio_latency),
       dataPort(p.data_port), commandPort(p.command_port),
-      statusReg(0), commandByte(0), dataReg(0), lastCommand(NoCommand),
       mouse(p.mouse), keyboard(p.keyboard)
 {
     fatal_if(!mouse, "The i8042 model requires a mouse instance");
     fatal_if(!keyboard, "The i8042 model requires a keyboard instance");
 
-    statusReg.passedSelfTest = 1;
-    statusReg.commandLast = 1;
     statusReg.keyboardUnlocked = 1;
 
     commandByte.convertScanCodes = 1;
-    commandByte.passedSelfTest = 1;
-    commandByte.keyboardFullInt = 1;
+    commandByte.disableMouse = 1;
+    commandByte.disableKeyboard = 1;
 
     for (int i = 0; i < p.port_keyboard_int_pin_connection_count; i++) {
         keyboardIntPin.push_back(new IntSourcePin<I8042>(
@@ -80,7 +76,6 @@
 X86ISA::I8042::getAddrRanges() const
 {
     AddrRangeList ranges;
-    // TODO: Are these really supposed to be a single byte and not 4?
     ranges.push_back(RangeSize(dataPort, 1));
     ranges.push_back(RangeSize(commandPort, 1));
     return ranges;
@@ -200,8 +195,8 @@
                     "get byte %d.\n", data - ReadControllerRamBase);
         } else if (data > WriteControllerRamBase &&
                 data < WriteControllerRamBase + RamSize) {
-            panic("Attempted to use i8042 read controller RAM command to "
-                    "get byte %d.\n", data - ReadControllerRamBase);
+            panic("Attempted to use i8042 write controller RAM command to "
+                    "get byte %d.\n", data - WriteControllerRamBase);
         } else if (data >= PulseOutputBitBase &&
                 data < PulseOutputBitBase + NumOutputBits) {
             panic("Attempted to use i8042 pulse output bit command to "
@@ -231,11 +226,26 @@
             commandByte.disableMouse = 0;
             break;
           case TestMouse:
-            panic("i8042 \"Test mouse\" command not implemented.\n");
+            // The response to this is from the 8042, not the mouse.
+            // Hard code no errors detected.
+            writeData(0x00);
+            break;
           case SelfTest:
-            panic("i8042 \"Self test\" command not implemented.\n");
+            // Exactly what this does is essentially undocumented, but this:
+            // https://www.os2museum.com/wp/
+            //          ibm-pcat-8042-keyboard-controller-commands/
+            // says that this should essentially reset some values.
+            commandByte.convertScanCodes = 1;
+            commandByte.disableMouse = 1;
+            commandByte.disableKeyboard = 1;
+            commandByte.passedSelfTest = 1;
+            statusReg.passedSelfTest = 1;
+            writeData(0x55); // Self test passed.
+            break;
           case InterfaceTest:
-            panic("i8042 \"Interface test\" command not implemented.\n");
+            // Hard code no errors detected.
+            writeData(0x00);
+            break;
           case DiagnosticDump:
             panic("i8042 \"Diagnostic dump\" command not implemented.\n");
           case DisableKeyboard:
diff --git a/src/dev/x86/i8042.hh b/src/dev/x86/i8042.hh
index 4ab86ac..d6e464b 100644
--- a/src/dev/x86/i8042.hh
+++ b/src/dev/x86/i8042.hh
@@ -43,7 +43,7 @@
 namespace X86ISA
 {
 
-class I8042 : public BasicPioDevice
+class I8042 : public PioDevice
 {
   protected:
     enum Command
@@ -98,25 +98,25 @@
         Bitfield<0> keyboardFullInt;
     EndBitUnion(CommandByte)
 
-    Tick latency;
-    Addr dataPort;
-    Addr commandPort;
+    Tick latency = 0;
+    Addr dataPort = 0;
+    Addr commandPort = 0;
 
-    StatusReg statusReg;
-    CommandByte commandByte;
+    StatusReg statusReg = 0;
+    CommandByte commandByte = 0;
 
-    uint8_t dataReg;
+    uint8_t dataReg = 0;
 
-    static const uint16_t NoCommand = (uint16_t)(-1);
-    uint16_t lastCommand;
+    static inline const uint16_t NoCommand = (uint16_t)(-1);
+    uint16_t lastCommand = NoCommand;
 
     std::vector<IntSourcePin<I8042> *> mouseIntPin;
     std::vector<IntSourcePin<I8042> *> keyboardIntPin;
 
-    ps2::Device *mouse;
-    ps2::Device *keyboard;
+    ps2::Device *mouse = nullptr;
+    ps2::Device *keyboard = nullptr;
 
-    void writeData(uint8_t newData, bool mouse = false);
+    void writeData(uint8_t newData, bool mouse=false);
     uint8_t readDataOut();
 
   public:
@@ -132,7 +132,7 @@
         else if (if_name == "keyboard_int_pin")
             return *keyboardIntPin.at(idx);
         else
-            return BasicPioDevice::getPort(if_name, idx);
+            return PioDevice::getPort(if_name, idx);
     }
 
     AddrRangeList getAddrRanges() const override;
diff --git a/src/dev/x86/i82094aa.cc b/src/dev/x86/i82094aa.cc
index f5e51b0..437b462 100644
--- a/src/dev/x86/i82094aa.cc
+++ b/src/dev/x86/i82094aa.cc
@@ -43,8 +43,7 @@
 {
 
 X86ISA::I82094AA::I82094AA(const Params &p)
-    : BasicPioDevice(p, 20), extIntPic(p.external_int_pic),
-      lowestPriorityOffset(0),
+    : BasicPioDevice(p, 20), lowestPriorityOffset(0),
       intRequestPort(name() + ".int_request", this, this, p.int_latency)
 {
     // This assumes there's only one I/O APIC in the system and since the apic
@@ -179,7 +178,7 @@
 }
 
 void
-X86ISA::I82094AA::signalInterrupt(int line)
+X86ISA::I82094AA::requestInterrupt(int line)
 {
     DPRINTF(I82094AA, "Received interrupt %d.\n", line);
     assert(line < TableSize);
@@ -187,67 +186,83 @@
     if (entry.mask) {
         DPRINTF(I82094AA, "Entry was masked.\n");
         return;
+    }
+
+    TriggerIntMessage message = 0;
+
+    message.destination = entry.dest;
+    message.deliveryMode = entry.deliveryMode;
+    message.destMode = entry.destMode;
+    message.level = entry.polarity;
+    message.trigger = entry.trigger;
+
+    if (entry.deliveryMode == delivery_mode::ExtInt) {
+        // We need to ask the I8259 for the vector.
+        PacketPtr pkt = buildIntAcknowledgePacket();
+        auto on_completion = [this, message](PacketPtr pkt) {
+            auto msg_copy = message;
+            msg_copy.vector = pkt->getLE<uint8_t>();
+            signalInterrupt(msg_copy);
+            delete pkt;
+        };
+        intRequestPort.sendMessage(pkt, sys->isTimingMode(),
+                on_completion);
     } else {
-        TriggerIntMessage message = 0;
-        message.destination = entry.dest;
-        if (entry.deliveryMode == delivery_mode::ExtInt) {
-            assert(extIntPic);
-            message.vector = extIntPic->getVector();
-        } else {
-            message.vector = entry.vector;
+        message.vector = entry.vector;
+        signalInterrupt(message);
+    }
+}
+
+void
+X86ISA::I82094AA::signalInterrupt(TriggerIntMessage message)
+{
+    std::list<int> apics;
+    int numContexts = sys->threads.size();
+    if (message.destMode == 0) {
+        if (message.deliveryMode == delivery_mode::LowestPriority) {
+            panic("Lowest priority delivery mode from the "
+                    "IO APIC aren't supported in physical "
+                    "destination mode.\n");
         }
-        message.deliveryMode = entry.deliveryMode;
-        message.destMode = entry.destMode;
-        message.level = entry.polarity;
-        message.trigger = entry.trigger;
-        std::list<int> apics;
-        int numContexts = sys->threads.size();
-        if (message.destMode == 0) {
-            if (message.deliveryMode == delivery_mode::LowestPriority) {
-                panic("Lowest priority delivery mode from the "
-                        "IO APIC aren't supported in physical "
-                        "destination mode.\n");
-            }
-            if (message.destination == 0xFF) {
-                for (int i = 0; i < numContexts; i++) {
-                    apics.push_back(i);
-                }
-            } else {
-                apics.push_back(message.destination);
-            }
-        } else {
+        if (message.destination == 0xFF) {
             for (int i = 0; i < numContexts; i++) {
-                BaseInterrupts *base_int = sys->threads[i]->
-                    getCpuPtr()->getInterruptController(0);
-                auto *localApic = dynamic_cast<Interrupts *>(base_int);
-                if ((localApic->readReg(APIC_LOGICAL_DESTINATION) >> 24) &
-                        message.destination) {
-                    apics.push_back(localApic->getInitialApicId());
-                }
+                apics.push_back(i);
             }
-            if (message.deliveryMode == delivery_mode::LowestPriority &&
-                    apics.size()) {
-                // The manual seems to suggest that the chipset just does
-                // something reasonable for these instead of actually using
-                // state from the local APIC. We'll just rotate an offset
-                // through the set of APICs selected above.
-                uint64_t modOffset = lowestPriorityOffset % apics.size();
-                lowestPriorityOffset++;
-                auto apicIt = apics.begin();
-                while (modOffset--) {
-                    apicIt++;
-                    assert(apicIt != apics.end());
-                }
-                int selected = *apicIt;
-                apics.clear();
-                apics.push_back(selected);
+        } else {
+            apics.push_back(message.destination);
+        }
+    } else {
+        for (int i = 0; i < numContexts; i++) {
+            BaseInterrupts *base_int = sys->threads[i]->
+                getCpuPtr()->getInterruptController(0);
+            auto *localApic = dynamic_cast<Interrupts *>(base_int);
+            if ((localApic->readReg(APIC_LOGICAL_DESTINATION) >> 24) &
+                    message.destination) {
+                apics.push_back(localApic->getInitialApicId());
             }
         }
-        for (auto id: apics) {
-            PacketPtr pkt = buildIntTriggerPacket(id, message);
-            intRequestPort.sendMessage(pkt, sys->isTimingMode());
+        if (message.deliveryMode == delivery_mode::LowestPriority &&
+                apics.size()) {
+            // The manual seems to suggest that the chipset just does
+            // something reasonable for these instead of actually using
+            // state from the local APIC. We'll just rotate an offset
+            // through the set of APICs selected above.
+            uint64_t modOffset = lowestPriorityOffset % apics.size();
+            lowestPriorityOffset++;
+            auto apicIt = apics.begin();
+            while (modOffset--) {
+                apicIt++;
+                assert(apicIt != apics.end());
+            }
+            int selected = *apicIt;
+            apics.clear();
+            apics.push_back(selected);
         }
     }
+    for (auto id: apics) {
+        PacketPtr pkt = buildIntTriggerPacket(id, message);
+        intRequestPort.sendMessage(pkt, sys->isTimingMode());
+    }
 }
 
 void
@@ -255,7 +270,7 @@
 {
     assert(number < TableSize);
     if (!pinStates[number])
-        signalInterrupt(number);
+        requestInterrupt(number);
     pinStates[number] = true;
 }
 
diff --git a/src/dev/x86/i82094aa.hh b/src/dev/x86/i82094aa.hh
index 6427dbf..b50d122 100644
--- a/src/dev/x86/i82094aa.hh
+++ b/src/dev/x86/i82094aa.hh
@@ -31,6 +31,7 @@
 
 #include <map>
 
+#include "arch/x86/intmessage.hh"
 #include "base/bitunion.hh"
 #include "dev/x86/intdev.hh"
 #include "dev/intpin.hh"
@@ -43,7 +44,6 @@
 namespace X86ISA
 {
 
-class I8259;
 class Interrupts;
 
 class I82094AA : public BasicPioDevice
@@ -66,8 +66,6 @@
     EndBitUnion(RedirTableEntry)
 
   protected:
-    I8259 * extIntPic;
-
     uint8_t regSel;
     uint8_t initialApicId;
     uint8_t id;
@@ -87,6 +85,8 @@
 
     IntRequestPort<I82094AA> intRequestPort;
 
+    void signalInterrupt(TriggerIntMessage message);
+
   public:
     using Params = I82094AAParams;
 
@@ -105,7 +105,7 @@
 
     bool recvResponse(PacketPtr pkt);
 
-    void signalInterrupt(int line);
+    void requestInterrupt(int line);
     void raiseInterruptPin(int number);
     void lowerInterruptPin(int number);
 
diff --git a/src/dev/x86/i8237.cc b/src/dev/x86/i8237.cc
index 437a376..e14e911 100644
--- a/src/dev/x86/i8237.cc
+++ b/src/dev/x86/i8237.cc
@@ -99,31 +99,52 @@
     // Add the other registers individually.
     regs.addRegisters({
         statusCommandReg.
-            reader(readUnimpl("status register")).
-            writer(writeUnimpl("command register")),
+            reader([this](auto &reg) -> uint8_t { return statusVal; }).
+            writer([this](auto &reg, const uint8_t &value) {
+                        commandVal = value;
+                    }),
 
         requestReg.
-            writer(writeUnimpl("request register")),
+            writer(this, &I8237::setRequestBit),
 
         setMaskBitReg.
             writer(this, &I8237::setMaskBit),
 
         modeReg.
-            writer(writeUnimpl("mode register")),
+            writer([this](auto &reg, const uint8_t &value) {
+                        channels[bits(value, 1, 0)].mode = value;
+                    }),
 
         clearFlipFlopReg.
-            writer(writeUnimpl("clear LSB/MSB flip-flop register")),
+            writer([this](auto &reg, const uint8_t &value) {
+                        highByte = false;
+                    }),
 
         temporaryMasterClearReg.
-            reader(readUnimpl("temporary register")).
-            writer(writeUnimpl("master clear register")),
+            reader([this](auto &reg) ->uint8_t { return tempVal; }).
+            writer([this](auto &reg, const uint8_t &value) { reset(); }),
 
         clearMaskReg.
-            writer(writeUnimpl("clear mask register")),
+            writer([this](auto &reg, const uint8_t &value) { maskVal = 0x0; }),
 
         writeMaskReg.
-            writer(writeUnimpl("write all mask register bits"))
+            writer([this](auto &reg, const uint8_t &value) {
+                        maskVal = bits(value, 3, 0);
+                    })
     });
+
+    reset();
+}
+
+void
+I8237::reset()
+{
+    maskVal = 0xf;
+    requestVal = 0x0;
+    commandVal = 0x0;
+    statusVal = 0x0;
+    tempVal = 0x0;
+    highByte = false;
 }
 
 void
@@ -131,9 +152,17 @@
 {
     uint8_t select = bits(command, 1, 0);
     uint8_t bitVal = bits(command, 2);
-    if (!bitVal)
-        panic("Turning on i8237 channels unimplemented.");
-    replaceBits(maskReg, select, bitVal);
+    panic_if(!bitVal, "Turning on i8237 channels unimplemented.");
+    replaceBits(maskVal, select, bitVal);
+}
+
+void
+I8237::setRequestBit(Register &reg, const uint8_t &command)
+{
+    uint8_t select = bits(command, 1, 0);
+    uint8_t bitVal = bits(command, 2);
+    panic_if(bitVal, "Requesting i8237 DMA transfers is unimplemented.");
+    replaceBits(requestVal, select, bitVal);
 }
 
 Tick
@@ -155,13 +184,13 @@
 void
 I8237::serialize(CheckpointOut &cp) const
 {
-    SERIALIZE_SCALAR(maskReg);
+    paramOut(cp, "maskReg", maskVal);
 }
 
 void
 I8237::unserialize(CheckpointIn &cp)
 {
-    UNSERIALIZE_SCALAR(maskReg);
+    paramIn(cp, "maskReg", maskVal);
 }
 
 } // namespace X86ISA
diff --git a/src/dev/x86/i8237.hh b/src/dev/x86/i8237.hh
index 9fb9b89..b80af63 100644
--- a/src/dev/x86/i8237.hh
+++ b/src/dev/x86/i8237.hh
@@ -48,7 +48,13 @@
 
   protected:
     Tick latency;
-    uint8_t maskReg = 0;
+    uint8_t maskVal;
+    //XXX These should be serialized.
+    uint8_t requestVal;
+    uint8_t commandVal;
+    uint8_t statusVal;
+    uint8_t tempVal;
+    bool highByte;
 
     RegisterBankLE regs;
 
@@ -68,6 +74,9 @@
 
         int number;
 
+        //XXX These should be serialized.
+        uint8_t mode = 0x0;
+
         ChannelAddrReg addrReg;
         ChannelRemainingReg remainingReg;
 
@@ -92,7 +101,9 @@
     WriteOnlyReg clearMaskReg;
     WriteOnlyReg writeMaskReg;
 
+    void reset();
     void setMaskBit(Register &reg, const uint8_t &command);
+    void setRequestBit(Register &reg, const uint8_t &command);
 
   public:
     using Params = I8237Params;
diff --git a/src/dev/x86/i8259.cc b/src/dev/x86/i8259.cc
index 5cae5b8..9596eee 100644
--- a/src/dev/x86/i8259.cc
+++ b/src/dev/x86/i8259.cc
@@ -28,6 +28,7 @@
 
 #include "dev/x86/i8259.hh"
 
+#include "arch/x86/x86_traits.hh"
 #include "base/bitfield.hh"
 #include "base/trace.hh"
 #include "debug/I8259.hh"
@@ -38,12 +39,8 @@
 namespace gem5
 {
 
-X86ISA::I8259::I8259(const Params &p)
-    : BasicPioDevice(p, 2),
-      latency(p.pio_latency),
-      mode(p.mode), slave(p.slave),
-      IRR(0), ISR(0), IMR(0),
-      readIRR(true), initControlWord(0), autoEOI(false)
+X86ISA::I8259::I8259(const Params &p) : BasicPioDevice(p, 2),
+      latency(p.pio_latency), mode(p.mode), slave(p.slave)
 {
     for (int i = 0; i < p.port_output_connection_count; i++) {
         output.push_back(new IntSourcePin<I8259>(
@@ -51,15 +48,23 @@
     }
 
     int in_count = p.port_inputs_connection_count;
-    panic_if(in_count >= NumLines,
+    panic_if(in_count > NumLines,
             "I8259 only supports 8 inputs, but there are %d.", in_count);
     for (int i = 0; i < in_count; i++) {
         inputs.push_back(new IntSinkPin<I8259>(
                     csprintf("%s.inputs[%d]", name(), i), i, this));
     }
+}
 
-    for (bool &state: pinStates)
-        state = false;
+AddrRangeList
+X86ISA::I8259::getAddrRanges() const
+{
+    AddrRangeList ranges = BasicPioDevice::getAddrRanges();
+    if (mode == enums::I8259Master || mode == enums::I8259Single) {
+        // Listen for INTA messages.
+        ranges.push_back(RangeSize(PhysAddrIntA, 1));
+    }
+    return ranges;
 }
 
 void
@@ -75,8 +80,11 @@
 X86ISA::I8259::read(PacketPtr pkt)
 {
     assert(pkt->getSize() == 1);
-    switch(pkt->getAddr() - pioAddr)
-    {
+    if (pkt->getAddr() == PhysAddrIntA) {
+        assert(mode == enums::I8259Master || mode == enums::I8259Single);
+        pkt->setLE<uint8_t>(getVector());
+    }
+    switch(pkt->getAddr() - pioAddr) {
       case 0x0:
         if (readIRR) {
             DPRINTF(I8259, "Reading IRR as %#x.\n", IRR);
diff --git a/src/dev/x86/i8259.hh b/src/dev/x86/i8259.hh
index dcc5987..7bb319d 100644
--- a/src/dev/x86/i8259.hh
+++ b/src/dev/x86/i8259.hh
@@ -43,8 +43,8 @@
 class I8259 : public BasicPioDevice
 {
   protected:
-    static const int NumLines = 8;
-    bool pinStates[NumLines];
+    static const inline int NumLines = 8;
+    bool pinStates[NumLines] = {};
 
     void init() override;
 
@@ -52,37 +52,39 @@
     std::vector<IntSourcePin<I8259> *> output;
     std::vector<IntSinkPin<I8259> *> inputs;
     enums::X86I8259CascadeMode mode;
-    I8259 *slave;
+    I8259 *slave = nullptr;
 
     // Interrupt Request Register
-    uint8_t IRR;
+    uint8_t IRR = 0;
     // In Service Register
-    uint8_t ISR;
+    uint8_t ISR = 0;
     // Interrupt Mask Register
-    uint8_t IMR;
+    uint8_t IMR = 0;
 
     // The higher order bits of the vector to return
-    uint8_t vectorOffset;
+    uint8_t vectorOffset = 0;
 
-    bool cascadeMode;
+    bool cascadeMode = false;
     // A bit vector of lines with responders attached, or the
     // responder id, depending
     // on if this is a requestor or responder PIC.
-    uint8_t cascadeBits;
+    uint8_t cascadeBits = 0;
 
-    bool edgeTriggered;
-    bool readIRR;
+    bool edgeTriggered = true;
+    bool readIRR = true;
 
     // State machine information for reading in initialization control words.
-    bool expectICW4;
-    int initControlWord;
+    bool expectICW4 = false;
+    int initControlWord = 0;
 
     // Whether or not the PIC is in auto EOI mode.
-    bool autoEOI;
+    bool autoEOI = false;
 
     void requestInterrupt(int line);
     void handleEOI(int line);
 
+    int getVector();
+
   public:
     using Params = I8259Params;
 
@@ -99,6 +101,8 @@
             return BasicPioDevice::getPort(if_name, idx);
     }
 
+    AddrRangeList getAddrRanges() const override;
+
     Tick read(PacketPtr pkt) override;
     Tick write(PacketPtr pkt) override;
 
@@ -117,7 +121,6 @@
     void signalInterrupt(int line);
     void raiseInterruptPin(int number);
     void lowerInterruptPin(int number);
-    int getVector();
 
     void serialize(CheckpointOut &cp) const override;
     void unserialize(CheckpointIn &cp) override;
diff --git a/src/dev/x86/ide_ctrl.cc b/src/dev/x86/ide_ctrl.cc
new file mode 100644
index 0000000..4825e83
--- /dev/null
+++ b/src/dev/x86/ide_ctrl.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dev/x86/ide_ctrl.hh"
+
+namespace gem5
+{
+
+X86IdeController::X86IdeController(const Params &p) : IdeController(p)
+{
+    for (int i = 0; i < p.port_int_primary_connection_count; i++) {
+        intPrimary.push_back(new IntSourcePin<X86IdeController>(
+                    csprintf("%s.int_primary[%d]", name(), i), i, this));
+    }
+    for (int i = 0; i < p.port_int_secondary_connection_count; i++) {
+        intSecondary.push_back(new IntSourcePin<X86IdeController>(
+                    csprintf("%s.int_secondary[%d]", name(), i), i, this));
+    }
+}
+
+void
+X86IdeController::postInterrupt(bool is_primary)
+{
+    auto &pin = is_primary ? intPrimary : intSecondary;
+    for (auto *wire: pin)
+        wire->raise();
+}
+
+void
+X86IdeController::clearInterrupt(bool is_primary)
+{
+    auto &pin = is_primary ? intPrimary : intSecondary;
+    for (auto *wire: pin)
+        wire->lower();
+}
+
+Port &
+X86IdeController::getPort(const std::string &if_name, PortID idx)
+{
+    if (if_name == "int_primary")
+        return *intPrimary.at(idx);
+    else if (if_name == "int_secondary")
+        return *intSecondary.at(idx);
+    else
+        return IdeController::getPort(if_name, idx);
+}
+
+} // namespace gem5
diff --git a/src/dev/x86/ide_ctrl.hh b/src/dev/x86/ide_ctrl.hh
new file mode 100644
index 0000000..b46150a
--- /dev/null
+++ b/src/dev/x86/ide_ctrl.hh
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_X86_IDE_CTRL_HH__
+#define __DEV_X86_IDE_CTRL_HH__
+
+#include <vector>
+
+#include "dev/intpin.hh"
+#include "dev/storage/ide_ctrl.hh"
+#include "params/X86IdeController.hh"
+
+namespace gem5
+{
+
+class X86IdeController : public IdeController
+{
+  private:
+    std::vector<IntSourcePin<X86IdeController> *> intPrimary;
+    std::vector<IntSourcePin<X86IdeController> *> intSecondary;
+
+  public:
+    PARAMS(X86IdeController);
+    X86IdeController(const Params &p);
+
+    Port &getPort(const std::string &if_name,
+            PortID idx=InvalidPortID) override;
+
+    void postInterrupt(bool is_primary) override;
+    void clearInterrupt(bool is_primary) override;
+};
+
+} // namespace gem5
+
+#endif // __DEV_X86_IDE_CTRL_HH_
diff --git a/src/dev/x86/pc.cc b/src/dev/x86/pc.cc
index 0473aa5..e7f4636 100644
--- a/src/dev/x86/pc.cc
+++ b/src/dev/x86/pc.cc
@@ -68,6 +68,7 @@
      */
     X86ISA::I82094AA &ioApic = *southBridge->ioApic;
     X86ISA::I82094AA::RedirTableEntry entry = 0;
+    entry.mask = 1;
     entry.deliveryMode = X86ISA::delivery_mode::ExtInt;
     entry.vector = 0x20;
     ioApic.writeReg(0x10, entry.bottomDW);
@@ -76,7 +77,6 @@
     entry.vector = 0x24;
     ioApic.writeReg(0x18, entry.bottomDW);
     ioApic.writeReg(0x19, entry.topDW);
-    entry.mask = 1;
     entry.vector = 0x21;
     ioApic.writeReg(0x12, entry.bottomDW);
     ioApic.writeReg(0x13, entry.topDW);
@@ -107,7 +107,7 @@
 void
 Pc::postConsoleInt()
 {
-    southBridge->ioApic->signalInterrupt(4);
+    southBridge->ioApic->requestInterrupt(4);
     southBridge->pic1->signalInterrupt(4);
 }
 
@@ -121,7 +121,7 @@
 void
 Pc::postPciInt(int line)
 {
-    southBridge->ioApic->signalInterrupt(line);
+    southBridge->ioApic->requestInterrupt(line);
 }
 
 void
diff --git a/src/dev/x86/qemu_fw_cfg.cc b/src/dev/x86/qemu_fw_cfg.cc
new file mode 100644
index 0000000..40e768a
--- /dev/null
+++ b/src/dev/x86/qemu_fw_cfg.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dev/x86/qemu_fw_cfg.hh"
+
+#include "arch/x86/bios/e820.hh"
+#include "base/compiler.hh"
+#include "base/logging.hh"
+#include "sim/byteswap.hh"
+
+namespace gem5
+{
+
+namespace qemu
+{
+
+FwCfgItemE820::FwCfgItemE820(const QemuFwCfgItemE820Params &p) :
+    FwCfgItemFixed(p.path, p.arch_specific, p.index)
+{
+    struct GEM5_PACKED Entry
+    {
+        uint64_t addr;
+        uint64_t size;
+        uint32_t type;
+    };
+
+    uint32_t count = p.entries.size();
+
+    panic_if(count >= 128, "Too many E820 entries (%d).", count);
+
+    size_t bytes = count * sizeof(Entry);
+    data.resize(bytes);
+
+    uint8_t *ptr = data.data();
+
+    // Write out the e820 entries.
+    for (auto *e: p.entries) {
+        Entry entry{htole(e->addr), htole(e->size), htole(e->type)};
+        std::memcpy(ptr, &entry, sizeof(entry));
+        ptr += sizeof(entry);
+    }
+};
+
+} // namespace qemu
+} // namespace gem5
diff --git a/src/dev/x86/qemu_fw_cfg.hh b/src/dev/x86/qemu_fw_cfg.hh
new file mode 100644
index 0000000..18f2638
--- /dev/null
+++ b/src/dev/x86/qemu_fw_cfg.hh
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEV_X86_QEMU_FW_CFG_HH__
+#define __DEV_X86_QEMU_FW_CFG_HH__
+
+#include <vector>
+
+#include "dev/qemu/fw_cfg.hh"
+#include "params/QemuFwCfgItemE820.hh"
+
+namespace gem5
+{
+
+namespace qemu
+{
+
+// An item which holds the E820 table, precomputed for the firmware.
+class FwCfgItemE820 : public FwCfgItemFixed
+{
+  private:
+    std::vector<uint8_t> data;
+
+  public:
+    FwCfgItemE820(const QemuFwCfgItemE820Params &p);
+
+    const void *bytes() const override { return data.data(); }
+    uint64_t length() const override { return data.size(); }
+};
+
+} // namespace qemu
+} // namespace gem5
+
+#endif //__DEV_X86_QEMU_FW_CFG_HH__
diff --git a/src/gpu-compute/GPU.py b/src/gpu-compute/GPU.py
index 99e7cd5..a0154a7 100644
--- a/src/gpu-compute/GPU.py
+++ b/src/gpu-compute/GPU.py
@@ -37,6 +37,7 @@
 from m5.objects.Device import DmaVirtDevice
 from m5.objects.LdsState import LdsState
 from m5.objects.Process import EmulatedDriver
+from m5.objects.VegaGPUTLB import VegaPagetableWalker
 
 class PrefetchType(Enum): vals = [
     'PF_CU',
@@ -223,6 +224,7 @@
     CUs = VectorParam.ComputeUnit('Number of compute units')
     gpu_cmd_proc = Param.GPUCommandProcessor('Command processor for GPU')
     dispatcher = Param.GPUDispatcher('GPU workgroup dispatcher')
+    system_hub = Param.AMDGPUSystemHub(NULL, 'GPU System Hub (FS Mode only)')
     n_wf = Param.Int(10, 'Number of wavefront slots per SIMD')
     impl_kern_launch_acq = Param.Bool(True, """Insert acq packet into
                                          ruby at kernel launch""")
@@ -272,6 +274,8 @@
     dispatcher = Param.GPUDispatcher('workgroup dispatcher for the GPU')
 
     hsapp = Param.HSAPacketProcessor('PP attached to this device')
+    walker = Param.VegaPagetableWalker(VegaPagetableWalker(),
+            "Page table walker")
 
 class StorageClassType(Enum): vals = [
     'SC_SPILL',
diff --git a/src/gpu-compute/SConscript b/src/gpu-compute/SConscript
index c6404bc..81f02d8 100644
--- a/src/gpu-compute/SConscript
+++ b/src/gpu-compute/SConscript
@@ -31,7 +31,7 @@
 
 Import('*')
 
-if not env['BUILD_GPU']:
+if not env['CONF']['BUILD_GPU']:
     Return()
 
 SimObject('GPU.py', sim_objects=[
diff --git a/src/gpu-compute/SConsopts b/src/gpu-compute/SConsopts
index 1737c13..251ac5d 100644
--- a/src/gpu-compute/SConsopts
+++ b/src/gpu-compute/SConsopts
@@ -27,5 +27,3 @@
 
 sticky_vars.Add(BoolVariable('BUILD_GPU', 'Build the compute-GPU model',
                              False))
-
-export_vars.append('BUILD_GPU')
diff --git a/src/gpu-compute/compute_unit.cc b/src/gpu-compute/compute_unit.cc
index 0273ae9..1b20530 100644
--- a/src/gpu-compute/compute_unit.cc
+++ b/src/gpu-compute/compute_unit.cc
@@ -33,6 +33,7 @@
 
 #include <limits>
 
+#include "arch/amdgpu/common/gpu_translation_state.hh"
 #include "arch/amdgpu/common/tlb.hh"
 #include "base/output.hh"
 #include "debug/GPUDisp.hh"
@@ -111,6 +112,12 @@
     scheduleToExecute(p),
     stats(this, p.n_wf)
 {
+    // This is not currently supported and would require adding more handling
+    // for system vs. device memory requests on the functional paths, so we
+    // fatal immediately in the constructor if this configuration is seen.
+    fatal_if(functionalTLB && FullSystem,
+             "Functional TLB not supported in full-system GPU simulation");
+
     /**
      * This check is necessary because std::bitset only provides conversion
      * to unsigned long or unsigned long long via to_ulong() or to_ullong().
@@ -800,6 +807,12 @@
 bool
 ComputeUnit::DataPort::recvTimingResp(PacketPtr pkt)
 {
+    return handleResponse(pkt);
+}
+
+bool
+ComputeUnit::DataPort::handleResponse(PacketPtr pkt)
+{
     // Ruby has completed the memory op. Schedule the mem_resp_event at the
     // appropriate cycle to process the timing memory response
     // This delay represents the pipeline delay
@@ -901,6 +914,12 @@
 bool
 ComputeUnit::ScalarDataPort::recvTimingResp(PacketPtr pkt)
 {
+    return handleResponse(pkt);
+}
+
+bool
+ComputeUnit::ScalarDataPort::handleResponse(PacketPtr pkt)
+{
     assert(!pkt->req->isKernel());
 
     // retrieve sender state
@@ -978,11 +997,18 @@
 bool
 ComputeUnit::SQCPort::recvTimingResp(PacketPtr pkt)
 {
-    computeUnit->fetchStage.processFetchReturn(pkt);
+    computeUnit->handleSQCReturn(pkt);
+
     return true;
 }
 
 void
+ComputeUnit::handleSQCReturn(PacketPtr pkt)
+{
+    fetchStage.processFetchReturn(pkt);
+}
+
+void
 ComputeUnit::SQCPort::recvReqRetry()
 {
     int len = retries.size();
@@ -1025,13 +1051,18 @@
     // only do some things if actually accessing data
     bool isDataAccess = pkt->isWrite() || pkt->isRead();
 
-    // For dGPUs, real hardware will extract MTYPE from the PTE.  Our model
+    // For dGPUs, real hardware will extract MTYPE from the PTE. SE mode
     // uses x86 pagetables which don't have fields to track GPU MTYPEs.
     // Rather than hacking up the pagetable to add these bits in, we just
     // keep a structure local to our GPUs that are populated in our
     // emulated driver whenever memory is allocated.  Consult that structure
     // here in case we need a memtype override.
-    shader->gpuCmdProc.driver()->setMtype(pkt->req);
+    //
+    // In full system mode these can be extracted from the PTE and assigned
+    // after address translation takes place.
+    if (!FullSystem) {
+        shader->gpuCmdProc.driver()->setMtype(pkt->req);
+    }
 
     // Check write before read for atomic operations
     // since atomic operations should use BaseMMU::Write
@@ -1049,7 +1080,7 @@
     PortID tlbPort_index = perLaneTLB ? index : 0;
 
     if (shader->timingSim) {
-        if (debugSegFault) {
+        if (!FullSystem && debugSegFault) {
             Process *p = shader->gpuTc->getProcessPtr();
             Addr vaddr = pkt->req->getVaddr();
             unsigned size = pkt->getSize();
@@ -1233,9 +1264,13 @@
     assert(gpuDynInst->isGlobalSeg() ||
            gpuDynInst->executedAs() == enums::SC_GLOBAL);
 
+    // Fences will never be issued to system memory, so we can mark the
+    // requestor as a device memory ID here.
     if (!req) {
         req = std::make_shared<Request>(
-            0, 0, 0, requestorId(), 0, gpuDynInst->wfDynId);
+            0, 0, 0, vramRequestorId(), 0, gpuDynInst->wfDynId);
+    } else {
+        req->requestorId(vramRequestorId());
     }
 
     // all mem sync requests have Paddr == 0
@@ -1327,8 +1362,10 @@
     // The status vector and global memory response for WriteResp packets get
     // handled by the WriteCompleteResp packets.
     if (pkt->cmd == MemCmd::WriteResp) {
-        delete pkt;
-        return;
+        if (!FullSystem || !pkt->req->systemReq()) {
+            delete pkt;
+            return;
+        }
     }
 
     // this is for read, write and atomic
@@ -1536,6 +1573,24 @@
             new ComputeUnit::DataPort::SenderState(gpuDynInst, mp_index,
                                                    nullptr);
 
+    // Set VRAM ID for device requests
+    // For now, system vmem requests use functional reads. This is not that
+    // critical to model as the region of interest should always be accessing
+    // device memory. System vmem requests are used by blit kernels to do
+    // memcpys and load code objects into device memory.
+    if (new_pkt->req->systemReq()) {
+        // There will be multiple packets returned for the same gpuDynInst,
+        // so first check if systemReq is not already set and if so, return
+        // the token acquired when the dispatch list is filled as system
+        // requests do not require a GPU coalescer token.
+        if (!gpuDynInst->isSystemReq()) {
+            computeUnit->getTokenManager()->recvTokens(1);
+            gpuDynInst->setSystemReq();
+        }
+    } else {
+        new_pkt->req->requestorId(computeUnit->vramRequestorId());
+    }
+
     // translation is done. Schedule the mem_req_event at the appropriate
     // cycle to send the timing memory request to ruby
     EventFunctionWrapper *mem_req_event =
@@ -1574,7 +1629,11 @@
     GPUDynInstPtr gpuDynInst = sender_state->_gpuDynInst;
     [[maybe_unused]] ComputeUnit *compute_unit = computeUnit;
 
-    if (!(sendTimingReq(pkt))) {
+    if (pkt->req->systemReq()) {
+        assert(compute_unit->shader->systemHub);
+        SystemHubEvent *resp_event = new SystemHubEvent(pkt, this);
+        compute_unit->shader->systemHub->sendRequest(pkt, resp_event);
+    } else if (!(sendTimingReq(pkt))) {
         retries.push_back(std::make_pair(pkt, gpuDynInst));
 
         DPRINTF(GPUPort,
@@ -1603,7 +1662,11 @@
     GPUDynInstPtr gpuDynInst = sender_state->_gpuDynInst;
     [[maybe_unused]] ComputeUnit *compute_unit = scalarDataPort.computeUnit;
 
-    if (!(scalarDataPort.sendTimingReq(pkt))) {
+    if (pkt->req->systemReq()) {
+        assert(compute_unit->shader->systemHub);
+        SystemHubEvent *resp_event = new SystemHubEvent(pkt, &scalarDataPort);
+        compute_unit->shader->systemHub->sendRequest(pkt, resp_event);
+    } else if (!(scalarDataPort.sendTimingReq(pkt))) {
         scalarDataPort.retries.push_back(pkt);
 
         DPRINTF(GPUPort,
@@ -1704,15 +1767,26 @@
     req_pkt->senderState =
         new ComputeUnit::ScalarDataPort::SenderState(gpuDynInst);
 
-    if (!computeUnit->scalarDataPort.sendTimingReq(req_pkt)) {
-        computeUnit->scalarDataPort.retries.push_back(req_pkt);
-        DPRINTF(GPUMem, "send scalar req failed for: %s\n",
-                gpuDynInst->disassemble());
+    // For a system request we want to mark the GPU instruction as a system
+    // load/store so that after the request is issued to system memory we can
+    // return any token acquired for the request. Since tokens are returned
+    // by the coalescer and system requests do not take that path, this needs
+    // to be tracked.
+    //
+    // Device requests change the requestor ID to something in the device
+    // memory Ruby network.
+    if (req_pkt->req->systemReq()) {
+        gpuDynInst->setSystemReq();
     } else {
-        DPRINTF(GPUMem, "send scalar req for: %s\n",
-                gpuDynInst->disassemble());
+        req_pkt->req->requestorId(computeUnit->vramRequestorId());
     }
 
+    ComputeUnit::ScalarDataPort::MemReqEvent *scalar_mem_req_event
+            = new ComputeUnit::ScalarDataPort::MemReqEvent
+                (computeUnit->scalarDataPort, req_pkt);
+    computeUnit->schedule(scalar_mem_req_event, curTick() +
+                          computeUnit->req_tick_latency);
+
     return true;
 }
 
@@ -2009,6 +2083,15 @@
 }
 
 /**
+ * Forward the VRAM requestor ID needed for device memory from shader.
+ */
+RequestorID
+ComputeUnit::vramRequestorId()
+{
+    return FullSystem ? shader->vramRequestorId() : requestorId();
+}
+
+/**
  * get the result of packets sent to the LDS when they return
  */
 bool
diff --git a/src/gpu-compute/compute_unit.hh b/src/gpu-compute/compute_unit.hh
index 10fd2f9..a080e3d 100644
--- a/src/gpu-compute/compute_unit.hh
+++ b/src/gpu-compute/compute_unit.hh
@@ -458,10 +458,13 @@
     void updatePageDivergenceDist(Addr addr);
 
     RequestorID requestorId() { return _requestorId; }
+    RequestorID vramRequestorId();
 
     bool isDone() const;
     bool isVectorAluIdle(uint32_t simdId) const;
 
+    void handleSQCReturn(PacketPtr pkt);
+
   protected:
     RequestorID _requestorId;
 
@@ -526,6 +529,28 @@
                   saved(sender_state) { }
         };
 
+        class SystemHubEvent : public Event
+        {
+          DataPort *dataPort;
+          PacketPtr reqPkt;
+
+          public:
+            SystemHubEvent(PacketPtr pkt, DataPort *_dataPort)
+                : dataPort(_dataPort), reqPkt(pkt)
+            {
+                setFlags(Event::AutoDelete);
+            }
+
+            void
+            process()
+            {
+                // DMAs do not operate on packets and therefore do not
+                // convert to a response. Do that here instead.
+                reqPkt->makeResponse();
+                dataPort->handleResponse(reqPkt);
+            }
+        };
+
         void processMemReqEvent(PacketPtr pkt);
         EventFunctionWrapper *createMemReqEvent(PacketPtr pkt);
 
@@ -534,6 +559,8 @@
 
         std::deque<std::pair<PacketPtr, GPUDynInstPtr>> retries;
 
+        bool handleResponse(PacketPtr pkt);
+
       protected:
         ComputeUnit *computeUnit;
 
@@ -593,6 +620,30 @@
             const char *description() const;
         };
 
+        class SystemHubEvent : public Event
+        {
+          ScalarDataPort *dataPort;
+          PacketPtr reqPkt;
+
+          public:
+            SystemHubEvent(PacketPtr pkt, ScalarDataPort *_dataPort)
+                : dataPort(_dataPort), reqPkt(pkt)
+            {
+                setFlags(Event::AutoDelete);
+            }
+
+            void
+            process()
+            {
+                // DMAs do not operate on packets and therefore do not
+                // convert to a response. Do that here instead.
+                reqPkt->makeResponse();
+                dataPort->handleResponse(reqPkt);
+            }
+        };
+
+        bool handleResponse(PacketPtr pkt);
+
         std::deque<PacketPtr> retries;
 
       private:
diff --git a/src/gpu-compute/dyn_pool_manager.cc b/src/gpu-compute/dyn_pool_manager.cc
index 62a39a9..3db5e7f 100644
--- a/src/gpu-compute/dyn_pool_manager.cc
+++ b/src/gpu-compute/dyn_pool_manager.cc
@@ -93,8 +93,24 @@
 DynPoolManager::canAllocate(uint32_t numRegions, uint32_t size)
 {
     uint32_t actualSize = minAllocatedElements(size);
-    DPRINTF(GPUVRF,"Can Allocate %d\n",actualSize);
-    return (_totRegSpaceAvailable >= actualSize);
+    uint32_t numAvailChunks = 0;
+    DPRINTF(GPUVRF, "Checking if we can allocate %d regions of size %d "
+                    "registers\n", numRegions, actualSize);
+    for (auto it : freeSpaceRecord) {
+        numAvailChunks += (it.second - it.first)/actualSize;
+    }
+
+    if (numAvailChunks >= numRegions) {
+        DPRINTF(GPUVRF, "Able to allocate %d regions of size %d; "
+                        "number of available regions: %d\n",
+                        numRegions, actualSize, numAvailChunks);
+        return true;
+    } else {
+        DPRINTF(GPUVRF, "Unable to allocate %d regions of size %d; "
+                        "number of available regions: %d\n",
+                        numRegions, actualSize, numAvailChunks);
+        return false;
+    }
 }
 
 uint32_t
@@ -105,7 +121,8 @@
     uint32_t actualSize = minAllocatedElements(size);
     auto it = freeSpaceRecord.begin();
     while (it != freeSpaceRecord.end()) {
-        if (it->second >= actualSize) {
+        uint32_t curChunkSize = it->second - it->first;
+        if (curChunkSize >= actualSize) {
             // assign the next block starting from here
             startIdx = it->first;
             _regionSize = actualSize;
@@ -115,14 +132,13 @@
             // This case sees if this chunk size is exactly equal to
             // the size of the requested chunk. If yes, then this can't
             // contribute to future requests and hence, should be removed
-            if (it->second == actualSize) {
+            if (curChunkSize == actualSize) {
                 it = freeSpaceRecord.erase(it);
                 // once entire freeSpaceRecord allocated, increment
                 // reservedSpaceRecord count
                 ++reservedSpaceRecord;
             } else {
                 it->first += actualSize;
-                it->second -= actualSize;
             }
             break;
         }
@@ -144,7 +160,32 @@
     // Current dynamic register allocation does not handle wraparound
     assert(firstIdx < lastIdx);
     _totRegSpaceAvailable += lastIdx-firstIdx;
-    freeSpaceRecord.push_back(std::make_pair(firstIdx,lastIdx-firstIdx));
+
+    // Consolidate with other regions. Need to check if firstIdx or lastIdx
+    // already exist
+    auto firstIt = std::find_if(
+            freeSpaceRecord.begin(),
+            freeSpaceRecord.end(),
+            [&](const std::pair<int, int>& element){
+                return element.second == firstIdx;} );
+
+    auto lastIt = std::find_if(
+            freeSpaceRecord.begin(),
+            freeSpaceRecord.end(),
+            [&](const std::pair<int, int>& element){
+                return element.first == lastIdx;} );
+
+    if (firstIt != freeSpaceRecord.end() && lastIt != freeSpaceRecord.end()) {
+        firstIt->second = lastIt->second;
+        freeSpaceRecord.erase(lastIt);
+    } else if (firstIt != freeSpaceRecord.end()) {
+        firstIt->second = lastIdx;
+    } else if (lastIt != freeSpaceRecord.end()) {
+        lastIt->first = firstIdx;
+    } else {
+        freeSpaceRecord.push_back(std::make_pair(firstIdx, lastIdx));
+    }
+
     // remove corresponding entry from reservedSpaceRecord too
     --reservedSpaceRecord;
 }
diff --git a/src/gpu-compute/fetch_unit.cc b/src/gpu-compute/fetch_unit.cc
index a5a0370..640e29b 100644
--- a/src/gpu-compute/fetch_unit.cc
+++ b/src/gpu-compute/fetch_unit.cc
@@ -31,6 +31,7 @@
 
 #include "gpu-compute/fetch_unit.hh"
 
+#include "arch/amdgpu/common/gpu_translation_state.hh"
 #include "arch/amdgpu/common/tlb.hh"
 #include "base/bitfield.hh"
 #include "debug/GPUFetch.hh"
@@ -205,6 +206,15 @@
 
         computeUnit.sqcTLBPort.sendFunctional(pkt);
 
+        /**
+         * For full system, if this is a device request we need to set the
+         * requestor ID of the packet to the GPU memory manager so it is routed
+         * through Ruby as a memory request and not a PIO request.
+         */
+        if (!pkt->req->systemReq()) {
+            pkt->req->requestorId(computeUnit.vramRequestorId());
+        }
+
         GpuTranslationState *sender_state =
              safe_cast<GpuTranslationState*>(pkt->senderState);
 
@@ -249,6 +259,15 @@
     }
 
     /**
+     * For full system, if this is a device request we need to set the
+     * requestor ID of the packet to the GPU memory manager so it is routed
+     * through Ruby as a memory request and not a PIO request.
+     */
+    if (!pkt->req->systemReq()) {
+        pkt->req->requestorId(computeUnit.vramRequestorId());
+    }
+
+    /**
      * we should have reserved an entry in the fetch buffer
      * for this cache line. here we get the pointer to the
      * entry used to buffer this request's line data.
@@ -262,7 +281,11 @@
     if (timingSim) {
         // translation is done. Send the appropriate timing memory request.
 
-        if (!computeUnit.sqcPort.sendTimingReq(pkt)) {
+        if (pkt->req->systemReq()) {
+            SystemHubEvent *resp_event = new SystemHubEvent(pkt, this);
+            assert(computeUnit.shader->systemHub);
+            computeUnit.shader->systemHub->sendRequest(pkt, resp_event);
+        } else if (!computeUnit.sqcPort.sendTimingReq(pkt)) {
             computeUnit.sqcPort.retries.push_back(std::make_pair(pkt,
                                                                    wavefront));
 
@@ -642,4 +665,11 @@
     return bytes_remaining;
 }
 
+void
+FetchUnit::SystemHubEvent::process()
+{
+    reqPkt->makeResponse();
+    fetchUnit->computeUnit.handleSQCReturn(reqPkt);
+}
+
 } // namespace gem5
diff --git a/src/gpu-compute/fetch_unit.hh b/src/gpu-compute/fetch_unit.hh
index 6002665..0ba88c7 100644
--- a/src/gpu-compute/fetch_unit.hh
+++ b/src/gpu-compute/fetch_unit.hh
@@ -44,6 +44,7 @@
 #include "config/the_gpu_isa.hh"
 #include "gpu-compute/scheduler.hh"
 #include "mem/packet.hh"
+#include "sim/eventq.hh"
 
 namespace gem5
 {
@@ -238,6 +239,21 @@
         TheGpuISA::Decoder *_decoder;
     };
 
+    class SystemHubEvent : public Event
+    {
+      FetchUnit *fetchUnit;
+      PacketPtr reqPkt;
+
+      public:
+        SystemHubEvent(PacketPtr pkt, FetchUnit *fetch_unit)
+            : fetchUnit(fetch_unit), reqPkt(pkt)
+        {
+            setFlags(Event::AutoDelete);
+        }
+
+        void process();
+    };
+
     bool timingSim;
     ComputeUnit &computeUnit;
     TheGpuISA::Decoder decoder;
diff --git a/src/gpu-compute/global_memory_pipeline.cc b/src/gpu-compute/global_memory_pipeline.cc
index f766f7e..08303a4 100644
--- a/src/gpu-compute/global_memory_pipeline.cc
+++ b/src/gpu-compute/global_memory_pipeline.cc
@@ -62,6 +62,10 @@
 bool
 GlobalMemPipeline::coalescerReady(GPUDynInstPtr mp) const
 {
+    // System requests do not need GPU coalescer tokens. Make sure nothing
+    // has bypassed the operand gather check stage.
+    assert(!mp->isSystemReq());
+
     // We require one token from the coalescer's uncoalesced table to
     // proceed
     int token_count = 1;
diff --git a/src/gpu-compute/gpu_command_processor.cc b/src/gpu-compute/gpu_command_processor.cc
index 46a2004..d46ace6 100644
--- a/src/gpu-compute/gpu_command_processor.cc
+++ b/src/gpu-compute/gpu_command_processor.cc
@@ -33,10 +33,14 @@
 
 #include <cassert>
 
+#include "arch/amdgpu/vega/pagetable_walker.hh"
 #include "base/chunk_generator.hh"
 #include "debug/GPUCommandProc.hh"
 #include "debug/GPUKernelInfo.hh"
+#include "dev/amdgpu/amdgpu_device.hh"
 #include "gpu-compute/dispatcher.hh"
+#include "mem/abstract_mem.hh"
+#include "mem/packet_access.hh"
 #include "mem/se_translating_port_proxy.hh"
 #include "mem/translating_port_proxy.hh"
 #include "params/GPUCommandProcessor.hh"
@@ -50,7 +54,7 @@
 
 GPUCommandProcessor::GPUCommandProcessor(const Params &p)
     : DmaVirtDevice(p), dispatcher(*p.dispatcher), _driver(nullptr),
-      hsaPP(p.hsapp)
+      walker(p.walker), hsaPP(p.hsapp)
 {
     assert(hsaPP);
     hsaPP->setDevice(this);
@@ -63,15 +67,32 @@
     return *hsaPP;
 }
 
+/**
+ * Forward the VRAM requestor ID needed for device memory from GPU device.
+ */
+RequestorID
+GPUCommandProcessor::vramRequestorId()
+{
+    return gpuDevice->vramRequestorId();
+}
+
 TranslationGenPtr
 GPUCommandProcessor::translate(Addr vaddr, Addr size)
 {
-    // Grab the process and try to translate the virtual address with it; with
-    // new extensions, it will likely be wrong to just arbitrarily grab context
-    // zero.
-    auto process = sys->threads[0]->getProcessPtr();
+    if (!FullSystem) {
+        // Grab the process and try to translate the virtual address with it;
+        // with new extensions, it will likely be wrong to just arbitrarily
+        // grab context zero.
+        auto process = sys->threads[0]->getProcessPtr();
 
-    return process->pTable->translateRange(vaddr, size);
+        return process->pTable->translateRange(vaddr, size);
+    }
+
+    // In full system use the page tables setup by the kernel driver rather
+    // than the CPU page tables.
+    return TranslationGenPtr(
+        new AMDGPUVM::UserTranslationGen(&gpuDevice->getVM(), walker,
+                                         1 /* vmid */, vaddr, size));
 }
 
 /**
@@ -109,6 +130,27 @@
     PortProxy &virt_proxy = FullSystem ? fs_proxy : se_proxy;
 
     /**
+     * In full system mode, the page table entry may point to a system page
+     * or a device page. System pages use the proxy as normal, but a device
+     * page needs to be read from device memory. Check what type it is here.
+     */
+    bool is_system_page = true;
+    Addr phys_addr = disp_pkt->kernel_object;
+    if (FullSystem) {
+        /**
+         * Full system currently only supports running on single VMID (one
+         * virtual memory space), i.e., one application running on GPU at a
+         * time. Because of this, for now we know the VMID is always 1. Later
+         * the VMID would have to be passed on to the command processor.
+         */
+        int vmid = 1;
+        unsigned tmp_bytes;
+        walker->startFunctional(gpuDevice->getVM().getPageTableBase(vmid),
+                                phys_addr, tmp_bytes, BaseMMU::Mode::Read,
+                                is_system_page);
+    }
+
+    /**
      * The kernel_object is a pointer to the machine code, whose entry
      * point is an 'amd_kernel_code_t' type, which is included in the
      * kernel binary, and describes various aspects of the kernel. The
@@ -118,8 +160,28 @@
      * instructions.
      */
     AMDKernelCode akc;
-    virt_proxy.readBlob(disp_pkt->kernel_object, (uint8_t*)&akc,
-        sizeof(AMDKernelCode));
+    if (is_system_page) {
+        DPRINTF(GPUCommandProc, "kernel_object in system, using proxy\n");
+        virt_proxy.readBlob(disp_pkt->kernel_object, (uint8_t*)&akc,
+            sizeof(AMDKernelCode));
+    } else {
+        assert(FullSystem);
+        DPRINTF(GPUCommandProc, "kernel_object in device, using device mem\n");
+        // Read from GPU memory manager
+        uint8_t raw_akc[sizeof(AMDKernelCode)];
+        for (int i = 0; i < sizeof(AMDKernelCode) / sizeof(uint8_t); ++i) {
+            Addr mmhubAddr = phys_addr + i*sizeof(uint8_t);
+            Request::Flags flags = Request::PHYSICAL;
+            RequestPtr request = std::make_shared<Request>(
+                mmhubAddr, sizeof(uint8_t), flags, walker->getDevRequestor());
+            Packet *readPkt = new Packet(request, MemCmd::ReadReq);
+            readPkt->allocate();
+            system()->getDeviceMemory(readPkt)->access(readPkt);
+            raw_akc[i] = readPkt->getLE<uint8_t>();
+            delete readPkt;
+        }
+        memcpy(&akc, &raw_akc, sizeof(AMDKernelCode));
+    }
 
     DPRINTF(GPUCommandProc, "GPU machine code is %lli bytes from start of the "
         "kernel object\n", akc.kernel_code_entry_byte_offset);
@@ -213,7 +275,17 @@
 
         DPRINTF(GPUCommandProc, "Calling signal wakeup event on "
                 "signal event value %d\n", *event_val);
-        signalWakeupEvent(*event_val);
+
+        // The mailbox/wakeup signal uses the SE mode proxy port to write
+        // the event value. This is not available in full system mode so
+        // instead we need to issue a DMA write to the address. The value of
+        // *event_val clears the event.
+        if (FullSystem) {
+            auto cb = new DmaVirtCallback<uint64_t>(function, *event_val);
+            dmaWriteVirt(mailbox_addr, sizeof(Addr), cb, &cb->dmaBuffer, 0);
+        } else {
+            signalWakeupEvent(*event_val);
+        }
     }
 }
 
@@ -357,6 +429,13 @@
 }
 
 void
+GPUCommandProcessor::setGPUDevice(AMDGPUDevice *gpu_device)
+{
+    gpuDevice = gpu_device;
+    walker->setDevRequestor(gpuDevice->vramRequestorId());
+}
+
+void
 GPUCommandProcessor::setShader(Shader *shader)
 {
     _shader = shader;
diff --git a/src/gpu-compute/gpu_command_processor.hh b/src/gpu-compute/gpu_command_processor.hh
index 332f2e0..ba8b007 100644
--- a/src/gpu-compute/gpu_command_processor.hh
+++ b/src/gpu-compute/gpu_command_processor.hh
@@ -57,6 +57,7 @@
 #include "gpu-compute/gpu_compute_driver.hh"
 #include "gpu-compute/hsa_queue_entry.hh"
 #include "params/GPUCommandProcessor.hh"
+#include "sim/full_system.hh"
 
 namespace gem5
 {
@@ -76,7 +77,9 @@
     GPUCommandProcessor(const Params &p);
 
     HSAPacketProcessor& hsaPacketProc();
+    RequestorID vramRequestorId();
 
+    void setGPUDevice(AMDGPUDevice *gpu_device);
     void setShader(Shader *shader);
     Shader* shader();
     GPUComputeDriver* driver();
@@ -128,6 +131,8 @@
     Shader *_shader;
     GPUDispatcher &dispatcher;
     GPUComputeDriver *_driver;
+    AMDGPUDevice *gpuDevice;
+    VegaISA::Walker *walker;
 
     // Typedefing dmaRead and dmaWrite function pointer
     typedef void (DmaDevice::*DmaFnPtr)(Addr, int, Event*, uint8_t*, Tick);
@@ -215,6 +220,10 @@
                     task->amdQueue.compute_tmpring_size_wavesize * 1024,
                     task->privMemPerItem());
 
+            // Currently this is not supported in GPU full system
+            fatal_if(FullSystem,
+                     "Runtime dynamic scratch allocation not supported");
+
             updateHsaSignal(task->amdQueue.queue_inactive_signal.handle, 1,
                             [ = ] (const uint64_t &dma_buffer)
                                 { WaitScratchDmaEvent(task, dma_buffer); });
diff --git a/src/gpu-compute/gpu_compute_driver.cc b/src/gpu-compute/gpu_compute_driver.cc
index e908f4e..203d087 100644
--- a/src/gpu-compute/gpu_compute_driver.cc
+++ b/src/gpu-compute/gpu_compute_driver.cc
@@ -67,14 +67,18 @@
 
     // Convert the 3 bit mtype specified in Shader.py to the proper type
     // used for requests.
-    if (MtypeFlags::SHARED & p.m_type)
+    std::bitset<MtypeFlags::NUM_MTYPE_BITS> mtype(p.m_type);
+    if (mtype.test(MtypeFlags::SHARED)) {
         defaultMtype.set(Request::SHARED);
+    }
 
-    if (MtypeFlags::READ_WRITE & p.m_type)
+    if (mtype.test(MtypeFlags::READ_WRITE)) {
         defaultMtype.set(Request::READ_WRITE);
+    }
 
-    if (MtypeFlags::CACHED & p.m_type)
+    if (mtype.test(MtypeFlags::CACHED)) {
         defaultMtype.set(Request::CACHED);
+    }
 }
 
 const char*
@@ -331,6 +335,7 @@
                         ldsApeBase(i + 1);
                     break;
                   case GfxVersion::gfx900:
+                  case GfxVersion::gfx902:
                     args->process_apertures[i].scratch_base =
                         scratchApeBaseV9();
                     args->process_apertures[i].lds_base =
@@ -631,6 +636,7 @@
                     ape_args->lds_base = ldsApeBase(i + 1);
                     break;
                   case GfxVersion::gfx900:
+                  case GfxVersion::gfx902:
                     ape_args->scratch_base = scratchApeBaseV9();
                     ape_args->lds_base = ldsApeBaseV9();
                     break;
@@ -1017,6 +1023,7 @@
 {
     // If we are a dGPU then set the MTYPE from our VMAs.
     if (isdGPU) {
+        assert(!FullSystem);
         AddrRange range = RangeSize(req->getVaddr(), req->getSize());
         auto vma = gpuVmas.contains(range);
         assert(vma != gpuVmas.end());
diff --git a/src/gpu-compute/gpu_compute_driver.hh b/src/gpu-compute/gpu_compute_driver.hh
index 868ad1c..def40f4 100644
--- a/src/gpu-compute/gpu_compute_driver.hh
+++ b/src/gpu-compute/gpu_compute_driver.hh
@@ -168,7 +168,8 @@
     {
         SHARED                  = 0,
         READ_WRITE              = 1,
-        CACHED                  = 2
+        CACHED                  = 2,
+        NUM_MTYPE_BITS
     };
 
     Request::CacheCoherenceFlags defaultMtype;
diff --git a/src/gpu-compute/gpu_dyn_inst.hh b/src/gpu-compute/gpu_dyn_inst.hh
index 17c7ee6..e2884a0 100644
--- a/src/gpu-compute/gpu_dyn_inst.hh
+++ b/src/gpu-compute/gpu_dyn_inst.hh
@@ -476,11 +476,16 @@
 
     // inst used to save/restore a wavefront context
     bool isSaveRestore;
+
+    bool isSystemReq() { return systemReq; }
+    void setSystemReq() { systemReq = true; }
+
   private:
     GPUStaticInst *_staticInst;
     const InstSeqNum _seqNum;
     int maxSrcVecRegOpSize;
     int maxSrcScalarRegOpSize;
+    bool systemReq = false;
 
     // the time the request was started
     Tick accessTime = -1;
diff --git a/src/gpu-compute/shader.cc b/src/gpu-compute/shader.cc
index 5a8c939..73d2366 100644
--- a/src/gpu-compute/shader.cc
+++ b/src/gpu-compute/shader.cc
@@ -33,6 +33,7 @@
 
 #include <limits>
 
+#include "arch/amdgpu/common/gpu_translation_state.hh"
 #include "arch/amdgpu/common/tlb.hh"
 #include "base/chunk_generator.hh"
 #include "debug/GPUAgentDisp.hh"
@@ -64,7 +65,7 @@
     trace_vgpr_all(1), n_cu((p.CUs).size()), n_wf(p.n_wf),
     globalMemSize(p.globalmem),
     nextSchedCu(0), sa_n(0), gpuCmdProc(*p.gpu_cmd_proc),
-    _dispatcher(*p.dispatcher),
+    _dispatcher(*p.dispatcher), systemHub(p.system_hub),
     max_valu_insts(p.max_valu_insts), total_valu_insts(0),
     stats(this, p.CUs[0]->wfSize())
 {
@@ -522,6 +523,15 @@
         stats.shaderActiveTicks += curTick() - _lastInactiveTick;
 }
 
+/**
+ * Forward the VRAM requestor ID needed for device memory from CP.
+ */
+RequestorID
+Shader::vramRequestorId()
+{
+    return gpuCmdProc.vramRequestorId();
+}
+
 Shader::ShaderStats::ShaderStats(statistics::Group *parent, int wf_size)
     : statistics::Group(parent),
       ADD_STAT(allLatencyDist, "delay distribution for all"),
diff --git a/src/gpu-compute/shader.hh b/src/gpu-compute/shader.hh
index 0ea8741..0978acb 100644
--- a/src/gpu-compute/shader.hh
+++ b/src/gpu-compute/shader.hh
@@ -44,6 +44,7 @@
 #include "cpu/simple_thread.hh"
 #include "cpu/thread_context.hh"
 #include "cpu/thread_state.hh"
+#include "dev/amdgpu/system_hub.hh"
 #include "gpu-compute/compute_unit.hh"
 #include "gpu-compute/gpu_dyn_inst.hh"
 #include "gpu-compute/hsa_queue_entry.hh"
@@ -183,6 +184,8 @@
         shHiddenPrivateBaseVmid = sh_hidden_base_new;
     }
 
+    RequestorID vramRequestorId();
+
     EventFunctionWrapper tickEvent;
 
     // is this simulation going to be timing mode in the memory?
@@ -223,6 +226,7 @@
 
     GPUCommandProcessor &gpuCmdProc;
     GPUDispatcher &_dispatcher;
+    AMDGPUSystemHub *systemHub;
 
     int64_t max_valu_insts;
     int64_t total_valu_insts;
diff --git a/src/gpu-compute/vector_register_file.cc b/src/gpu-compute/vector_register_file.cc
index 9ab1cfd..b5f17c8 100644
--- a/src/gpu-compute/vector_register_file.cc
+++ b/src/gpu-compute/vector_register_file.cc
@@ -48,7 +48,7 @@
 VectorRegisterFile::VectorRegisterFile(const VectorRegisterFileParams &p)
     : RegisterFile(p)
 {
-    regFile.resize(numRegs(), VecRegContainer());
+    regFile.resize(numRegs());
 
     for (auto &reg : regFile) {
         reg.zero();
diff --git a/src/kern/SConscript b/src/kern/SConscript
index 3d5872b..7ccff2a 100644
--- a/src/kern/SConscript
+++ b/src/kern/SConscript
@@ -28,7 +28,7 @@
 
 Import('*')
 
-if env['TARGET_ISA'] == 'null':
+if env['CONF']['TARGET_ISA'] == 'null':
     Return()
 
 Source('linux/events.cc')
diff --git a/src/kern/linux/helpers.cc b/src/kern/linux/helpers.cc
index 3c648f1..b024226 100644
--- a/src/kern/linux/helpers.cc
+++ b/src/kern/linux/helpers.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 ARM Limited
+ * Copyright (c) 2016, 2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -37,16 +37,25 @@
 
 #include "kern/linux/helpers.hh"
 
+#include <type_traits>
+
 #include "base/compiler.hh"
+#include "base/loader/object_file.hh"
 #include "cpu/thread_context.hh"
 #include "mem/port_proxy.hh"
 #include "mem/translating_port_proxy.hh"
 #include "sim/byteswap.hh"
 #include "sim/system.hh"
 
-namespace gem5
-{
+namespace gem5 {
 
+namespace linux {
+
+namespace {
+
+namespace pre5_10 {
+
+/** Dmesg entry for Linux versions pre-v5.10 */
 struct GEM5_PACKED DmesgEntry
 {
     uint64_t ts_nsec;
@@ -57,8 +66,10 @@
     uint8_t flags;
 };
 
+/** Dump a Linux Demsg entry, pre-v5.10. */
 static int
-dumpDmesgEntry(const uint8_t *base, const uint8_t *end, const ByteOrder bo,
+dumpDmesgEntry(const uint8_t *base, const uint8_t *end,
+               const ByteOrder bo,
                std::ostream &os)
 {
     const size_t max_length = end - base;
@@ -92,8 +103,9 @@
     return de.len;
 }
 
+/** Dump the kernel Dmesg ringbuffer for Linux versions pre-v5.10 */
 void
-linux::dumpDmesg(ThreadContext *tc, std::ostream &os)
+dumpDmesg(ThreadContext *tc, std::ostream &os)
 {
     System *system = tc->getSystemPtr();
     const ByteOrder bo = system->getGuestByteOrder();
@@ -153,4 +165,480 @@
     }
 }
 
+} // namespace pre5_10
+
+namespace post5_10 {
+
+/** Metadata record for the Linux dmesg ringbuffer, post-v5.10.
+ *
+ *  Struct data members are compatible with the equivalent Linux data
+ *  structure. Should be templated on atomic_var_t=int32_t for 32-bit
+ *  Linux and atomic_var_t=int64_t for 64-bit Linux.
+ *
+ *  Also includes helper methods for reading the data structure into
+ *  the gem5 world.
+ *
+ */
+template<typename atomic_var_t>
+struct GEM5_PACKED DmesgMetadataRecord
+{
+    using guest_ptr_t = typename std::make_unsigned_t<atomic_var_t>;
+
+    // Struct data members
+    atomic_var_t state;
+    struct
+    {
+        guest_ptr_t curr_offset;
+        guest_ptr_t next_offset;
+    } data_buffer;
+
+    /** Read a DmesgMetadataRecord from guest memory. */
+    static DmesgMetadataRecord
+    read(const TranslatingPortProxy & proxy,
+         Addr address,
+         guest_ptr_t data_offset_mask,
+         const ByteOrder & bo)
+    {
+        DmesgMetadataRecord metadata;
+        proxy.readBlob(address, &metadata, sizeof(metadata));
+
+        // Convert members to host byte order
+        metadata.state = gtoh(metadata.state, bo);
+        metadata.data_buffer.curr_offset =
+            gtoh(metadata.data_buffer.curr_offset, bo);
+        metadata.data_buffer.next_offset =
+            gtoh(metadata.data_buffer.next_offset, bo);
+
+        // Mask the offsets
+        metadata.data_buffer.curr_offset =
+            metadata.data_buffer.curr_offset & data_offset_mask;
+        metadata.data_buffer.next_offset =
+            metadata.data_buffer.next_offset & data_offset_mask;
+
+        return metadata;
+    }
+};
+
+/** Info record for the Linux dmesg ringbuffer, post-v5.10.
+ *
+ *  Struct data members are compatible with the equivalent Linux data
+ *  structure. Should be templated on atomic_var_t=int32_t for 32-bit
+ *  Linux and atomic_var_t=int64_t for 64-bit Linux.
+ *
+ *  Also includes helper methods for reading the data structure into
+ *  the gem5 world.
+ *
+ */
+struct GEM5_PACKED DmesgInfoRecord
+{
+    // Struct data members
+    uint64_t unused1;
+    uint64_t ts_nsec;
+    uint16_t message_size;
+    uint8_t unused2;
+    uint8_t unused3;
+    uint32_t unused4;
+    struct
+    {
+        char unused5_1[16];
+        char unused5_2[48];
+    } unused5;
+
+    /** Read a DmesgInfoRecord from guest memory. */
+    static DmesgInfoRecord
+    read(const TranslatingPortProxy & proxy,
+         Addr address,
+         const ByteOrder & bo)
+    {
+        DmesgInfoRecord info;
+        proxy.readBlob(address, &info, sizeof(info));
+
+        // Convert members to host byte order
+        info.ts_nsec = gtoh(info.ts_nsec, bo);
+        info.message_size = gtoh(info.message_size, bo);
+
+        return info;
+    }
+};
+
+/** Top-level ringbuffer record for the Linux dmesg ringbuffer, post-v5.10.
+ *
+ *  Struct data members are compatible with the equivalent Linux data
+ *  structure. Should be templated on AtomicVarType=int32_t for 32-bit
+ *  Linux and AtomicVarType=int64_t for 64-bit Linux.
+ *
+ *  Also includes helper methods for reading the data structure into
+ *  the gem5 world, and reading/generating appropriate masks.
+ *
+ */
+template<typename AtomicVarType>
+struct GEM5_PACKED DmesgRingbuffer
+{
+    static_assert(
+        std::disjunction<
+            std::is_same<AtomicVarType, int32_t>,
+            std::is_same<AtomicVarType, int64_t>
+        >::value,
+        "AtomicVarType must be int32_t or int64_t");
+
+    using atomic_var_t = AtomicVarType;
+    using guest_ptr_t = typename std::make_unsigned_t<atomic_var_t>;
+    using metadata_record_t = DmesgMetadataRecord<atomic_var_t>;
+
+    // Struct data members
+    struct
+    {
+        unsigned int mask_bits;
+        guest_ptr_t metadata_ring_ptr;
+        guest_ptr_t info_ring_ptr;
+        atomic_var_t unused1;
+        atomic_var_t unused2;
+    } metadata;
+    struct
+    {
+        unsigned int mask_bits;
+        guest_ptr_t data_ring_ptr;
+        atomic_var_t head_offset;
+        atomic_var_t tail_offset;
+    } data;
+    atomic_var_t fail;
+
+    /** Read a DmesgRingbuffer from guest memory. */
+    static DmesgRingbuffer
+    read(const TranslatingPortProxy & proxy,
+         const Addr address,
+         const ByteOrder & bo)
+    {
+        DmesgRingbuffer rb;
+        proxy.readBlob(address, &rb, sizeof(rb));
+
+        // Convert members to host byte order
+        rb.metadata.mask_bits =
+            gtoh(rb.metadata.mask_bits, bo);
+        rb.metadata.metadata_ring_ptr =
+            gtoh(rb.metadata.metadata_ring_ptr, bo);
+        rb.metadata.info_ring_ptr =
+            gtoh(rb.metadata.info_ring_ptr, bo);
+
+        rb.data.mask_bits = gtoh(rb.data.mask_bits, bo);
+        rb.data.data_ring_ptr = gtoh(rb.data.data_ring_ptr, bo);
+        rb.data.head_offset = gtoh(rb.data.head_offset, bo);
+        rb.data.tail_offset = gtoh(rb.data.tail_offset, bo);
+
+        // Mask offsets to the correct number of bits
+        rb.data.head_offset =
+            rb.mask_data_offset(rb.data.head_offset);
+        rb.data.tail_offset =
+            rb.mask_data_offset(rb.data.tail_offset);
+
+        return rb;
+    }
+
+    /** Make a mask for the bottom mask_bits of an `atomic_var_t`, then
+     *  cast it to the required `as_type`.
+     */
+    template<typename as_type>
+    static as_type
+    make_offset_mask_as(const unsigned int mask_bits)
+    {
+        using unsigned_atomic_var_t =
+            typename std::make_unsigned<atomic_var_t>::type;
+        const atomic_var_t offset_mask =
+            static_cast<atomic_var_t>(
+                (static_cast<unsigned_atomic_var_t>(1) << mask_bits) - 1);
+        return static_cast<as_type>(offset_mask);
+    }
+
+    /** Make a mask for an offset into the metadata or info ringbuffers. */
+    template<typename metadata_offset_t>
+    metadata_offset_t
+    make_metadata_offset_mask() const
+    {
+        return make_offset_mask_as<metadata_offset_t>(metadata.mask_bits);
+    }
+
+    /** Make a mask for an offset into the data ringbuffer. */
+    template<typename data_offset_t>
+    data_offset_t
+    make_data_offset_mask() const
+    {
+        return make_offset_mask_as<data_offset_t>(data.mask_bits);
+    }
+
+    /** Apply the correct masking to an offset into the metadata or info
+        ringbuffers. */
+    template<typename metadata_offset_t>
+    metadata_offset_t
+    mask_metadata_offset(const metadata_offset_t metadata_offset) const
+    {
+        const atomic_var_t MASK =
+            make_metadata_offset_mask<metadata_offset_t>();
+        return metadata_offset & MASK;
+    }
+
+    /** Apply the correct masking to an offset into the data ringbuffer. */
+    template<typename data_offset_t>
+    data_offset_t
+    mask_data_offset(const data_offset_t data_offset) const
+    {
+        const atomic_var_t MASK = make_data_offset_mask<data_offset_t>();
+        return data_offset & MASK;
+    }
+};
+
+// Aliases for the two types of Ringbuffer that could be used.
+using Linux64_Ringbuffer = DmesgRingbuffer<int64_t>;
+using Linux32_Ringbuffer = DmesgRingbuffer<int32_t>;
+
+/** Print the record at the specified offset into the data ringbuffer,
+ *  and return the offset of the next entry in the data ringbuffer,
+ *  post-v5.10.
+ *
+ * The `first_metadata_offset` argument is used to check for
+ * wraparound. If the final data record of the ringbuffer is not large
+ * enough to hold the message, the record will be left empty and
+ * repeated at the beginning of the data ringbuffer. In this case the
+ * metadata offset at the beginning of the last record will match the
+ * metadata offset of the first record of the ringbuffer, and the last
+ * record of the ring buffer should be skipped.
+ *
+ */
+template <typename ringbuffer_t,
+          typename atomic_var_t=typename ringbuffer_t::atomic_var_t,
+          typename guest_ptr_t=typename ringbuffer_t::guest_ptr_t>
+atomic_var_t
+iterateDataRingbuffer(std::ostream & os,
+                      const TranslatingPortProxy & proxy,
+                      const ringbuffer_t & rb,
+                      const atomic_var_t offset,
+                      const guest_ptr_t first_metadata_offset,
+                      const ByteOrder bo)
+{
+    using metadata_record_t = typename ringbuffer_t::metadata_record_t;
+
+    constexpr size_t METADATA_RECORD_SIZE =
+        sizeof(typename ringbuffer_t::metadata_record_t);
+    constexpr size_t INFO_RECORD_SIZE = sizeof(DmesgInfoRecord);
+
+    const guest_ptr_t DATA_OFFSET_MASK =
+        rb.template make_data_offset_mask<guest_ptr_t>();
+
+    // Read the offset of the metadata record from the beginning of
+    // the data record.
+    guest_ptr_t metadata_info_offset = rb.mask_metadata_offset(
+        proxy.read<guest_ptr_t>(rb.data.data_ring_ptr + offset, bo));
+
+    // If the metadata offset of the block is the same as the metadata
+    // offset of the first block of the data ringbuffer, then this
+    // data block is unsused (padding), and the iteration can wrap
+    // around to the beginning of the data ringbuffer (offset == 0).
+    if (metadata_info_offset == first_metadata_offset) {
+        return static_cast<atomic_var_t>(0);
+    }
+
+    // Read the metadata record from the metadata ringbuffer.
+    guest_ptr_t metadata_address =
+        rb.metadata.metadata_ring_ptr +
+        (metadata_info_offset * METADATA_RECORD_SIZE);
+    metadata_record_t metadata =
+        metadata_record_t::read(proxy, metadata_address, DATA_OFFSET_MASK, bo);
+
+    // Read the info record from the info ringbuffer.
+    guest_ptr_t info_address =
+        rb.metadata.info_ring_ptr +
+        (metadata_info_offset * INFO_RECORD_SIZE);
+    DmesgInfoRecord info =
+        DmesgInfoRecord::read(proxy, info_address, bo);
+
+    // The metadata record should point back to the same data record
+    // in the data ringbuffer.
+    if (metadata.data_buffer.curr_offset != offset) {
+        warn_once("Dmesg dump: metadata record (at 0x%08x) does not point "
+                  "back to the correponding data record (at 0x%08x). Dmesg "
+                  "buffer may be corrupted",
+             metadata.data_buffer.next_offset, offset);
+    }
+
+    // Read the message from the data record. This is placed
+    // immediately after the `guest_ptr_t` sized metadata offset at
+    // the beginning of the record.
+    std::vector<uint8_t> message(info.message_size);
+    proxy.readBlob(rb.data.data_ring_ptr + offset + sizeof(guest_ptr_t),
+                   message.data(), info.message_size);
+
+    // Print the record
+    ccprintf(os, "[%.6f] ", info.ts_nsec * 10e-9);
+    os.write((char *)message.data(), info.message_size);
+    os << "\n";
+
+    // Return the offset of the next data record in the data
+    // ringbuffer.
+    return metadata.data_buffer.next_offset;
+}
+
+/** Dump the kernel Dmesg ringbuffer for Linux versions post-v5.10.
+
+    Templated implementation specific to 32-bit or 64-bit Linux.
+*/
+template <typename ringbuffer_t>
+void
+dumpDmesgImpl(ThreadContext *tc, std::ostream &os)
+{
+    using atomic_var_t = typename ringbuffer_t::atomic_var_t;
+    using guest_ptr_t = typename ringbuffer_t::guest_ptr_t;
+
+    System *system = tc->getSystemPtr();
+    const ByteOrder bo = system->getGuestByteOrder();
+    const auto &symtab = system->workload->symtab(tc);
+    TranslatingPortProxy proxy(tc);
+
+    auto symtab_end_it = symtab.end();
+
+    // Read the dynamic ringbuffer structure from guest memory, if present.
+    ringbuffer_t dynamic_rb;
+    auto dynamic_rb_symbol = symtab.find("printk_rb_dynamic");
+    if (dynamic_rb_symbol != symtab_end_it) {
+        dynamic_rb = ringbuffer_t::read(proxy, dynamic_rb_symbol->address, bo);
+    } else {
+        warn("Failed to find required dmesg symbols.\n");
+        return;
+    }
+
+    // Read the static ringbuffer structure from guest memory, if present.
+    ringbuffer_t static_rb;
+    auto static_rb_symbol = symtab.find("printk_rb_static");
+    if (static_rb_symbol != symtab_end_it) {
+        static_rb = ringbuffer_t::read(proxy, static_rb_symbol->address, bo);
+    } else {
+        warn("Failed to find required dmesg symbols.\n");
+        return;
+    }
+
+    // Read the pointer to the active ringbuffer structure from guest
+    // memory. This should point to one of the two ringbuffer
+    // structures already read from guest memory.
+    guest_ptr_t active_ringbuffer_ptr = 0x0;
+    auto active_ringbuffer_ptr_symbol = symtab.find("prb");
+    if (active_ringbuffer_ptr_symbol != symtab_end_it) {
+        active_ringbuffer_ptr =
+            proxy.read<guest_ptr_t>(active_ringbuffer_ptr_symbol->address, bo);
+    } else {
+        warn("Failed to find required dmesg symbols.\n");
+        return;
+    }
+
+    if (active_ringbuffer_ptr == 0 ||
+        (active_ringbuffer_ptr != dynamic_rb_symbol->address &&
+         active_ringbuffer_ptr != static_rb_symbol->address)) {
+        warn("Kernel Dmesg ringbuffer appears to be invalid.\n");
+        return;
+    }
+
+    ringbuffer_t & rb =
+        (active_ringbuffer_ptr == dynamic_rb_symbol->address)
+        ? dynamic_rb : static_rb;
+
+    atomic_var_t head_offset = rb.data.head_offset;
+    atomic_var_t tail_offset = rb.data.tail_offset;
+
+    // Get some marker offsets into the data ringbuffer which will be
+    // used as end values to control the iteration.
+    const guest_ptr_t first_metadata_offset = rb.mask_metadata_offset(
+        proxy.read<guest_ptr_t>(rb.data.data_ring_ptr, bo));
+    const guest_ptr_t invalid_metadata_offset =
+        rb.template make_metadata_offset_mask<guest_ptr_t>() + 1;
+
+    // Iterate over the active ringbuffer, printing each message to
+    // `os`. Use the maximum number of possible info records plus one
+    // (invalid_metadata_offset) as an escape counter to make sure the
+    // process doesn't iterate infinitely if the kernel data
+    // structures have been corrupted.
+
+    // When head is behind tail, read to the end of the ringbuffer,
+    // then loop back to the begining.
+    //
+    // iterateDataRingbuffer will return offset at the beginning of
+    // the data ringbuffer when it loops back.
+    //
+    // `first_metadata_offset` is used to detect cases where the final data
+    // block is unused.
+    guest_ptr_t count = 0;
+    while (head_offset < tail_offset && count < invalid_metadata_offset) {
+        tail_offset =
+            iterateDataRingbuffer<ringbuffer_t>(
+                os, proxy, rb, tail_offset, first_metadata_offset, bo);
+        ++count;
+    }
+
+    // When tail is behind head, read forwards from the tail offset to
+    // the head offset.
+    count = 0;
+    while (tail_offset < head_offset && count < invalid_metadata_offset) {
+        tail_offset =
+            iterateDataRingbuffer<ringbuffer_t>(
+                os, proxy, rb, tail_offset, invalid_metadata_offset, bo);
+        ++count;
+    }
+}
+
+/** Dump the kernel Dmesg ringbuffer for Linux versions post-v5.10.
+ *
+ *  Delegates to an architecture specific template funtion instance.
+ *
+ */
+void
+dumpDmesg(ThreadContext *tc, std::ostream &os)
+{
+    System *system = tc->getSystemPtr();
+    const bool os_is_64_bit = loader::archIs64Bit(system->workload->getArch());
+
+    if (os_is_64_bit) {
+        dumpDmesgImpl<Linux64_Ringbuffer>(tc, os);
+    } else {
+        dumpDmesgImpl<Linux32_Ringbuffer>(tc, os);
+    }
+}
+
+} // namespace post5_10
+
+} // anonymous namespace
+
+void
+dumpDmesg(ThreadContext *tc, std::ostream &os)
+{
+    System *system = tc->getSystemPtr();
+    const auto &symtab = system->workload->symtab(tc);
+
+    auto end_it = symtab.end();
+
+    // Search for symbols associated with the Kernel Dmesg ringbuffer,
+    // pre-v5.10.
+    auto lb = symtab.find("__log_buf");
+    auto lb_len = symtab.find("log_buf_len");
+    auto first = symtab.find("log_first_idx");
+    auto next = symtab.find("log_next_idx");
+
+    if (lb != end_it && lb_len != end_it &&
+            first != end_it && next != end_it) {
+        linux::pre5_10::dumpDmesg(tc, os);
+        return;
+    }
+
+    // Search for symbols associated with the Kernel Dmesg ringbuffer,
+    // post-v5.10.
+    auto printk_rb_static = symtab.find("printk_rb_static");
+    auto printk_rb_dynamic = symtab.find("printk_rb_dynamic");
+
+    if (printk_rb_dynamic != end_it || printk_rb_static != end_it) {
+        linux::post5_10::dumpDmesg(tc, os);
+        return;
+    }
+
+    // Required symbols relating to the Kernel Dmesg buffer were not
+    // found for any supported version of Linux.
+    warn("Failed to find kernel dmesg symbols.\n");
+}
+
+} // namespace linux
+
 } // namespace gem5
diff --git a/src/mem/DRAMInterface.py b/src/mem/DRAMInterface.py
index 3f938dd..3d062c3 100644
--- a/src/mem/DRAMInterface.py
+++ b/src/mem/DRAMInterface.py
@@ -48,7 +48,7 @@
 
 class DRAMInterface(MemInterface):
     type = 'DRAMInterface'
-    cxx_header = "mem/mem_interface.hh"
+    cxx_header = "mem/dram_interface.hh"
     cxx_class = 'gem5::memory::DRAMInterface'
 
     # scheduler page policy
@@ -79,11 +79,18 @@
     # timing behaviour and constraints - all in nanoseconds
 
     # the amount of time in nanoseconds from issuing an activate command
-    # to the data being available in the row buffer for a read/write
-    tRCD = Param.Latency("RAS to CAS delay")
+    # to the data being available in the row buffer for a read
+    tRCD = Param.Latency("RAS to Read CAS delay")
 
-    # the time from issuing a read/write command to seeing the actual data
-    tCL = Param.Latency("CAS latency")
+    # the amount of time in nanoseconds from issuing an activate command
+    # to the data being available in the row buffer for a write
+    tRCD_WR = Param.Latency(Self.tRCD, "RAS to Write CAS delay")
+
+    # the time from issuing a read command to seeing the actual data
+    tCL = Param.Latency("Read CAS latency")
+
+    # the time from issuing a write command to seeing the actual data
+    tCWL = Param.Latency(Self.tCL, "Write CAS latency")
 
     # minimum time between a precharge and subsequent activate
     tRP = Param.Latency("Row precharge time")
@@ -1145,6 +1152,87 @@
     # self refresh exit time
     tXS = '65ns'
 
+# A single HBM2 x64 interface (tested with HBMCtrl in gem5)
+# to be used as a single pseudo channel. The timings are based
+# on HBM gen2 specifications. 4H stack, 8Gb per die and total capacity
+# of 4GiB.
+class HBM_2000_4H_1x64(DRAMInterface):
+
+    # 64-bit interface for a single pseudo channel
+    device_bus_width = 64
+
+    # HBM2 supports BL4
+    burst_length = 4
+
+    # size of channel in bytes, 4H stack of 8Gb dies is 4GiB per stack;
+    # with 16 pseudo channels, 256MiB per pseudo channel
+    device_size = "256MiB"
+
+    device_rowbuffer_size = "1KiB"
+
+    # 1x128 configuration
+    devices_per_rank = 1
+
+    ranks_per_channel = 1
+
+    banks_per_rank = 16
+    bank_groups_per_rank = 4
+
+    # 1000 MHz for 2Gbps DDR data rate
+    tCK = "1ns"
+
+    tRP = "14ns"
+
+    tCCD_L = "3ns"
+
+    tRCD = "12ns"
+    tRCD_WR = "6ns"
+    tCL = "18ns"
+    tCWL = "7ns"
+    tRAS = "28ns"
+
+    # BL4 in pseudo channel mode
+    # DDR @ 1000 MHz means 4 * 1ns / 2 = 2ns
+    tBURST = "2ns"
+
+    # value for 2Gb device from JEDEC spec
+    tRFC = "220ns"
+
+    # value for 2Gb device from JEDEC spec
+    tREFI = "3.9us"
+
+    tWR = "14ns"
+    tRTP = "5ns"
+    tWTR = "4ns"
+    tWTR_L = "9ns"
+    tRTW = "18ns"
+
+    #tAAD from RBus
+    tAAD = "1ns"
+
+    # single rank device, set to 0
+    tCS = "0ns"
+
+    tRRD = "4ns"
+    tRRD_L = "6ns"
+
+    # for a single pseudo channel
+    tXAW = "16ns"
+    activation_limit = 4
+
+    # 4tCK
+    tXP = "8ns"
+
+    # start with tRFC + tXP -> 160ns + 8ns = 168ns
+    tXS = "216ns"
+
+    page_policy = 'close_adaptive'
+
+    read_buffer_size = 64
+    write_buffer_size = 64
+
+    two_cycle_activate = True
+
 # A single LPDDR5 x16 interface (one command/address bus)
 # for a single x16 channel with default timings based on
 # initial JEDEC specification
diff --git a/src/cpu/o3/O3Checker.py b/src/mem/HBMCtrl.py
similarity index 63%
copy from src/cpu/o3/O3Checker.py
copy to src/mem/HBMCtrl.py
index c343cd6..a7be7c8 100644
--- a/src/cpu/o3/O3Checker.py
+++ b/src/mem/HBMCtrl.py
@@ -1,5 +1,5 @@
-# Copyright (c) 2007 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright (c) 2022 The Regents of the University of California
+# All Rights Reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -25,9 +25,25 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from m5.params import *
-from m5.objects.CheckerCPU import CheckerCPU
+from m5.proxy import *
+from m5.objects.MemCtrl import *
 
-class O3Checker(CheckerCPU):
-    type = 'O3Checker'
-    cxx_class = 'gem5::o3::Checker'
-    cxx_header = 'cpu/o3/checker.hh'
+# HBMCtrl manages two pseudo channels of HBM2
+
+class HBMCtrl(MemCtrl):
+    type = 'HBMCtrl'
+    cxx_header = "mem/hbm_ctrl.hh"
+    cxx_class = 'gem5::memory::HBMCtrl'
+
+    # HBMCtrl uses the SimpleMemCtlr's interface
+    # `dram` as the first pseudo channel, the second
+    # pseudo channel interface is following
+    # HBMCtrl has been tested with two HBM_2000_4H_1x64 interfaces
+    dram_2 = Param.DRAMInterface("DRAM memory interface")
+
+    # For mixed traffic, HBMCtrl with HBM_2000_4H_1x64 interfaaces
+    # gives the best results with following min_r/w_per_switch
+    min_reads_per_switch = 64
+    min_writes_per_switch = 64
+
+    partitioned_q = Param.Bool(True, "split queues for pseudo channels")
diff --git a/src/cpu/simple/NonCachingSimpleCPU.py b/src/mem/HeteroMemCtrl.py
similarity index 73%
copy from src/cpu/simple/NonCachingSimpleCPU.py
copy to src/mem/HeteroMemCtrl.py
index e01905a..d0ba84d 100644
--- a/src/cpu/simple/NonCachingSimpleCPU.py
+++ b/src/mem/HeteroMemCtrl.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2012, 2018 ARM Limited
+# Copyright (c) 2012-2020 ARM Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -10,6 +10,11 @@
 # unmodified and in its entirety in all distributions of the software,
 # modified or unmodified, in source code or in binary form.
 #
+# Copyright (c) 2013 Amin Farmahini-Farahani
+# Copyright (c) 2015 University of Kaiserslautern
+# Copyright (c) 2015 The University of Bologna
+# All rights reserved.
+#
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met: redistributions of source code must retain the above copyright
@@ -34,27 +39,18 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from m5.params import *
-from m5.objects.AtomicSimpleCPU import AtomicSimpleCPU
+from m5.proxy import *
+from m5.objects.MemCtrl import *
 
-class NonCachingSimpleCPU(AtomicSimpleCPU):
-    """Simple CPU model based on the atomic CPU. Unlike the atomic CPU,
-    this model causes the memory system to bypass caches and is
-    therefore slightly faster in some cases. However, its main purpose
-    is as a substitute for hardware virtualized CPUs when
-    stress-testing the memory system.
 
-    """
+# HeteroMemCtrl controls a dram and an nvm interface
+# Both memory interfaces share the data and command bus
+class HeteroMemCtrl(MemCtrl):
+    type = 'HeteroMemCtrl'
+    cxx_header = "mem/hetero_mem_ctrl.hh"
+    cxx_class = 'gem5::memory::HeteroMemCtrl'
 
-    type = 'NonCachingSimpleCPU'
-    cxx_header = "cpu/simple/noncaching.hh"
-    cxx_class = 'gem5::NonCachingSimpleCPU'
-
-    numThreads = 1
-
-    @classmethod
-    def memory_mode(cls):
-        return 'atomic_noncaching'
-
-    @classmethod
-    def support_take_over(cls):
-        return True
+    # Interface to nvm memory media
+    # The dram interface `dram` used by HeteroMemCtrl is defined in
+    # the MemCtrl
+    nvm = Param.NVMInterface("NVM memory interface to use")
diff --git a/src/mem/MemCtrl.py b/src/mem/MemCtrl.py
index 90d0e500..ea199ee 100644
--- a/src/mem/MemCtrl.py
+++ b/src/mem/MemCtrl.py
@@ -59,11 +59,9 @@
     # bus in front of the controller for multiple ports
     port = ResponsePort("This port responds to memory requests")
 
-    # Interface to volatile, DRAM media
-    dram = Param.DRAMInterface(NULL, "DRAM interface")
-
-    # Interface to non-volatile media
-    nvm = Param.NVMInterface(NULL, "NVM interface")
+    # Interface to memory media
+    dram = Param.MemInterface("Memory interface, can be a DRAM"
+                              "or an NVM interface ")
 
     # read and write buffer depths are set in the interface
     # the controller will read these values when instantiated
@@ -80,6 +78,10 @@
     min_writes_per_switch = Param.Unsigned(16, "Minimum write bursts before "
                                            "switching to reads")
 
+    # minimum read bursts to schedule before switching back to writes
+    min_reads_per_switch = Param.Unsigned(16, "Minimum read bursts before "
+                                           "switching to writes")
+
     # scheduler, address map and page policy
     mem_sched_policy = Param.MemSched('frfcfs', "Memory scheduling policy")
 
diff --git a/src/mem/NVMInterface.py b/src/mem/NVMInterface.py
index 5c8b27b..a73e1d8 100644
--- a/src/mem/NVMInterface.py
+++ b/src/mem/NVMInterface.py
@@ -35,6 +35,7 @@
 
 from m5.params import *
 from m5.proxy import *
+from m5.objects.MemCtrl import MemCtrl
 from m5.objects.MemInterface import MemInterface
 from m5.objects.DRAMInterface import AddrMap
 
@@ -43,7 +44,7 @@
 # are modeled without getting into too much detail of the media itself.
 class NVMInterface(MemInterface):
     type = 'NVMInterface'
-    cxx_header = "mem/mem_interface.hh"
+    cxx_header = "mem/nvm_interface.hh"
     cxx_class = 'gem5::memory::NVMInterface'
 
     # NVM DIMM could have write buffer to offload writes
@@ -65,6 +66,16 @@
     two_cycle_rdwr = Param.Bool(False,
                      "Two cycles required to send read and write commands")
 
+
+    def controller(self):
+        """
+        Instantiate the memory controller and bind it to
+        the current interface.
+        """
+        controller = MemCtrl()
+        controller.dram = self
+        return controller
+
 # NVM delays and device architecture defined to mimic PCM like memory.
 # Can be configured with DDR4_2400 sharing the channel
 class NVM_2400_1x64(NVMInterface):
diff --git a/src/mem/SConscript b/src/mem/SConscript
index 2fe6c8c..0f2efed 100644
--- a/src/mem/SConscript
+++ b/src/mem/SConscript
@@ -46,7 +46,12 @@
 SimObject('AbstractMemory.py', sim_objects=['AbstractMemory'])
 SimObject('AddrMapper.py', sim_objects=['AddrMapper', 'RangeAddrMapper'])
 SimObject('Bridge.py', sim_objects=['Bridge'])
-SimObject('MemCtrl.py', sim_objects=['MemCtrl'], enums=['MemSched'])
+SimObject('SysBridge.py', sim_objects=['SysBridge'])
+DebugFlag('SysBridge')
+SimObject('MemCtrl.py', sim_objects=['MemCtrl'],
+        enums=['MemSched'])
+SimObject('HeteroMemCtrl.py', sim_objects=['HeteroMemCtrl'])
+SimObject('HBMCtrl.py', sim_objects=['HBMCtrl'])
 SimObject('MemInterface.py', sim_objects=['MemInterface'], enums=['AddrMap'])
 SimObject('DRAMInterface.py', sim_objects=['DRAMInterface'],
         enums=['PageManage'])
@@ -54,6 +59,7 @@
 SimObject('ExternalMaster.py', sim_objects=['ExternalMaster'])
 SimObject('ExternalSlave.py', sim_objects=['ExternalSlave'])
 SimObject('CfiMemory.py', sim_objects=['CfiMemory'])
+SimObject('SharedMemoryServer.py', sim_objects=['SharedMemoryServer'])
 SimObject('SimpleMemory.py', sim_objects=['SimpleMemory'])
 SimObject('XBar.py', sim_objects=[
     'BaseXBar', 'NoncoherentXBar', 'CoherentXBar', 'SnoopFilter'])
@@ -71,16 +77,22 @@
 Source('external_master.cc')
 Source('external_slave.cc')
 Source('mem_ctrl.cc')
+Source('hetero_mem_ctrl.cc')
+Source('hbm_ctrl.cc')
 Source('mem_interface.cc')
+Source('dram_interface.cc')
+Source('nvm_interface.cc')
 Source('noncoherent_xbar.cc')
 Source('packet.cc')
 Source('port.cc')
 Source('packet_queue.cc')
 Source('port_proxy.cc')
 Source('physical.cc')
+Source('shared_memory_server.cc')
 Source('simple_mem.cc')
 Source('snoop_filter.cc')
 Source('stack_dist_calc.cc')
+Source('sys_bridge.cc')
 Source('token_port.cc')
 Source('tport.cc')
 Source('xbar.cc')
@@ -92,7 +104,7 @@
 
 GTest('translation_gen.test', 'translation_gen.test.cc')
 
-if env['TARGET_ISA'] != 'null':
+if env['CONF']['TARGET_ISA'] != 'null':
     Source('translating_port_proxy.cc')
     Source('se_translating_port_proxy.cc')
     Source('page_table.cc')
diff --git a/src/mem/SharedMemoryServer.py b/src/mem/SharedMemoryServer.py
new file mode 100644
index 0000000..3a63f45
--- /dev/null
+++ b/src/mem/SharedMemoryServer.py
@@ -0,0 +1,15 @@
+from m5.SimObject import SimObject
+from m5.params import Param
+from m5.proxy import Parent
+
+
+class SharedMemoryServer(SimObject):
+    type = "SharedMemoryServer"
+    cxx_header = "mem/shared_memory_server.hh"
+    cxx_class = "gem5::memory::SharedMemoryServer"
+
+    system = Param.System(
+        Parent.any,
+        "The system where the target shared memory is actually stored.")
+    server_path = Param.String(
+        "The unix socket path where the server should be running upon.")
diff --git a/src/cpu/o3/O3Checker.py b/src/mem/SysBridge.py
similarity index 70%
copy from src/cpu/o3/O3Checker.py
copy to src/mem/SysBridge.py
index c343cd6..2c42b75 100644
--- a/src/cpu/o3/O3Checker.py
+++ b/src/mem/SysBridge.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2007 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2021 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -25,9 +24,19 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from m5.params import *
-from m5.objects.CheckerCPU import CheckerCPU
+from m5.SimObject import SimObject
 
-class O3Checker(CheckerCPU):
-    type = 'O3Checker'
-    cxx_class = 'gem5::o3::Checker'
-    cxx_header = 'cpu/o3/checker.hh'
+class SysBridge(SimObject):
+    '''Use this bridge to connect the memory systems belonging to two different
+       Systems SimObjects. See the header file for more information.'''
+    type = 'SysBridge'
+    cxx_header = "mem/sys_bridge.hh"
+    cxx_class = 'gem5::SysBridge'
+
+    source = Param.System("Source System")
+    target = Param.System("Target System")
+
+    target_port = RequestPort(
+            "A port which sends requests to a target system.")
+    source_port = ResponsePort(
+            "A port which sends responses to a source system")
diff --git a/src/mem/cache/base.cc b/src/mem/cache/base.cc
index dc21151..cf6c9fe 100644
--- a/src/mem/cache/base.cc
+++ b/src/mem/cache/base.cc
@@ -223,6 +223,59 @@
 void
 BaseCache::handleTimingReqHit(PacketPtr pkt, CacheBlk *blk, Tick request_time)
 {
+
+    // handle special cases for LockedRMW transactions
+    if (pkt->isLockedRMW()) {
+        Addr blk_addr = pkt->getBlockAddr(blkSize);
+
+        if (pkt->isRead()) {
+            // Read hit for LockedRMW.  Since it requires exclusive
+            // permissions, there should be no outstanding access.
+            assert(!mshrQueue.findMatch(blk_addr, pkt->isSecure()));
+            // The keys to LockedRMW are that (1) we always have an MSHR
+            // allocated during the RMW interval to catch snoops and
+            // defer them until after the RMW completes, and (2) we
+            // clear permissions on the block to turn any upstream
+            // access other than the matching write into a miss, causing
+            // it to append to the MSHR as well.
+
+            // Because we hit in the cache, we have to fake an MSHR to
+            // achieve part (1).  If the read had missed, this MSHR
+            // would get allocated as part of normal miss processing.
+            // Basically we need to get the MSHR in the same state as if
+            // we had missed and just received the response.
+            // Request *req2 = new Request(*(pkt->req));
+            RequestPtr req2 = std::make_shared<Request>(*(pkt->req));
+            PacketPtr pkt2 = new Packet(req2, pkt->cmd);
+            MSHR *mshr = allocateMissBuffer(pkt2, curTick(), true);
+            // Mark the MSHR "in service" (even though it's not) to prevent
+            // the cache from sending out a request.
+            mshrQueue.markInService(mshr, false);
+            // Part (2): mark block inaccessible
+            assert(blk);
+            blk->clearCoherenceBits(CacheBlk::ReadableBit);
+            blk->clearCoherenceBits(CacheBlk::WritableBit);
+        } else {
+            assert(pkt->isWrite());
+            // All LockedRMW writes come here, as they cannot miss.
+            // Need to undo the two things described above.  Block
+            // permissions were already restored earlier in this
+            // function, prior to the access() call.  Now we just need
+            // to clear out the MSHR.
+
+            // Read should have already allocated MSHR.
+            MSHR *mshr = mshrQueue.findMatch(blk_addr, pkt->isSecure());
+            assert(mshr);
+            // Fake up a packet and "respond" to the still-pending
+            // LockedRMWRead, to process any pending targets and clear
+            // out the MSHR
+            PacketPtr resp_pkt =
+                new Packet(pkt->req, MemCmd::LockedRMWWriteResp);
+            resp_pkt->senderState = mshr;
+            recvTimingResp(resp_pkt);
+        }
+    }
+
     if (pkt->needsResponse()) {
         // These delays should have been consumed by now
         assert(pkt->headerDelay == 0);
@@ -353,6 +406,20 @@
     // the delay provided by the crossbar
     Tick forward_time = clockEdge(forwardLatency) + pkt->headerDelay;
 
+    if (pkt->cmd == MemCmd::LockedRMWWriteReq) {
+        // For LockedRMW accesses, we mark the block inaccessible after the
+        // read (see below), to make sure no one gets in before the write.
+        // Now that the write is here, mark it accessible again, so the
+        // write will succeed.  LockedRMWReadReq brings the block in in
+        // exclusive mode, so we know it was previously writable.
+        CacheBlk *blk = tags->findBlock(pkt->getAddr(), pkt->isSecure());
+        assert(blk && blk->isValid());
+        assert(!blk->isSet(CacheBlk::WritableBit) &&
+               !blk->isSet(CacheBlk::ReadableBit));
+        blk->setCoherenceBits(CacheBlk::ReadableBit);
+        blk->setCoherenceBits(CacheBlk::WritableBit);
+    }
+
     Cycles lat;
     CacheBlk *blk = nullptr;
     bool satisfied = false;
@@ -438,7 +505,7 @@
 
     // if this is a write, we should be looking at an uncacheable
     // write
-    if (pkt->isWrite()) {
+    if (pkt->isWrite() && pkt->cmd != MemCmd::LockedRMWWriteResp) {
         assert(pkt->req->isUncacheable());
         handleUncacheableWriteResp(pkt);
         return;
@@ -491,61 +558,68 @@
         ppFill->notify(pkt);
     }
 
-    if (blk && blk->isValid() && pkt->isClean() && !pkt->isInvalidate()) {
-        // The block was marked not readable while there was a pending
-        // cache maintenance operation, restore its flag.
-        blk->setCoherenceBits(CacheBlk::ReadableBit);
+    // Don't want to promote the Locked RMW Read until
+    // the locked write comes in
+    if (!mshr->hasLockedRMWReadTarget()) {
+        if (blk && blk->isValid() && pkt->isClean() && !pkt->isInvalidate()) {
+            // The block was marked not readable while there was a pending
+            // cache maintenance operation, restore its flag.
+            blk->setCoherenceBits(CacheBlk::ReadableBit);
 
-        // This was a cache clean operation (without invalidate)
-        // and we have a copy of the block already. Since there
-        // is no invalidation, we can promote targets that don't
-        // require a writable copy
-        mshr->promoteReadable();
-    }
+            // This was a cache clean operation (without invalidate)
+            // and we have a copy of the block already. Since there
+            // is no invalidation, we can promote targets that don't
+            // require a writable copy
+            mshr->promoteReadable();
+        }
 
-    if (blk && blk->isSet(CacheBlk::WritableBit) &&
-        !pkt->req->isCacheInvalidate()) {
-        // If at this point the referenced block is writable and the
-        // response is not a cache invalidate, we promote targets that
-        // were deferred as we couldn't guarrantee a writable copy
-        mshr->promoteWritable();
+        if (blk && blk->isSet(CacheBlk::WritableBit) &&
+            !pkt->req->isCacheInvalidate()) {
+            // If at this point the referenced block is writable and the
+            // response is not a cache invalidate, we promote targets that
+            // were deferred as we couldn't guarrantee a writable copy
+            mshr->promoteWritable();
+        }
     }
 
     serviceMSHRTargets(mshr, pkt, blk);
+    // We are stopping servicing targets early for the Locked RMW Read until
+    // the write comes.
+    if (!mshr->hasLockedRMWReadTarget()) {
+        if (mshr->promoteDeferredTargets()) {
+            // avoid later read getting stale data while write miss is
+            // outstanding.. see comment in timingAccess()
+            if (blk) {
+                blk->clearCoherenceBits(CacheBlk::ReadableBit);
+            }
+            mshrQueue.markPending(mshr);
+            schedMemSideSendEvent(clockEdge() + pkt->payloadDelay);
+        } else {
+            // while we deallocate an mshr from the queue we still have to
+            // check the isFull condition before and after as we might
+            // have been using the reserved entries already
+            const bool was_full = mshrQueue.isFull();
+            mshrQueue.deallocate(mshr);
+            if (was_full && !mshrQueue.isFull()) {
+                clearBlocked(Blocked_NoMSHRs);
+            }
 
-    if (mshr->promoteDeferredTargets()) {
-        // avoid later read getting stale data while write miss is
-        // outstanding.. see comment in timingAccess()
-        if (blk) {
-            blk->clearCoherenceBits(CacheBlk::ReadableBit);
-        }
-        mshrQueue.markPending(mshr);
-        schedMemSideSendEvent(clockEdge() + pkt->payloadDelay);
-    } else {
-        // while we deallocate an mshr from the queue we still have to
-        // check the isFull condition before and after as we might
-        // have been using the reserved entries already
-        const bool was_full = mshrQueue.isFull();
-        mshrQueue.deallocate(mshr);
-        if (was_full && !mshrQueue.isFull()) {
-            clearBlocked(Blocked_NoMSHRs);
+            // Request the bus for a prefetch if this deallocation freed enough
+            // MSHRs for a prefetch to take place
+            if (prefetcher && mshrQueue.canPrefetch() && !isBlocked()) {
+                Tick next_pf_time = std::max(
+                    prefetcher->nextPrefetchReadyTime(), clockEdge());
+                if (next_pf_time != MaxTick)
+                    schedMemSideSendEvent(next_pf_time);
+            }
         }
 
-        // Request the bus for a prefetch if this deallocation freed enough
-        // MSHRs for a prefetch to take place
-        if (prefetcher && mshrQueue.canPrefetch() && !isBlocked()) {
-            Tick next_pf_time = std::max(prefetcher->nextPrefetchReadyTime(),
-                                         clockEdge());
-            if (next_pf_time != MaxTick)
-                schedMemSideSendEvent(next_pf_time);
+        // if we used temp block, check to see if its valid and then clear it
+        if (blk == tempBlock && tempBlock->isValid()) {
+            evictBlock(blk, writebacks);
         }
     }
 
-    // if we used temp block, check to see if its valid and then clear it out
-    if (blk == tempBlock && tempBlock->isValid()) {
-        evictBlock(blk, writebacks);
-    }
-
     const Tick forward_time = clockEdge(forwardLatency) + pkt->headerDelay;
     // copy writebacks to write buffer
     doWritebacks(writebacks, forward_time);
@@ -2148,10 +2222,10 @@
     ADD_STAT(overallMissRate, statistics::units::Ratio::get(),
              "miss rate for overall accesses"),
     ADD_STAT(demandAvgMissLatency, statistics::units::Rate<
-                statistics::units::Cycle, statistics::units::Count>::get(),
-             "average overall miss latency"),
+                statistics::units::Tick, statistics::units::Count>::get(),
+             "average overall miss latency in ticks"),
     ADD_STAT(overallAvgMissLatency, statistics::units::Rate<
-                statistics::units::Cycle, statistics::units::Count>::get(),
+                statistics::units::Tick, statistics::units::Count>::get(),
              "average overall miss latency"),
     ADD_STAT(blockedCycles, statistics::units::Cycle::get(),
             "number of cycles access was blocked"),
@@ -2183,13 +2257,13 @@
     ADD_STAT(overallMshrMissRate, statistics::units::Ratio::get(),
              "mshr miss ratio for overall accesses"),
     ADD_STAT(demandAvgMshrMissLatency, statistics::units::Rate<
-                statistics::units::Cycle, statistics::units::Count>::get(),
+                statistics::units::Tick, statistics::units::Count>::get(),
              "average overall mshr miss latency"),
     ADD_STAT(overallAvgMshrMissLatency, statistics::units::Rate<
-                statistics::units::Cycle, statistics::units::Count>::get(),
+                statistics::units::Tick, statistics::units::Count>::get(),
              "average overall mshr miss latency"),
     ADD_STAT(overallAvgMshrUncacheableLatency, statistics::units::Rate<
-                statistics::units::Cycle, statistics::units::Count>::get(),
+                statistics::units::Tick, statistics::units::Count>::get(),
              "average overall mshr uncacheable latency"),
     ADD_STAT(replacements, statistics::units::Count::get(),
              "number of replacements"),
diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh
index 0dc64e1..6fc7628 100644
--- a/src/mem/cache/base.hh
+++ b/src/mem/cache/base.hh
@@ -1006,12 +1006,12 @@
             @sa Packet::Command */
         statistics::Vector misses;
         /**
-         * Total number of cycles per thread/command spent waiting for a hit.
+         * Total number of ticks per thread/command spent waiting for a hit.
          * Used to calculate the average hit latency.
          */
         statistics::Vector hitLatency;
         /**
-         * Total number of cycles per thread/command spent waiting for a miss.
+         * Total number of ticks per thread/command spent waiting for a miss.
          * Used to calculate the average miss latency.
          */
         statistics::Vector missLatency;
@@ -1027,9 +1027,9 @@
         statistics::Vector mshrMisses;
         /** Number of misses that miss in the MSHRs, per command and thread. */
         statistics::Vector mshrUncacheable;
-        /** Total cycle latency of each MSHR miss, per command and thread. */
+        /** Total tick latency of each MSHR miss, per command and thread. */
         statistics::Vector mshrMissLatency;
-        /** Total cycle latency of each MSHR miss, per command and thread. */
+        /** Total tick latency of each MSHR miss, per command and thread. */
         statistics::Vector mshrUncacheableLatency;
         /** The miss rate in the MSHRs pre command and thread. */
         statistics::Formula mshrMissRate;
@@ -1055,9 +1055,9 @@
         statistics::Formula demandHits;
         /** Number of hit for all accesses. */
         statistics::Formula overallHits;
-        /** Total number of cycles spent waiting for demand hits. */
+        /** Total number of ticks spent waiting for demand hits. */
         statistics::Formula demandHitLatency;
-        /** Total number of cycles spent waiting for all hits. */
+        /** Total number of ticks spent waiting for all hits. */
         statistics::Formula overallHitLatency;
 
         /** Number of misses for demand accesses. */
@@ -1065,9 +1065,9 @@
         /** Number of misses for all accesses. */
         statistics::Formula overallMisses;
 
-        /** Total number of cycles spent waiting for demand misses. */
+        /** Total number of ticks spent waiting for demand misses. */
         statistics::Formula demandMissLatency;
-        /** Total number of cycles spent waiting for all misses. */
+        /** Total number of ticks spent waiting for all misses. */
         statistics::Formula overallMissLatency;
 
         /** The number of demand accesses. */
@@ -1109,12 +1109,12 @@
         /** Total number of misses that miss in the MSHRs. */
         statistics::Formula overallMshrUncacheable;
 
-        /** Total cycle latency of demand MSHR misses. */
+        /** Total tick latency of demand MSHR misses. */
         statistics::Formula demandMshrMissLatency;
-        /** Total cycle latency of overall MSHR misses. */
+        /** Total tick latency of overall MSHR misses. */
         statistics::Formula overallMshrMissLatency;
 
-        /** Total cycle latency of overall MSHR misses. */
+        /** Total tick latency of overall MSHR misses. */
         statistics::Formula overallMshrUncacheableLatency;
 
         /** The demand miss rate in the MSHRs. */
diff --git a/src/mem/cache/cache.cc b/src/mem/cache/cache.cc
index 3c24343..24b3fe7 100644
--- a/src/mem/cache/cache.cc
+++ b/src/mem/cache/cache.cc
@@ -324,6 +324,9 @@
 Cache::handleTimingReqMiss(PacketPtr pkt, CacheBlk *blk, Tick forward_time,
                            Tick request_time)
 {
+
+    // These should always hit due to the earlier Locked Read
+    assert(pkt->cmd != MemCmd::LockedRMWWriteReq);
     if (pkt->req->isUncacheable()) {
         // ignore any existing MSHR if we are dealing with an
         // uncacheable request
@@ -693,11 +696,31 @@
     bool is_invalidate = pkt->isInvalidate() &&
         !mshr->wasWholeLineWrite;
 
+    bool from_core = false;
+    bool from_pref = false;
+
+    if (pkt->cmd == MemCmd::LockedRMWWriteResp) {
+        // This is the fake response generated by the write half of the RMW;
+        // see comments in recvTimingReq().  The first target on the list
+        // should be the LockedRMWReadReq which has already been satisfied,
+        // either because it was a hit (and the MSHR was allocated in
+        // recvTimingReq()) or because it was left there after the inital
+        // response in extractServiceableTargets. In either case, we
+        // don't need to respond now, so pop it off to prevent the loop
+        // below from generating another response.
+        assert(initial_tgt->pkt->cmd == MemCmd::LockedRMWReadReq);
+        mshr->popTarget();
+        delete initial_tgt->pkt;
+        initial_tgt = nullptr;
+    }
+
     MSHR::TargetList targets = mshr->extractServiceableTargets(pkt);
     for (auto &target: targets) {
         Packet *tgt_pkt = target.pkt;
         switch (target.source) {
           case MSHR::Target::FromCPU:
+            from_core = true;
+
             Tick completion_time;
             // Here we charge on completion_time the delay of the xbar if the
             // packet comes from it, charged on headerDelay.
@@ -781,6 +804,21 @@
                 stats.cmdStats(tgt_pkt)
                     .missLatency[tgt_pkt->req->requestorId()] +=
                     completion_time - target.recvTime;
+
+                if (tgt_pkt->cmd == MemCmd::LockedRMWReadReq) {
+                    // We're going to leave a target in the MSHR until the
+                    // write half of the RMW occurs (see comments above in
+                    // recvTimingReq()).  Since we'll be using the current
+                    // request packet (which has the allocated data pointer)
+                    // to form the response, we have to allocate a new dummy
+                    // packet to save in the MSHR target.
+                    mshr->updateLockedRMWReadTarget(tgt_pkt);
+                    // skip the rest of target processing after we
+                    // send the response
+                    // Mark block inaccessible until write arrives
+                    blk->clearCoherenceBits(CacheBlk::WritableBit);
+                    blk->clearCoherenceBits(CacheBlk::ReadableBit);
+                }
             } else if (pkt->cmd == MemCmd::UpgradeFailResp) {
                 // failed StoreCond upgrade
                 assert(tgt_pkt->cmd == MemCmd::StoreCondReq ||
@@ -792,6 +830,11 @@
                 completion_time += clockEdge(responseLatency) +
                     pkt->payloadDelay;
                 tgt_pkt->req->setExtraData(0);
+            } else if (pkt->cmd == MemCmd::LockedRMWWriteResp) {
+                // Fake response on LockedRMW completion, see above.
+                // Since the data is already in the cache, we just use
+                // responseLatency with no extra penalties.
+                completion_time = clockEdge(responseLatency);
             } else {
                 if (is_invalidate && blk && blk->isValid()) {
                     // We are about to send a response to a cache above
@@ -852,8 +895,8 @@
 
           case MSHR::Target::FromPrefetcher:
             assert(tgt_pkt->cmd == MemCmd::HardPFReq);
-            if (blk)
-                blk->setPrefetched();
+            from_pref = true;
+
             delete tgt_pkt;
             break;
 
@@ -882,16 +925,22 @@
         }
     }
 
-    maintainClusivity(targets.hasFromCache, blk);
+    if (blk && !from_core && from_pref) {
+        blk->setPrefetched();
+    }
 
-    if (blk && blk->isValid()) {
-        // an invalidate response stemming from a write line request
-        // should not invalidate the block, so check if the
-        // invalidation should be discarded
-        if (is_invalidate || mshr->hasPostInvalidate()) {
-            invalidateBlock(blk);
-        } else if (mshr->hasPostDowngrade()) {
-            blk->clearCoherenceBits(CacheBlk::WritableBit);
+    if (!mshr->hasLockedRMWReadTarget()) {
+        maintainClusivity(targets.hasFromCache, blk);
+
+        if (blk && blk->isValid()) {
+            // an invalidate response stemming from a write line request
+            // should not invalidate the block, so check if the
+            // invalidation should be discarded
+            if (is_invalidate || mshr->hasPostInvalidate()) {
+                invalidateBlock(blk);
+            } else if (mshr->hasPostDowngrade()) {
+                blk->clearCoherenceBits(CacheBlk::WritableBit);
+            }
         }
     }
 }
diff --git a/src/mem/cache/mshr.cc b/src/mem/cache/mshr.cc
index 6aaaf9e..871125a 100644
--- a/src/mem/cache/mshr.cc
+++ b/src/mem/cache/mshr.cc
@@ -137,7 +137,7 @@
         const Request::FlagsType no_merge_flags =
             Request::UNCACHEABLE | Request::STRICT_ORDER |
             Request::PRIVILEGED | Request::LLSC | Request::MEM_SWAP |
-            Request::MEM_SWAP_COND | Request::SECURE;
+            Request::MEM_SWAP_COND | Request::SECURE | Request::LOCKED_RMW;
         const auto &req_flags = pkt->req->getFlags();
         bool compat_write = !req_flags.isSet(no_merge_flags);
 
@@ -558,19 +558,34 @@
         assert((it->source == Target::FromCPU) ||
                (it->source == Target::FromPrefetcher));
         ready_targets.push_back(*it);
-        it = targets.erase(it);
-        while (it != targets.end()) {
-            if (it->source == Target::FromCPU) {
-                it++;
-            } else {
-                assert(it->source == Target::FromSnoop);
-                ready_targets.push_back(*it);
-                it = targets.erase(it);
+        // Leave the Locked RMW Read until the corresponding Locked Write
+        // request comes in
+        if (it->pkt->cmd != MemCmd::LockedRMWReadReq) {
+            it = targets.erase(it);
+            while (it != targets.end()) {
+                if (it->source == Target::FromCPU) {
+                    it++;
+                } else {
+                    assert(it->source == Target::FromSnoop);
+                    ready_targets.push_back(*it);
+                    it = targets.erase(it);
+                }
             }
         }
         ready_targets.populateFlags();
     } else {
-        std::swap(ready_targets, targets);
+        auto it = targets.begin();
+        while (it != targets.end()) {
+            ready_targets.push_back(*it);
+            if (it->pkt->cmd == MemCmd::LockedRMWReadReq) {
+                // Leave the Locked RMW Read until the corresponding Locked
+                // Write comes in. Also don't service any later targets as the
+                // line is now "locked".
+                break;
+            }
+            it = targets.erase(it);
+        }
+        ready_targets.populateFlags();
     }
     targets.populateFlags();
 
@@ -663,6 +678,9 @@
 void
 MSHR::promoteWritable()
 {
+    if (deferredTargets.empty()) {
+        return;
+    }
     PacketPtr def_tgt_pkt = deferredTargets.front().pkt;
     if (deferredTargets.needsWritable &&
         !(hasPostInvalidate() || hasPostDowngrade()) &&
@@ -763,4 +781,23 @@
     return entry->matchBlockAddr(blkAddr, isSecure);
 }
 
+void
+MSHR::updateLockedRMWReadTarget(PacketPtr pkt)
+{
+    assert(!targets.empty() && targets.front().pkt == pkt);
+    RequestPtr r = std::make_shared<Request>(*(pkt->req));
+    targets.front().pkt = new Packet(r, MemCmd::LockedRMWReadReq);
+}
+
+bool
+MSHR::hasLockedRMWReadTarget()
+{
+    if (!targets.empty() &&
+        targets.front().pkt->cmd == MemCmd::LockedRMWReadReq) {
+        return true;
+    }
+    return false;
+}
+
+
 } // namespace gem5
diff --git a/src/mem/cache/mshr.hh b/src/mem/cache/mshr.hh
index a9deec6..4d5c3b6 100644
--- a/src/mem/cache/mshr.hh
+++ b/src/mem/cache/mshr.hh
@@ -350,6 +350,22 @@
         return targets.hasFromCache;
     }
 
+    /**
+     * Replaces the matching packet in the Targets list with a dummy packet to
+     * ensure the MSHR remains allocated until the corresponding locked write
+     * arrives.
+     *
+     * @param pkt The LockedRMWRead packet to be updated
+     */
+    void updateLockedRMWReadTarget(PacketPtr pkt);
+
+    /**
+     * Determine if there are any LockedRMWReads in the Targets list
+     *
+     * @return true if Targets list contains a LockedRMWRead
+     */
+    bool hasLockedRMWReadTarget();
+
   private:
     /**
      * Promotes deferred targets that satisfy a predicate
diff --git a/src/mem/cache/noncoherent_cache.cc b/src/mem/cache/noncoherent_cache.cc
index 314025f..9e95a20 100644
--- a/src/mem/cache/noncoherent_cache.cc
+++ b/src/mem/cache/noncoherent_cache.cc
@@ -245,6 +245,9 @@
     // First offset for critical word first calculations
     const int initial_offset = mshr->getTarget()->pkt->getOffset(blkSize);
 
+    bool from_core = false;
+    bool from_pref = false;
+
     MSHR::TargetList targets = mshr->extractServiceableTargets(pkt);
     for (auto &target: targets) {
         Packet *tgt_pkt = target.pkt;
@@ -254,6 +257,8 @@
             // handle deferred requests comming from a cache or core
             // above
 
+            from_core = true;
+
             Tick completion_time;
             // Here we charge on completion_time the delay of the xbar if the
             // packet comes from it, charged on headerDelay.
@@ -292,8 +297,7 @@
             // attached to this cache
             assert(tgt_pkt->cmd == MemCmd::HardPFReq);
 
-            if (blk)
-                blk->setPrefetched();
+            from_pref = true;
 
             // We have filled the block and the prefetcher does not
             // require responses.
@@ -307,6 +311,10 @@
         }
     }
 
+    if (blk && !from_core && from_pref) {
+        blk->setPrefetched();
+    }
+
     // Reponses are filling and bring in writable blocks, therefore
     // there should be no deferred targets and all the non-deferred
     // targets are now serviced.
diff --git a/src/mem/cache/prefetch/Prefetcher.py b/src/mem/cache/prefetch/Prefetcher.py
index 7d70488..024004e 100644
--- a/src/mem/cache/prefetch/Prefetcher.py
+++ b/src/mem/cache/prefetch/Prefetcher.py
@@ -488,6 +488,8 @@
 
     region_miss_order_buffer_entries = Param.Unsigned(131072,
         "Number of entries of the Region Miss Order Buffer")
+    add_duplicate_entries_to_rmob = Param.Bool(True,
+        "Add duplicate entries to RMOB")
     reconstruction_entries = Param.Unsigned(256,
         "Number of reconstruction entries")
 
diff --git a/src/mem/cache/prefetch/multi.cc b/src/mem/cache/prefetch/multi.cc
index 230db50..ddf0e30 100644
--- a/src/mem/cache/prefetch/multi.cc
+++ b/src/mem/cache/prefetch/multi.cc
@@ -48,7 +48,8 @@
 
 Multi::Multi(const MultiPrefetcherParams &p)
   : Base(p),
-    prefetchers(p.prefetchers.begin(), p.prefetchers.end())
+    prefetchers(p.prefetchers.begin(), p.prefetchers.end()),
+    lastChosenPf(0)
 {
 }
 
@@ -73,14 +74,18 @@
 PacketPtr
 Multi::getPacket()
 {
-    for (auto pf : prefetchers) {
-        if (pf->nextPrefetchReadyTime() <= curTick()) {
-            PacketPtr pkt = pf->getPacket();
+    lastChosenPf = (lastChosenPf + 1) % prefetchers.size();
+    uint8_t pf_turn = lastChosenPf;
+
+    for (int pf = 0 ;  pf < prefetchers.size(); pf++) {
+        if (prefetchers[pf_turn]->nextPrefetchReadyTime() <= curTick()) {
+            PacketPtr pkt = prefetchers[pf_turn]->getPacket();
             panic_if(!pkt, "Prefetcher is ready but didn't return a packet.");
             prefetchStats.pfIssued++;
             issuedPrefetches++;
             return pkt;
         }
+        pf_turn = (pf_turn + 1) % prefetchers.size();
     }
 
     return nullptr;
diff --git a/src/mem/cache/prefetch/multi.hh b/src/mem/cache/prefetch/multi.hh
index 037d23e..ff17918 100644
--- a/src/mem/cache/prefetch/multi.hh
+++ b/src/mem/cache/prefetch/multi.hh
@@ -38,6 +38,8 @@
 #ifndef __MEM_CACHE_PREFETCH_MULTI_HH__
 #define __MEM_CACHE_PREFETCH_MULTI_HH__
 
+#include <vector>
+
 #include "mem/cache/prefetch/base.hh"
 
 namespace gem5
@@ -70,7 +72,8 @@
 
   protected:
     /** List of sub-prefetchers ordered by priority. */
-    std::list<Base*> prefetchers;
+    std::vector<Base*> prefetchers;
+    uint8_t lastChosenPf;
 };
 
 } // namespace prefetch
diff --git a/src/mem/cache/prefetch/queued.cc b/src/mem/cache/prefetch/queued.cc
index 597c88a..da9cbf4 100644
--- a/src/mem/cache/prefetch/queued.cc
+++ b/src/mem/cache/prefetch/queued.cc
@@ -210,6 +210,10 @@
 
         if (!samePage(addr_prio.first, pfi.getAddr())) {
             statsQueued.pfSpanPage += 1;
+
+            if (hasBeenPrefetched(pkt->getAddr(), pkt->isSecure())) {
+                statsQueued.pfUsefulSpanPage += 1;
+            }
         }
 
         bool can_cross_page = (tlb != nullptr);
@@ -272,7 +276,9 @@
     ADD_STAT(pfRemovedFull, statistics::units::Count::get(),
              "number of prefetches dropped due to prefetch queue size"),
     ADD_STAT(pfSpanPage, statistics::units::Count::get(),
-             "number of prefetches that crossed the page")
+             "number of prefetches that crossed the page"),
+    ADD_STAT(pfUsefulSpanPage, statistics::units::Count::get(),
+             "number of prefetches that is useful and crossed the page")
 {
 }
 
diff --git a/src/mem/cache/prefetch/queued.hh b/src/mem/cache/prefetch/queued.hh
index 1062630..c769b38 100644
--- a/src/mem/cache/prefetch/queued.hh
+++ b/src/mem/cache/prefetch/queued.hh
@@ -185,6 +185,7 @@
         statistics::Scalar pfRemovedDemand;
         statistics::Scalar pfRemovedFull;
         statistics::Scalar pfSpanPage;
+        statistics::Scalar pfUsefulSpanPage;
     } statsQueued;
   public:
     using AddrPriority = std::pair<Addr, int32_t>;
diff --git a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc
index d0f4119..3c9b9eb 100644
--- a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc
+++ b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.cc
@@ -55,7 +55,9 @@
                          p.pattern_sequence_table_replacement_policy,
                          ActiveGenerationTableEntry(
                              spatialRegionSize / blkSize)),
-    rmob(p.region_miss_order_buffer_entries)
+    rmob(p.region_miss_order_buffer_entries),
+    addDuplicateEntriesToRMOB(p.add_duplicate_entries_to_rmob),
+    lastTriggerCounter(0)
 {
     fatal_if(!isPowerOf2(spatialRegionSize),
         "The spatial region size must be a power of 2.");
@@ -73,6 +75,7 @@
         if (agt_entry.isValid()) {
             bool generation_ended = false;
             bool sr_is_secure = agt_entry.isSecure();
+            Addr pst_addr = 0;
             for (auto &seq_entry : agt_entry.sequence) {
                 if (seq_entry.counter > 0) {
                     Addr cache_addr =
@@ -80,6 +83,8 @@
                     if (!inCache(cache_addr, sr_is_secure) &&
                             !inMissQueue(cache_addr, sr_is_secure)) {
                         generation_ended = true;
+                        pst_addr = (agt_entry.pc << spatialRegionSizeBits)
+                                    + seq_entry.offset;
                         break;
                     }
                 }
@@ -87,13 +92,13 @@
             if (generation_ended) {
                 // PST is indexed using the PC (secure bit is unused)
                 ActiveGenerationTableEntry *pst_entry =
-                    patternSequenceTable.findEntry(agt_entry.pc,
+                    patternSequenceTable.findEntry(pst_addr,
                                                    false /*unused*/);
                 if (pst_entry == nullptr) {
                     // Tipically an entry will not exist
-                    pst_entry = patternSequenceTable.findVictim(agt_entry.pc);
+                    pst_entry = patternSequenceTable.findVictim(pst_addr);
                     assert(pst_entry != nullptr);
-                    patternSequenceTable.insertEntry(agt_entry.pc,
+                    patternSequenceTable.insertEntry(pst_addr,
                             false /*unused*/, pst_entry);
                 } else {
                     patternSequenceTable.accessEntry(pst_entry);
@@ -116,6 +121,16 @@
     rmob_entry.pstAddress = pst_addr;
     rmob_entry.delta = delta;
 
+    if (!addDuplicateEntriesToRMOB) {
+        for (const auto& entry : rmob) {
+            if (entry.srAddress == sr_addr &&
+                entry.pstAddress == pst_addr &&
+                entry.delta == delta) {
+                return;
+            }
+        }
+    }
+
     rmob.push_back(rmob_entry);
 }
 
diff --git a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh
index e5914b4..bee746c 100644
--- a/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh
+++ b/src/mem/cache/prefetch/spatio_temporal_memory_streaming.hh
@@ -175,6 +175,9 @@
     /** Region Miss Order Buffer (RMOB) */
     CircularQueue<RegionMissOrderBufferEntry> rmob;
 
+    /** Add duplicate entries to RMOB  */
+    bool addDuplicateEntriesToRMOB;
+
     /** Counter to keep the count of accesses between trigger accesses */
     unsigned int lastTriggerCounter;
 
diff --git a/src/mem/cache/queue_entry.hh b/src/mem/cache/queue_entry.hh
index d891365..55c1928 100644
--- a/src/mem/cache/queue_entry.hh
+++ b/src/mem/cache/queue_entry.hh
@@ -90,7 +90,7 @@
         const Tick recvTime;  //!< Time when request was received (for stats)
         const Tick readyTime; //!< Time when request is ready to be serviced
         const Counter order;  //!< Global order (for memory consistency mgmt)
-        const PacketPtr pkt;  //!< Pending request packet.
+        PacketPtr pkt;  //!< Pending request packet.
 
         /**
          * Default constructor. Assigns the current tick as the arrival time
diff --git a/src/mem/cache/replacement_policies/SConscript b/src/mem/cache/replacement_policies/SConscript
index 19f987b..027093f 100644
--- a/src/mem/cache/replacement_policies/SConscript
+++ b/src/mem/cache/replacement_policies/SConscript
@@ -45,3 +45,5 @@
 Source('ship_rp.cc')
 Source('tree_plru_rp.cc')
 Source('weighted_lru_rp.cc')
+
+GTest('replaceable_entry.test', 'replaceable_entry.test.cc')
diff --git a/src/mem/cache/replacement_policies/replaceable_entry.test.cc b/src/mem/cache/replacement_policies/replaceable_entry.test.cc
new file mode 100644
index 0000000..fde5775
--- /dev/null
+++ b/src/mem/cache/replacement_policies/replaceable_entry.test.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021 Daniel R. Carvalho
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gtest/gtest.h>
+
+#include "mem/cache/replacement_policies/replaceable_entry.hh"
+
+using namespace gem5;
+
+TEST(ReplaceableEntryTest, SetPosition)
+{
+    ReplaceableEntry entry;
+    uint32_t set = 10, way = 20;
+    entry.setPosition(set, way);
+    ASSERT_EQ(entry.getSet(), set);
+    ASSERT_EQ(entry.getWay(), way);
+}
diff --git a/src/mem/dram_interface.cc b/src/mem/dram_interface.cc
new file mode 100644
index 0000000..159e0bf
--- /dev/null
+++ b/src/mem/dram_interface.cc
@@ -0,0 +1,2031 @@
+/*
+ * Copyright (c) 2010-2020 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2013 Amin Farmahini-Farahani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mem/dram_interface.hh"
+
+#include "base/bitfield.hh"
+#include "base/cprintf.hh"
+#include "base/trace.hh"
+#include "debug/DRAM.hh"
+#include "debug/DRAMPower.hh"
+#include "debug/DRAMState.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+
+using namespace Data;
+
+namespace memory
+{
+
+std::pair<MemPacketQueue::iterator, Tick>
+DRAMInterface::chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const
+{
+    std::vector<uint32_t> earliest_banks(ranksPerChannel, 0);
+
+    // Has minBankPrep been called to populate earliest_banks?
+    bool filled_earliest_banks = false;
+    // can the PRE/ACT sequence be done without impacting utlization?
+    bool hidden_bank_prep = false;
+
+    // search for seamless row hits first, if no seamless row hit is
+    // found then determine if there are other packets that can be issued
+    // without incurring additional bus delay due to bank timing
+    // Will select closed rows first to enable more open row possibilies
+    // in future selections
+    bool found_hidden_bank = false;
+
+    // remember if we found a row hit, not seamless, but bank prepped
+    // and ready
+    bool found_prepped_pkt = false;
+
+    // if we have no row hit, prepped or not, and no seamless packet,
+    // just go for the earliest possible
+    bool found_earliest_pkt = false;
+
+    Tick selected_col_at = MaxTick;
+    auto selected_pkt_it = queue.end();
+
+    for (auto i = queue.begin(); i != queue.end() ; ++i) {
+        MemPacket* pkt = *i;
+
+        // select optimal DRAM packet in Q
+        if (pkt->isDram() && (pkt->pseudoChannel == pseudoChannel)) {
+            const Bank& bank = ranks[pkt->rank]->banks[pkt->bank];
+            const Tick col_allowed_at = pkt->isRead() ? bank.rdAllowedAt :
+                                                        bank.wrAllowedAt;
+
+            DPRINTF(DRAM, "%s checking DRAM packet in bank %d, row %d\n",
+                    __func__, pkt->bank, pkt->row);
+
+            // check if rank is not doing a refresh and thus is available,
+            // if not, jump to the next packet
+            if (burstReady(pkt)) {
+
+                DPRINTF(DRAM,
+                        "%s bank %d - Rank %d available\n", __func__,
+                        pkt->bank, pkt->rank);
+
+                // check if it is a row hit
+                if (bank.openRow == pkt->row) {
+                    // no additional rank-to-rank or same bank-group
+                    // delays, or we switched read/write and might as well
+                    // go for the row hit
+                    if (col_allowed_at <= min_col_at) {
+                        // FCFS within the hits, giving priority to
+                        // commands that can issue seamlessly, without
+                        // additional delay, such as same rank accesses
+                        // and/or different bank-group accesses
+                        DPRINTF(DRAM, "%s Seamless buffer hit\n", __func__);
+                        selected_pkt_it = i;
+                        selected_col_at = col_allowed_at;
+                        // no need to look through the remaining queue entries
+                        break;
+                    } else if (!found_hidden_bank && !found_prepped_pkt) {
+                        // if we did not find a packet to a closed row that can
+                        // issue the bank commands without incurring delay, and
+                        // did not yet find a packet to a prepped row, remember
+                        // the current one
+                        selected_pkt_it = i;
+                        selected_col_at = col_allowed_at;
+                        found_prepped_pkt = true;
+                        DPRINTF(DRAM, "%s Prepped row buffer hit\n", __func__);
+                    }
+                } else if (!found_earliest_pkt) {
+                    // if we have not initialised the bank status, do it
+                    // now, and only once per scheduling decisions
+                    if (!filled_earliest_banks) {
+                        // determine entries with earliest bank delay
+                        std::tie(earliest_banks, hidden_bank_prep) =
+                            minBankPrep(queue, min_col_at);
+                        filled_earliest_banks = true;
+                    }
+
+                    // bank is amongst first available banks
+                    // minBankPrep will give priority to packets that can
+                    // issue seamlessly
+                    if (bits(earliest_banks[pkt->rank],
+                             pkt->bank, pkt->bank)) {
+                        found_earliest_pkt = true;
+                        found_hidden_bank = hidden_bank_prep;
+
+                        // give priority to packets that can issue
+                        // bank commands 'behind the scenes'
+                        // any additional delay if any will be due to
+                        // col-to-col command requirements
+                        if (hidden_bank_prep || !found_prepped_pkt) {
+                            selected_pkt_it = i;
+                            selected_col_at = col_allowed_at;
+                        }
+                    }
+                }
+            } else {
+                DPRINTF(DRAM, "%s bank %d - Rank %d not available\n", __func__,
+                        pkt->bank, pkt->rank);
+            }
+        }
+    }
+
+    if (selected_pkt_it == queue.end()) {
+        DPRINTF(DRAM, "%s no available DRAM ranks found\n", __func__);
+    }
+
+    return std::make_pair(selected_pkt_it, selected_col_at);
+}
+
+void
+DRAMInterface::activateBank(Rank& rank_ref, Bank& bank_ref,
+                       Tick act_tick, uint32_t row)
+{
+    assert(rank_ref.actTicks.size() == activationLimit);
+
+    // verify that we have command bandwidth to issue the activate
+    // if not, shift to next burst window
+    Tick act_at;
+    if (twoCycleActivate)
+        act_at = ctrl->verifyMultiCmd(act_tick, maxCommandsPerWindow, tAAD);
+    else
+        act_at = ctrl->verifySingleCmd(act_tick, maxCommandsPerWindow, true);
+
+    DPRINTF(DRAM, "Activate at tick %d\n", act_at);
+
+    // update the open row
+    assert(bank_ref.openRow == Bank::NO_ROW);
+    bank_ref.openRow = row;
+
+    // start counting anew, this covers both the case when we
+    // auto-precharged, and when this access is forced to
+    // precharge
+    bank_ref.bytesAccessed = 0;
+    bank_ref.rowAccesses = 0;
+
+    ++rank_ref.numBanksActive;
+    assert(rank_ref.numBanksActive <= banksPerRank);
+
+    DPRINTF(DRAM, "Activate bank %d, rank %d at tick %lld, now got "
+            "%d active\n", bank_ref.bank, rank_ref.rank, act_at,
+            ranks[rank_ref.rank]->numBanksActive);
+
+    rank_ref.cmdList.push_back(Command(MemCommand::ACT, bank_ref.bank,
+                               act_at));
+
+    DPRINTF(DRAMPower, "%llu,ACT,%d,%d\n", divCeil(act_at, tCK) -
+            timeStampOffset, bank_ref.bank, rank_ref.rank);
+
+    // The next access has to respect tRAS for this bank
+    bank_ref.preAllowedAt = act_at + tRAS;
+
+    // Respect the row-to-column command delay for both read and write cmds
+    bank_ref.rdAllowedAt = std::max(act_at + tRCD_RD, bank_ref.rdAllowedAt);
+    bank_ref.wrAllowedAt = std::max(act_at + tRCD_WR, bank_ref.wrAllowedAt);
+
+    // start by enforcing tRRD
+    for (int i = 0; i < banksPerRank; i++) {
+        // next activate to any bank in this rank must not happen
+        // before tRRD
+        if (bankGroupArch && (bank_ref.bankgr == rank_ref.banks[i].bankgr)) {
+            // bank group architecture requires longer delays between
+            // ACT commands within the same bank group.  Use tRRD_L
+            // in this case
+            rank_ref.banks[i].actAllowedAt = std::max(act_at + tRRD_L,
+                                             rank_ref.banks[i].actAllowedAt);
+        } else {
+            // use shorter tRRD value when either
+            // 1) bank group architecture is not supportted
+            // 2) bank is in a different bank group
+            rank_ref.banks[i].actAllowedAt = std::max(act_at + tRRD,
+                                             rank_ref.banks[i].actAllowedAt);
+        }
+    }
+
+    // next, we deal with tXAW, if the activation limit is disabled
+    // then we directly schedule an activate power event
+    if (!rank_ref.actTicks.empty()) {
+        // sanity check
+        if (rank_ref.actTicks.back() &&
+           (act_at - rank_ref.actTicks.back()) < tXAW) {
+            panic("Got %d activates in window %d (%llu - %llu) which "
+                  "is smaller than %llu\n", activationLimit, act_at -
+                  rank_ref.actTicks.back(), act_at,
+                  rank_ref.actTicks.back(), tXAW);
+        }
+
+        // shift the times used for the book keeping, the last element
+        // (highest index) is the oldest one and hence the lowest value
+        rank_ref.actTicks.pop_back();
+
+        // record an new activation (in the future)
+        rank_ref.actTicks.push_front(act_at);
+
+        // cannot activate more than X times in time window tXAW, push the
+        // next one (the X + 1'st activate) to be tXAW away from the
+        // oldest in our window of X
+        if (rank_ref.actTicks.back() &&
+           (act_at - rank_ref.actTicks.back()) < tXAW) {
+            DPRINTF(DRAM, "Enforcing tXAW with X = %d, next activate "
+                    "no earlier than %llu\n", activationLimit,
+                    rank_ref.actTicks.back() + tXAW);
+            for (int j = 0; j < banksPerRank; j++)
+                // next activate must not happen before end of window
+                rank_ref.banks[j].actAllowedAt =
+                    std::max(rank_ref.actTicks.back() + tXAW,
+                             rank_ref.banks[j].actAllowedAt);
+        }
+    }
+
+    // at the point when this activate takes place, make sure we
+    // transition to the active power state
+    if (!rank_ref.activateEvent.scheduled())
+        schedule(rank_ref.activateEvent, act_at);
+    else if (rank_ref.activateEvent.when() > act_at)
+        // move it sooner in time
+        reschedule(rank_ref.activateEvent, act_at);
+}
+
+void
+DRAMInterface::prechargeBank(Rank& rank_ref, Bank& bank, Tick pre_tick,
+                             bool auto_or_preall, bool trace)
+{
+    // make sure the bank has an open row
+    assert(bank.openRow != Bank::NO_ROW);
+
+    // sample the bytes per activate here since we are closing
+    // the page
+    stats.bytesPerActivate.sample(bank.bytesAccessed);
+
+    bank.openRow = Bank::NO_ROW;
+
+    Tick pre_at = pre_tick;
+    if (auto_or_preall) {
+        // no precharge allowed before this one
+        bank.preAllowedAt = pre_at;
+    } else {
+        // Issuing an explicit PRE command
+        // Verify that we have command bandwidth to issue the precharge
+        // if not, shift to next burst window
+        pre_at = ctrl->verifySingleCmd(pre_tick, maxCommandsPerWindow, true);
+        // enforce tPPD
+        for (int i = 0; i < banksPerRank; i++) {
+            rank_ref.banks[i].preAllowedAt = std::max(pre_at + tPPD,
+                                             rank_ref.banks[i].preAllowedAt);
+        }
+    }
+
+    Tick pre_done_at = pre_at + tRP;
+
+    bank.actAllowedAt = std::max(bank.actAllowedAt, pre_done_at);
+
+    assert(rank_ref.numBanksActive != 0);
+    --rank_ref.numBanksActive;
+
+    DPRINTF(DRAM, "Precharging bank %d, rank %d at tick %lld, now got "
+            "%d active\n", bank.bank, rank_ref.rank, pre_at,
+            rank_ref.numBanksActive);
+
+    if (trace) {
+
+        rank_ref.cmdList.push_back(Command(MemCommand::PRE, bank.bank,
+                                   pre_at));
+        DPRINTF(DRAMPower, "%llu,PRE,%d,%d\n", divCeil(pre_at, tCK) -
+                timeStampOffset, bank.bank, rank_ref.rank);
+    }
+
+    // if we look at the current number of active banks we might be
+    // tempted to think the DRAM is now idle, however this can be
+    // undone by an activate that is scheduled to happen before we
+    // would have reached the idle state, so schedule an event and
+    // rather check once we actually make it to the point in time when
+    // the (last) precharge takes place
+    if (!rank_ref.prechargeEvent.scheduled()) {
+        schedule(rank_ref.prechargeEvent, pre_done_at);
+        // New event, increment count
+        ++rank_ref.outstandingEvents;
+    } else if (rank_ref.prechargeEvent.when() < pre_done_at) {
+        reschedule(rank_ref.prechargeEvent, pre_done_at);
+    }
+}
+
+std::pair<Tick, Tick>
+DRAMInterface::doBurstAccess(MemPacket* mem_pkt, Tick next_burst_at,
+                             const std::vector<MemPacketQueue>& queue)
+{
+    DPRINTF(DRAM, "Timing access to addr %#x, rank/bank/row %d %d %d\n",
+            mem_pkt->addr, mem_pkt->rank, mem_pkt->bank, mem_pkt->row);
+
+    // get the rank
+    Rank& rank_ref = *ranks[mem_pkt->rank];
+
+    assert(rank_ref.inRefIdleState());
+
+    // are we in or transitioning to a low-power state and have not scheduled
+    // a power-up event?
+    // if so, wake up from power down to issue RD/WR burst
+    if (rank_ref.inLowPowerState) {
+        assert(rank_ref.pwrState != PWR_SREF);
+        rank_ref.scheduleWakeUpEvent(tXP);
+    }
+
+    // get the bank
+    Bank& bank_ref = rank_ref.banks[mem_pkt->bank];
+
+    // for the state we need to track if it is a row hit or not
+    bool row_hit = true;
+
+    // Determine the access latency and update the bank state
+    if (bank_ref.openRow == mem_pkt->row) {
+        // nothing to do
+    } else {
+        row_hit = false;
+
+        // If there is a page open, precharge it.
+        if (bank_ref.openRow != Bank::NO_ROW) {
+            prechargeBank(rank_ref, bank_ref, std::max(bank_ref.preAllowedAt,
+                                                   curTick()));
+        }
+
+        // next we need to account for the delay in activating the page
+        Tick act_tick = std::max(bank_ref.actAllowedAt, curTick());
+
+        // Record the activation and deal with all the global timing
+        // constraints caused be a new activation (tRRD and tXAW)
+        activateBank(rank_ref, bank_ref, act_tick, mem_pkt->row);
+    }
+
+    // respect any constraints on the command (e.g. tRCD or tCCD)
+    const Tick col_allowed_at = mem_pkt->isRead() ?
+                                bank_ref.rdAllowedAt : bank_ref.wrAllowedAt;
+
+    // we need to wait until the bus is available before we can issue
+    // the command; need to ensure minimum bus delay requirement is met
+    Tick cmd_at = std::max({col_allowed_at, next_burst_at, curTick()});
+
+    // verify that we have command bandwidth to issue the burst
+    // if not, shift to next burst window
+    Tick max_sync = clkResyncDelay + (mem_pkt->isRead() ? tRL : tWL);
+    if (dataClockSync && ((cmd_at - rank_ref.lastBurstTick) > max_sync))
+        cmd_at = ctrl->verifyMultiCmd(cmd_at, maxCommandsPerWindow, tCK);
+    else
+        cmd_at = ctrl->verifySingleCmd(cmd_at, maxCommandsPerWindow, false);
+
+    // if we are interleaving bursts, ensure that
+    // 1) we don't double interleave on next burst issue
+    // 2) we are at an interleave boundary; if not, shift to next boundary
+    Tick burst_gap = tBURST_MIN;
+    if (burstInterleave) {
+        if (cmd_at == (rank_ref.lastBurstTick + tBURST_MIN)) {
+            // already interleaving, push next command to end of full burst
+            burst_gap = tBURST;
+        } else if (cmd_at < (rank_ref.lastBurstTick + tBURST)) {
+            // not at an interleave boundary after bandwidth check
+            // Shift command to tBURST boundary to avoid data contention
+            // Command will remain in the same burst window given that
+            // tBURST is less than tBURST_MAX
+            cmd_at = rank_ref.lastBurstTick + tBURST;
+        }
+    }
+    DPRINTF(DRAM, "Schedule RD/WR burst at tick %d\n", cmd_at);
+
+    // update the packet ready time
+    if (mem_pkt->isRead()) {
+        mem_pkt->readyTime = cmd_at + tRL + tBURST;
+    } else {
+        mem_pkt->readyTime = cmd_at + tWL + tBURST;
+    }
+
+    rank_ref.lastBurstTick = cmd_at;
+
+    // update the time for the next read/write burst for each
+    // bank (add a max with tCCD/tCCD_L/tCCD_L_WR here)
+    Tick dly_to_rd_cmd;
+    Tick dly_to_wr_cmd;
+    for (int j = 0; j < ranksPerChannel; j++) {
+        for (int i = 0; i < banksPerRank; i++) {
+            if (mem_pkt->rank == j) {
+                if (bankGroupArch &&
+                   (bank_ref.bankgr == ranks[j]->banks[i].bankgr)) {
+                    // bank group architecture requires longer delays between
+                    // RD/WR burst commands to the same bank group.
+                    // tCCD_L is default requirement for same BG timing
+                    // tCCD_L_WR is required for write-to-write
+                    // Need to also take bus turnaround delays into account
+                    dly_to_rd_cmd = mem_pkt->isRead() ?
+                                    tCCD_L : std::max(tCCD_L, wrToRdDlySameBG);
+                    dly_to_wr_cmd = mem_pkt->isRead() ?
+                                    std::max(tCCD_L, rdToWrDlySameBG) :
+                                    tCCD_L_WR;
+                } else {
+                    // tBURST is default requirement for diff BG timing
+                    // Need to also take bus turnaround delays into account
+                    dly_to_rd_cmd = mem_pkt->isRead() ? burst_gap :
+                                                       writeToReadDelay();
+                    dly_to_wr_cmd = mem_pkt->isRead() ? readToWriteDelay() :
+                                                       burst_gap;
+                }
+            } else {
+                // different rank is by default in a different bank group and
+                // doesn't require longer tCCD or additional RTW, WTR delays
+                // Need to account for rank-to-rank switching
+                dly_to_wr_cmd = rankToRankDelay();
+                dly_to_rd_cmd = rankToRankDelay();
+            }
+            ranks[j]->banks[i].rdAllowedAt = std::max(cmd_at + dly_to_rd_cmd,
+                                             ranks[j]->banks[i].rdAllowedAt);
+            ranks[j]->banks[i].wrAllowedAt = std::max(cmd_at + dly_to_wr_cmd,
+                                             ranks[j]->banks[i].wrAllowedAt);
+        }
+    }
+
+    // Save rank of current access
+    activeRank = mem_pkt->rank;
+
+    // If this is a write, we also need to respect the write recovery
+    // time before a precharge, in the case of a read, respect the
+    // read to precharge constraint
+    bank_ref.preAllowedAt = std::max(bank_ref.preAllowedAt,
+                                 mem_pkt->isRead() ? cmd_at + tRTP :
+                                 mem_pkt->readyTime + tWR);
+
+    // increment the bytes accessed and the accesses per row
+    bank_ref.bytesAccessed += burstSize;
+    ++bank_ref.rowAccesses;
+
+    // if we reached the max, then issue with an auto-precharge
+    bool auto_precharge = pageMgmt == enums::close ||
+        bank_ref.rowAccesses == maxAccessesPerRow;
+
+    // if we did not hit the limit, we might still want to
+    // auto-precharge
+    if (!auto_precharge &&
+        (pageMgmt == enums::open_adaptive ||
+         pageMgmt == enums::close_adaptive)) {
+        // a twist on the open and close page policies:
+        // 1) open_adaptive page policy does not blindly keep the
+        // page open, but close it if there are no row hits, and there
+        // are bank conflicts in the queue
+        // 2) close_adaptive page policy does not blindly close the
+        // page, but closes it only if there are no row hits in the queue.
+        // In this case, only force an auto precharge when there
+        // are no same page hits in the queue
+        bool got_more_hits = false;
+        bool got_bank_conflict = false;
+
+        for (uint8_t i = 0; i < ctrl->numPriorities(); ++i) {
+            auto p = queue[i].begin();
+            // keep on looking until we find a hit or reach the end of the
+            // queue
+            // 1) if a hit is found, then both open and close adaptive
+            //    policies keep the page open
+            // 2) if no hit is found, got_bank_conflict is set to true if a
+            //    bank conflict request is waiting in the queue
+            // 3) make sure we are not considering the packet that we are
+            //    currently dealing with
+            while (!got_more_hits && p != queue[i].end()) {
+
+                if ((*p)->pseudoChannel != pseudoChannel) {
+                    // only consider if this pkt belongs to this interface
+                    ++p;
+                    continue;
+                }
+
+                if (mem_pkt != (*p)) {
+                    bool same_rank_bank = (mem_pkt->rank == (*p)->rank) &&
+                                          (mem_pkt->bank == (*p)->bank);
+
+                    bool same_row = mem_pkt->row == (*p)->row;
+                    got_more_hits |= same_rank_bank && same_row;
+                    got_bank_conflict |= same_rank_bank && !same_row;
+                }
+                ++p;
+            }
+
+            if (got_more_hits)
+                break;
+        }
+
+        // auto pre-charge when either
+        // 1) open_adaptive policy, we have not got any more hits, and
+        //    have a bank conflict
+        // 2) close_adaptive policy and we have not got any more hits
+        auto_precharge = !got_more_hits &&
+            (got_bank_conflict || pageMgmt == enums::close_adaptive);
+    }
+
+    // DRAMPower trace command to be written
+    std::string mem_cmd = mem_pkt->isRead() ? "RD" : "WR";
+
+    // MemCommand required for DRAMPower library
+    MemCommand::cmds command = (mem_cmd == "RD") ? MemCommand::RD :
+                                                   MemCommand::WR;
+
+    rank_ref.cmdList.push_back(Command(command, mem_pkt->bank, cmd_at));
+
+    DPRINTF(DRAMPower, "%llu,%s,%d,%d\n", divCeil(cmd_at, tCK) -
+            timeStampOffset, mem_cmd, mem_pkt->bank, mem_pkt->rank);
+
+    // if this access should use auto-precharge, then we are
+    // closing the row after the read/write burst
+    if (auto_precharge) {
+        // if auto-precharge push a PRE command at the correct tick to the
+        // list used by DRAMPower library to calculate power
+        prechargeBank(rank_ref, bank_ref, std::max(curTick(),
+                      bank_ref.preAllowedAt), true);
+
+        DPRINTF(DRAM, "Auto-precharged bank: %d\n", mem_pkt->bankId);
+    }
+
+    // Update the stats and schedule the next request
+    if (mem_pkt->isRead()) {
+        // Every respQueue which will generate an event, increment count
+        ++rank_ref.outstandingEvents;
+
+        stats.readBursts++;
+        if (row_hit)
+            stats.readRowHits++;
+        stats.bytesRead += burstSize;
+        stats.perBankRdBursts[mem_pkt->bankId]++;
+
+        // Update latency stats
+        stats.totMemAccLat += mem_pkt->readyTime - mem_pkt->entryTime;
+        stats.totQLat += cmd_at - mem_pkt->entryTime;
+        stats.totBusLat += tBURST;
+    } else {
+        // Schedule write done event to decrement event count
+        // after the readyTime has been reached
+        // Only schedule latest write event to minimize events
+        // required; only need to ensure that final event scheduled covers
+        // the time that writes are outstanding and bus is active
+        // to holdoff power-down entry events
+        if (!rank_ref.writeDoneEvent.scheduled()) {
+            schedule(rank_ref.writeDoneEvent, mem_pkt->readyTime);
+            // New event, increment count
+            ++rank_ref.outstandingEvents;
+
+        } else if (rank_ref.writeDoneEvent.when() < mem_pkt->readyTime) {
+            reschedule(rank_ref.writeDoneEvent, mem_pkt->readyTime);
+        }
+        // will remove write from queue when returned to parent function
+        // decrement count for DRAM rank
+        --rank_ref.writeEntries;
+
+        stats.writeBursts++;
+        if (row_hit)
+            stats.writeRowHits++;
+        stats.bytesWritten += burstSize;
+        stats.perBankWrBursts[mem_pkt->bankId]++;
+
+    }
+    // Update bus state to reflect when previous command was issued
+    return std::make_pair(cmd_at, cmd_at + burst_gap);
+}
+
+void
+DRAMInterface::addRankToRankDelay(Tick cmd_at)
+{
+    // update timing for DRAM ranks due to bursts issued
+    // to ranks on other media interfaces
+    for (auto n : ranks) {
+        for (int i = 0; i < banksPerRank; i++) {
+            // different rank by default
+            // Need to only account for rank-to-rank switching
+            n->banks[i].rdAllowedAt = std::max(cmd_at + rankToRankDelay(),
+                                             n->banks[i].rdAllowedAt);
+            n->banks[i].wrAllowedAt = std::max(cmd_at + rankToRankDelay(),
+                                             n->banks[i].wrAllowedAt);
+        }
+    }
+}
+
+DRAMInterface::DRAMInterface(const DRAMInterfaceParams &_p)
+    : MemInterface(_p),
+      bankGroupsPerRank(_p.bank_groups_per_rank),
+      bankGroupArch(_p.bank_groups_per_rank > 0),
+      tRL(_p.tCL),
+      tWL(_p.tCWL),
+      tBURST_MIN(_p.tBURST_MIN), tBURST_MAX(_p.tBURST_MAX),
+      tCCD_L_WR(_p.tCCD_L_WR), tCCD_L(_p.tCCD_L),
+      tRCD_RD(_p.tRCD), tRCD_WR(_p.tRCD_WR),
+      tRP(_p.tRP), tRAS(_p.tRAS), tWR(_p.tWR), tRTP(_p.tRTP),
+      tRFC(_p.tRFC), tREFI(_p.tREFI), tRRD(_p.tRRD), tRRD_L(_p.tRRD_L),
+      tPPD(_p.tPPD), tAAD(_p.tAAD),
+      tXAW(_p.tXAW), tXP(_p.tXP), tXS(_p.tXS),
+      clkResyncDelay(_p.tBURST_MAX),
+      dataClockSync(_p.data_clock_sync),
+      burstInterleave(tBURST != tBURST_MIN),
+      twoCycleActivate(_p.two_cycle_activate),
+      activationLimit(_p.activation_limit),
+      wrToRdDlySameBG(tWL + _p.tBURST_MAX + _p.tWTR_L),
+      rdToWrDlySameBG(_p.tRTW + _p.tBURST_MAX),
+      pageMgmt(_p.page_policy),
+      maxAccessesPerRow(_p.max_accesses_per_row),
+      timeStampOffset(0), activeRank(0),
+      enableDRAMPowerdown(_p.enable_dram_powerdown),
+      lastStatsResetTick(0),
+      stats(*this)
+{
+    DPRINTF(DRAM, "Setting up DRAM Interface\n");
+
+    fatal_if(!isPowerOf2(burstSize), "DRAM burst size %d is not allowed, "
+             "must be a power of two\n", burstSize);
+
+    // sanity check the ranks since we rely on bit slicing for the
+    // address decoding
+    fatal_if(!isPowerOf2(ranksPerChannel), "DRAM rank count of %d is "
+             "not allowed, must be a power of two\n", ranksPerChannel);
+
+    for (int i = 0; i < ranksPerChannel; i++) {
+        DPRINTF(DRAM, "Creating DRAM rank %d \n", i);
+        Rank* rank = new Rank(_p, i, *this);
+        ranks.push_back(rank);
+    }
+
+    // determine the dram actual capacity from the DRAM config in Mbytes
+    uint64_t deviceCapacity = deviceSize / (1024 * 1024) * devicesPerRank *
+                              ranksPerChannel;
+
+    uint64_t capacity = 1ULL << ceilLog2(AbstractMemory::size());
+
+    DPRINTF(DRAM, "Memory capacity %lld (%lld) bytes\n", capacity,
+            AbstractMemory::size());
+
+    // if actual DRAM size does not match memory capacity in system warn!
+    if (deviceCapacity != capacity / (1024 * 1024))
+        warn("DRAM device capacity (%d Mbytes) does not match the "
+             "address range assigned (%d Mbytes)\n", deviceCapacity,
+             capacity / (1024 * 1024));
+
+    DPRINTF(DRAM, "Row buffer size %d bytes with %d bursts per row buffer\n",
+            rowBufferSize, burstsPerRowBuffer);
+
+    rowsPerBank = capacity / (rowBufferSize * banksPerRank * ranksPerChannel);
+
+    // some basic sanity checks
+    if (tREFI <= tRP || tREFI <= tRFC) {
+        fatal("tREFI (%d) must be larger than tRP (%d) and tRFC (%d)\n",
+              tREFI, tRP, tRFC);
+    }
+
+    // basic bank group architecture checks ->
+    if (bankGroupArch) {
+        // must have at least one bank per bank group
+        if (bankGroupsPerRank > banksPerRank) {
+            fatal("banks per rank (%d) must be equal to or larger than "
+                  "banks groups per rank (%d)\n",
+                  banksPerRank, bankGroupsPerRank);
+        }
+        // must have same number of banks in each bank group
+        if ((banksPerRank % bankGroupsPerRank) != 0) {
+            fatal("Banks per rank (%d) must be evenly divisible by bank "
+                  "groups per rank (%d) for equal banks per bank group\n",
+                  banksPerRank, bankGroupsPerRank);
+        }
+        // tCCD_L should be greater than minimal, back-to-back burst delay
+        if (tCCD_L <= tBURST) {
+            fatal("tCCD_L (%d) should be larger than the minimum bus delay "
+                  "(%d) when bank groups per rank (%d) is greater than 1\n",
+                  tCCD_L, tBURST, bankGroupsPerRank);
+        }
+        // tCCD_L_WR should be greater than minimal, back-to-back burst delay
+        if (tCCD_L_WR <= tBURST) {
+            fatal("tCCD_L_WR (%d) should be larger than the minimum bus delay "
+                  " (%d) when bank groups per rank (%d) is greater than 1\n",
+                  tCCD_L_WR, tBURST, bankGroupsPerRank);
+        }
+        // tRRD_L is greater than minimal, same bank group ACT-to-ACT delay
+        // some datasheets might specify it equal to tRRD
+        if (tRRD_L < tRRD) {
+            fatal("tRRD_L (%d) should be larger than tRRD (%d) when "
+                  "bank groups per rank (%d) is greater than 1\n",
+                  tRRD_L, tRRD, bankGroupsPerRank);
+        }
+    }
+}
+
+void
+DRAMInterface::init()
+{
+    AbstractMemory::init();
+
+    // a bit of sanity checks on the interleaving, save it for here to
+    // ensure that the system pointer is initialised
+    if (range.interleaved()) {
+        if (addrMapping == enums::RoRaBaChCo) {
+            if (rowBufferSize != range.granularity()) {
+                fatal("Channel interleaving of %s doesn't match RoRaBaChCo "
+                      "address map\n", name());
+            }
+        } else if (addrMapping == enums::RoRaBaCoCh ||
+                   addrMapping == enums::RoCoRaBaCh) {
+            // for the interleavings with channel bits in the bottom,
+            // if the system uses a channel striping granularity that
+            // is larger than the DRAM burst size, then map the
+            // sequential accesses within a stripe to a number of
+            // columns in the DRAM, effectively placing some of the
+            // lower-order column bits as the least-significant bits
+            // of the address (above the ones denoting the burst size)
+            assert(burstsPerStripe >= 1);
+
+            // channel striping has to be done at a granularity that
+            // is equal or larger to a cache line
+            if (system()->cacheLineSize() > range.granularity()) {
+                fatal("Channel interleaving of %s must be at least as large "
+                      "as the cache line size\n", name());
+            }
+
+            // ...and equal or smaller than the row-buffer size
+            if (rowBufferSize < range.granularity()) {
+                fatal("Channel interleaving of %s must be at most as large "
+                      "as the row-buffer size\n", name());
+            }
+            // this is essentially the check above, so just to be sure
+            assert(burstsPerStripe <= burstsPerRowBuffer);
+        }
+    }
+}
+
+void
+DRAMInterface::startup()
+{
+    if (system()->isTimingMode()) {
+        // timestamp offset should be in clock cycles for DRAMPower
+        timeStampOffset = divCeil(curTick(), tCK);
+
+        for (auto r : ranks) {
+            r->startup(curTick() + tREFI - tRP);
+        }
+    }
+}
+
+bool
+DRAMInterface::isBusy(bool read_queue_empty, bool all_writes_nvm)
+{
+    int busy_ranks = 0;
+    for (auto r : ranks) {
+        if (!r->inRefIdleState()) {
+            if (r->pwrState != PWR_SREF) {
+                // rank is busy refreshing
+                DPRINTF(DRAMState, "Rank %d is not available\n", r->rank);
+                busy_ranks++;
+
+                // let the rank know that if it was waiting to drain, it
+                // is now done and ready to proceed
+                r->checkDrainDone();
+            }
+
+            // check if we were in self-refresh and haven't started
+            // to transition out
+            if ((r->pwrState == PWR_SREF) && r->inLowPowerState) {
+                DPRINTF(DRAMState, "Rank %d is in self-refresh\n", r->rank);
+                // if we have commands queued to this rank and we don't have
+                // a minimum number of active commands enqueued,
+                // exit self-refresh
+                if (r->forceSelfRefreshExit()) {
+                    DPRINTF(DRAMState, "rank %d was in self refresh and"
+                           " should wake up\n", r->rank);
+                    //wake up from self-refresh
+                    r->scheduleWakeUpEvent(tXS);
+                    // things are brought back into action once a refresh is
+                    // performed after self-refresh
+                    // continue with selection for other ranks
+                }
+            }
+        }
+    }
+    return (busy_ranks == ranksPerChannel);
+}
+
+MemPacket*
+DRAMInterface::decodePacket(const PacketPtr pkt, Addr pkt_addr,
+                       unsigned size, bool is_read, uint8_t pseudo_channel)
+{
+    // decode the address based on the address mapping scheme, with
+    // Ro, Ra, Co, Ba and Ch denoting row, rank, column, bank and
+    // channel, respectively
+    uint8_t rank;
+    uint8_t bank;
+    // use a 64-bit unsigned during the computations as the row is
+    // always the top bits, and check before creating the packet
+    uint64_t row;
+
+    // Get packed address, starting at 0
+    Addr addr = getCtrlAddr(pkt_addr);
+
+    // truncate the address to a memory burst, which makes it unique to
+    // a specific buffer, row, bank, rank and channel
+    addr = addr / burstSize;
+
+    // we have removed the lowest order address bits that denote the
+    // position within the column
+    if (addrMapping == enums::RoRaBaChCo || addrMapping == enums::RoRaBaCoCh) {
+        // the lowest order bits denote the column to ensure that
+        // sequential cache lines occupy the same row
+        addr = addr / burstsPerRowBuffer;
+
+        // after the channel bits, get the bank bits to interleave
+        // over the banks
+        bank = addr % banksPerRank;
+        addr = addr / banksPerRank;
+
+        // after the bank, we get the rank bits which thus interleaves
+        // over the ranks
+        rank = addr % ranksPerChannel;
+        addr = addr / ranksPerChannel;
+
+        // lastly, get the row bits, no need to remove them from addr
+        row = addr % rowsPerBank;
+    } else if (addrMapping == enums::RoCoRaBaCh) {
+        // with emerging technologies, could have small page size with
+        // interleaving granularity greater than row buffer
+        if (burstsPerStripe > burstsPerRowBuffer) {
+            // remove column bits which are a subset of burstsPerStripe
+            addr = addr / burstsPerRowBuffer;
+        } else {
+            // remove lower column bits below channel bits
+            addr = addr / burstsPerStripe;
+        }
+
+        // start with the bank bits, as this provides the maximum
+        // opportunity for parallelism between requests
+        bank = addr % banksPerRank;
+        addr = addr / banksPerRank;
+
+        // next get the rank bits
+        rank = addr % ranksPerChannel;
+        addr = addr / ranksPerChannel;
+
+        // next, the higher-order column bites
+        if (burstsPerStripe < burstsPerRowBuffer) {
+            addr = addr / (burstsPerRowBuffer / burstsPerStripe);
+        }
+
+        // lastly, get the row bits, no need to remove them from addr
+        row = addr % rowsPerBank;
+    } else
+        panic("Unknown address mapping policy chosen!");
+
+    assert(rank < ranksPerChannel);
+    assert(bank < banksPerRank);
+    assert(row < rowsPerBank);
+    assert(row < Bank::NO_ROW);
+
+    DPRINTF(DRAM, "Address: %#x Rank %d Bank %d Row %d\n",
+            pkt_addr, rank, bank, row);
+
+    // create the corresponding memory packet with the entry time and
+    // ready time set to the current tick, the latter will be updated
+    // later
+    uint16_t bank_id = banksPerRank * rank + bank;
+
+    return new MemPacket(pkt, is_read, true, pseudo_channel, rank, bank, row,
+                   bank_id, pkt_addr, size);
+}
+
+void DRAMInterface::setupRank(const uint8_t rank, const bool is_read)
+{
+    // increment entry count of the rank based on packet type
+    if (is_read) {
+        ++ranks[rank]->readEntries;
+    } else {
+        ++ranks[rank]->writeEntries;
+    }
+}
+
+void
+DRAMInterface::respondEvent(uint8_t rank)
+{
+    Rank& rank_ref = *ranks[rank];
+
+    // if a read has reached its ready-time, decrement the number of reads
+    // At this point the packet has been handled and there is a possibility
+    // to switch to low-power mode if no other packet is available
+    --rank_ref.readEntries;
+    DPRINTF(DRAM, "number of read entries for rank %d is %d\n",
+            rank, rank_ref.readEntries);
+
+    // counter should at least indicate one outstanding request
+    // for this read
+    assert(rank_ref.outstandingEvents > 0);
+    // read response received, decrement count
+    --rank_ref.outstandingEvents;
+
+    // at this moment should not have transitioned to a low-power state
+    assert((rank_ref.pwrState != PWR_SREF) &&
+           (rank_ref.pwrState != PWR_PRE_PDN) &&
+           (rank_ref.pwrState != PWR_ACT_PDN));
+
+    // track if this is the last packet before idling
+    // and that there are no outstanding commands to this rank
+    if (rank_ref.isQueueEmpty() && rank_ref.outstandingEvents == 0 &&
+        rank_ref.inRefIdleState() && enableDRAMPowerdown) {
+        // verify that there are no events scheduled
+        assert(!rank_ref.activateEvent.scheduled());
+        assert(!rank_ref.prechargeEvent.scheduled());
+
+        // if coming from active state, schedule power event to
+        // active power-down else go to precharge power-down
+        DPRINTF(DRAMState, "Rank %d sleep at tick %d; current power state is "
+                "%d\n", rank, curTick(), rank_ref.pwrState);
+
+        // default to ACT power-down unless already in IDLE state
+        // could be in IDLE if PRE issued before data returned
+        PowerState next_pwr_state = PWR_ACT_PDN;
+        if (rank_ref.pwrState == PWR_IDLE) {
+            next_pwr_state = PWR_PRE_PDN;
+        }
+
+        rank_ref.powerDownSleep(next_pwr_state, curTick());
+    }
+}
+
+void
+DRAMInterface::checkRefreshState(uint8_t rank)
+{
+    Rank& rank_ref = *ranks[rank];
+
+    if ((rank_ref.refreshState == REF_PRE) &&
+        !rank_ref.prechargeEvent.scheduled()) {
+          // kick the refresh event loop into action again if banks already
+          // closed and just waiting for read to complete
+          schedule(rank_ref.refreshEvent, curTick());
+    }
+}
+
+void
+DRAMInterface::drainRanks()
+{
+    // also need to kick off events to exit self-refresh
+    for (auto r : ranks) {
+        // force self-refresh exit, which in turn will issue auto-refresh
+        if (r->pwrState == PWR_SREF) {
+            DPRINTF(DRAM,"Rank%d: Forcing self-refresh wakeup in drain\n",
+                    r->rank);
+            r->scheduleWakeUpEvent(tXS);
+        }
+    }
+}
+
+bool
+DRAMInterface::allRanksDrained() const
+{
+    // true until proven false
+    bool all_ranks_drained = true;
+    for (auto r : ranks) {
+        // then verify that the power state is IDLE ensuring all banks are
+        // closed and rank is not in a low power state. Also verify that rank
+        // is idle from a refresh point of view.
+        all_ranks_drained = r->inPwrIdleState() && r->inRefIdleState() &&
+            all_ranks_drained;
+    }
+    return all_ranks_drained;
+}
+
+void
+DRAMInterface::suspend()
+{
+    for (auto r : ranks) {
+        r->suspend();
+    }
+}
+
+std::pair<std::vector<uint32_t>, bool>
+DRAMInterface::minBankPrep(const MemPacketQueue& queue,
+                      Tick min_col_at) const
+{
+    Tick min_act_at = MaxTick;
+    std::vector<uint32_t> bank_mask(ranksPerChannel, 0);
+
+    // Flag condition when burst can issue back-to-back with previous burst
+    bool found_seamless_bank = false;
+
+    // Flag condition when bank can be opened without incurring additional
+    // delay on the data bus
+    bool hidden_bank_prep = false;
+
+    // determine if we have queued transactions targetting the
+    // bank in question
+    std::vector<bool> got_waiting(ranksPerChannel * banksPerRank, false);
+    for (const auto& p : queue) {
+        if (p->pseudoChannel != pseudoChannel)
+            continue;
+        if (p->isDram() && ranks[p->rank]->inRefIdleState())
+            got_waiting[p->bankId] = true;
+    }
+
+    // Find command with optimal bank timing
+    // Will prioritize commands that can issue seamlessly.
+    for (int i = 0; i < ranksPerChannel; i++) {
+        for (int j = 0; j < banksPerRank; j++) {
+            uint16_t bank_id = i * banksPerRank + j;
+
+            // if we have waiting requests for the bank, and it is
+            // amongst the first available, update the mask
+            if (got_waiting[bank_id]) {
+                // make sure this rank is not currently refreshing.
+                assert(ranks[i]->inRefIdleState());
+                // simplistic approximation of when the bank can issue
+                // an activate, ignoring any rank-to-rank switching
+                // cost in this calculation
+                Tick act_at = ranks[i]->banks[j].openRow == Bank::NO_ROW ?
+                    std::max(ranks[i]->banks[j].actAllowedAt, curTick()) :
+                    std::max(ranks[i]->banks[j].preAllowedAt, curTick()) + tRP;
+
+                // latest Tick for which ACT can occur without
+                // incurring additoinal delay on the data bus
+                const Tick tRCD = ctrl->inReadBusState(false) ?
+                                                 tRCD_RD : tRCD_WR;
+                const Tick hidden_act_max =
+                            std::max(min_col_at - tRCD, curTick());
+
+                // When is the earliest the R/W burst can issue?
+                const Tick col_allowed_at = ctrl->inReadBusState(false) ?
+                                              ranks[i]->banks[j].rdAllowedAt :
+                                              ranks[i]->banks[j].wrAllowedAt;
+                Tick col_at = std::max(col_allowed_at, act_at + tRCD);
+
+                // bank can issue burst back-to-back (seamlessly) with
+                // previous burst
+                bool new_seamless_bank = col_at <= min_col_at;
+
+                // if we found a new seamless bank or we have no
+                // seamless banks, and got a bank with an earlier
+                // activate time, it should be added to the bit mask
+                if (new_seamless_bank ||
+                    (!found_seamless_bank && act_at <= min_act_at)) {
+                    // if we did not have a seamless bank before, and
+                    // we do now, reset the bank mask, also reset it
+                    // if we have not yet found a seamless bank and
+                    // the activate time is smaller than what we have
+                    // seen so far
+                    if (!found_seamless_bank &&
+                        (new_seamless_bank || act_at < min_act_at)) {
+                        std::fill(bank_mask.begin(), bank_mask.end(), 0);
+                    }
+
+                    found_seamless_bank |= new_seamless_bank;
+
+                    // ACT can occur 'behind the scenes'
+                    hidden_bank_prep = act_at <= hidden_act_max;
+
+                    // set the bit corresponding to the available bank
+                    replaceBits(bank_mask[i], j, j, 1);
+                    min_act_at = act_at;
+                }
+            }
+        }
+    }
+
+    return std::make_pair(bank_mask, hidden_bank_prep);
+}
+
+DRAMInterface::Rank::Rank(const DRAMInterfaceParams &_p,
+                         int _rank, DRAMInterface& _dram)
+    : EventManager(&_dram), dram(_dram),
+      pwrStateTrans(PWR_IDLE), pwrStatePostRefresh(PWR_IDLE),
+      pwrStateTick(0), refreshDueAt(0), pwrState(PWR_IDLE),
+      refreshState(REF_IDLE), inLowPowerState(false), rank(_rank),
+      readEntries(0), writeEntries(0), outstandingEvents(0),
+      wakeUpAllowedAt(0), power(_p, false), banks(_p.banks_per_rank),
+      numBanksActive(0), actTicks(_p.activation_limit, 0), lastBurstTick(0),
+      writeDoneEvent([this]{ processWriteDoneEvent(); }, name()),
+      activateEvent([this]{ processActivateEvent(); }, name()),
+      prechargeEvent([this]{ processPrechargeEvent(); }, name()),
+      refreshEvent([this]{ processRefreshEvent(); }, name()),
+      powerEvent([this]{ processPowerEvent(); }, name()),
+      wakeUpEvent([this]{ processWakeUpEvent(); }, name()),
+      stats(_dram, *this)
+{
+    for (int b = 0; b < _p.banks_per_rank; b++) {
+        banks[b].bank = b;
+        // GDDR addressing of banks to BG is linear.
+        // Here we assume that all DRAM generations address bank groups as
+        // follows:
+        if (_p.bank_groups_per_rank > 0) {
+            // Simply assign lower bits to bank group in order to
+            // rotate across bank groups as banks are incremented
+            // e.g. with 4 banks per bank group and 16 banks total:
+            //    banks 0,4,8,12  are in bank group 0
+            //    banks 1,5,9,13  are in bank group 1
+            //    banks 2,6,10,14 are in bank group 2
+            //    banks 3,7,11,15 are in bank group 3
+            banks[b].bankgr = b % _p.bank_groups_per_rank;
+        } else {
+            // No bank groups; simply assign to bank number
+            banks[b].bankgr = b;
+        }
+    }
+}
+
+void
+DRAMInterface::Rank::startup(Tick ref_tick)
+{
+    assert(ref_tick > curTick());
+
+    pwrStateTick = curTick();
+
+    // kick off the refresh, and give ourselves enough time to
+    // precharge
+    schedule(refreshEvent, ref_tick);
+}
+
+void
+DRAMInterface::Rank::suspend()
+{
+    deschedule(refreshEvent);
+
+    // Update the stats
+    updatePowerStats();
+
+    // don't automatically transition back to LP state after next REF
+    pwrStatePostRefresh = PWR_IDLE;
+}
+
+bool
+DRAMInterface::Rank::isQueueEmpty() const
+{
+    // check commmands in Q based on current bus direction
+    bool no_queued_cmds = (dram.ctrl->inReadBusState(true) &&
+                          (readEntries == 0))
+                       || (dram.ctrl->inWriteBusState(true) &&
+                          (writeEntries == 0));
+    return no_queued_cmds;
+}
+
+void
+DRAMInterface::Rank::checkDrainDone()
+{
+    // if this rank was waiting to drain it is now able to proceed to
+    // precharge
+    if (refreshState == REF_DRAIN) {
+        DPRINTF(DRAM, "Refresh drain done, now precharging\n");
+
+        refreshState = REF_PD_EXIT;
+
+        // hand control back to the refresh event loop
+        schedule(refreshEvent, curTick());
+    }
+}
+
+void
+DRAMInterface::Rank::flushCmdList()
+{
+    // at the moment sort the list of commands and update the counters
+    // for DRAMPower libray when doing a refresh
+    sort(cmdList.begin(), cmdList.end(), DRAMInterface::sortTime);
+
+    auto next_iter = cmdList.begin();
+    // push to commands to DRAMPower
+    for ( ; next_iter != cmdList.end() ; ++next_iter) {
+         Command cmd = *next_iter;
+         if (cmd.timeStamp <= curTick()) {
+             // Move all commands at or before curTick to DRAMPower
+             power.powerlib.doCommand(cmd.type, cmd.bank,
+                                      divCeil(cmd.timeStamp, dram.tCK) -
+                                      dram.timeStampOffset);
+         } else {
+             // done - found all commands at or before curTick()
+             // next_iter references the 1st command after curTick
+             break;
+         }
+    }
+    // reset cmdList to only contain commands after curTick
+    // if there are no commands after curTick, updated cmdList will be empty
+    // in this case, next_iter is cmdList.end()
+    cmdList.assign(next_iter, cmdList.end());
+}
+
+void
+DRAMInterface::Rank::processActivateEvent()
+{
+    // we should transition to the active state as soon as any bank is active
+    if (pwrState != PWR_ACT)
+        // note that at this point numBanksActive could be back at
+        // zero again due to a precharge scheduled in the future
+        schedulePowerEvent(PWR_ACT, curTick());
+}
+
+void
+DRAMInterface::Rank::processPrechargeEvent()
+{
+    // counter should at least indicate one outstanding request
+    // for this precharge
+    assert(outstandingEvents > 0);
+    // precharge complete, decrement count
+    --outstandingEvents;
+
+    // if we reached zero, then special conditions apply as we track
+    // if all banks are precharged for the power models
+    if (numBanksActive == 0) {
+        // no reads to this rank in the Q and no pending
+        // RD/WR or refresh commands
+        if (isQueueEmpty() && outstandingEvents == 0 &&
+            dram.enableDRAMPowerdown) {
+            // should still be in ACT state since bank still open
+            assert(pwrState == PWR_ACT);
+
+            // All banks closed - switch to precharge power down state.
+            DPRINTF(DRAMState, "Rank %d sleep at tick %d\n",
+                    rank, curTick());
+            powerDownSleep(PWR_PRE_PDN, curTick());
+        } else {
+            // we should transition to the idle state when the last bank
+            // is precharged
+            schedulePowerEvent(PWR_IDLE, curTick());
+        }
+    }
+}
+
+void
+DRAMInterface::Rank::processWriteDoneEvent()
+{
+    // counter should at least indicate one outstanding request
+    // for this write
+    assert(outstandingEvents > 0);
+    // Write transfer on bus has completed
+    // decrement per rank counter
+    --outstandingEvents;
+}
+
+void
+DRAMInterface::Rank::processRefreshEvent()
+{
+    // when first preparing the refresh, remember when it was due
+    if ((refreshState == REF_IDLE) || (refreshState == REF_SREF_EXIT)) {
+        // remember when the refresh is due
+        refreshDueAt = curTick();
+
+        // proceed to drain
+        refreshState = REF_DRAIN;
+
+        // make nonzero while refresh is pending to ensure
+        // power down and self-refresh are not entered
+        ++outstandingEvents;
+
+        DPRINTF(DRAM, "Refresh due\n");
+    }
+
+    // let any scheduled read or write to the same rank go ahead,
+    // after which it will
+    // hand control back to this event loop
+    if (refreshState == REF_DRAIN) {
+        // if a request is at the moment being handled and this request is
+        // accessing the current rank then wait for it to finish
+        if ((rank == dram.activeRank)
+            && (dram.ctrl->requestEventScheduled(dram.pseudoChannel))) {
+            // hand control over to the request loop until it is
+            // evaluated next
+            DPRINTF(DRAM, "Refresh awaiting draining\n");
+            return;
+        } else {
+            refreshState = REF_PD_EXIT;
+        }
+    }
+
+    // at this point, ensure that rank is not in a power-down state
+    if (refreshState == REF_PD_EXIT) {
+        // if rank was sleeping and we have't started exit process,
+        // wake-up for refresh
+        if (inLowPowerState) {
+            DPRINTF(DRAM, "Wake Up for refresh\n");
+            // save state and return after refresh completes
+            scheduleWakeUpEvent(dram.tXP);
+            return;
+        } else {
+            refreshState = REF_PRE;
+        }
+    }
+
+    // at this point, ensure that all banks are precharged
+    if (refreshState == REF_PRE) {
+        // precharge any active bank
+        if (numBanksActive != 0) {
+            // at the moment, we use a precharge all even if there is
+            // only a single bank open
+            DPRINTF(DRAM, "Precharging all\n");
+
+            // first determine when we can precharge
+            Tick pre_at = curTick();
+
+            for (auto &b : banks) {
+                // respect both causality and any existing bank
+                // constraints, some banks could already have a
+                // (auto) precharge scheduled
+                pre_at = std::max(b.preAllowedAt, pre_at);
+            }
+
+            // make sure all banks per rank are precharged, and for those that
+            // already are, update their availability
+            Tick act_allowed_at = pre_at + dram.tRP;
+
+            for (auto &b : banks) {
+                if (b.openRow != Bank::NO_ROW) {
+                    dram.prechargeBank(*this, b, pre_at, true, false);
+                } else {
+                    b.actAllowedAt = std::max(b.actAllowedAt, act_allowed_at);
+                    b.preAllowedAt = std::max(b.preAllowedAt, pre_at);
+                }
+            }
+
+            // precharge all banks in rank
+            cmdList.push_back(Command(MemCommand::PREA, 0, pre_at));
+
+            DPRINTF(DRAMPower, "%llu,PREA,0,%d\n",
+                    divCeil(pre_at, dram.tCK) -
+                            dram.timeStampOffset, rank);
+        } else if ((pwrState == PWR_IDLE) && (outstandingEvents == 1))  {
+            // Banks are closed, have transitioned to IDLE state, and
+            // no outstanding ACT,RD/WR,Auto-PRE sequence scheduled
+            DPRINTF(DRAM, "All banks already precharged, starting refresh\n");
+
+            // go ahead and kick the power state machine into gear since
+            // we are already idle
+            schedulePowerEvent(PWR_REF, curTick());
+        } else {
+            // banks state is closed but haven't transitioned pwrState to IDLE
+            // or have outstanding ACT,RD/WR,Auto-PRE sequence scheduled
+            // should have outstanding precharge or read response event
+            assert(prechargeEvent.scheduled() ||
+                   dram.ctrl->respondEventScheduled());
+            // will start refresh when pwrState transitions to IDLE
+        }
+
+        assert(numBanksActive == 0);
+
+        // wait for all banks to be precharged or read to complete
+        // When precharge commands are done, power state machine will
+        // transition to the idle state, and automatically move to a
+        // refresh, at that point it will also call this method to get
+        // the refresh event loop going again
+        // Similarly, when read response completes, if all banks are
+        // precharged, will call this method to get loop re-started
+        return;
+    }
+
+    // last but not least we perform the actual refresh
+    if (refreshState == REF_START) {
+        // should never get here with any banks active
+        assert(numBanksActive == 0);
+        assert(pwrState == PWR_REF);
+
+        Tick ref_done_at = curTick() + dram.tRFC;
+
+        for (auto &b : banks) {
+            b.actAllowedAt = ref_done_at;
+        }
+
+        // at the moment this affects all ranks
+        cmdList.push_back(Command(MemCommand::REF, 0, curTick()));
+
+        // Update the stats
+        updatePowerStats();
+
+        DPRINTF(DRAMPower, "%llu,REF,0,%d\n", divCeil(curTick(), dram.tCK) -
+                dram.timeStampOffset, rank);
+
+        // Update for next refresh
+        refreshDueAt += dram.tREFI;
+
+        // make sure we did not wait so long that we cannot make up
+        // for it
+        if (refreshDueAt < ref_done_at) {
+            fatal("Refresh was delayed so long we cannot catch up\n");
+        }
+
+        // Run the refresh and schedule event to transition power states
+        // when refresh completes
+        refreshState = REF_RUN;
+        schedule(refreshEvent, ref_done_at);
+        return;
+    }
+
+    if (refreshState == REF_RUN) {
+        // should never get here with any banks active
+        assert(numBanksActive == 0);
+        assert(pwrState == PWR_REF);
+
+        assert(!powerEvent.scheduled());
+
+        if ((dram.ctrl->drainState() == DrainState::Draining) ||
+            (dram.ctrl->drainState() == DrainState::Drained)) {
+            // if draining, do not re-enter low-power mode.
+            // simply go to IDLE and wait
+            schedulePowerEvent(PWR_IDLE, curTick());
+        } else {
+            // At the moment, we sleep when the refresh ends and wait to be
+            // woken up again if previously in a low-power state.
+            if (pwrStatePostRefresh != PWR_IDLE) {
+                // power State should be power Refresh
+                assert(pwrState == PWR_REF);
+                DPRINTF(DRAMState, "Rank %d sleeping after refresh and was in "
+                        "power state %d before refreshing\n", rank,
+                        pwrStatePostRefresh);
+                powerDownSleep(pwrState, curTick());
+
+            // Force PRE power-down if there are no outstanding commands
+            // in Q after refresh.
+            } else if (isQueueEmpty() && dram.enableDRAMPowerdown) {
+                // still have refresh event outstanding but there should
+                // be no other events outstanding
+                assert(outstandingEvents == 1);
+                DPRINTF(DRAMState, "Rank %d sleeping after refresh but was NOT"
+                        " in a low power state before refreshing\n", rank);
+                powerDownSleep(PWR_PRE_PDN, curTick());
+
+            } else {
+                // move to the idle power state once the refresh is done, this
+                // will also move the refresh state machine to the refresh
+                // idle state
+                schedulePowerEvent(PWR_IDLE, curTick());
+            }
+        }
+
+        // At this point, we have completed the current refresh.
+        // In the SREF bypass case, we do not get to this state in the
+        // refresh STM and therefore can always schedule next event.
+        // Compensate for the delay in actually performing the refresh
+        // when scheduling the next one
+        schedule(refreshEvent, refreshDueAt - dram.tRP);
+
+        DPRINTF(DRAMState, "Refresh done at %llu and next refresh"
+                " at %llu\n", curTick(), refreshDueAt);
+    }
+}
+
+void
+DRAMInterface::Rank::schedulePowerEvent(PowerState pwr_state, Tick tick)
+{
+    // respect causality
+    assert(tick >= curTick());
+
+    if (!powerEvent.scheduled()) {
+        DPRINTF(DRAMState, "Scheduling power event at %llu to state %d\n",
+                tick, pwr_state);
+
+        // insert the new transition
+        pwrStateTrans = pwr_state;
+
+        schedule(powerEvent, tick);
+    } else {
+        panic("Scheduled power event at %llu to state %d, "
+              "with scheduled event at %llu to %d\n", tick, pwr_state,
+              powerEvent.when(), pwrStateTrans);
+    }
+}
+
+void
+DRAMInterface::Rank::powerDownSleep(PowerState pwr_state, Tick tick)
+{
+    // if low power state is active low, schedule to active low power state.
+    // in reality tCKE is needed to enter active low power. This is neglected
+    // here and could be added in the future.
+    if (pwr_state == PWR_ACT_PDN) {
+        schedulePowerEvent(pwr_state, tick);
+        // push command to DRAMPower
+        cmdList.push_back(Command(MemCommand::PDN_F_ACT, 0, tick));
+        DPRINTF(DRAMPower, "%llu,PDN_F_ACT,0,%d\n", divCeil(tick,
+                dram.tCK) - dram.timeStampOffset, rank);
+    } else if (pwr_state == PWR_PRE_PDN) {
+        // if low power state is precharge low, schedule to precharge low
+        // power state. In reality tCKE is needed to enter active low power.
+        // This is neglected here.
+        schedulePowerEvent(pwr_state, tick);
+        //push Command to DRAMPower
+        cmdList.push_back(Command(MemCommand::PDN_F_PRE, 0, tick));
+        DPRINTF(DRAMPower, "%llu,PDN_F_PRE,0,%d\n", divCeil(tick,
+                dram.tCK) - dram.timeStampOffset, rank);
+    } else if (pwr_state == PWR_REF) {
+        // if a refresh just occurred
+        // transition to PRE_PDN now that all banks are closed
+        // precharge power down requires tCKE to enter. For simplicity
+        // this is not considered.
+        schedulePowerEvent(PWR_PRE_PDN, tick);
+        //push Command to DRAMPower
+        cmdList.push_back(Command(MemCommand::PDN_F_PRE, 0, tick));
+        DPRINTF(DRAMPower, "%llu,PDN_F_PRE,0,%d\n", divCeil(tick,
+                dram.tCK) - dram.timeStampOffset, rank);
+    } else if (pwr_state == PWR_SREF) {
+        // should only enter SREF after PRE-PD wakeup to do a refresh
+        assert(pwrStatePostRefresh == PWR_PRE_PDN);
+        // self refresh requires time tCKESR to enter. For simplicity,
+        // this is not considered.
+        schedulePowerEvent(PWR_SREF, tick);
+        // push Command to DRAMPower
+        cmdList.push_back(Command(MemCommand::SREN, 0, tick));
+        DPRINTF(DRAMPower, "%llu,SREN,0,%d\n", divCeil(tick,
+                dram.tCK) - dram.timeStampOffset, rank);
+    }
+    // Ensure that we don't power-down and back up in same tick
+    // Once we commit to PD entry, do it and wait for at least 1tCK
+    // This could be replaced with tCKE if/when that is added to the model
+    wakeUpAllowedAt = tick + dram.tCK;
+
+    // Transitioning to a low power state, set flag
+    inLowPowerState = true;
+}
+
+void
+DRAMInterface::Rank::scheduleWakeUpEvent(Tick exit_delay)
+{
+    Tick wake_up_tick = std::max(curTick(), wakeUpAllowedAt);
+
+    DPRINTF(DRAMState, "Scheduling wake-up for rank %d at tick %d\n",
+            rank, wake_up_tick);
+
+    // if waking for refresh, hold previous state
+    // else reset state back to IDLE
+    if (refreshState == REF_PD_EXIT) {
+        pwrStatePostRefresh = pwrState;
+    } else {
+        // don't automatically transition back to LP state after next REF
+        pwrStatePostRefresh = PWR_IDLE;
+    }
+
+    // schedule wake-up with event to ensure entry has completed before
+    // we try to wake-up
+    schedule(wakeUpEvent, wake_up_tick);
+
+    for (auto &b : banks) {
+        // respect both causality and any existing bank
+        // constraints, some banks could already have a
+        // (auto) precharge scheduled
+        b.wrAllowedAt = std::max(wake_up_tick + exit_delay, b.wrAllowedAt);
+        b.rdAllowedAt = std::max(wake_up_tick + exit_delay, b.rdAllowedAt);
+        b.preAllowedAt = std::max(wake_up_tick + exit_delay, b.preAllowedAt);
+        b.actAllowedAt = std::max(wake_up_tick + exit_delay, b.actAllowedAt);
+    }
+    // Transitioning out of low power state, clear flag
+    inLowPowerState = false;
+
+    // push to DRAMPower
+    // use pwrStateTrans for cases where we have a power event scheduled
+    // to enter low power that has not yet been processed
+    if (pwrStateTrans == PWR_ACT_PDN) {
+        cmdList.push_back(Command(MemCommand::PUP_ACT, 0, wake_up_tick));
+        DPRINTF(DRAMPower, "%llu,PUP_ACT,0,%d\n", divCeil(wake_up_tick,
+                dram.tCK) - dram.timeStampOffset, rank);
+
+    } else if (pwrStateTrans == PWR_PRE_PDN) {
+        cmdList.push_back(Command(MemCommand::PUP_PRE, 0, wake_up_tick));
+        DPRINTF(DRAMPower, "%llu,PUP_PRE,0,%d\n", divCeil(wake_up_tick,
+                dram.tCK) - dram.timeStampOffset, rank);
+    } else if (pwrStateTrans == PWR_SREF) {
+        cmdList.push_back(Command(MemCommand::SREX, 0, wake_up_tick));
+        DPRINTF(DRAMPower, "%llu,SREX,0,%d\n", divCeil(wake_up_tick,
+                dram.tCK) - dram.timeStampOffset, rank);
+    }
+}
+
+void
+DRAMInterface::Rank::processWakeUpEvent()
+{
+    // Should be in a power-down or self-refresh state
+    assert((pwrState == PWR_ACT_PDN) || (pwrState == PWR_PRE_PDN) ||
+           (pwrState == PWR_SREF));
+
+    // Check current state to determine transition state
+    if (pwrState == PWR_ACT_PDN) {
+        // banks still open, transition to PWR_ACT
+        schedulePowerEvent(PWR_ACT, curTick());
+    } else {
+        // transitioning from a precharge power-down or self-refresh state
+        // banks are closed - transition to PWR_IDLE
+        schedulePowerEvent(PWR_IDLE, curTick());
+    }
+}
+
+void
+DRAMInterface::Rank::processPowerEvent()
+{
+    assert(curTick() >= pwrStateTick);
+    // remember where we were, and for how long
+    Tick duration = curTick() - pwrStateTick;
+    PowerState prev_state = pwrState;
+
+    // update the accounting
+    stats.pwrStateTime[prev_state] += duration;
+
+    // track to total idle time
+    if ((prev_state == PWR_PRE_PDN) || (prev_state == PWR_ACT_PDN) ||
+        (prev_state == PWR_SREF)) {
+        stats.totalIdleTime += duration;
+    }
+
+    pwrState = pwrStateTrans;
+    pwrStateTick = curTick();
+
+    // if rank was refreshing, make sure to start scheduling requests again
+    if (prev_state == PWR_REF) {
+        // bus IDLED prior to REF
+        // counter should be one for refresh command only
+        assert(outstandingEvents == 1);
+        // REF complete, decrement count and go back to IDLE
+        --outstandingEvents;
+        refreshState = REF_IDLE;
+
+        DPRINTF(DRAMState, "Was refreshing for %llu ticks\n", duration);
+        // if moving back to power-down after refresh
+        if (pwrState != PWR_IDLE) {
+            assert(pwrState == PWR_PRE_PDN);
+            DPRINTF(DRAMState, "Switching to power down state after refreshing"
+                    " rank %d at %llu tick\n", rank, curTick());
+        }
+
+        // completed refresh event, ensure next request is scheduled
+        if (!(dram.ctrl->requestEventScheduled(dram.pseudoChannel))) {
+            DPRINTF(DRAM, "Scheduling next request after refreshing"
+                           " rank %d\n", rank);
+            dram.ctrl->restartScheduler(curTick(), dram.pseudoChannel);
+        }
+    }
+
+    if ((pwrState == PWR_ACT) && (refreshState == REF_PD_EXIT)) {
+        // have exited ACT PD
+        assert(prev_state == PWR_ACT_PDN);
+
+        // go back to REF event and close banks
+        refreshState = REF_PRE;
+        schedule(refreshEvent, curTick());
+    } else if (pwrState == PWR_IDLE) {
+        DPRINTF(DRAMState, "All banks precharged\n");
+        if (prev_state == PWR_SREF) {
+            // set refresh state to REF_SREF_EXIT, ensuring inRefIdleState
+            // continues to return false during tXS after SREF exit
+            // Schedule a refresh which kicks things back into action
+            // when it finishes
+            refreshState = REF_SREF_EXIT;
+            schedule(refreshEvent, curTick() + dram.tXS);
+        } else {
+            // if we have a pending refresh, and are now moving to
+            // the idle state, directly transition to, or schedule refresh
+            if ((refreshState == REF_PRE) || (refreshState == REF_PD_EXIT)) {
+                // ensure refresh is restarted only after final PRE command.
+                // do not restart refresh if controller is in an intermediate
+                // state, after PRE_PDN exit, when banks are IDLE but an
+                // ACT is scheduled.
+                if (!activateEvent.scheduled()) {
+                    // there should be nothing waiting at this point
+                    assert(!powerEvent.scheduled());
+                    if (refreshState == REF_PD_EXIT) {
+                        // exiting PRE PD, will be in IDLE until tXP expires
+                        // and then should transition to PWR_REF state
+                        assert(prev_state == PWR_PRE_PDN);
+                        schedulePowerEvent(PWR_REF, curTick() + dram.tXP);
+                    } else if (refreshState == REF_PRE) {
+                        // can directly move to PWR_REF state and proceed below
+                        pwrState = PWR_REF;
+                    }
+                } else {
+                    // must have PRE scheduled to transition back to IDLE
+                    // and re-kick off refresh
+                    assert(prechargeEvent.scheduled());
+                }
+            }
+        }
+    }
+
+    // transition to the refresh state and re-start refresh process
+    // refresh state machine will schedule the next power state transition
+    if (pwrState == PWR_REF) {
+        // completed final PRE for refresh or exiting power-down
+        assert(refreshState == REF_PRE || refreshState == REF_PD_EXIT);
+
+        // exited PRE PD for refresh, with no pending commands
+        // bypass auto-refresh and go straight to SREF, where memory
+        // will issue refresh immediately upon entry
+        if (pwrStatePostRefresh == PWR_PRE_PDN && isQueueEmpty() &&
+           (dram.ctrl->drainState() != DrainState::Draining) &&
+           (dram.ctrl->drainState() != DrainState::Drained) &&
+           dram.enableDRAMPowerdown) {
+            DPRINTF(DRAMState, "Rank %d bypassing refresh and transitioning "
+                    "to self refresh at %11u tick\n", rank, curTick());
+            powerDownSleep(PWR_SREF, curTick());
+
+            // Since refresh was bypassed, remove event by decrementing count
+            assert(outstandingEvents == 1);
+            --outstandingEvents;
+
+            // reset state back to IDLE temporarily until SREF is entered
+            pwrState = PWR_IDLE;
+
+        // Not bypassing refresh for SREF entry
+        } else {
+            DPRINTF(DRAMState, "Refreshing\n");
+
+            // there should be nothing waiting at this point
+            assert(!powerEvent.scheduled());
+
+            // kick the refresh event loop into action again, and that
+            // in turn will schedule a transition to the idle power
+            // state once the refresh is done
+            schedule(refreshEvent, curTick());
+
+            // Banks transitioned to IDLE, start REF
+            refreshState = REF_START;
+        }
+    }
+
+}
+
+void
+DRAMInterface::Rank::updatePowerStats()
+{
+    // All commands up to refresh have completed
+    // flush cmdList to DRAMPower
+    flushCmdList();
+
+    // Call the function that calculates window energy at intermediate update
+    // events like at refresh, stats dump as well as at simulation exit.
+    // Window starts at the last time the calcWindowEnergy function was called
+    // and is upto current time.
+    power.powerlib.calcWindowEnergy(divCeil(curTick(), dram.tCK) -
+                                    dram.timeStampOffset);
+
+    // Get the energy from DRAMPower
+    Data::MemoryPowerModel::Energy energy = power.powerlib.getEnergy();
+
+    // The energy components inside the power lib are calculated over
+    // the window so accumulate into the corresponding gem5 stat
+    stats.actEnergy += energy.act_energy * dram.devicesPerRank;
+    stats.preEnergy += energy.pre_energy * dram.devicesPerRank;
+    stats.readEnergy += energy.read_energy * dram.devicesPerRank;
+    stats.writeEnergy += energy.write_energy * dram.devicesPerRank;
+    stats.refreshEnergy += energy.ref_energy * dram.devicesPerRank;
+    stats.actBackEnergy += energy.act_stdby_energy * dram.devicesPerRank;
+    stats.preBackEnergy += energy.pre_stdby_energy * dram.devicesPerRank;
+    stats.actPowerDownEnergy += energy.f_act_pd_energy * dram.devicesPerRank;
+    stats.prePowerDownEnergy += energy.f_pre_pd_energy * dram.devicesPerRank;
+    stats.selfRefreshEnergy += energy.sref_energy * dram.devicesPerRank;
+
+    // Accumulate window energy into the total energy.
+    stats.totalEnergy += energy.window_energy * dram.devicesPerRank;
+    // Average power must not be accumulated but calculated over the time
+    // since last stats reset. sim_clock::Frequency is tick period not tick
+    // frequency.
+    //              energy (pJ)     1e-9
+    // power (mW) = ----------- * ----------
+    //              time (tick)   tick_frequency
+    stats.averagePower = (stats.totalEnergy.value() /
+                    (curTick() - dram.lastStatsResetTick)) *
+                    (sim_clock::Frequency / 1000000000.0);
+}
+
+void
+DRAMInterface::Rank::computeStats()
+{
+    DPRINTF(DRAM,"Computing stats due to a dump callback\n");
+
+    // Update the stats
+    updatePowerStats();
+
+    // final update of power state times
+    stats.pwrStateTime[pwrState] += (curTick() - pwrStateTick);
+    pwrStateTick = curTick();
+}
+
+void
+DRAMInterface::Rank::resetStats() {
+    // The only way to clear the counters in DRAMPower is to call
+    // calcWindowEnergy function as that then calls clearCounters. The
+    // clearCounters method itself is private.
+    power.powerlib.calcWindowEnergy(divCeil(curTick(), dram.tCK) -
+                                    dram.timeStampOffset);
+
+}
+
+bool
+DRAMInterface::Rank::forceSelfRefreshExit() const {
+    return (readEntries != 0) ||
+           (dram.ctrl->inWriteBusState(true) && (writeEntries != 0));
+}
+
+void
+DRAMInterface::DRAMStats::resetStats()
+{
+    dram.lastStatsResetTick = curTick();
+}
+
+DRAMInterface::DRAMStats::DRAMStats(DRAMInterface &_dram)
+    : statistics::Group(&_dram),
+    dram(_dram),
+
+    ADD_STAT(readBursts, statistics::units::Count::get(),
+             "Number of DRAM read bursts"),
+    ADD_STAT(writeBursts, statistics::units::Count::get(),
+             "Number of DRAM write bursts"),
+
+    ADD_STAT(perBankRdBursts, statistics::units::Count::get(),
+             "Per bank write bursts"),
+    ADD_STAT(perBankWrBursts, statistics::units::Count::get(),
+             "Per bank write bursts"),
+
+    ADD_STAT(totQLat, statistics::units::Tick::get(),
+             "Total ticks spent queuing"),
+    ADD_STAT(totBusLat, statistics::units::Tick::get(),
+             "Total ticks spent in databus transfers"),
+    ADD_STAT(totMemAccLat, statistics::units::Tick::get(),
+             "Total ticks spent from burst creation until serviced "
+             "by the DRAM"),
+
+    ADD_STAT(avgQLat, statistics::units::Rate<
+                statistics::units::Tick, statistics::units::Count>::get(),
+             "Average queueing delay per DRAM burst"),
+    ADD_STAT(avgBusLat, statistics::units::Rate<
+                statistics::units::Tick, statistics::units::Count>::get(),
+             "Average bus latency per DRAM burst"),
+    ADD_STAT(avgMemAccLat, statistics::units::Rate<
+                statistics::units::Tick, statistics::units::Count>::get(),
+             "Average memory access latency per DRAM burst"),
+
+    ADD_STAT(readRowHits, statistics::units::Count::get(),
+             "Number of row buffer hits during reads"),
+    ADD_STAT(writeRowHits, statistics::units::Count::get(),
+             "Number of row buffer hits during writes"),
+    ADD_STAT(readRowHitRate, statistics::units::Ratio::get(),
+             "Row buffer hit rate for reads"),
+    ADD_STAT(writeRowHitRate, statistics::units::Ratio::get(),
+             "Row buffer hit rate for writes"),
+
+    ADD_STAT(bytesPerActivate, statistics::units::Byte::get(),
+             "Bytes accessed per row activation"),
+    ADD_STAT(bytesRead, statistics::units::Byte::get(),
+            "Total bytes read"),
+    ADD_STAT(bytesWritten, statistics::units::Byte::get(),
+            "Total bytes written"),
+
+    ADD_STAT(avgRdBW, statistics::units::Rate<
+                statistics::units::Byte, statistics::units::Second>::get(),
+             "Average DRAM read bandwidth in MiBytes/s"),
+    ADD_STAT(avgWrBW, statistics::units::Rate<
+                statistics::units::Byte, statistics::units::Second>::get(),
+             "Average DRAM write bandwidth in MiBytes/s"),
+    ADD_STAT(peakBW,  statistics::units::Rate<
+                statistics::units::Byte, statistics::units::Second>::get(),
+             "Theoretical peak bandwidth in MiByte/s"),
+
+    ADD_STAT(busUtil, statistics::units::Ratio::get(),
+             "Data bus utilization in percentage"),
+    ADD_STAT(busUtilRead, statistics::units::Ratio::get(),
+             "Data bus utilization in percentage for reads"),
+    ADD_STAT(busUtilWrite, statistics::units::Ratio::get(),
+             "Data bus utilization in percentage for writes"),
+
+    ADD_STAT(pageHitRate, statistics::units::Ratio::get(),
+             "Row buffer hit rate, read and write combined")
+
+{
+}
+
+void
+DRAMInterface::DRAMStats::regStats()
+{
+    using namespace statistics;
+
+    avgQLat.precision(2);
+    avgBusLat.precision(2);
+    avgMemAccLat.precision(2);
+
+    readRowHitRate.precision(2);
+    writeRowHitRate.precision(2);
+
+    perBankRdBursts.init(dram.banksPerRank * dram.ranksPerChannel);
+    perBankWrBursts.init(dram.banksPerRank * dram.ranksPerChannel);
+
+    bytesPerActivate
+        .init(dram.maxAccessesPerRow ?
+              dram.maxAccessesPerRow : dram.rowBufferSize)
+        .flags(nozero);
+
+    peakBW.precision(2);
+    busUtil.precision(2);
+    busUtilWrite.precision(2);
+    busUtilRead.precision(2);
+
+    pageHitRate.precision(2);
+
+    // Formula stats
+    avgQLat = totQLat / readBursts;
+    avgBusLat = totBusLat / readBursts;
+    avgMemAccLat = totMemAccLat / readBursts;
+
+    readRowHitRate = (readRowHits / readBursts) * 100;
+    writeRowHitRate = (writeRowHits / writeBursts) * 100;
+
+    avgRdBW = (bytesRead / 1000000) / simSeconds;
+    avgWrBW = (bytesWritten / 1000000) / simSeconds;
+    peakBW = (sim_clock::Frequency / dram.burstDelay()) *
+              dram.bytesPerBurst() / 1000000;
+
+    busUtil = (avgRdBW + avgWrBW) / peakBW * 100;
+    busUtilRead = avgRdBW / peakBW * 100;
+    busUtilWrite = avgWrBW / peakBW * 100;
+
+    pageHitRate = (writeRowHits + readRowHits) /
+        (writeBursts + readBursts) * 100;
+}
+
+DRAMInterface::RankStats::RankStats(DRAMInterface &_dram, Rank &_rank)
+    : statistics::Group(&_dram, csprintf("rank%d", _rank.rank).c_str()),
+    rank(_rank),
+
+    ADD_STAT(actEnergy, statistics::units::Joule::get(),
+             "Energy for activate commands per rank (pJ)"),
+    ADD_STAT(preEnergy, statistics::units::Joule::get(),
+             "Energy for precharge commands per rank (pJ)"),
+    ADD_STAT(readEnergy, statistics::units::Joule::get(),
+             "Energy for read commands per rank (pJ)"),
+    ADD_STAT(writeEnergy, statistics::units::Joule::get(),
+             "Energy for write commands per rank (pJ)"),
+    ADD_STAT(refreshEnergy, statistics::units::Joule::get(),
+             "Energy for refresh commands per rank (pJ)"),
+    ADD_STAT(actBackEnergy, statistics::units::Joule::get(),
+             "Energy for active background per rank (pJ)"),
+    ADD_STAT(preBackEnergy, statistics::units::Joule::get(),
+             "Energy for precharge background per rank (pJ)"),
+    ADD_STAT(actPowerDownEnergy, statistics::units::Joule::get(),
+             "Energy for active power-down per rank (pJ)"),
+    ADD_STAT(prePowerDownEnergy, statistics::units::Joule::get(),
+             "Energy for precharge power-down per rank (pJ)"),
+    ADD_STAT(selfRefreshEnergy, statistics::units::Joule::get(),
+             "Energy for self refresh per rank (pJ)"),
+
+    ADD_STAT(totalEnergy, statistics::units::Joule::get(),
+             "Total energy per rank (pJ)"),
+    ADD_STAT(averagePower, statistics::units::Watt::get(),
+             "Core power per rank (mW)"),
+
+    ADD_STAT(totalIdleTime, statistics::units::Tick::get(),
+             "Total Idle time Per DRAM Rank"),
+    ADD_STAT(pwrStateTime, statistics::units::Tick::get(),
+             "Time in different power states")
+{
+}
+
+void
+DRAMInterface::RankStats::regStats()
+{
+    statistics::Group::regStats();
+
+    pwrStateTime
+        .init(6)
+        .subname(0, "IDLE")
+        .subname(1, "REF")
+        .subname(2, "SREF")
+        .subname(3, "PRE_PDN")
+        .subname(4, "ACT")
+        .subname(5, "ACT_PDN");
+}
+
+void
+DRAMInterface::RankStats::resetStats()
+{
+    statistics::Group::resetStats();
+
+    rank.resetStats();
+}
+
+void
+DRAMInterface::RankStats::preDumpStats()
+{
+    statistics::Group::preDumpStats();
+
+    rank.computeStats();
+}
+
+} // namespace memory
+} // namespace gem5
diff --git a/src/mem/dram_interface.hh b/src/mem/dram_interface.hh
new file mode 100644
index 0000000..fa9d319
--- /dev/null
+++ b/src/mem/dram_interface.hh
@@ -0,0 +1,798 @@
+/*
+ * Copyright (c) 2012-2020 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2013 Amin Farmahini-Farahani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * DRAMInterface declaration
+ */
+
+#ifndef __DRAM_INTERFACE_HH__
+#define __DRAM_INTERFACE_HH__
+
+#include "mem/drampower.hh"
+#include "mem/mem_interface.hh"
+#include "params/DRAMInterface.hh"
+
+namespace gem5
+{
+
+namespace memory
+{
+
+/**
+ * Interface to DRAM devices with media specific parameters,
+ * statistics, and functions.
+ * The DRAMInterface includes a class for individual ranks
+ * and per rank functions.
+ */
+class DRAMInterface : public MemInterface
+{
+  private:
+    /**
+     * Simple structure to hold the values needed to keep track of
+     * commands for DRAMPower
+     */
+    struct Command
+    {
+       Data::MemCommand::cmds type;
+       uint8_t bank;
+       Tick timeStamp;
+
+       constexpr Command(Data::MemCommand::cmds _type, uint8_t _bank,
+                         Tick time_stamp)
+            : type(_type), bank(_bank), timeStamp(time_stamp)
+        { }
+    };
+
+    /**
+     * The power state captures the different operational states of
+     * the DRAM and interacts with the bus read/write state machine,
+     * and the refresh state machine.
+     *
+     * PWR_IDLE      : The idle state in which all banks are closed
+     *                 From here can transition to:  PWR_REF, PWR_ACT,
+     *                 PWR_PRE_PDN
+     *
+     * PWR_REF       : Auto-refresh state.  Will transition when refresh is
+     *                 complete based on power state prior to PWR_REF
+     *                 From here can transition to:  PWR_IDLE, PWR_PRE_PDN,
+     *                 PWR_SREF
+     *
+     * PWR_SREF      : Self-refresh state.  Entered after refresh if
+     *                 previous state was PWR_PRE_PDN
+     *                 From here can transition to:  PWR_IDLE
+     *
+     * PWR_PRE_PDN   : Precharge power down state
+     *                 From here can transition to:  PWR_REF, PWR_IDLE
+     *
+     * PWR_ACT       : Activate state in which one or more banks are open
+     *                 From here can transition to:  PWR_IDLE, PWR_ACT_PDN
+     *
+     * PWR_ACT_PDN   : Activate power down state
+     *                 From here can transition to:  PWR_ACT
+     */
+    enum PowerState
+    {
+        PWR_IDLE = 0,
+        PWR_REF,
+        PWR_SREF,
+        PWR_PRE_PDN,
+        PWR_ACT,
+        PWR_ACT_PDN
+    };
+
+    /**
+     * The refresh state is used to control the progress of the
+     * refresh scheduling. When normal operation is in progress the
+     * refresh state is idle. Once tREFI has elasped, a refresh event
+     * is triggered to start the following STM transitions which are
+     * used to issue a refresh and return back to normal operation
+     *
+     * REF_IDLE      : IDLE state used during normal operation
+     *                 From here can transition to:  REF_DRAIN
+     *
+     * REF_SREF_EXIT : Exiting a self-refresh; refresh event scheduled
+     *                 after self-refresh exit completes
+     *                 From here can transition to:  REF_DRAIN
+     *
+     * REF_DRAIN     : Drain state in which on going accesses complete.
+     *                 From here can transition to:  REF_PD_EXIT
+     *
+     * REF_PD_EXIT   : Evaluate pwrState and issue wakeup if needed
+     *                 Next state dependent on whether banks are open
+     *                 From here can transition to:  REF_PRE, REF_START
+     *
+     * REF_PRE       : Close (precharge) all open banks
+     *                 From here can transition to:  REF_START
+     *
+     * REF_START     : Issue refresh command and update DRAMPower stats
+     *                 From here can transition to:  REF_RUN
+     *
+     * REF_RUN       : Refresh running, waiting for tRFC to expire
+     *                 From here can transition to:  REF_IDLE, REF_SREF_EXIT
+     */
+    enum RefreshState
+    {
+        REF_IDLE = 0,
+        REF_DRAIN,
+        REF_PD_EXIT,
+        REF_SREF_EXIT,
+        REF_PRE,
+        REF_START,
+        REF_RUN
+    };
+
+    class Rank;
+    struct RankStats : public statistics::Group
+    {
+        RankStats(DRAMInterface &dram, Rank &rank);
+
+        void regStats() override;
+        void resetStats() override;
+        void preDumpStats() override;
+
+        Rank &rank;
+
+        /*
+         * Command energies
+         */
+        statistics::Scalar actEnergy;
+        statistics::Scalar preEnergy;
+        statistics::Scalar readEnergy;
+        statistics::Scalar writeEnergy;
+        statistics::Scalar refreshEnergy;
+
+        /*
+         * Active Background Energy
+         */
+        statistics::Scalar actBackEnergy;
+
+        /*
+         * Precharge Background Energy
+         */
+        statistics::Scalar preBackEnergy;
+
+        /*
+         * Active Power-Down Energy
+         */
+        statistics::Scalar actPowerDownEnergy;
+
+        /*
+         * Precharge Power-Down Energy
+         */
+        statistics::Scalar prePowerDownEnergy;
+
+        /*
+         * self Refresh Energy
+         */
+        statistics::Scalar selfRefreshEnergy;
+
+        statistics::Scalar totalEnergy;
+        statistics::Scalar averagePower;
+
+        /**
+         * Stat to track total DRAM idle time
+         *
+         */
+        statistics::Scalar totalIdleTime;
+
+        /**
+         * Track time spent in each power state.
+         */
+        statistics::Vector pwrStateTime;
+    };
+
+    /**
+     * Rank class includes a vector of banks. Refresh and Power state
+     * machines are defined per rank. Events required to change the
+     * state of the refresh and power state machine are scheduled per
+     * rank. This class allows the implementation of rank-wise refresh
+     * and rank-wise power-down.
+     */
+    class Rank : public EventManager
+    {
+      private:
+
+        /**
+         * A reference to the parent DRAMInterface instance
+         */
+        DRAMInterface& dram;
+
+        /**
+         * Since we are taking decisions out of order, we need to keep
+         * track of what power transition is happening at what time
+         */
+        PowerState pwrStateTrans;
+
+        /**
+         * Previous low-power state, which will be re-entered after refresh.
+         */
+        PowerState pwrStatePostRefresh;
+
+        /**
+         * Track when we transitioned to the current power state
+         */
+        Tick pwrStateTick;
+
+        /**
+         * Keep track of when a refresh is due.
+         */
+        Tick refreshDueAt;
+
+        /**
+         * Function to update Power Stats
+         */
+        void updatePowerStats();
+
+        /**
+         * Schedule a power state transition in the future, and
+         * potentially override an already scheduled transition.
+         *
+         * @param pwr_state Power state to transition to
+         * @param tick Tick when transition should take place
+         */
+        void schedulePowerEvent(PowerState pwr_state, Tick tick);
+
+      public:
+
+        /**
+         * Current power state.
+         */
+        PowerState pwrState;
+
+       /**
+         * current refresh state
+         */
+        RefreshState refreshState;
+
+        /**
+         * rank is in or transitioning to power-down or self-refresh
+         */
+        bool inLowPowerState;
+
+        /**
+         * Current Rank index
+         */
+        uint8_t rank;
+
+       /**
+         * Track number of packets in read queue going to this rank
+         */
+        uint32_t readEntries;
+
+       /**
+         * Track number of packets in write queue going to this rank
+         */
+        uint32_t writeEntries;
+
+        /**
+         * Number of ACT, RD, and WR events currently scheduled
+         * Incremented when a refresh event is started as well
+         * Used to determine when a low-power state can be entered
+         */
+        uint8_t outstandingEvents;
+
+        /**
+         * delay low-power exit until this requirement is met
+         */
+        Tick wakeUpAllowedAt;
+
+        /**
+         * One DRAMPower instance per rank
+         */
+        DRAMPower power;
+
+        /**
+         * List of commands issued, to be sent to DRAMPpower at refresh
+         * and stats dump.  Keep commands here since commands to different
+         * banks are added out of order.  Will only pass commands up to
+         * curTick() to DRAMPower after sorting.
+         */
+        std::vector<Command> cmdList;
+
+        /**
+         * Vector of Banks. Each rank is made of several devices which in
+         * term are made from several banks.
+         */
+        std::vector<Bank> banks;
+
+        /**
+         *  To track number of banks which are currently active for
+         *  this rank.
+         */
+        unsigned int numBanksActive;
+
+        /** List to keep track of activate ticks */
+        std::deque<Tick> actTicks;
+
+        /**
+         * Track when we issued the last read/write burst
+         */
+        Tick lastBurstTick;
+
+        Rank(const DRAMInterfaceParams &_p, int _rank,
+             DRAMInterface& _dram);
+
+        const std::string name() const { return csprintf("%d", rank); }
+
+        /**
+         * Kick off accounting for power and refresh states and
+         * schedule initial refresh.
+         *
+         * @param ref_tick Tick for first refresh
+         */
+        void startup(Tick ref_tick);
+
+        /**
+         * Stop the refresh events.
+         */
+        void suspend();
+
+        /**
+         * Check if there is no refresh and no preparation of refresh ongoing
+         * i.e. the refresh state machine is in idle
+         *
+         * @param Return true if the rank is idle from a refresh point of view
+         */
+        bool inRefIdleState() const { return refreshState == REF_IDLE; }
+
+        /**
+         * Check if the current rank has all banks closed and is not
+         * in a low power state
+         *
+         * @param Return true if the rank is idle from a bank
+         *        and power point of view
+         */
+        bool inPwrIdleState() const { return pwrState == PWR_IDLE; }
+
+        /**
+         * Trigger a self-refresh exit if there are entries enqueued
+         * Exit if there are any read entries regardless of the bus state.
+         * If we are currently issuing write commands, exit if we have any
+         * write commands enqueued as well.
+         * Could expand this in the future to analyze state of entire queue
+         * if needed.
+         *
+         * @return boolean indicating self-refresh exit should be scheduled
+         */
+        bool forceSelfRefreshExit() const;
+
+        /**
+         * Check if the command queue of current rank is idle
+         *
+         * @param Return true if the there are no commands in Q.
+         *                    Bus direction determines queue checked.
+         */
+        bool isQueueEmpty() const;
+
+        /**
+         * Let the rank check if it was waiting for requests to drain
+         * to allow it to transition states.
+         */
+        void checkDrainDone();
+
+        /**
+         * Push command out of cmdList queue that are scheduled at
+         * or before curTick() to DRAMPower library
+         * All commands before curTick are guaranteed to be complete
+         * and can safely be flushed.
+         */
+        void flushCmdList();
+
+        /**
+         * Computes stats just prior to dump event
+         */
+        void computeStats();
+
+        /**
+         * Reset stats on a stats event
+         */
+        void resetStats();
+
+        /**
+         * Schedule a transition to power-down (sleep)
+         *
+         * @param pwr_state Power state to transition to
+         * @param tick Absolute tick when transition should take place
+         */
+        void powerDownSleep(PowerState pwr_state, Tick tick);
+
+       /**
+         * schedule and event to wake-up from power-down or self-refresh
+         * and update bank timing parameters
+         *
+         * @param exit_delay Relative tick defining the delay required between
+         *                   low-power exit and the next command
+         */
+        void scheduleWakeUpEvent(Tick exit_delay);
+
+        void processWriteDoneEvent();
+        EventFunctionWrapper writeDoneEvent;
+
+        void processActivateEvent();
+        EventFunctionWrapper activateEvent;
+
+        void processPrechargeEvent();
+        EventFunctionWrapper prechargeEvent;
+
+        void processRefreshEvent();
+        EventFunctionWrapper refreshEvent;
+
+        void processPowerEvent();
+        EventFunctionWrapper powerEvent;
+
+        void processWakeUpEvent();
+        EventFunctionWrapper wakeUpEvent;
+
+      protected:
+        RankStats stats;
+    };
+
+    /**
+     * Function for sorting Command structures based on timeStamp
+     *
+     * @param a Memory Command
+     * @param next Memory Command
+     * @return true if timeStamp of Command 1 < timeStamp of Command 2
+     */
+    static bool
+    sortTime(const Command& cmd, const Command& cmd_next)
+    {
+        return cmd.timeStamp < cmd_next.timeStamp;
+    }
+
+    /**
+     * DRAM specific device characteristics
+     */
+    const uint32_t bankGroupsPerRank;
+    const bool bankGroupArch;
+
+    /**
+     * DRAM specific timing requirements
+     */
+    const Tick tRL;
+    const Tick tWL;
+    const Tick tBURST_MIN;
+    const Tick tBURST_MAX;
+    const Tick tCCD_L_WR;
+    const Tick tCCD_L;
+    const Tick tRCD_RD;
+    const Tick tRCD_WR;
+    const Tick tRP;
+    const Tick tRAS;
+    const Tick tWR;
+    const Tick tRTP;
+    const Tick tRFC;
+    const Tick tREFI;
+    const Tick tRRD;
+    const Tick tRRD_L;
+    const Tick tPPD;
+    const Tick tAAD;
+    const Tick tXAW;
+    const Tick tXP;
+    const Tick tXS;
+    const Tick clkResyncDelay;
+    const bool dataClockSync;
+    const bool burstInterleave;
+    const uint8_t twoCycleActivate;
+    const uint32_t activationLimit;
+    const Tick wrToRdDlySameBG;
+    const Tick rdToWrDlySameBG;
+
+
+    enums::PageManage pageMgmt;
+    /**
+     * Max column accesses (read and write) per row, before forefully
+     * closing it.
+     */
+    const uint32_t maxAccessesPerRow;
+
+    // timestamp offset
+    uint64_t timeStampOffset;
+
+    // Holds the value of the DRAM rank of burst issued
+    uint8_t activeRank;
+
+    /** Enable or disable DRAM powerdown states. */
+    bool enableDRAMPowerdown;
+
+    /** The time when stats were last reset used to calculate average power */
+    Tick lastStatsResetTick;
+
+    /**
+     * Keep track of when row activations happen, in order to enforce
+     * the maximum number of activations in the activation window. The
+     * method updates the time that the banks become available based
+     * on the current limits.
+     *
+     * @param rank_ref Reference to the rank
+     * @param bank_ref Reference to the bank
+     * @param act_tick Time when the activation takes place
+     * @param row Index of the row
+     */
+    void activateBank(Rank& rank_ref, Bank& bank_ref, Tick act_tick,
+                      uint32_t row);
+
+    /**
+     * Precharge a given bank and also update when the precharge is
+     * done. This will also deal with any stats related to the
+     * accesses to the open page.
+     *
+     * @param rank_ref The rank to precharge
+     * @param bank_ref The bank to precharge
+     * @param pre_tick Time when the precharge takes place
+     * @param auto_or_preall Is this an auto-precharge or precharge all command
+     * @param trace Is this an auto precharge then do not add to trace
+     */
+    void prechargeBank(Rank& rank_ref, Bank& bank_ref,
+                       Tick pre_tick, bool auto_or_preall = false,
+                       bool trace = true);
+
+    struct DRAMStats : public statistics::Group
+    {
+        DRAMStats(DRAMInterface &dram);
+
+        void regStats() override;
+        void resetStats() override;
+
+        DRAMInterface &dram;
+
+        /** total number of DRAM bursts serviced */
+        statistics::Scalar readBursts;
+        statistics::Scalar writeBursts;
+
+        /** DRAM per bank stats */
+        statistics::Vector perBankRdBursts;
+        statistics::Vector perBankWrBursts;
+
+        // Latencies summed over all requests
+        statistics::Scalar totQLat;
+        statistics::Scalar totBusLat;
+        statistics::Scalar totMemAccLat;
+
+        // Average latencies per request
+        statistics::Formula avgQLat;
+        statistics::Formula avgBusLat;
+        statistics::Formula avgMemAccLat;
+
+        // Row hit count and rate
+        statistics::Scalar readRowHits;
+        statistics::Scalar writeRowHits;
+        statistics::Formula readRowHitRate;
+        statistics::Formula writeRowHitRate;
+        statistics::Histogram bytesPerActivate;
+        // Number of bytes transferred to/from DRAM
+        statistics::Scalar bytesRead;
+        statistics::Scalar bytesWritten;
+
+        // Average bandwidth
+        statistics::Formula avgRdBW;
+        statistics::Formula avgWrBW;
+        statistics::Formula peakBW;
+        // bus utilization
+        statistics::Formula busUtil;
+        statistics::Formula busUtilRead;
+        statistics::Formula busUtilWrite;
+        statistics::Formula pageHitRate;
+    };
+
+    DRAMStats stats;
+
+    /**
+      * Vector of dram ranks
+      */
+    std::vector<Rank*> ranks;
+
+    /*
+     * @return delay between write and read commands
+     */
+    Tick writeToReadDelay() const override { return tBURST + tWTR + tWL; }
+
+    /**
+     * Find which are the earliest banks ready to issue an activate
+     * for the enqueued requests. Assumes maximum of 32 banks per rank
+     * Also checks if the bank is already prepped.
+     *
+     * @param queue Queued requests to consider
+     * @param min_col_at time of seamless burst command
+     * @return One-hot encoded mask of bank indices
+     * @return boolean indicating burst can issue seamlessly, with no gaps
+     */
+    std::pair<std::vector<uint32_t>, bool>
+    minBankPrep(const MemPacketQueue& queue, Tick min_col_at) const;
+
+    /*
+     * @return time to send a burst of data without gaps
+     */
+    Tick
+    burstDelay() const
+    {
+        return (burstInterleave ? tBURST_MAX / 2 : tBURST);
+    }
+
+  public:
+    /**
+     * Initialize the DRAM interface and verify parameters
+     */
+    void init() override;
+
+    /**
+     * Iterate through dram ranks and instantiate per rank startup routine
+     */
+    void startup() override;
+
+    /**
+     * Setup the rank based on packet received
+     *
+     * @param integer value of rank to be setup. used to index ranks vector
+     * @param are we setting up rank for read or write packet?
+     */
+    void setupRank(const uint8_t rank, const bool is_read) override;
+
+    MemPacket* decodePacket(const PacketPtr pkt, Addr pkt_addr,
+                           unsigned int size, bool is_read,
+                           uint8_t pseudo_channel = 0) override;
+
+    /**
+     * Iterate through dram ranks to exit self-refresh in order to drain
+     */
+    void drainRanks() override;
+
+    /**
+     * Return true once refresh is complete for all ranks and there are no
+     * additional commands enqueued.  (only evaluated when draining)
+     * This will ensure that all banks are closed, power state is IDLE, and
+     * power stats have been updated
+     *
+     * @return true if all ranks have refreshed, with no commands enqueued
+     *
+     */
+    bool allRanksDrained() const override;
+
+    /**
+     * Iterate through DRAM ranks and suspend them
+     */
+    void suspend() override;
+
+    /*
+     * @return time to offset next command
+     */
+    Tick commandOffset() const override
+    {
+        return (tRP + std::max(tRCD_RD, tRCD_WR));
+    }
+
+    /*
+     * Function to calulate unloaded, closed bank access latency
+     */
+    Tick accessLatency() const override { return (tRP + tRCD_RD + tRL); }
+
+    /**
+     * For FR-FCFS policy, find first DRAM command that can issue
+     *
+     * @param queue Queued requests to consider
+     * @param min_col_at Minimum tick for 'seamless' issue
+     * @return an iterator to the selected packet, else queue.end()
+     * @return the tick when the packet selected will issue
+     */
+    std::pair<MemPacketQueue::iterator, Tick>
+    chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const override;
+
+    /**
+     * Actually do the burst - figure out the latency it
+     * will take to service the req based on bank state, channel state etc
+     * and then update those states to account for this request. Based
+     * on this, update the packet's "readyTime" and move it to the
+     * response q from where it will eventually go back to the outside
+     * world.
+     *
+     * @param mem_pkt The packet created from the outside world pkt
+     * @param next_burst_at Minimum bus timing requirement from controller
+     * @param queue Reference to the read or write queue with the packet
+     * @return pair, tick when current burst is issued and
+     *               tick when next burst can issue
+     */
+    std::pair<Tick, Tick>
+    doBurstAccess(MemPacket* mem_pkt, Tick next_burst_at,
+                  const std::vector<MemPacketQueue>& queue) override;
+
+    /**
+     * Check if a burst operation can be issued to the DRAM
+     *
+     * @param Return true if RD/WR can issue
+     *                    This requires the DRAM to be in the
+     *                    REF IDLE state
+     */
+    bool
+    burstReady(MemPacket* pkt) const override
+    {
+        return ranks[pkt->rank]->inRefIdleState();
+    }
+
+    /**
+     * This function checks if ranks are actively refreshing and
+     * therefore busy. The function also checks if ranks are in
+     * the self-refresh state, in which case, a self-refresh exit
+     * is initiated.
+     * The arguments are NVM-specific and will be ignored by DRAM.
+     * return boolean if all ranks are in refresh and therefore busy
+     */
+    bool isBusy(bool read_queue_empty, bool all_writes_nvm) override;
+
+    /**
+     *  Add rank to rank delay to bus timing to all DRAM banks in alli ranks
+     *  when access to an alternate interface is issued
+     *
+     *  param cmd_at Time of current command used as starting point for
+     *               addition of rank-to-rank delay
+     */
+    void addRankToRankDelay(Tick cmd_at) override;
+
+    /**
+     * Complete response process for DRAM when read burst is complete
+     * This will update the counters and check if a power down state
+     * can be entered.
+     *
+     * @param rank Specifies rank associated with read burst
+     */
+    void respondEvent(uint8_t rank) override;
+
+    /**
+     * Check the refresh state to determine if refresh needs
+     * to be kicked back into action after a read response
+     *
+     * @param rank Specifies rank associated with read burst
+     */
+    void checkRefreshState(uint8_t rank) override;
+
+    /**
+     * The next three functions are NVM-specific and will be ignored by DRAM.
+     */
+    bool readsWaitingToIssue() const override { return false;}
+    void chooseRead(MemPacketQueue& queue) override { }
+    bool writeRespQueueFull() const override { return false;}
+
+    DRAMInterface(const DRAMInterfaceParams &_p);
+};
+
+} // namespace memory
+} // namespace gem5
+
+#endif //__DRAM_INTERFACE_HH__
diff --git a/src/mem/hbm_ctrl.cc b/src/mem/hbm_ctrl.cc
new file mode 100644
index 0000000..03cfec6
--- /dev/null
+++ b/src/mem/hbm_ctrl.cc
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 2022 The Regents of the University of California
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mem/hbm_ctrl.hh"
+
+#include "base/trace.hh"
+#include "debug/DRAM.hh"
+#include "debug/Drain.hh"
+#include "debug/MemCtrl.hh"
+#include "debug/QOS.hh"
+#include "mem/dram_interface.hh"
+#include "mem/mem_interface.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+
+namespace memory
+{
+
+HBMCtrl::HBMCtrl(const HBMCtrlParams &p) :
+    MemCtrl(p),
+    retryRdReqPC1(false), retryWrReqPC1(false),
+    nextReqEventPC1([this] {processNextReqEvent(pc1Int, respQueuePC1,
+                         respondEventPC1, nextReqEventPC1, retryWrReqPC1);},
+                         name()),
+    respondEventPC1([this] {processRespondEvent(pc1Int, respQueuePC1,
+                         respondEventPC1, retryRdReqPC1); }, name()),
+    pc1Int(p.dram_2),
+    partitionedQ(p.partitioned_q)
+{
+    DPRINTF(MemCtrl, "Setting up HBM controller\n");
+
+    pc0Int = dynamic_cast<DRAMInterface*>(dram);
+
+    assert(dynamic_cast<DRAMInterface*>(p.dram_2) != nullptr);
+
+    readBufferSize = pc0Int->readBufferSize + pc1Int->readBufferSize;
+    writeBufferSize = pc0Int->writeBufferSize + pc1Int->writeBufferSize;
+
+    fatal_if(!pc0Int, "Memory controller must have pc0 interface");
+    fatal_if(!pc1Int, "Memory controller must have pc1 interface");
+
+    pc0Int->setCtrl(this, commandWindow, 0);
+    pc1Int->setCtrl(this, commandWindow, 1);
+
+    if (partitionedQ) {
+        writeHighThreshold = (writeBufferSize * (p.write_high_thresh_perc/2)
+                             / 100.0);
+        writeLowThreshold = (writeBufferSize * (p.write_low_thresh_perc/2)
+                            / 100.0);
+    } else {
+        writeHighThreshold = (writeBufferSize * p.write_high_thresh_perc
+                            / 100.0);
+        writeLowThreshold = (writeBufferSize * p.write_low_thresh_perc
+                            / 100.0);
+    }
+}
+
+void
+HBMCtrl::init()
+{
+    MemCtrl::init();
+}
+
+void
+HBMCtrl::startup()
+{
+    MemCtrl::startup();
+
+    isTimingMode = system()->isTimingMode();
+    if (isTimingMode) {
+        // shift the bus busy time sufficiently far ahead that we never
+        // have to worry about negative values when computing the time for
+        // the next request, this will add an insignificant bubble at the
+        // start of simulation
+        pc1Int->nextBurstAt = curTick() + pc1Int->commandOffset();
+    }
+}
+
+Tick
+HBMCtrl::recvAtomic(PacketPtr pkt)
+{
+    Tick latency = 0;
+
+    if (pc0Int->getAddrRange().contains(pkt->getAddr())) {
+        latency = MemCtrl::recvAtomicLogic(pkt, pc0Int);
+    } else if (pc1Int->getAddrRange().contains(pkt->getAddr())) {
+        latency = MemCtrl::recvAtomicLogic(pkt, pc1Int);
+    } else {
+        panic("Can't handle address range for packet %s\n", pkt->print());
+    }
+
+    return latency;
+}
+
+void
+HBMCtrl::recvFunctional(PacketPtr pkt)
+{
+    bool found = MemCtrl::recvFunctionalLogic(pkt, pc0Int);
+
+    if (!found) {
+        found = MemCtrl::recvFunctionalLogic(pkt, pc1Int);
+    }
+
+    if (!found) {
+        panic("Can't handle address range for packet %s\n", pkt->print());
+    }
+}
+
+Tick
+HBMCtrl::recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor)
+{
+    Tick latency = recvAtomic(pkt);
+
+    if (pc0Int && pc0Int->getAddrRange().contains(pkt->getAddr())) {
+        pc0Int->getBackdoor(backdoor);
+    } else if (pc1Int && pc1Int->getAddrRange().contains(pkt->getAddr())) {
+        pc1Int->getBackdoor(backdoor);
+    }
+    else {
+        panic("Can't handle address range for packet %s\n",
+              pkt->print());
+    }
+    return latency;
+}
+
+bool
+HBMCtrl::writeQueueFullPC0(unsigned int neededEntries) const
+{
+    DPRINTF(MemCtrl,
+            "Write queue limit %d, PC0 size %d, entries needed %d\n",
+            writeBufferSize, writeQueueSizePC0, neededEntries);
+
+    unsigned int wrsize_new = (writeQueueSizePC0 + neededEntries);
+    return wrsize_new > (writeBufferSize/2);
+}
+
+bool
+HBMCtrl::writeQueueFullPC1(unsigned int neededEntries) const
+{
+    DPRINTF(MemCtrl,
+            "Write queue limit %d, PC1 size %d, entries needed %d\n",
+            writeBufferSize, writeQueueSizePC1, neededEntries);
+
+    unsigned int wrsize_new = (writeQueueSizePC1 + neededEntries);
+    return wrsize_new > (writeBufferSize/2);
+}
+
+bool
+HBMCtrl::readQueueFullPC0(unsigned int neededEntries) const
+{
+    DPRINTF(MemCtrl,
+            "Read queue limit %d, PC0 size %d, entries needed %d\n",
+            readBufferSize, readQueueSizePC0 + respQueue.size(),
+            neededEntries);
+
+    unsigned int rdsize_new = readQueueSizePC0 + respQueue.size()
+                                               + neededEntries;
+    return rdsize_new > (readBufferSize/2);
+}
+
+bool
+HBMCtrl::readQueueFullPC1(unsigned int neededEntries) const
+{
+    DPRINTF(MemCtrl,
+            "Read queue limit %d, PC1 size %d, entries needed %d\n",
+            readBufferSize, readQueueSizePC1 + respQueuePC1.size(),
+            neededEntries);
+
+    unsigned int rdsize_new = readQueueSizePC1 + respQueuePC1.size()
+                                               + neededEntries;
+    return rdsize_new > (readBufferSize/2);
+}
+
+bool
+HBMCtrl::readQueueFull(unsigned int neededEntries) const
+{
+    DPRINTF(MemCtrl,
+            "HBMCtrl: Read queue limit %d, entries needed %d\n",
+            readBufferSize, neededEntries);
+
+    unsigned int rdsize_new = totalReadQueueSize + respQueue.size() +
+                                respQueuePC1.size() + neededEntries;
+    return rdsize_new > readBufferSize;
+}
+
+bool
+HBMCtrl::recvTimingReq(PacketPtr pkt)
+{
+    // This is where we enter from the outside world
+    DPRINTF(MemCtrl, "recvTimingReq: request %s addr %#x size %d\n",
+            pkt->cmdString(), pkt->getAddr(), pkt->getSize());
+
+    panic_if(pkt->cacheResponding(), "Should not see packets where cache "
+                                        "is responding");
+
+    panic_if(!(pkt->isRead() || pkt->isWrite()),
+                "Should only see read and writes at memory controller\n");
+
+    // Calc avg gap between requests
+    if (prevArrival != 0) {
+        stats.totGap += curTick() - prevArrival;
+    }
+    prevArrival = curTick();
+
+    // What type of media does this packet access?
+    bool is_pc0;
+
+    // TODO: make the interleaving bit across pseudo channels a parameter
+    if (bits(pkt->getAddr(), 6) == 0) {
+        is_pc0 = true;
+    } else {
+        is_pc0 = false;
+    }
+
+    // Find out how many memory packets a pkt translates to
+    // If the burst size is equal or larger than the pkt size, then a pkt
+    // translates to only one memory packet. Otherwise, a pkt translates to
+    // multiple memory packets
+    unsigned size = pkt->getSize();
+    uint32_t burst_size = pc0Int->bytesPerBurst();
+    unsigned offset = pkt->getAddr() & (burst_size - 1);
+    unsigned int pkt_count = divCeil(offset + size, burst_size);
+
+    // run the QoS scheduler and assign a QoS priority value to the packet
+    qosSchedule({&readQueue, &writeQueue}, burst_size, pkt);
+
+    // check local buffers and do not accept if full
+    if (pkt->isWrite()) {
+        if (is_pc0) {
+            if (partitionedQ ? writeQueueFullPC0(pkt_count) :
+                                        writeQueueFull(pkt_count))
+            {
+                DPRINTF(MemCtrl, "Write queue full, not accepting\n");
+                // remember that we have to retry this port
+                MemCtrl::retryWrReq = true;
+                stats.numWrRetry++;
+                return false;
+            } else {
+                addToWriteQueue(pkt, pkt_count, pc0Int);
+                stats.writeReqs++;
+                stats.bytesWrittenSys += size;
+            }
+        } else {
+            if (partitionedQ ? writeQueueFullPC1(pkt_count) :
+                                        writeQueueFull(pkt_count))
+            {
+                DPRINTF(MemCtrl, "Write queue full, not accepting\n");
+                // remember that we have to retry this port
+                retryWrReqPC1 = true;
+                stats.numWrRetry++;
+                return false;
+            } else {
+                addToWriteQueue(pkt, pkt_count, pc1Int);
+                stats.writeReqs++;
+                stats.bytesWrittenSys += size;
+            }
+        }
+    } else {
+
+        assert(pkt->isRead());
+        assert(size != 0);
+
+        if (is_pc0) {
+            if (partitionedQ ? readQueueFullPC0(pkt_count) :
+                                        HBMCtrl::readQueueFull(pkt_count)) {
+                DPRINTF(MemCtrl, "Read queue full, not accepting\n");
+                // remember that we have to retry this port
+                retryRdReqPC1 = true;
+                stats.numRdRetry++;
+                return false;
+            } else {
+                if (!addToReadQueue(pkt, pkt_count, pc0Int)) {
+                    if (!nextReqEvent.scheduled()) {
+                        DPRINTF(MemCtrl, "Request scheduled immediately\n");
+                        schedule(nextReqEvent, curTick());
+                    }
+                }
+
+                stats.readReqs++;
+                stats.bytesReadSys += size;
+            }
+        } else {
+            if (partitionedQ ? readQueueFullPC1(pkt_count) :
+                                        HBMCtrl::readQueueFull(pkt_count)) {
+                DPRINTF(MemCtrl, "Read queue full, not accepting\n");
+                // remember that we have to retry this port
+                retryRdReqPC1 = true;
+                stats.numRdRetry++;
+                return false;
+            } else {
+                if (!addToReadQueue(pkt, pkt_count, pc1Int)) {
+                    if (!nextReqEventPC1.scheduled()) {
+                        DPRINTF(MemCtrl, "Request scheduled immediately\n");
+                        schedule(nextReqEventPC1, curTick());
+                    }
+                }
+                stats.readReqs++;
+                stats.bytesReadSys += size;
+            }
+        }
+    }
+
+    return true;
+}
+
+void
+HBMCtrl::pruneRowBurstTick()
+{
+    auto it = rowBurstTicks.begin();
+    while (it != rowBurstTicks.end()) {
+        auto current_it = it++;
+        if (MemCtrl::getBurstWindow(curTick()) > *current_it) {
+            DPRINTF(MemCtrl, "Removing burstTick for %d\n", *current_it);
+            rowBurstTicks.erase(current_it);
+        }
+    }
+}
+
+void
+HBMCtrl::pruneColBurstTick()
+{
+    auto it = colBurstTicks.begin();
+    while (it != colBurstTicks.end()) {
+        auto current_it = it++;
+        if (MemCtrl::getBurstWindow(curTick()) > *current_it) {
+            DPRINTF(MemCtrl, "Removing burstTick for %d\n", *current_it);
+            colBurstTicks.erase(current_it);
+        }
+    }
+}
+
+void
+HBMCtrl::pruneBurstTick()
+{
+    pruneRowBurstTick();
+    pruneColBurstTick();
+}
+
+Tick
+HBMCtrl::verifySingleCmd(Tick cmd_tick, Tick max_cmds_per_burst, bool row_cmd)
+{
+    // start with assumption that there is no contention on command bus
+    Tick cmd_at = cmd_tick;
+
+    // get tick aligned to burst window
+    Tick burst_tick = MemCtrl::getBurstWindow(cmd_tick);
+
+    // verify that we have command bandwidth to issue the command
+    // if not, iterate over next window(s) until slot found
+
+    if (row_cmd) {
+        while (rowBurstTicks.count(burst_tick) >= max_cmds_per_burst) {
+            DPRINTF(MemCtrl, "Contention found on row command bus at %d\n",
+                    burst_tick);
+            burst_tick += commandWindow;
+            cmd_at = burst_tick;
+        }
+        DPRINTF(MemCtrl, "Now can send a row cmd_at %d\n",
+                    cmd_at);
+        rowBurstTicks.insert(burst_tick);
+
+    } else {
+        while (colBurstTicks.count(burst_tick) >= max_cmds_per_burst) {
+            DPRINTF(MemCtrl, "Contention found on col command bus at %d\n",
+                    burst_tick);
+            burst_tick += commandWindow;
+            cmd_at = burst_tick;
+        }
+        DPRINTF(MemCtrl, "Now can send a col cmd_at %d\n",
+                    cmd_at);
+        colBurstTicks.insert(burst_tick);
+    }
+    return cmd_at;
+}
+
+Tick
+HBMCtrl::verifyMultiCmd(Tick cmd_tick, Tick max_cmds_per_burst,
+                        Tick max_multi_cmd_split)
+{
+
+    // start with assumption that there is no contention on command bus
+    Tick cmd_at = cmd_tick;
+
+    // get tick aligned to burst window
+    Tick burst_tick = MemCtrl::getBurstWindow(cmd_tick);
+
+    // Command timing requirements are from 2nd command
+    // Start with assumption that 2nd command will issue at cmd_at and
+    // find prior slot for 1st command to issue
+    // Given a maximum latency of max_multi_cmd_split between the commands,
+    // find the burst at the maximum latency prior to cmd_at
+    Tick burst_offset = 0;
+    Tick first_cmd_offset = cmd_tick % commandWindow;
+    while (max_multi_cmd_split > (first_cmd_offset + burst_offset)) {
+        burst_offset += commandWindow;
+    }
+    // get the earliest burst aligned address for first command
+    // ensure that the time does not go negative
+    Tick first_cmd_tick = burst_tick - std::min(burst_offset, burst_tick);
+
+    // Can required commands issue?
+    bool first_can_issue = false;
+    bool second_can_issue = false;
+    // verify that we have command bandwidth to issue the command(s)
+    while (!first_can_issue || !second_can_issue) {
+        bool same_burst = (burst_tick == first_cmd_tick);
+        auto first_cmd_count = rowBurstTicks.count(first_cmd_tick);
+        auto second_cmd_count = same_burst ?
+                        first_cmd_count + 1 : rowBurstTicks.count(burst_tick);
+
+        first_can_issue = first_cmd_count < max_cmds_per_burst;
+        second_can_issue = second_cmd_count < max_cmds_per_burst;
+
+        if (!second_can_issue) {
+            DPRINTF(MemCtrl, "Contention (cmd2) found on command bus at %d\n",
+                    burst_tick);
+            burst_tick += commandWindow;
+            cmd_at = burst_tick;
+        }
+
+        // Verify max_multi_cmd_split isn't violated when command 2 is shifted
+        // If commands initially were issued in same burst, they are
+        // now in consecutive bursts and can still issue B2B
+        bool gap_violated = !same_burst &&
+                        ((burst_tick - first_cmd_tick) > max_multi_cmd_split);
+
+        if (!first_can_issue || (!second_can_issue && gap_violated)) {
+            DPRINTF(MemCtrl, "Contention (cmd1) found on command bus at %d\n",
+                    first_cmd_tick);
+            first_cmd_tick += commandWindow;
+        }
+    }
+
+    // Add command to burstTicks
+    rowBurstTicks.insert(burst_tick);
+    rowBurstTicks.insert(first_cmd_tick);
+
+    return cmd_at;
+}
+
+void
+HBMCtrl::drainResume()
+{
+
+    MemCtrl::drainResume();
+
+    if (!isTimingMode && system()->isTimingMode()) {
+        // if we switched to timing mode, kick things into action,
+        // and behave as if we restored from a checkpoint
+        startup();
+        pc1Int->startup();
+    } else if (isTimingMode && !system()->isTimingMode()) {
+        // if we switch from timing mode, stop the refresh events to
+        // not cause issues with KVM
+        if (pc1Int) {
+            pc1Int->drainRanks();
+        }
+    }
+
+    // update the mode
+    isTimingMode = system()->isTimingMode();
+}
+
+} // namespace memory
+} // namespace gem5
diff --git a/src/mem/hbm_ctrl.hh b/src/mem/hbm_ctrl.hh
new file mode 100644
index 0000000..6c73010
--- /dev/null
+++ b/src/mem/hbm_ctrl.hh
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2022 The Regents of the University of California
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * HBMCtrl declaration
+ */
+
+#ifndef __HBM_CTRL_HH__
+#define __HBM_CTRL_HH__
+
+#include <deque>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "mem/mem_ctrl.hh"
+#include "params/HBMCtrl.hh"
+
+namespace gem5
+{
+
+namespace memory
+{
+
+class MemInterface;
+class DRAMInterface;
+
+
+/**
+ * HBM2 is divided into two pseudo channels which have independent data buses
+ * but share a command bus (separate row and column command bus). Therefore,
+ * the HBM memory controller should be able to control both pseudo channels.
+ * This HBM memory controller inherits from gem5's default
+ * memory controller (pseudo channel 0) and manages the additional HBM pseudo
+ * channel (pseudo channel 1).
+ */
+class HBMCtrl : public MemCtrl
+{
+
+  protected:
+
+    bool respQEmpty() override
+    {
+        return (respQueue.empty() && respQueuePC1.empty());
+    }
+
+  private:
+
+    /**
+     * Remember if we have to retry a request for second pseudo channel.
+     */
+    bool retryRdReqPC1;
+    bool retryWrReqPC1;
+
+    /**
+     * Remove commands that have already issued from rowBurstTicks
+     * and colBurstTicks
+     */
+    void pruneBurstTick() override;
+
+  public:
+    HBMCtrl(const HBMCtrlParams &p);
+
+    void pruneRowBurstTick();
+    void pruneColBurstTick();
+
+    /**
+     * Check for command bus contention for single cycle command.
+     * If there is contention, shift command to next burst.
+     * Check verifies that the commands issued per burst is less
+     * than a defined max number, maxCommandsPerWindow.
+     * Therefore, contention per cycle is not verified and instead
+     * is done based on a burst window.
+     *
+     * @param cmd_tick Initial tick of command, to be verified
+     * @param max_cmds_per_burst Number of commands that can issue
+     *                           in a burst window
+     * @return tick for command issue without contention
+     */
+    Tick verifySingleCmd(Tick cmd_tick, Tick max_cmds_per_burst,
+                        bool row_cmd) override;
+
+    /**
+     * Check for command bus contention for multi-cycle (2 currently)
+     * command. If there is contention, shift command(s) to next burst.
+     * Check verifies that the commands issued per burst is less
+     * than a defined max number, maxCommandsPerWindow.
+     * Therefore, contention per cycle is not verified and instead
+     * is done based on a burst window.
+     * For HBM2, only row cmds (activate) can be multi-cycle
+     *
+     * @param cmd_tick Initial tick of command, to be verified
+     * @param max_multi_cmd_split Maximum delay between commands
+     * @param max_cmds_per_burst Number of commands that can issue
+     *                           in a burst window
+     * @return tick for command issue without contention
+     */
+    Tick verifyMultiCmd(Tick cmd_tick, Tick max_cmds_per_burst,
+                        Tick max_multi_cmd_split = 0) override;
+
+    /**
+     * NextReq and Respond events for second pseudo channel
+     *
+     */
+    EventFunctionWrapper nextReqEventPC1;
+    EventFunctionWrapper respondEventPC1;
+
+    /**
+     * Check if the read queue partition of both pseudo
+     * channels has room for more entries. This is used when the HBM ctrl
+     * is run with partitioned queues
+     *
+     * @param pkt_count The number of entries needed in the read queue
+     * @return true if read queue partition is full, false otherwise
+     */
+    bool readQueueFullPC0(unsigned int pkt_count) const;
+    bool readQueueFullPC1(unsigned int pkt_count) const;
+    bool readQueueFull(unsigned int pkt_count) const;
+
+    /**
+     * Check if the write queue partition of both pseudo
+     * channels has room for more entries. This is used when the HBM ctrl
+     * is run with partitioned queues
+     *
+     * @param pkt_count The number of entries needed in the write queue
+     * @return true if write queue is full, false otherwise
+     */
+    bool writeQueueFullPC0(unsigned int pkt_count) const;
+    bool writeQueueFullPC1(unsigned int pkt_count) const;
+
+    /**
+     * Following counters are used to keep track of the entries in read/write
+     * queue for each pseudo channel (useful when the partitioned queues are
+     * used)
+     */
+    uint64_t readQueueSizePC0 = 0;
+    uint64_t readQueueSizePC1 = 0;
+    uint64_t writeQueueSizePC0 = 0;
+    uint64_t writeQueueSizePC1 = 0;
+
+    /**
+     * Response queue for pkts sent to second pseudo channel
+     * The first pseudo channel uses MemCtrl::respQueue
+     */
+    std::deque<MemPacket*> respQueuePC1;
+
+    /**
+     * Holds count of row commands issued in burst window starting at
+     * defined Tick. This is used to ensure that the row command bandwidth
+     * does not exceed the allowable media constraints.
+     */
+    std::unordered_multiset<Tick> rowBurstTicks;
+
+    /**
+     * This is used to ensure that the column command bandwidth
+     * does not exceed the allowable media constraints. HBM2 has separate
+     * command bus for row and column commands
+     */
+    std::unordered_multiset<Tick> colBurstTicks;
+
+    /**
+     * Pointers to interfaces of the two pseudo channels
+     * pc0Int is same as MemCtrl::dram (it will be pointing to
+     * the DRAM interface defined in base MemCtrl)
+     */
+    DRAMInterface* pc0Int;
+    DRAMInterface* pc1Int;
+
+    /**
+     * This indicates if the R/W queues will be partitioned among
+     * pseudo channels
+     */
+    bool partitionedQ;
+
+  public:
+
+    /**
+     * Is there a respondEvent for pseudo channel 1 scheduled?
+     *
+     * @return true if event is scheduled
+     */
+    bool respondEventPC1Scheduled() const
+    {
+        return respondEventPC1.scheduled();
+    }
+
+    /**
+     * Is there a read/write burst Event scheduled?
+     *
+     * @return true if event is scheduled
+     */
+    bool requestEventScheduled(uint8_t pseudo_channel) const override
+    {
+        if (pseudo_channel == 0) {
+            return MemCtrl::requestEventScheduled(pseudo_channel);
+        } else {
+            assert(pseudo_channel == 1);
+            return nextReqEventPC1.scheduled();
+        }
+    }
+
+    /**
+     * restart the controller scheduler
+     *
+     * @param Tick to schedule next event
+     * @param pseudo_channel pseudo channel number for which scheduler
+     * needs to restart
+     */
+    void restartScheduler(Tick tick, uint8_t pseudo_channel) override
+    {
+        if (pseudo_channel == 0) {
+            MemCtrl::restartScheduler(tick);
+        } else {
+            schedule(nextReqEventPC1, tick);
+        }
+    }
+
+
+    virtual void init() override;
+    virtual void startup() override;
+    virtual void drainResume() override;
+
+
+  protected:
+    Tick recvAtomic(PacketPtr pkt) override;
+    Tick recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor) override;
+    void recvFunctional(PacketPtr pkt) override;
+    bool recvTimingReq(PacketPtr pkt) override;
+
+};
+
+} // namespace memory
+} // namespace gem5
+
+#endif //__HBM_CTRL_HH__
diff --git a/src/mem/hetero_mem_ctrl.cc b/src/mem/hetero_mem_ctrl.cc
new file mode 100644
index 0000000..5a9534b
--- /dev/null
+++ b/src/mem/hetero_mem_ctrl.cc
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2010-2020 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2013 Amin Farmahini-Farahani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mem/hetero_mem_ctrl.hh"
+
+#include "base/trace.hh"
+#include "debug/DRAM.hh"
+#include "debug/Drain.hh"
+#include "debug/MemCtrl.hh"
+#include "debug/NVM.hh"
+#include "debug/QOS.hh"
+#include "mem/dram_interface.hh"
+#include "mem/mem_interface.hh"
+#include "mem/nvm_interface.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+
+namespace memory
+{
+
+HeteroMemCtrl::HeteroMemCtrl(const HeteroMemCtrlParams &p) :
+    MemCtrl(p),
+    nvm(p.nvm)
+{
+    DPRINTF(MemCtrl, "Setting up controller\n");
+    readQueue.resize(p.qos_priorities);
+    writeQueue.resize(p.qos_priorities);
+
+    fatal_if(dynamic_cast<DRAMInterface*>(dram) == nullptr,
+            "HeteroMemCtrl's dram interface must be of type DRAMInterface.\n");
+    fatal_if(dynamic_cast<NVMInterface*>(nvm) == nullptr,
+            "HeteroMemCtrl's nvm interface must be of type NVMInterface.\n");
+
+    // hook up interfaces to the controller
+    dram->setCtrl(this, commandWindow);
+    nvm->setCtrl(this, commandWindow);
+
+    readBufferSize = dram->readBufferSize + nvm->readBufferSize;
+    writeBufferSize = dram->writeBufferSize + nvm->writeBufferSize;
+
+    writeHighThreshold = writeBufferSize * p.write_high_thresh_perc / 100.0;
+    writeLowThreshold = writeBufferSize * p.write_low_thresh_perc / 100.0;
+
+    // perform a basic check of the write thresholds
+    if (p.write_low_thresh_perc >= p.write_high_thresh_perc)
+        fatal("Write buffer low threshold %d must be smaller than the "
+              "high threshold %d\n", p.write_low_thresh_perc,
+              p.write_high_thresh_perc);
+}
+
+Tick
+HeteroMemCtrl::recvAtomic(PacketPtr pkt)
+{
+    Tick latency = 0;
+
+    if (dram->getAddrRange().contains(pkt->getAddr())) {
+        latency = MemCtrl::recvAtomicLogic(pkt, dram);
+    } else if (nvm->getAddrRange().contains(pkt->getAddr())) {
+        latency = MemCtrl::recvAtomicLogic(pkt, nvm);
+    } else {
+        panic("Can't handle address range for packet %s\n", pkt->print());
+    }
+
+    return latency;
+}
+
+bool
+HeteroMemCtrl::recvTimingReq(PacketPtr pkt)
+{
+    // This is where we enter from the outside world
+    DPRINTF(MemCtrl, "recvTimingReq: request %s addr %#x size %d\n",
+            pkt->cmdString(), pkt->getAddr(), pkt->getSize());
+
+    panic_if(pkt->cacheResponding(), "Should not see packets where cache "
+             "is responding");
+
+    panic_if(!(pkt->isRead() || pkt->isWrite()),
+             "Should only see read and writes at memory controller\n");
+
+    // Calc avg gap between requests
+    if (prevArrival != 0) {
+        stats.totGap += curTick() - prevArrival;
+    }
+    prevArrival = curTick();
+
+    // What type of media does this packet access?
+    bool is_dram;
+    if (dram->getAddrRange().contains(pkt->getAddr())) {
+        is_dram = true;
+    } else if (nvm->getAddrRange().contains(pkt->getAddr())) {
+        is_dram = false;
+    } else {
+        panic("Can't handle address range for packet %s\n",
+              pkt->print());
+    }
+
+    // Find out how many memory packets a pkt translates to
+    // If the burst size is equal or larger than the pkt size, then a pkt
+    // translates to only one memory packet. Otherwise, a pkt translates to
+    // multiple memory packets
+    unsigned size = pkt->getSize();
+    uint32_t burst_size = is_dram ? dram->bytesPerBurst() :
+                                    nvm->bytesPerBurst();
+    unsigned offset = pkt->getAddr() & (burst_size - 1);
+    unsigned int pkt_count = divCeil(offset + size, burst_size);
+
+    // run the QoS scheduler and assign a QoS priority value to the packet
+    qosSchedule( { &readQueue, &writeQueue }, burst_size, pkt);
+
+    // check local buffers and do not accept if full
+    if (pkt->isWrite()) {
+        assert(size != 0);
+        if (writeQueueFull(pkt_count)) {
+            DPRINTF(MemCtrl, "Write queue full, not accepting\n");
+            // remember that we have to retry this port
+            retryWrReq = true;
+            stats.numWrRetry++;
+            return false;
+        } else {
+            addToWriteQueue(pkt, pkt_count, is_dram ? dram : nvm);
+            // If we are not already scheduled to get a request out of the
+            // queue, do so now
+            if (!nextReqEvent.scheduled()) {
+                DPRINTF(MemCtrl, "Request scheduled immediately\n");
+                schedule(nextReqEvent, curTick());
+            }
+            stats.writeReqs++;
+            stats.bytesWrittenSys += size;
+        }
+    } else {
+        assert(pkt->isRead());
+        assert(size != 0);
+        if (readQueueFull(pkt_count)) {
+            DPRINTF(MemCtrl, "Read queue full, not accepting\n");
+            // remember that we have to retry this port
+            retryRdReq = true;
+            stats.numRdRetry++;
+            return false;
+        } else {
+            if (!addToReadQueue(pkt, pkt_count, is_dram ? dram : nvm)) {
+                // If we are not already scheduled to get a request out of the
+                // queue, do so now
+                if (!nextReqEvent.scheduled()) {
+                    DPRINTF(MemCtrl, "Request scheduled immediately\n");
+                    schedule(nextReqEvent, curTick());
+                }
+            }
+            stats.readReqs++;
+            stats.bytesReadSys += size;
+        }
+    }
+
+    return true;
+}
+
+void
+HeteroMemCtrl::processRespondEvent(MemInterface* mem_intr,
+                        MemPacketQueue& queue,
+                        EventFunctionWrapper& resp_event,
+                        bool& retry_rd_req)
+{
+    DPRINTF(MemCtrl,
+            "processRespondEvent(): Some req has reached its readyTime\n");
+
+    if (queue.front()->isDram()) {
+        MemCtrl::processRespondEvent(dram, queue, resp_event, retry_rd_req);
+    } else {
+        MemCtrl::processRespondEvent(nvm, queue, resp_event, retry_rd_req);
+    }
+}
+
+MemPacketQueue::iterator
+HeteroMemCtrl::chooseNext(MemPacketQueue& queue, Tick extra_col_delay,
+                    MemInterface* mem_int)
+{
+    // This method does the arbitration between requests.
+
+    MemPacketQueue::iterator ret = queue.end();
+
+    if (!queue.empty()) {
+        if (queue.size() == 1) {
+            // available rank corresponds to state refresh idle
+            MemPacket* mem_pkt = *(queue.begin());
+            if (packetReady(mem_pkt, mem_pkt->isDram()? dram : nvm)) {
+                ret = queue.begin();
+                DPRINTF(MemCtrl, "Single request, going to a free rank\n");
+            } else {
+                DPRINTF(MemCtrl, "Single request, going to a busy rank\n");
+            }
+        } else if (memSchedPolicy == enums::fcfs) {
+            // check if there is a packet going to a free rank
+            for (auto i = queue.begin(); i != queue.end(); ++i) {
+                MemPacket* mem_pkt = *i;
+                if (packetReady(mem_pkt, mem_pkt->isDram()? dram : nvm)) {
+                    ret = i;
+                    break;
+                }
+            }
+        } else if (memSchedPolicy == enums::frfcfs) {
+            Tick col_allowed_at;
+            std::tie(ret, col_allowed_at)
+                    = chooseNextFRFCFS(queue, extra_col_delay, mem_int);
+        } else {
+            panic("No scheduling policy chosen\n");
+        }
+    }
+    return ret;
+}
+
+std::pair<MemPacketQueue::iterator, Tick>
+HeteroMemCtrl::chooseNextFRFCFS(MemPacketQueue& queue, Tick extra_col_delay,
+                          MemInterface* mem_intr)
+{
+
+    auto selected_pkt_it = queue.end();
+    auto nvm_pkt_it = queue.end();
+    Tick col_allowed_at = MaxTick;
+    Tick nvm_col_allowed_at = MaxTick;
+
+    std::tie(selected_pkt_it, col_allowed_at) =
+            MemCtrl::chooseNextFRFCFS(queue, extra_col_delay, dram);
+
+    std::tie(nvm_pkt_it, nvm_col_allowed_at) =
+            MemCtrl::chooseNextFRFCFS(queue, extra_col_delay, nvm);
+
+
+    // Compare DRAM and NVM and select NVM if it can issue
+    // earlier than the DRAM packet
+    if (col_allowed_at > nvm_col_allowed_at) {
+        selected_pkt_it = nvm_pkt_it;
+        col_allowed_at = nvm_col_allowed_at;
+    }
+
+    return std::make_pair(selected_pkt_it, col_allowed_at);
+}
+
+
+Tick
+HeteroMemCtrl::doBurstAccess(MemPacket* mem_pkt, MemInterface* mem_intr)
+{
+    // mem_intr will be dram by default in HeteroMemCtrl
+
+    // When was command issued?
+    Tick cmd_at;
+
+    if (mem_pkt->isDram()) {
+        cmd_at = MemCtrl::doBurstAccess(mem_pkt, mem_intr);
+        // Update timing for NVM ranks if NVM is configured on this channel
+        nvm->addRankToRankDelay(cmd_at);
+        // Since nextBurstAt and nextReqAt are part of the interface, making
+        // sure that they are same for both nvm and dram interfaces
+        nvm->nextBurstAt = dram->nextBurstAt;
+        nvm->nextReqTime = dram->nextReqTime;
+
+    } else {
+        cmd_at = MemCtrl::doBurstAccess(mem_pkt, nvm);
+        // Update timing for NVM ranks if NVM is configured on this channel
+        dram->addRankToRankDelay(cmd_at);
+        dram->nextBurstAt = nvm->nextBurstAt;
+        dram->nextReqTime = nvm->nextReqTime;
+    }
+
+    return cmd_at;
+}
+
+bool
+HeteroMemCtrl::memBusy(MemInterface* mem_intr) {
+
+    // mem_intr in case of HeteroMemCtrl will always be dram
+
+    // check ranks for refresh/wakeup - uses busStateNext, so done after
+    // turnaround decisions
+    // Default to busy status and update based on interface specifics
+    bool dram_busy, nvm_busy = true;
+    // DRAM
+    dram_busy = mem_intr->isBusy(false, false);
+    // NVM
+    bool read_queue_empty = totalReadQueueSize == 0;
+    bool all_writes_nvm = nvm->numWritesQueued == totalWriteQueueSize;
+    nvm_busy = nvm->isBusy(read_queue_empty, all_writes_nvm);
+
+    // Default state of unused interface is 'true'
+    // Simply AND the busy signals to determine if system is busy
+    if (dram_busy && nvm_busy) {
+        // if all ranks are refreshing wait for them to finish
+        // and stall this state machine without taking any further
+        // action, and do not schedule a new nextReqEvent
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void
+HeteroMemCtrl::nonDetermReads(MemInterface* mem_intr)
+{
+    // mem_intr by default points to dram in case
+    // of HeteroMemCtrl, therefore, calling nonDetermReads
+    // from MemCtrl using nvm interace
+    MemCtrl::nonDetermReads(nvm);
+}
+
+bool
+HeteroMemCtrl::nvmWriteBlock(MemInterface* mem_intr)
+{
+    // mem_intr by default points to dram in case
+    // of HeteroMemCtrl, therefore, calling nvmWriteBlock
+    // from MemCtrl using nvm interface
+    return MemCtrl::nvmWriteBlock(nvm);
+}
+
+Tick
+HeteroMemCtrl::minReadToWriteDataGap()
+{
+    return std::min(dram->minReadToWriteDataGap(),
+                    nvm->minReadToWriteDataGap());
+}
+
+Tick
+HeteroMemCtrl::minWriteToReadDataGap()
+{
+    return std::min(dram->minWriteToReadDataGap(),
+                    nvm->minWriteToReadDataGap());
+}
+
+Addr
+HeteroMemCtrl::burstAlign(Addr addr, MemInterface* mem_intr) const
+{
+    // mem_intr will point to dram interface in HeteroMemCtrl
+    if (mem_intr->getAddrRange().contains(addr)) {
+        return (addr & ~(Addr(mem_intr->bytesPerBurst() - 1)));
+    } else {
+        assert(nvm->getAddrRange().contains(addr));
+        return (addr & ~(Addr(nvm->bytesPerBurst() - 1)));
+    }
+}
+
+bool
+HeteroMemCtrl::pktSizeCheck(MemPacket* mem_pkt, MemInterface* mem_intr) const
+{
+    // mem_intr will point to dram interface in HeteroMemCtrl
+    if (mem_pkt->isDram()) {
+        return (mem_pkt->size <= mem_intr->bytesPerBurst());
+    } else {
+        return (mem_pkt->size <= nvm->bytesPerBurst());
+    }
+}
+
+void
+HeteroMemCtrl::recvFunctional(PacketPtr pkt)
+{
+    bool found;
+
+    found = MemCtrl::recvFunctionalLogic(pkt, dram);
+
+    if (!found) {
+        found = MemCtrl::recvFunctionalLogic(pkt, nvm);
+    }
+
+    if (!found) {
+        panic("Can't handle address range for packet %s\n", pkt->print());
+    }
+}
+
+bool
+HeteroMemCtrl::allIntfDrained() const
+{
+    // ensure dram is in power down and refresh IDLE states
+    bool dram_drained = dram->allRanksDrained();
+    // No outstanding NVM writes
+    // All other queues verified as needed with calling logic
+    bool nvm_drained = nvm->allRanksDrained();
+    return (dram_drained && nvm_drained);
+}
+
+DrainState
+HeteroMemCtrl::drain()
+{
+    // if there is anything in any of our internal queues, keep track
+    // of that as well
+    if (!(!totalWriteQueueSize && !totalReadQueueSize && respQueue.empty() &&
+          allIntfDrained())) {
+
+        DPRINTF(Drain, "Memory controller not drained, write: %d, read: %d,"
+                " resp: %d\n", totalWriteQueueSize, totalReadQueueSize,
+                respQueue.size());
+
+        // the only queue that is not drained automatically over time
+        // is the write queue, thus kick things into action if needed
+        if (!totalWriteQueueSize && !nextReqEvent.scheduled()) {
+            schedule(nextReqEvent, curTick());
+        }
+
+        dram->drainRanks();
+
+        return DrainState::Draining;
+    } else {
+        return DrainState::Drained;
+    }
+}
+
+void
+HeteroMemCtrl::drainResume()
+{
+    if (!isTimingMode && system()->isTimingMode()) {
+        // if we switched to timing mode, kick things into action,
+        // and behave as if we restored from a checkpoint
+        startup();
+        dram->startup();
+    } else if (isTimingMode && !system()->isTimingMode()) {
+        // if we switch from timing mode, stop the refresh events to
+        // not cause issues with KVM
+        dram->suspend();
+    }
+
+    // update the mode
+    isTimingMode = system()->isTimingMode();
+}
+
+AddrRangeList
+HeteroMemCtrl::getAddrRanges()
+{
+    AddrRangeList ranges;
+    ranges.push_back(dram->getAddrRange());
+    ranges.push_back(nvm->getAddrRange());
+    return ranges;
+}
+
+} // namespace memory
+} // namespace gem5
diff --git a/src/mem/hetero_mem_ctrl.hh b/src/mem/hetero_mem_ctrl.hh
new file mode 100644
index 0000000..ee1aa0c
--- /dev/null
+++ b/src/mem/hetero_mem_ctrl.hh
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2012-2020 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2013 Amin Farmahini-Farahani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * HeteroMemCtrl declaration
+ */
+
+#ifndef __HETERO_MEM_CTRL_HH__
+#define __HETERO_MEM_CTRL_HH__
+
+#include "mem/mem_ctrl.hh"
+#include "params/HeteroMemCtrl.hh"
+
+namespace gem5
+{
+
+namespace memory
+{
+class HeteroMemCtrl : public MemCtrl
+{
+  private:
+
+    /**
+     * Create pointer to interface of the actual nvm media when connected.
+     */
+    NVMInterface* nvm;
+    MemPacketQueue::iterator chooseNext(MemPacketQueue& queue,
+                      Tick extra_col_delay, MemInterface* mem_int) override;
+    virtual std::pair<MemPacketQueue::iterator, Tick>
+    chooseNextFRFCFS(MemPacketQueue& queue, Tick extra_col_delay,
+                    MemInterface* mem_intr) override;
+    Tick doBurstAccess(MemPacket* mem_pkt, MemInterface* mem_int) override;
+    Tick minReadToWriteDataGap() override;
+    Tick minWriteToReadDataGap() override;
+    AddrRangeList getAddrRanges() override;
+
+    /**
+     * Burst-align an address.
+     *
+     * @param addr The potentially unaligned address
+     * @param mem_intr The DRAM memory interface this packet belongs to
+     *
+     * @return An address aligned to a memory burst
+     */
+    virtual Addr burstAlign(Addr addr, MemInterface* mem_intr) const override;
+
+    /**
+     * Check if mem pkt's size is sane
+     *
+     * @param mem_pkt memory packet
+     * @param mem_intr memory interface
+     * @return a boolean indicating if the mem pkt size is less than
+     * the burst size of the related mem interface
+     */
+    virtual bool
+    pktSizeCheck(MemPacket* mem_pkt, MemInterface* mem_intr) const override;
+
+    virtual void processRespondEvent(MemInterface* mem_intr,
+                        MemPacketQueue& queue,
+                        EventFunctionWrapper& resp_event,
+                        bool& retry_rd_req) override;
+
+    /**
+     * Checks if the memory interface is already busy
+     *
+     * @param mem_intr memory interface to check
+     * @return a boolean indicating if memory is busy
+     */
+    virtual bool memBusy(MemInterface* mem_intr) override;
+
+    /**
+     * Will access nvm memory interface and select non-deterministic
+     * reads to issue
+     */
+    virtual void nonDetermReads(MemInterface* mem_intr) override;
+
+    /**
+     * Will check if all writes are for nvm interface
+     * and nvm's write resp queue is full.
+     *
+     * @param mem_intr memory interface to use
+     * @return a boolean showing if nvm is blocked with writes
+     */
+    virtual bool nvmWriteBlock(MemInterface* mem_intr) override;
+
+  public:
+
+    HeteroMemCtrl(const HeteroMemCtrlParams &p);
+
+    bool allIntfDrained() const override;
+    DrainState drain() override;
+    void drainResume() override;
+
+  protected:
+
+    Tick recvAtomic(PacketPtr pkt) override;
+    void recvFunctional(PacketPtr pkt) override;
+    bool recvTimingReq(PacketPtr pkt) override;
+
+};
+
+} // namespace memory
+} // namespace gem5
+
+#endif //__HETERO_MEM_CTRL_HH__
diff --git a/src/mem/mem_ctrl.cc b/src/mem/mem_ctrl.cc
index 18b8476..3baa1b0 100644
--- a/src/mem/mem_ctrl.cc
+++ b/src/mem/mem_ctrl.cc
@@ -46,7 +46,9 @@
 #include "debug/MemCtrl.hh"
 #include "debug/NVM.hh"
 #include "debug/QOS.hh"
+#include "mem/dram_interface.hh"
 #include "mem/mem_interface.hh"
+#include "mem/nvm_interface.hh"
 #include "sim/system.hh"
 
 namespace gem5
@@ -59,36 +61,31 @@
     qos::MemCtrl(p),
     port(name() + ".port", *this), isTimingMode(false),
     retryRdReq(false), retryWrReq(false),
-    nextReqEvent([this]{ processNextReqEvent(); }, name()),
-    respondEvent([this]{ processRespondEvent(); }, name()),
-    dram(p.dram), nvm(p.nvm),
-    readBufferSize((dram ? dram->readBufferSize : 0) +
-                   (nvm ? nvm->readBufferSize : 0)),
-    writeBufferSize((dram ? dram->writeBufferSize : 0) +
-                    (nvm ? nvm->writeBufferSize : 0)),
+    nextReqEvent([this] {processNextReqEvent(dram, respQueue,
+                         respondEvent, nextReqEvent, retryWrReq);}, name()),
+    respondEvent([this] {processRespondEvent(dram, respQueue,
+                         respondEvent, retryRdReq); }, name()),
+    dram(p.dram),
+    readBufferSize(dram->readBufferSize),
+    writeBufferSize(dram->writeBufferSize),
     writeHighThreshold(writeBufferSize * p.write_high_thresh_perc / 100.0),
     writeLowThreshold(writeBufferSize * p.write_low_thresh_perc / 100.0),
     minWritesPerSwitch(p.min_writes_per_switch),
+    minReadsPerSwitch(p.min_reads_per_switch),
     writesThisTime(0), readsThisTime(0),
     memSchedPolicy(p.mem_sched_policy),
     frontendLatency(p.static_frontend_latency),
     backendLatency(p.static_backend_latency),
     commandWindow(p.command_window),
-    nextBurstAt(0), prevArrival(0),
-    nextReqTime(0),
+    prevArrival(0),
     stats(*this)
 {
     DPRINTF(MemCtrl, "Setting up controller\n");
+
     readQueue.resize(p.qos_priorities);
     writeQueue.resize(p.qos_priorities);
 
-    // Hook up interfaces to the controller
-    if (dram)
-        dram->setCtrl(this, commandWindow);
-    if (nvm)
-        nvm->setCtrl(this, commandWindow);
-
-    fatal_if(!dram && !nvm, "Memory controller must have an interface");
+    dram->setCtrl(this, commandWindow);
 
     // perform a basic check of the write thresholds
     if (p.write_low_thresh_perc >= p.write_high_thresh_perc)
@@ -118,55 +115,48 @@
         // have to worry about negative values when computing the time for
         // the next request, this will add an insignificant bubble at the
         // start of simulation
-        nextBurstAt = curTick() + (dram ? dram->commandOffset() :
-                                          nvm->commandOffset());
+        dram->nextBurstAt = curTick() + dram->commandOffset();
     }
 }
 
 Tick
 MemCtrl::recvAtomic(PacketPtr pkt)
 {
+    if (!dram->getAddrRange().contains(pkt->getAddr())) {
+        panic("Can't handle address range for packet %s\n", pkt->print());
+    }
+
+    return recvAtomicLogic(pkt, dram);
+}
+
+
+Tick
+MemCtrl::recvAtomicLogic(PacketPtr pkt, MemInterface* mem_intr)
+{
     DPRINTF(MemCtrl, "recvAtomic: %s 0x%x\n",
                      pkt->cmdString(), pkt->getAddr());
 
     panic_if(pkt->cacheResponding(), "Should not see packets where cache "
              "is responding");
 
-    Tick latency = 0;
     // do the actual memory access and turn the packet into a response
-    if (dram && dram->getAddrRange().contains(pkt->getAddr())) {
-        dram->access(pkt);
+    mem_intr->access(pkt);
 
-        if (pkt->hasData()) {
-            // this value is not supposed to be accurate, just enough to
-            // keep things going, mimic a closed page
-            latency = dram->accessLatency();
-        }
-    } else if (nvm && nvm->getAddrRange().contains(pkt->getAddr())) {
-        nvm->access(pkt);
-
-        if (pkt->hasData()) {
-            // this value is not supposed to be accurate, just enough to
-            // keep things going, mimic a closed page
-            latency = nvm->accessLatency();
-        }
-    } else {
-        panic("Can't handle address range for packet %s\n",
-              pkt->print());
+    if (pkt->hasData()) {
+        // this value is not supposed to be accurate, just enough to
+        // keep things going, mimic a closed page
+        // also this latency can't be 0
+        return mem_intr->accessLatency();
     }
 
-    return latency;
+    return 0;
 }
 
 Tick
 MemCtrl::recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor)
 {
     Tick latency = recvAtomic(pkt);
-    if (dram) {
-        dram->getBackdoor(backdoor);
-    } else if (nvm) {
-        nvm->getBackdoor(backdoor);
-    }
+    dram->getBackdoor(backdoor);
     return latency;
 }
 
@@ -193,8 +183,9 @@
     return  wrsize_new > writeBufferSize;
 }
 
-void
-MemCtrl::addToReadQueue(PacketPtr pkt, unsigned int pkt_count, bool is_dram)
+bool
+MemCtrl::addToReadQueue(PacketPtr pkt,
+                unsigned int pkt_count, MemInterface* mem_intr)
 {
     // only add to the read queue here. whenever the request is
     // eventually done, set the readyTime, and call schedule()
@@ -213,8 +204,8 @@
     unsigned pktsServicedByWrQ = 0;
     BurstHelper* burst_helper = NULL;
 
-    uint32_t burst_size = is_dram ? dram->bytesPerBurst() :
-                                    nvm->bytesPerBurst();
+    uint32_t burst_size = mem_intr->bytesPerBurst();
+
     for (int cnt = 0; cnt < pkt_count; ++cnt) {
         unsigned size = std::min((addr | (burst_size - 1)) + 1,
                         base_addr + pkt->getSize()) - addr;
@@ -225,7 +216,7 @@
         // First check write buffer to see if the data is already at
         // the controller
         bool foundInWrQ = false;
-        Addr burst_addr = burstAlign(addr, is_dram);
+        Addr burst_addr = burstAlign(addr, mem_intr);
         // if the burst address is not present then there is no need
         // looking any further
         if (isInWriteQueue.find(burst_addr) != isInWriteQueue.end()) {
@@ -262,17 +253,14 @@
             }
 
             MemPacket* mem_pkt;
-            if (is_dram) {
-                mem_pkt = dram->decodePacket(pkt, addr, size, true, true);
-                // increment read entries of the rank
-                dram->setupRank(mem_pkt->rank, true);
-            } else {
-                mem_pkt = nvm->decodePacket(pkt, addr, size, true, false);
-                // Increment count to trigger issue of non-deterministic read
-                nvm->setupRank(mem_pkt->rank, true);
-                // Default readyTime to Max; will be reset once read is issued
-                mem_pkt->readyTime = MaxTick;
-            }
+            mem_pkt = mem_intr->decodePacket(pkt, addr, size, true,
+                                                    mem_intr->pseudoChannel);
+
+            // Increment read entries of the rank (dram)
+            // Increment count to trigger issue of non-deterministic read (nvm)
+            mem_intr->setupRank(mem_pkt->rank, true);
+            // Default readyTime to Max; will be reset once read is issued
+            mem_pkt->readyTime = MaxTick;
             mem_pkt->burstHelper = burst_helper;
 
             assert(!readQueueFull(1));
@@ -283,8 +271,8 @@
             readQueue[mem_pkt->qosValue()].push_back(mem_pkt);
 
             // log packet
-            logRequest(MemCtrl::READ, pkt->requestorId(), pkt->qosValue(),
-                       mem_pkt->addr, 1);
+            logRequest(MemCtrl::READ, pkt->requestorId(),
+                       pkt->qosValue(), mem_pkt->addr, 1);
 
             // Update stats
             stats.avgRdQLen = totalReadQueueSize + respQueue.size();
@@ -296,24 +284,21 @@
 
     // If all packets are serviced by write queue, we send the repsonse back
     if (pktsServicedByWrQ == pkt_count) {
-        accessAndRespond(pkt, frontendLatency);
-        return;
+        accessAndRespond(pkt, frontendLatency, mem_intr);
+        return true;
     }
 
     // Update how many split packets are serviced by write queue
     if (burst_helper != NULL)
         burst_helper->burstsServiced = pktsServicedByWrQ;
 
-    // If we are not already scheduled to get a request out of the
-    // queue, do so now
-    if (!nextReqEvent.scheduled()) {
-        DPRINTF(MemCtrl, "Request scheduled immediately\n");
-        schedule(nextReqEvent, curTick());
-    }
+    // not all/any packets serviced by the write queue
+    return false;
 }
 
 void
-MemCtrl::addToWriteQueue(PacketPtr pkt, unsigned int pkt_count, bool is_dram)
+MemCtrl::addToWriteQueue(PacketPtr pkt, unsigned int pkt_count,
+                                MemInterface* mem_intr)
 {
     // only add to the write queue here. whenever the request is
     // eventually done, set the readyTime, and call schedule()
@@ -323,8 +308,8 @@
     // multiple packets
     const Addr base_addr = pkt->getAddr();
     Addr addr = base_addr;
-    uint32_t burst_size = is_dram ? dram->bytesPerBurst() :
-                                    nvm->bytesPerBurst();
+    uint32_t burst_size = mem_intr->bytesPerBurst();
+
     for (int cnt = 0; cnt < pkt_count; ++cnt) {
         unsigned size = std::min((addr | (burst_size - 1)) + 1,
                         base_addr + pkt->getSize()) - addr;
@@ -334,31 +319,32 @@
 
         // see if we can merge with an existing item in the write
         // queue and keep track of whether we have merged or not
-        bool merged = isInWriteQueue.find(burstAlign(addr, is_dram)) !=
+        bool merged = isInWriteQueue.find(burstAlign(addr, mem_intr)) !=
             isInWriteQueue.end();
 
         // if the item was not merged we need to create a new write
         // and enqueue it
         if (!merged) {
             MemPacket* mem_pkt;
-            if (is_dram) {
-                mem_pkt = dram->decodePacket(pkt, addr, size, false, true);
-                dram->setupRank(mem_pkt->rank, false);
-            } else {
-                mem_pkt = nvm->decodePacket(pkt, addr, size, false, false);
-                nvm->setupRank(mem_pkt->rank, false);
-            }
+            mem_pkt = mem_intr->decodePacket(pkt, addr, size, false,
+                                                    mem_intr->pseudoChannel);
+            // Default readyTime to Max if nvm interface;
+            //will be reset once read is issued
+            mem_pkt->readyTime = MaxTick;
+
+            mem_intr->setupRank(mem_pkt->rank, false);
+
             assert(totalWriteQueueSize < writeBufferSize);
             stats.wrQLenPdf[totalWriteQueueSize]++;
 
             DPRINTF(MemCtrl, "Adding to write queue\n");
 
             writeQueue[mem_pkt->qosValue()].push_back(mem_pkt);
-            isInWriteQueue.insert(burstAlign(addr, is_dram));
+            isInWriteQueue.insert(burstAlign(addr, mem_intr));
 
             // log packet
-            logRequest(MemCtrl::WRITE, pkt->requestorId(), pkt->qosValue(),
-                       mem_pkt->addr, 1);
+            logRequest(MemCtrl::WRITE, pkt->requestorId(),
+                       pkt->qosValue(), mem_pkt->addr, 1);
 
             assert(totalWriteQueueSize == isInWriteQueue.size());
 
@@ -383,14 +369,7 @@
     // snoop the write queue for any upcoming reads
     // @todo, if a pkt size is larger than burst size, we might need a
     // different front end latency
-    accessAndRespond(pkt, frontendLatency);
-
-    // If we are not already scheduled to get a request out of the
-    // queue, do so now
-    if (!nextReqEvent.scheduled()) {
-        DPRINTF(MemCtrl, "Request scheduled immediately\n");
-        schedule(nextReqEvent, curTick());
-    }
+    accessAndRespond(pkt, frontendLatency, mem_intr);
 }
 
 void
@@ -437,25 +416,16 @@
     }
     prevArrival = curTick();
 
-    // What type of media does this packet access?
-    bool is_dram;
-    if (dram && dram->getAddrRange().contains(pkt->getAddr())) {
-        is_dram = true;
-    } else if (nvm && nvm->getAddrRange().contains(pkt->getAddr())) {
-        is_dram = false;
-    } else {
-        panic("Can't handle address range for packet %s\n",
-              pkt->print());
-    }
-
+    panic_if(!(dram->getAddrRange().contains(pkt->getAddr())),
+             "Can't handle address range for packet %s\n", pkt->print());
 
     // Find out how many memory packets a pkt translates to
     // If the burst size is equal or larger than the pkt size, then a pkt
     // translates to only one memory packet. Otherwise, a pkt translates to
     // multiple memory packets
     unsigned size = pkt->getSize();
-    uint32_t burst_size = is_dram ? dram->bytesPerBurst() :
-                                    nvm->bytesPerBurst();
+    uint32_t burst_size = dram->bytesPerBurst();
+
     unsigned offset = pkt->getAddr() & (burst_size - 1);
     unsigned int pkt_count = divCeil(offset + size, burst_size);
 
@@ -472,7 +442,13 @@
             stats.numWrRetry++;
             return false;
         } else {
-            addToWriteQueue(pkt, pkt_count, is_dram);
+            addToWriteQueue(pkt, pkt_count, dram);
+            // If we are not already scheduled to get a request out of the
+            // queue, do so now
+            if (!nextReqEvent.scheduled()) {
+                DPRINTF(MemCtrl, "Request scheduled immediately\n");
+                schedule(nextReqEvent, curTick());
+            }
             stats.writeReqs++;
             stats.bytesWrittenSys += size;
         }
@@ -486,7 +462,14 @@
             stats.numRdRetry++;
             return false;
         } else {
-            addToReadQueue(pkt, pkt_count, is_dram);
+            if (!addToReadQueue(pkt, pkt_count, dram)) {
+                // If we are not already scheduled to get a request out of the
+                // queue, do so now
+                if (!nextReqEvent.scheduled()) {
+                    DPRINTF(MemCtrl, "Request scheduled immediately\n");
+                    schedule(nextReqEvent, curTick());
+                }
+            }
             stats.readReqs++;
             stats.bytesReadSys += size;
         }
@@ -496,17 +479,20 @@
 }
 
 void
-MemCtrl::processRespondEvent()
+MemCtrl::processRespondEvent(MemInterface* mem_intr,
+                        MemPacketQueue& queue,
+                        EventFunctionWrapper& resp_event,
+                        bool& retry_rd_req)
 {
+
     DPRINTF(MemCtrl,
             "processRespondEvent(): Some req has reached its readyTime\n");
 
-    MemPacket* mem_pkt = respQueue.front();
+    MemPacket* mem_pkt = queue.front();
 
-    if (mem_pkt->isDram()) {
-        // media specific checks and functions when read response is complete
-        dram->respondEvent(mem_pkt->rank);
-    }
+    // media specific checks and functions when read response is complete
+    // DRAM only
+    mem_intr->respondEvent(mem_pkt->rank);
 
     if (mem_pkt->burstHelper) {
         // it is a split packet
@@ -517,21 +503,23 @@
             // so we can now respond to the requestor
             // @todo we probably want to have a different front end and back
             // end latency for split packets
-            accessAndRespond(mem_pkt->pkt, frontendLatency + backendLatency);
+            accessAndRespond(mem_pkt->pkt, frontendLatency + backendLatency,
+                             mem_intr);
             delete mem_pkt->burstHelper;
             mem_pkt->burstHelper = NULL;
         }
     } else {
         // it is not a split packet
-        accessAndRespond(mem_pkt->pkt, frontendLatency + backendLatency);
+        accessAndRespond(mem_pkt->pkt, frontendLatency + backendLatency,
+                         mem_intr);
     }
 
-    respQueue.pop_front();
+    queue.pop_front();
 
-    if (!respQueue.empty()) {
-        assert(respQueue.front()->readyTime >= curTick());
-        assert(!respondEvent.scheduled());
-        schedule(respondEvent, respQueue.front()->readyTime);
+    if (!queue.empty()) {
+        assert(queue.front()->readyTime >= curTick());
+        assert(!resp_event.scheduled());
+        schedule(resp_event, queue.front()->readyTime);
     } else {
         // if there is nothing left in any queue, signal a drain
         if (drainState() == DrainState::Draining &&
@@ -540,11 +528,12 @@
 
             DPRINTF(Drain, "Controller done draining\n");
             signalDrainDone();
-        } else if (mem_pkt->isDram()) {
+        } else {
             // check the refresh state and kick the refresh event loop
             // into action again if banks already closed and just waiting
             // for read to complete
-            dram->checkRefreshState(mem_pkt->rank);
+            // DRAM only
+            mem_intr->checkRefreshState(mem_pkt->rank);
         }
     }
 
@@ -552,14 +541,15 @@
 
     // We have made a location in the queue available at this point,
     // so if there is a read that was forced to wait, retry now
-    if (retryRdReq) {
-        retryRdReq = false;
+    if (retry_rd_req) {
+        retry_rd_req = false;
         port.sendRetryReq();
     }
 }
 
 MemPacketQueue::iterator
-MemCtrl::chooseNext(MemPacketQueue& queue, Tick extra_col_delay)
+MemCtrl::chooseNext(MemPacketQueue& queue, Tick extra_col_delay,
+                                                MemInterface* mem_intr)
 {
     // This method does the arbitration between requests.
 
@@ -569,7 +559,10 @@
         if (queue.size() == 1) {
             // available rank corresponds to state refresh idle
             MemPacket* mem_pkt = *(queue.begin());
-            if (packetReady(mem_pkt)) {
+            if (mem_pkt->pseudoChannel != mem_intr->pseudoChannel) {
+                return ret;
+            }
+            if (packetReady(mem_pkt, mem_intr)) {
                 ret = queue.begin();
                 DPRINTF(MemCtrl, "Single request, going to a free rank\n");
             } else {
@@ -579,13 +572,15 @@
             // check if there is a packet going to a free rank
             for (auto i = queue.begin(); i != queue.end(); ++i) {
                 MemPacket* mem_pkt = *i;
-                if (packetReady(mem_pkt)) {
+                if (packetReady(mem_pkt, mem_intr)) {
                     ret = i;
                     break;
                 }
             }
         } else if (memSchedPolicy == enums::frfcfs) {
-            ret = chooseNextFRFCFS(queue, extra_col_delay);
+            Tick col_allowed_at;
+            std::tie(ret, col_allowed_at)
+                    = chooseNextFRFCFS(queue, extra_col_delay, mem_intr);
         } else {
             panic("No scheduling policy chosen\n");
         }
@@ -593,64 +588,39 @@
     return ret;
 }
 
-MemPacketQueue::iterator
-MemCtrl::chooseNextFRFCFS(MemPacketQueue& queue, Tick extra_col_delay)
+std::pair<MemPacketQueue::iterator, Tick>
+MemCtrl::chooseNextFRFCFS(MemPacketQueue& queue, Tick extra_col_delay,
+                                MemInterface* mem_intr)
 {
     auto selected_pkt_it = queue.end();
     Tick col_allowed_at = MaxTick;
 
     // time we need to issue a column command to be seamless
-    const Tick min_col_at = std::max(nextBurstAt + extra_col_delay, curTick());
+    const Tick min_col_at = std::max(mem_intr->nextBurstAt + extra_col_delay,
+                                    curTick());
 
-    // find optimal packet for each interface
-    if (dram && nvm) {
-        // create 2nd set of parameters for NVM
-        auto nvm_pkt_it = queue.end();
-        Tick nvm_col_at = MaxTick;
-
-        // Select packet by default to give priority if both
-        // can issue at the same time or seamlessly
-        std::tie(selected_pkt_it, col_allowed_at) =
-                 dram->chooseNextFRFCFS(queue, min_col_at);
-        std::tie(nvm_pkt_it, nvm_col_at) =
-                 nvm->chooseNextFRFCFS(queue, min_col_at);
-
-        // Compare DRAM and NVM and select NVM if it can issue
-        // earlier than the DRAM packet
-        if (col_allowed_at > nvm_col_at) {
-            selected_pkt_it = nvm_pkt_it;
-        }
-    } else if (dram) {
-        std::tie(selected_pkt_it, col_allowed_at) =
-                 dram->chooseNextFRFCFS(queue, min_col_at);
-    } else if (nvm) {
-        std::tie(selected_pkt_it, col_allowed_at) =
-                 nvm->chooseNextFRFCFS(queue, min_col_at);
-    }
+    std::tie(selected_pkt_it, col_allowed_at) =
+                 mem_intr->chooseNextFRFCFS(queue, min_col_at);
 
     if (selected_pkt_it == queue.end()) {
         DPRINTF(MemCtrl, "%s no available packets found\n", __func__);
     }
 
-    return selected_pkt_it;
+    return std::make_pair(selected_pkt_it, col_allowed_at);
 }
 
 void
-MemCtrl::accessAndRespond(PacketPtr pkt, Tick static_latency)
+MemCtrl::accessAndRespond(PacketPtr pkt, Tick static_latency,
+                                                MemInterface* mem_intr)
 {
     DPRINTF(MemCtrl, "Responding to Address %#x.. \n", pkt->getAddr());
 
     bool needsResponse = pkt->needsResponse();
     // do the actual memory access which also turns the packet into a
     // response
-    if (dram && dram->getAddrRange().contains(pkt->getAddr())) {
-        dram->access(pkt);
-    } else if (nvm && nvm->getAddrRange().contains(pkt->getAddr())) {
-        nvm->access(pkt);
-    } else {
-        panic("Can't handle address range for packet %s\n",
-              pkt->print());
-    }
+    panic_if(!mem_intr->getAddrRange().contains(pkt->getAddr()),
+             "Can't handle address range for packet %s\n", pkt->print());
+    mem_intr->access(pkt);
 
     // turn packet around to go back to requestor if response expected
     if (needsResponse) {
@@ -701,7 +671,7 @@
 }
 
 Tick
-MemCtrl::verifySingleCmd(Tick cmd_tick, Tick max_cmds_per_burst)
+MemCtrl::verifySingleCmd(Tick cmd_tick, Tick max_cmds_per_burst, bool row_cmd)
 {
     // start with assumption that there is no contention on command bus
     Tick cmd_at = cmd_tick;
@@ -813,8 +783,8 @@
     }
 }
 
-void
-MemCtrl::doBurstAccess(MemPacket* mem_pkt)
+Tick
+MemCtrl::doBurstAccess(MemPacket* mem_pkt, MemInterface* mem_intr)
 {
     // first clean up the burstTick set, removing old entries
     // before adding new entries for next burst
@@ -825,35 +795,18 @@
 
     // Issue the next burst and update bus state to reflect
     // when previous command was issued
-    if (mem_pkt->isDram()) {
-        std::vector<MemPacketQueue>& queue = selQueue(mem_pkt->isRead());
-        std::tie(cmd_at, nextBurstAt) =
-                 dram->doBurstAccess(mem_pkt, nextBurstAt, queue);
-
-        // Update timing for NVM ranks if NVM is configured on this channel
-        if (nvm)
-            nvm->addRankToRankDelay(cmd_at);
-
-    } else {
-        std::tie(cmd_at, nextBurstAt) =
-                 nvm->doBurstAccess(mem_pkt, nextBurstAt);
-
-        // Update timing for NVM ranks if NVM is configured on this channel
-        if (dram)
-            dram->addRankToRankDelay(cmd_at);
-
-    }
+    std::vector<MemPacketQueue>& queue = selQueue(mem_pkt->isRead());
+    std::tie(cmd_at, mem_intr->nextBurstAt) =
+            mem_intr->doBurstAccess(mem_pkt, mem_intr->nextBurstAt, queue);
 
     DPRINTF(MemCtrl, "Access to %#x, ready at %lld next burst at %lld.\n",
-            mem_pkt->addr, mem_pkt->readyTime, nextBurstAt);
+            mem_pkt->addr, mem_pkt->readyTime, mem_intr->nextBurstAt);
 
     // Update the minimum timing between the requests, this is a
     // conservative estimate of when we have to schedule the next
     // request to not introduce any unecessary bubbles. In most cases
     // we will wake up sooner than we have to.
-    nextReqTime = nextBurstAt - (dram ? dram->commandOffset() :
-                                        nvm->commandOffset());
-
+    mem_intr->nextReqTime = mem_intr->nextBurstAt - mem_intr->commandOffset();
 
     // Update the common bus stats
     if (mem_pkt->isRead()) {
@@ -868,11 +821,59 @@
         stats.requestorWriteTotalLat[mem_pkt->requestorId()] +=
             mem_pkt->readyTime - mem_pkt->entryTime;
     }
+
+    return cmd_at;
+}
+
+bool
+MemCtrl::memBusy(MemInterface* mem_intr) {
+
+    // check ranks for refresh/wakeup - uses busStateNext, so done after
+    // turnaround decisions
+    // Default to busy status and update based on interface specifics
+    // Default state of unused interface is 'true'
+    bool mem_busy = true;
+    bool all_writes_nvm = mem_intr->numWritesQueued == totalWriteQueueSize;
+    bool read_queue_empty = totalReadQueueSize == 0;
+    mem_busy = mem_intr->isBusy(read_queue_empty, all_writes_nvm);
+    if (mem_busy) {
+        // if all ranks are refreshing wait for them to finish
+        // and stall this state machine without taking any further
+        // action, and do not schedule a new nextReqEvent
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool
+MemCtrl::nvmWriteBlock(MemInterface* mem_intr) {
+
+    bool all_writes_nvm = mem_intr->numWritesQueued == totalWriteQueueSize;
+    return (mem_intr->writeRespQueueFull() && all_writes_nvm);
 }
 
 void
-MemCtrl::processNextReqEvent()
-{
+MemCtrl::nonDetermReads(MemInterface* mem_intr) {
+
+    for (auto queue = readQueue.rbegin();
+            queue != readQueue.rend(); ++queue) {
+            // select non-deterministic NVM read to issue
+            // assume that we have the command bandwidth to issue this along
+            // with additional RD/WR burst with needed bank operations
+            if (mem_intr->readsWaitingToIssue()) {
+                // select non-deterministic NVM read to issue
+                mem_intr->chooseRead(*queue);
+            }
+    }
+}
+
+void
+MemCtrl::processNextReqEvent(MemInterface* mem_intr,
+                        MemPacketQueue& resp_queue,
+                        EventFunctionWrapper& resp_event,
+                        EventFunctionWrapper& next_req_event,
+                        bool& retry_wr_req) {
     // transition is handled by QoS algorithm if enabled
     if (turnPolicy) {
         // select bus state - only done if QoS algorithms are in use
@@ -907,36 +908,9 @@
     // updates current state
     busState = busStateNext;
 
-    if (nvm) {
-        for (auto queue = readQueue.rbegin();
-             queue != readQueue.rend(); ++queue) {
-             // select non-deterministic NVM read to issue
-             // assume that we have the command bandwidth to issue this along
-             // with additional RD/WR burst with needed bank operations
-             if (nvm->readsWaitingToIssue()) {
-                 // select non-deterministic NVM read to issue
-                 nvm->chooseRead(*queue);
-             }
-        }
-    }
+    nonDetermReads(mem_intr);
 
-    // check ranks for refresh/wakeup - uses busStateNext, so done after
-    // turnaround decisions
-    // Default to busy status and update based on interface specifics
-    bool dram_busy = dram ? dram->isBusy() : true;
-    bool nvm_busy = true;
-    bool all_writes_nvm = false;
-    if (nvm) {
-        all_writes_nvm = nvm->numWritesQueued == totalWriteQueueSize;
-        bool read_queue_empty = totalReadQueueSize == 0;
-        nvm_busy = nvm->isBusy(read_queue_empty, all_writes_nvm);
-    }
-    // Default state of unused interface is 'true'
-    // Simply AND the busy signals to determine if system is busy
-    if (dram_busy && nvm_busy) {
-        // if all ranks are refreshing wait for them to finish
-        // and stall this state machine without taking any further
-        // action, and do not schedule a new nextReqEvent
+    if (memBusy(mem_intr)) {
         return;
     }
 
@@ -963,7 +937,7 @@
                 // ensuring all banks are closed and
                 // have exited low power states
                 if (drainState() == DrainState::Draining &&
-                    respQueue.empty() && allIntfDrained()) {
+                    respQEmpty() && allIntfDrained()) {
 
                     DPRINTF(Drain, "MemCtrl controller done draining\n");
                     signalDrainDone();
@@ -992,7 +966,7 @@
                 // If we are changing command type, incorporate the minimum
                 // bus turnaround delay which will be rank to rank delay
                 to_read = chooseNext((*queue), switched_cmd_type ?
-                                               minWriteToReadDataGap() : 0);
+                                     minWriteToReadDataGap() : 0, mem_intr);
 
                 if (to_read != queue->end()) {
                     // candidate read found
@@ -1013,12 +987,13 @@
 
             auto mem_pkt = *to_read;
 
-            doBurstAccess(mem_pkt);
+            Tick cmd_at = doBurstAccess(mem_pkt, mem_intr);
+
+            DPRINTF(MemCtrl,
+            "Command for %#x, issued at %lld.\n", mem_pkt->addr, cmd_at);
 
             // sanity check
-            assert(mem_pkt->size <= (mem_pkt->isDram() ?
-                                      dram->bytesPerBurst() :
-                                      nvm->bytesPerBurst()) );
+            assert(pktSizeCheck(mem_pkt, mem_intr));
             assert(mem_pkt->readyTime >= curTick());
 
             // log the response
@@ -1029,21 +1004,24 @@
 
             // Insert into response queue. It will be sent back to the
             // requestor at its readyTime
-            if (respQueue.empty()) {
-                assert(!respondEvent.scheduled());
-                schedule(respondEvent, mem_pkt->readyTime);
+            if (resp_queue.empty()) {
+                assert(!resp_event.scheduled());
+                schedule(resp_event, mem_pkt->readyTime);
             } else {
-                assert(respQueue.back()->readyTime <= mem_pkt->readyTime);
-                assert(respondEvent.scheduled());
+                assert(resp_queue.back()->readyTime <= mem_pkt->readyTime);
+                assert(resp_event.scheduled());
             }
 
-            respQueue.push_back(mem_pkt);
+            resp_queue.push_back(mem_pkt);
 
             // we have so many writes that we have to transition
             // don't transition if the writeRespQueue is full and
             // there are no other writes that can issue
+            // Also ensure that we've issued a minimum defined number
+            // of reads before switching, or have emptied the readQ
             if ((totalWriteQueueSize > writeHighThreshold) &&
-               !(nvm && all_writes_nvm && nvm->writeRespQueueFull())) {
+               (readsThisTime >= minReadsPerSwitch || totalReadQueueSize == 0)
+               && !(nvmWriteBlock(mem_intr))) {
                 switch_to_writes = true;
             }
 
@@ -1077,7 +1055,7 @@
             // If we are changing command type, incorporate the minimum
             // bus turnaround delay
             to_write = chooseNext((*queue),
-                     switched_cmd_type ? minReadToWriteDataGap() : 0);
+                    switched_cmd_type ? minReadToWriteDataGap() : 0, mem_intr);
 
             if (to_write != queue->end()) {
                 write_found = true;
@@ -1098,13 +1076,13 @@
         auto mem_pkt = *to_write;
 
         // sanity check
-        assert(mem_pkt->size <= (mem_pkt->isDram() ?
-                                  dram->bytesPerBurst() :
-                                  nvm->bytesPerBurst()) );
+        assert(pktSizeCheck(mem_pkt, mem_intr));
 
-        doBurstAccess(mem_pkt);
+        Tick cmd_at = doBurstAccess(mem_pkt, mem_intr);
+        DPRINTF(MemCtrl,
+        "Command for %#x, issued at %lld.\n", mem_pkt->addr, cmd_at);
 
-        isInWriteQueue.erase(burstAlign(mem_pkt->addr, mem_pkt->isDram()));
+        isInWriteQueue.erase(burstAlign(mem_pkt->addr, mem_intr));
 
         // log the response
         logResponse(MemCtrl::WRITE, mem_pkt->requestorId(),
@@ -1129,8 +1107,7 @@
         if (totalWriteQueueSize == 0 ||
             (below_threshold && drainState() != DrainState::Draining) ||
             (totalReadQueueSize && writesThisTime >= minWritesPerSwitch) ||
-            (totalReadQueueSize && nvm && nvm->writeRespQueueFull() &&
-             all_writes_nvm)) {
+            (totalReadQueueSize && (nvmWriteBlock(mem_intr)))) {
 
             // turn the bus back around for reads again
             busStateNext = MemCtrl::READ;
@@ -1143,49 +1120,43 @@
     }
     // It is possible that a refresh to another rank kicks things back into
     // action before reaching this point.
-    if (!nextReqEvent.scheduled())
-        schedule(nextReqEvent, std::max(nextReqTime, curTick()));
+    if (!next_req_event.scheduled())
+        schedule(next_req_event, std::max(mem_intr->nextReqTime, curTick()));
 
-    // If there is space available and we have writes waiting then let
-    // them retry. This is done here to ensure that the retry does not
-    // cause a nextReqEvent to be scheduled before we do so as part of
-    // the next request processing
-    if (retryWrReq && totalWriteQueueSize < writeBufferSize) {
-        retryWrReq = false;
+    if (retry_wr_req && totalWriteQueueSize < writeBufferSize) {
+        retry_wr_req = false;
         port.sendRetryReq();
     }
 }
 
 bool
-MemCtrl::packetReady(MemPacket* pkt)
+MemCtrl::packetReady(MemPacket* pkt, MemInterface* mem_intr)
 {
-    return (pkt->isDram() ?
-        dram->burstReady(pkt) : nvm->burstReady(pkt));
+    return mem_intr->burstReady(pkt);
 }
 
 Tick
 MemCtrl::minReadToWriteDataGap()
 {
-    Tick dram_min = dram ?  dram->minReadToWriteDataGap() : MaxTick;
-    Tick nvm_min = nvm ?  nvm->minReadToWriteDataGap() : MaxTick;
-    return std::min(dram_min, nvm_min);
+    return dram->minReadToWriteDataGap();
 }
 
 Tick
 MemCtrl::minWriteToReadDataGap()
 {
-    Tick dram_min = dram ? dram->minWriteToReadDataGap() : MaxTick;
-    Tick nvm_min = nvm ?  nvm->minWriteToReadDataGap() : MaxTick;
-    return std::min(dram_min, nvm_min);
+    return dram->minWriteToReadDataGap();
 }
 
 Addr
-MemCtrl::burstAlign(Addr addr, bool is_dram) const
+MemCtrl::burstAlign(Addr addr, MemInterface* mem_intr) const
 {
-    if (is_dram)
-        return (addr & ~(Addr(dram->bytesPerBurst() - 1)));
-    else
-        return (addr & ~(Addr(nvm->bytesPerBurst() - 1)));
+    return (addr & ~(Addr(mem_intr->bytesPerBurst() - 1)));
+}
+
+bool
+MemCtrl::pktSizeCheck(MemPacket* mem_pkt, MemInterface* mem_intr) const
+{
+    return (mem_pkt->size <= mem_intr->bytesPerBurst());
 }
 
 MemCtrl::CtrlStats::CtrlStats(MemCtrl &_ctrl)
@@ -1252,7 +1223,8 @@
                 statistics::units::Byte, statistics::units::Second>::get(),
              "Average system write bandwidth in Byte/s"),
 
-    ADD_STAT(totGap, statistics::units::Tick::get(), "Total gap between requests"),
+    ADD_STAT(totGap, statistics::units::Tick::get(),
+             "Total gap between requests"),
     ADD_STAT(avgGap, statistics::units::Rate<
                 statistics::units::Tick, statistics::units::Count>::get(),
              "Average gap between requests"),
@@ -1383,16 +1355,22 @@
 void
 MemCtrl::recvFunctional(PacketPtr pkt)
 {
-    if (dram && dram->getAddrRange().contains(pkt->getAddr())) {
+    bool found = recvFunctionalLogic(pkt, dram);
+
+    panic_if(!found, "Can't handle address range for packet %s\n",
+             pkt->print());
+}
+
+bool
+MemCtrl::recvFunctionalLogic(PacketPtr pkt, MemInterface* mem_intr)
+{
+    if (mem_intr->getAddrRange().contains(pkt->getAddr())) {
         // rely on the abstract memory
-        dram->functionalAccess(pkt);
-    } else if (nvm && nvm->getAddrRange().contains(pkt->getAddr())) {
-        // rely on the abstract memory
-        nvm->functionalAccess(pkt);
-   } else {
-        panic("Can't handle address range for packet %s\n",
-              pkt->print());
-   }
+        mem_intr->functionalAccess(pkt);
+        return true;
+    } else {
+        return false;
+    }
 }
 
 Port &
@@ -1408,12 +1386,10 @@
 bool
 MemCtrl::allIntfDrained() const
 {
-   // ensure dram is in power down and refresh IDLE states
-   bool dram_drained = !dram || dram->allRanksDrained();
-   // No outstanding NVM writes
-   // All other queues verified as needed with calling logic
-   bool nvm_drained = !nvm || nvm->allRanksDrained();
-   return (dram_drained && nvm_drained);
+   // DRAM: ensure dram is in power down and refresh IDLE states
+   // NVM: No outstanding NVM writes
+   // NVM: All other queues verified as needed with calling logic
+   return dram->allRanksDrained();
 }
 
 DrainState
@@ -1434,8 +1410,7 @@
             schedule(nextReqEvent, curTick());
         }
 
-        if (dram)
-            dram->drainRanks();
+        dram->drainRanks();
 
         return DrainState::Draining;
     } else {
@@ -1454,15 +1429,23 @@
     } else if (isTimingMode && !system()->isTimingMode()) {
         // if we switch from timing mode, stop the refresh events to
         // not cause issues with KVM
-        if (dram)
-            dram->suspend();
+        dram->suspend();
     }
 
     // update the mode
     isTimingMode = system()->isTimingMode();
 }
 
-MemCtrl::MemoryPort::MemoryPort(const std::string& name, MemCtrl& _ctrl)
+AddrRangeList
+MemCtrl::getAddrRanges()
+{
+    AddrRangeList range;
+    range.push_back(dram->getAddrRange());
+    return range;
+}
+
+MemCtrl::MemoryPort::
+MemoryPort(const std::string& name, MemCtrl& _ctrl)
     : QueuedResponsePort(name, &_ctrl, queue), queue(_ctrl, *this, true),
       ctrl(_ctrl)
 { }
@@ -1470,16 +1453,7 @@
 AddrRangeList
 MemCtrl::MemoryPort::getAddrRanges() const
 {
-    AddrRangeList ranges;
-    if (ctrl.dram) {
-        DPRINTF(DRAM, "Pushing DRAM ranges to port\n");
-        ranges.push_back(ctrl.dram->getAddrRange());
-    }
-    if (ctrl.nvm) {
-        DPRINTF(NVM, "Pushing NVM ranges to port\n");
-        ranges.push_back(ctrl.nvm->getAddrRange());
-    }
-    return ranges;
+    return ctrl.getAddrRanges();
 }
 
 void
diff --git a/src/mem/mem_ctrl.hh b/src/mem/mem_ctrl.hh
index 4eef268..3b623fb 100644
--- a/src/mem/mem_ctrl.hh
+++ b/src/mem/mem_ctrl.hh
@@ -66,6 +66,7 @@
 namespace memory
 {
 
+class MemInterface;
 class DRAMInterface;
 class NVMInterface;
 
@@ -115,6 +116,9 @@
     /** Does this packet access DRAM?*/
     const bool dram;
 
+    /** pseudo channel num*/
+    const uint8_t pseudoChannel;
+
     /** Will be populated by address decoder */
     const uint8_t rank;
     const uint8_t bank;
@@ -199,14 +203,14 @@
      */
     inline bool isDram() const { return dram; }
 
-    MemPacket(PacketPtr _pkt, bool is_read, bool is_dram, uint8_t _rank,
-               uint8_t _bank, uint32_t _row, uint16_t bank_id, Addr _addr,
-               unsigned int _size)
+    MemPacket(PacketPtr _pkt, bool is_read, bool is_dram, uint8_t _channel,
+               uint8_t _rank, uint8_t _bank, uint32_t _row, uint16_t bank_id,
+               Addr _addr, unsigned int _size)
         : entryTime(curTick()), readyTime(curTick()), pkt(_pkt),
           _requestorId(pkt->requestorId()),
-          read(is_read), dram(is_dram), rank(_rank), bank(_bank), row(_row),
-          bankId(bank_id), addr(_addr), size(_size), burstHelper(NULL),
-          _qosValue(_pkt->qosValue())
+          read(is_read), dram(is_dram), pseudoChannel(_channel), rank(_rank),
+          bank(_bank), row(_row), bankId(bank_id), addr(_addr), size(_size),
+          burstHelper(NULL), _qosValue(_pkt->qosValue())
     { }
 
 };
@@ -241,7 +245,7 @@
  */
 class MemCtrl : public qos::MemCtrl
 {
-  private:
+  protected:
 
     // For now, make use of a queued response port to avoid dealing with
     // flow control for the responses being sent back
@@ -292,10 +296,17 @@
      * processRespondEvent is called; no parameters are allowed
      * in these methods
      */
-    void processNextReqEvent();
+    virtual void processNextReqEvent(MemInterface* mem_intr,
+                          MemPacketQueue& resp_queue,
+                          EventFunctionWrapper& resp_event,
+                          EventFunctionWrapper& next_req_event,
+                          bool& retry_wr_req);
     EventFunctionWrapper nextReqEvent;
 
-    void processRespondEvent();
+    virtual void processRespondEvent(MemInterface* mem_intr,
+                        MemPacketQueue& queue,
+                        EventFunctionWrapper& resp_event,
+                        bool& retry_rd_req);
     EventFunctionWrapper respondEvent;
 
     /**
@@ -325,11 +336,12 @@
      *
      * @param pkt The request packet from the outside world
      * @param pkt_count The number of memory bursts the pkt
-     * @param is_dram Does this packet access DRAM?
-     * translate to. If pkt size is larger then one full burst,
-     * then pkt_count is greater than one.
+     * @param mem_intr The memory interface this pkt will
+     * eventually go to
+     * @return if all the read pkts are already serviced by wrQ
      */
-    void addToReadQueue(PacketPtr pkt, unsigned int pkt_count, bool is_dram);
+    bool addToReadQueue(PacketPtr pkt, unsigned int pkt_count,
+                        MemInterface* mem_intr);
 
     /**
      * Decode the incoming pkt, create a mem_pkt and push to the
@@ -339,19 +351,22 @@
      *
      * @param pkt The request packet from the outside world
      * @param pkt_count The number of memory bursts the pkt
-     * @param is_dram Does this packet access DRAM?
-     * translate to. If pkt size is larger then one full burst,
-     * then pkt_count is greater than one.
+     * @param mem_intr The memory interface this pkt will
+     * eventually go to
      */
-    void addToWriteQueue(PacketPtr pkt, unsigned int pkt_count, bool is_dram);
+    void addToWriteQueue(PacketPtr pkt, unsigned int pkt_count,
+                         MemInterface* mem_intr);
 
     /**
      * Actually do the burst based on media specific access function.
      * Update bus statistics when complete.
      *
      * @param mem_pkt The memory packet created from the outside world pkt
+     * @param mem_intr The memory interface to access
+     * @return Time when the command was issued
+     *
      */
-    void doBurstAccess(MemPacket* mem_pkt);
+    virtual Tick doBurstAccess(MemPacket* mem_pkt, MemInterface* mem_intr);
 
     /**
      * When a packet reaches its "readyTime" in the response Q,
@@ -361,29 +376,31 @@
      *
      * @param pkt The packet from the outside world
      * @param static_latency Static latency to add before sending the packet
+     * @param mem_intr the memory interface to access
      */
-    void accessAndRespond(PacketPtr pkt, Tick static_latency);
+    virtual void accessAndRespond(PacketPtr pkt, Tick static_latency,
+                                                MemInterface* mem_intr);
 
     /**
      * Determine if there is a packet that can issue.
      *
      * @param pkt The packet to evaluate
      */
-    bool packetReady(MemPacket* pkt);
+    virtual bool packetReady(MemPacket* pkt, MemInterface* mem_intr);
 
     /**
      * Calculate the minimum delay used when scheduling a read-to-write
      * transision.
      * @param return minimum delay
      */
-    Tick minReadToWriteDataGap();
+    virtual Tick minReadToWriteDataGap();
 
     /**
      * Calculate the minimum delay used when scheduling a write-to-read
      * transision.
      * @param return minimum delay
      */
-    Tick minWriteToReadDataGap();
+    virtual Tick minWriteToReadDataGap();
 
     /**
      * The memory schduler/arbiter - picks which request needs to
@@ -394,10 +411,11 @@
      *
      * @param queue Queued requests to consider
      * @param extra_col_delay Any extra delay due to a read/write switch
+     * @param mem_intr the memory interface to choose from
      * @return an iterator to the selected packet, else queue.end()
      */
-    MemPacketQueue::iterator chooseNext(MemPacketQueue& queue,
-        Tick extra_col_delay);
+    virtual MemPacketQueue::iterator chooseNext(MemPacketQueue& queue,
+        Tick extra_col_delay, MemInterface* mem_intr);
 
     /**
      * For FR-FCFS policy reorder the read/write queue depending on row buffer
@@ -407,8 +425,9 @@
      * @param extra_col_delay Any extra delay due to a read/write switch
      * @return an iterator to the selected packet, else queue.end()
      */
-    MemPacketQueue::iterator chooseNextFRFCFS(MemPacketQueue& queue,
-            Tick extra_col_delay);
+    virtual std::pair<MemPacketQueue::iterator, Tick>
+    chooseNextFRFCFS(MemPacketQueue& queue, Tick extra_col_delay,
+                    MemInterface* mem_intr);
 
     /**
      * Calculate burst window aligned tick
@@ -427,11 +446,21 @@
      * Burst-align an address.
      *
      * @param addr The potentially unaligned address
-     * @param is_dram Does this packet access DRAM?
+     * @param mem_intr The DRAM interface this pkt belongs to
      *
      * @return An address aligned to a memory burst
      */
-    Addr burstAlign(Addr addr, bool is_dram) const;
+    virtual Addr burstAlign(Addr addr, MemInterface* mem_intr) const;
+
+    /**
+     * Check if mem pkt's size is sane
+     *
+     * @param mem_pkt memory packet
+     * @param mem_intr memory interface
+     * @return An address aligned to a memory burst
+     */
+    virtual bool pktSizeCheck(MemPacket* mem_pkt,
+                              MemInterface* mem_intr) const;
 
     /**
      * The controller's main read and write queues,
@@ -467,14 +496,11 @@
     std::unordered_multiset<Tick> burstTicks;
 
     /**
-     * Create pointer to interface of the actual dram media when connected
-     */
-    DRAMInterface* const dram;
++    * Create pointer to interface of the actual memory media when connected
++    */
+    MemInterface* dram;
 
-    /**
-     * Create pointer to interface of the actual nvm media when connected
-     */
-    NVMInterface* const nvm;
+    virtual AddrRangeList getAddrRanges();
 
     /**
      * The following are basic design parameters of the memory
@@ -482,11 +508,12 @@
      * The rowsPerBank is determined based on the capacity, number of
      * ranks and banks, the burst size, and the row buffer size.
      */
-    const uint32_t readBufferSize;
-    const uint32_t writeBufferSize;
-    const uint32_t writeHighThreshold;
-    const uint32_t writeLowThreshold;
+    uint32_t readBufferSize;
+    uint32_t writeBufferSize;
+    uint32_t writeHighThreshold;
+    uint32_t writeLowThreshold;
     const uint32_t minWritesPerSwitch;
+    const uint32_t minReadsPerSwitch;
     uint32_t writesThisTime;
     uint32_t readsThisTime;
 
@@ -610,10 +637,40 @@
         return (is_read ? readQueue : writeQueue);
     };
 
+    virtual bool respQEmpty()
+    {
+        return respQueue.empty();
+    }
+
+    /**
+     * Checks if the memory interface is already busy
+     *
+     * @param mem_intr memory interface to check
+     * @return a boolean indicating if memory is busy
+     */
+    virtual bool memBusy(MemInterface* mem_intr);
+
+    /**
+     * Will access memory interface and select non-deterministic
+     * reads to issue
+     * @param mem_intr memory interface to use
+     */
+    virtual void nonDetermReads(MemInterface* mem_intr);
+
+    /**
+     * Will check if all writes are for nvm interface
+     * and nvm's write resp queue is full. The generic mem_intr is
+     * used as the same function can be called for a dram interface,
+     * in which case dram functions will eventually return false
+     * @param mem_intr memory interface to use
+     * @return a boolean showing if nvm is blocked with writes
+     */
+    virtual bool nvmWriteBlock(MemInterface* mem_intr);
+
     /**
      * Remove commands that have already issued from burstTicks
      */
-    void pruneBurstTick();
+    virtual void pruneBurstTick();
 
   public:
 
@@ -624,7 +681,7 @@
      *
      * @return bool flag, set once drain complete
      */
-    bool allIntfDrained() const;
+    virtual bool allIntfDrained() const;
 
     DrainState drain() override;
 
@@ -641,7 +698,8 @@
      *                           in a burst window
      * @return tick for command issue without contention
      */
-    Tick verifySingleCmd(Tick cmd_tick, Tick max_cmds_per_burst);
+    virtual Tick verifySingleCmd(Tick cmd_tick, Tick max_cmds_per_burst,
+                                bool row_cmd);
 
     /**
      * Check for command bus contention for multi-cycle (2 currently)
@@ -657,7 +715,7 @@
      *                           in a burst window
      * @return tick for command issue without contention
      */
-    Tick verifyMultiCmd(Tick cmd_tick, Tick max_cmds_per_burst,
+    virtual Tick verifyMultiCmd(Tick cmd_tick, Tick max_cmds_per_burst,
                         Tick max_multi_cmd_split = 0);
 
     /**
@@ -672,16 +730,26 @@
      *
      * @return true if event is scheduled
      */
-    bool requestEventScheduled() const { return nextReqEvent.scheduled(); }
+    virtual bool requestEventScheduled(uint8_t pseudo_channel = 0) const
+    {
+        assert(pseudo_channel == 0);
+        return nextReqEvent.scheduled();
+    }
 
     /**
      * restart the controller
      * This can be used by interfaces to restart the
      * scheduler after maintainence commands complete
-     *
      * @param Tick to schedule next event
+     * @param pseudo_channel pseudo channel number for which scheduler
+     * needs to restart, will always be 0 for controllers which control
+     * only a single channel
      */
-    void restartScheduler(Tick tick) { schedule(nextReqEvent, tick); }
+    virtual void restartScheduler(Tick tick, uint8_t pseudo_channel = 0)
+    {
+        assert(pseudo_channel == 0);
+        schedule(nextReqEvent, tick);
+    }
 
     /**
      * Check the current direction of the memory channel
@@ -708,10 +776,13 @@
 
   protected:
 
-    Tick recvAtomic(PacketPtr pkt);
-    Tick recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor);
-    void recvFunctional(PacketPtr pkt);
-    bool recvTimingReq(PacketPtr pkt);
+    virtual Tick recvAtomic(PacketPtr pkt);
+    virtual Tick recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor);
+    virtual void recvFunctional(PacketPtr pkt);
+    virtual bool recvTimingReq(PacketPtr pkt);
+
+    bool recvFunctionalLogic(PacketPtr pkt, MemInterface* mem_intr);
+    Tick recvAtomicLogic(PacketPtr pkt, MemInterface* mem_intr);
 
 };
 
diff --git a/src/mem/mem_interface.cc b/src/mem/mem_interface.cc
index f8cb01a..e97448f 100644
--- a/src/mem/mem_interface.cc
+++ b/src/mem/mem_interface.cc
@@ -43,17 +43,11 @@
 #include "base/bitfield.hh"
 #include "base/cprintf.hh"
 #include "base/trace.hh"
-#include "debug/DRAM.hh"
-#include "debug/DRAMPower.hh"
-#include "debug/DRAMState.hh"
-#include "debug/NVM.hh"
 #include "sim/system.hh"
 
 namespace gem5
 {
 
-using namespace Data;
-
 namespace memory
 {
 
@@ -75,2554 +69,18 @@
       tRTW(_p.tRTW),
       tWTR(_p.tWTR),
       readBufferSize(_p.read_buffer_size),
-      writeBufferSize(_p.write_buffer_size)
+      writeBufferSize(_p.write_buffer_size),
+      numWritesQueued(0)
 {}
 
 void
-MemInterface::setCtrl(MemCtrl* _ctrl, unsigned int command_window)
+MemInterface::setCtrl(MemCtrl* _ctrl, unsigned int command_window,
+                                            uint8_t pseudo_channel)
 {
     ctrl = _ctrl;
     maxCommandsPerWindow = command_window / tCK;
-}
-
-MemPacket*
-MemInterface::decodePacket(const PacketPtr pkt, Addr pkt_addr,
-                       unsigned size, bool is_read, bool is_dram)
-{
-    // decode the address based on the address mapping scheme, with
-    // Ro, Ra, Co, Ba and Ch denoting row, rank, column, bank and
-    // channel, respectively
-    uint8_t rank;
-    uint8_t bank;
-    // use a 64-bit unsigned during the computations as the row is
-    // always the top bits, and check before creating the packet
-    uint64_t row;
-
-    // Get packed address, starting at 0
-    Addr addr = getCtrlAddr(pkt_addr);
-
-    // truncate the address to a memory burst, which makes it unique to
-    // a specific buffer, row, bank, rank and channel
-    addr = addr / burstSize;
-
-    // we have removed the lowest order address bits that denote the
-    // position within the column
-    if (addrMapping == enums::RoRaBaChCo || addrMapping == enums::RoRaBaCoCh) {
-        // the lowest order bits denote the column to ensure that
-        // sequential cache lines occupy the same row
-        addr = addr / burstsPerRowBuffer;
-
-        // after the channel bits, get the bank bits to interleave
-        // over the banks
-        bank = addr % banksPerRank;
-        addr = addr / banksPerRank;
-
-        // after the bank, we get the rank bits which thus interleaves
-        // over the ranks
-        rank = addr % ranksPerChannel;
-        addr = addr / ranksPerChannel;
-
-        // lastly, get the row bits, no need to remove them from addr
-        row = addr % rowsPerBank;
-    } else if (addrMapping == enums::RoCoRaBaCh) {
-        // with emerging technologies, could have small page size with
-        // interleaving granularity greater than row buffer
-        if (burstsPerStripe > burstsPerRowBuffer) {
-            // remove column bits which are a subset of burstsPerStripe
-            addr = addr / burstsPerRowBuffer;
-        } else {
-            // remove lower column bits below channel bits
-            addr = addr / burstsPerStripe;
-        }
-
-        // start with the bank bits, as this provides the maximum
-        // opportunity for parallelism between requests
-        bank = addr % banksPerRank;
-        addr = addr / banksPerRank;
-
-        // next get the rank bits
-        rank = addr % ranksPerChannel;
-        addr = addr / ranksPerChannel;
-
-        // next, the higher-order column bites
-        if (burstsPerStripe < burstsPerRowBuffer) {
-            addr = addr / (burstsPerRowBuffer / burstsPerStripe);
-        }
-
-        // lastly, get the row bits, no need to remove them from addr
-        row = addr % rowsPerBank;
-    } else
-        panic("Unknown address mapping policy chosen!");
-
-    assert(rank < ranksPerChannel);
-    assert(bank < banksPerRank);
-    assert(row < rowsPerBank);
-    assert(row < Bank::NO_ROW);
-
-    DPRINTF(DRAM, "Address: %#x Rank %d Bank %d Row %d\n",
-            pkt_addr, rank, bank, row);
-
-    // create the corresponding memory packet with the entry time and
-    // ready time set to the current tick, the latter will be updated
-    // later
-    uint16_t bank_id = banksPerRank * rank + bank;
-
-    return new MemPacket(pkt, is_read, is_dram, rank, bank, row, bank_id,
-                   pkt_addr, size);
-}
-
-std::pair<MemPacketQueue::iterator, Tick>
-DRAMInterface::chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const
-{
-    std::vector<uint32_t> earliest_banks(ranksPerChannel, 0);
-
-    // Has minBankPrep been called to populate earliest_banks?
-    bool filled_earliest_banks = false;
-    // can the PRE/ACT sequence be done without impacting utlization?
-    bool hidden_bank_prep = false;
-
-    // search for seamless row hits first, if no seamless row hit is
-    // found then determine if there are other packets that can be issued
-    // without incurring additional bus delay due to bank timing
-    // Will select closed rows first to enable more open row possibilies
-    // in future selections
-    bool found_hidden_bank = false;
-
-    // remember if we found a row hit, not seamless, but bank prepped
-    // and ready
-    bool found_prepped_pkt = false;
-
-    // if we have no row hit, prepped or not, and no seamless packet,
-    // just go for the earliest possible
-    bool found_earliest_pkt = false;
-
-    Tick selected_col_at = MaxTick;
-    auto selected_pkt_it = queue.end();
-
-    for (auto i = queue.begin(); i != queue.end() ; ++i) {
-        MemPacket* pkt = *i;
-
-        // select optimal DRAM packet in Q
-        if (pkt->isDram()) {
-            const Bank& bank = ranks[pkt->rank]->banks[pkt->bank];
-            const Tick col_allowed_at = pkt->isRead() ? bank.rdAllowedAt :
-                                                        bank.wrAllowedAt;
-
-            DPRINTF(DRAM, "%s checking DRAM packet in bank %d, row %d\n",
-                    __func__, pkt->bank, pkt->row);
-
-            // check if rank is not doing a refresh and thus is available,
-            // if not, jump to the next packet
-            if (burstReady(pkt)) {
-
-                DPRINTF(DRAM,
-                        "%s bank %d - Rank %d available\n", __func__,
-                        pkt->bank, pkt->rank);
-
-                // check if it is a row hit
-                if (bank.openRow == pkt->row) {
-                    // no additional rank-to-rank or same bank-group
-                    // delays, or we switched read/write and might as well
-                    // go for the row hit
-                    if (col_allowed_at <= min_col_at) {
-                        // FCFS within the hits, giving priority to
-                        // commands that can issue seamlessly, without
-                        // additional delay, such as same rank accesses
-                        // and/or different bank-group accesses
-                        DPRINTF(DRAM, "%s Seamless buffer hit\n", __func__);
-                        selected_pkt_it = i;
-                        selected_col_at = col_allowed_at;
-                        // no need to look through the remaining queue entries
-                        break;
-                    } else if (!found_hidden_bank && !found_prepped_pkt) {
-                        // if we did not find a packet to a closed row that can
-                        // issue the bank commands without incurring delay, and
-                        // did not yet find a packet to a prepped row, remember
-                        // the current one
-                        selected_pkt_it = i;
-                        selected_col_at = col_allowed_at;
-                        found_prepped_pkt = true;
-                        DPRINTF(DRAM, "%s Prepped row buffer hit\n", __func__);
-                    }
-                } else if (!found_earliest_pkt) {
-                    // if we have not initialised the bank status, do it
-                    // now, and only once per scheduling decisions
-                    if (!filled_earliest_banks) {
-                        // determine entries with earliest bank delay
-                        std::tie(earliest_banks, hidden_bank_prep) =
-                            minBankPrep(queue, min_col_at);
-                        filled_earliest_banks = true;
-                    }
-
-                    // bank is amongst first available banks
-                    // minBankPrep will give priority to packets that can
-                    // issue seamlessly
-                    if (bits(earliest_banks[pkt->rank],
-                             pkt->bank, pkt->bank)) {
-                        found_earliest_pkt = true;
-                        found_hidden_bank = hidden_bank_prep;
-
-                        // give priority to packets that can issue
-                        // bank commands 'behind the scenes'
-                        // any additional delay if any will be due to
-                        // col-to-col command requirements
-                        if (hidden_bank_prep || !found_prepped_pkt) {
-                            selected_pkt_it = i;
-                            selected_col_at = col_allowed_at;
-                        }
-                    }
-                }
-            } else {
-                DPRINTF(DRAM, "%s bank %d - Rank %d not available\n", __func__,
-                        pkt->bank, pkt->rank);
-            }
-        }
-    }
-
-    if (selected_pkt_it == queue.end()) {
-        DPRINTF(DRAM, "%s no available DRAM ranks found\n", __func__);
-    }
-
-    return std::make_pair(selected_pkt_it, selected_col_at);
-}
-
-void
-DRAMInterface::activateBank(Rank& rank_ref, Bank& bank_ref,
-                       Tick act_tick, uint32_t row)
-{
-    assert(rank_ref.actTicks.size() == activationLimit);
-
-    // verify that we have command bandwidth to issue the activate
-    // if not, shift to next burst window
-    Tick act_at;
-    if (twoCycleActivate)
-        act_at = ctrl->verifyMultiCmd(act_tick, maxCommandsPerWindow, tAAD);
-    else
-        act_at = ctrl->verifySingleCmd(act_tick, maxCommandsPerWindow);
-
-    DPRINTF(DRAM, "Activate at tick %d\n", act_at);
-
-    // update the open row
-    assert(bank_ref.openRow == Bank::NO_ROW);
-    bank_ref.openRow = row;
-
-    // start counting anew, this covers both the case when we
-    // auto-precharged, and when this access is forced to
-    // precharge
-    bank_ref.bytesAccessed = 0;
-    bank_ref.rowAccesses = 0;
-
-    ++rank_ref.numBanksActive;
-    assert(rank_ref.numBanksActive <= banksPerRank);
-
-    DPRINTF(DRAM, "Activate bank %d, rank %d at tick %lld, now got "
-            "%d active\n", bank_ref.bank, rank_ref.rank, act_at,
-            ranks[rank_ref.rank]->numBanksActive);
-
-    rank_ref.cmdList.push_back(Command(MemCommand::ACT, bank_ref.bank,
-                               act_at));
-
-    DPRINTF(DRAMPower, "%llu,ACT,%d,%d\n", divCeil(act_at, tCK) -
-            timeStampOffset, bank_ref.bank, rank_ref.rank);
-
-    // The next access has to respect tRAS for this bank
-    bank_ref.preAllowedAt = act_at + tRAS;
-
-    // Respect the row-to-column command delay for both read and write cmds
-    bank_ref.rdAllowedAt = std::max(act_at + tRCD, bank_ref.rdAllowedAt);
-    bank_ref.wrAllowedAt = std::max(act_at + tRCD, bank_ref.wrAllowedAt);
-
-    // start by enforcing tRRD
-    for (int i = 0; i < banksPerRank; i++) {
-        // next activate to any bank in this rank must not happen
-        // before tRRD
-        if (bankGroupArch && (bank_ref.bankgr == rank_ref.banks[i].bankgr)) {
-            // bank group architecture requires longer delays between
-            // ACT commands within the same bank group.  Use tRRD_L
-            // in this case
-            rank_ref.banks[i].actAllowedAt = std::max(act_at + tRRD_L,
-                                             rank_ref.banks[i].actAllowedAt);
-        } else {
-            // use shorter tRRD value when either
-            // 1) bank group architecture is not supportted
-            // 2) bank is in a different bank group
-            rank_ref.banks[i].actAllowedAt = std::max(act_at + tRRD,
-                                             rank_ref.banks[i].actAllowedAt);
-        }
-    }
-
-    // next, we deal with tXAW, if the activation limit is disabled
-    // then we directly schedule an activate power event
-    if (!rank_ref.actTicks.empty()) {
-        // sanity check
-        if (rank_ref.actTicks.back() &&
-           (act_at - rank_ref.actTicks.back()) < tXAW) {
-            panic("Got %d activates in window %d (%llu - %llu) which "
-                  "is smaller than %llu\n", activationLimit, act_at -
-                  rank_ref.actTicks.back(), act_at,
-                  rank_ref.actTicks.back(), tXAW);
-        }
-
-        // shift the times used for the book keeping, the last element
-        // (highest index) is the oldest one and hence the lowest value
-        rank_ref.actTicks.pop_back();
-
-        // record an new activation (in the future)
-        rank_ref.actTicks.push_front(act_at);
-
-        // cannot activate more than X times in time window tXAW, push the
-        // next one (the X + 1'st activate) to be tXAW away from the
-        // oldest in our window of X
-        if (rank_ref.actTicks.back() &&
-           (act_at - rank_ref.actTicks.back()) < tXAW) {
-            DPRINTF(DRAM, "Enforcing tXAW with X = %d, next activate "
-                    "no earlier than %llu\n", activationLimit,
-                    rank_ref.actTicks.back() + tXAW);
-            for (int j = 0; j < banksPerRank; j++)
-                // next activate must not happen before end of window
-                rank_ref.banks[j].actAllowedAt =
-                    std::max(rank_ref.actTicks.back() + tXAW,
-                             rank_ref.banks[j].actAllowedAt);
-        }
-    }
-
-    // at the point when this activate takes place, make sure we
-    // transition to the active power state
-    if (!rank_ref.activateEvent.scheduled())
-        schedule(rank_ref.activateEvent, act_at);
-    else if (rank_ref.activateEvent.when() > act_at)
-        // move it sooner in time
-        reschedule(rank_ref.activateEvent, act_at);
-}
-
-void
-DRAMInterface::prechargeBank(Rank& rank_ref, Bank& bank, Tick pre_tick,
-                             bool auto_or_preall, bool trace)
-{
-    // make sure the bank has an open row
-    assert(bank.openRow != Bank::NO_ROW);
-
-    // sample the bytes per activate here since we are closing
-    // the page
-    stats.bytesPerActivate.sample(bank.bytesAccessed);
-
-    bank.openRow = Bank::NO_ROW;
-
-    Tick pre_at = pre_tick;
-    if (auto_or_preall) {
-        // no precharge allowed before this one
-        bank.preAllowedAt = pre_at;
-    } else {
-        // Issuing an explicit PRE command
-        // Verify that we have command bandwidth to issue the precharge
-        // if not, shift to next burst window
-        pre_at = ctrl->verifySingleCmd(pre_tick, maxCommandsPerWindow);
-        // enforce tPPD
-        for (int i = 0; i < banksPerRank; i++) {
-            rank_ref.banks[i].preAllowedAt = std::max(pre_at + tPPD,
-                                             rank_ref.banks[i].preAllowedAt);
-        }
-    }
-
-    Tick pre_done_at = pre_at + tRP;
-
-    bank.actAllowedAt = std::max(bank.actAllowedAt, pre_done_at);
-
-    assert(rank_ref.numBanksActive != 0);
-    --rank_ref.numBanksActive;
-
-    DPRINTF(DRAM, "Precharging bank %d, rank %d at tick %lld, now got "
-            "%d active\n", bank.bank, rank_ref.rank, pre_at,
-            rank_ref.numBanksActive);
-
-    if (trace) {
-
-        rank_ref.cmdList.push_back(Command(MemCommand::PRE, bank.bank,
-                                   pre_at));
-        DPRINTF(DRAMPower, "%llu,PRE,%d,%d\n", divCeil(pre_at, tCK) -
-                timeStampOffset, bank.bank, rank_ref.rank);
-    }
-
-    // if we look at the current number of active banks we might be
-    // tempted to think the DRAM is now idle, however this can be
-    // undone by an activate that is scheduled to happen before we
-    // would have reached the idle state, so schedule an event and
-    // rather check once we actually make it to the point in time when
-    // the (last) precharge takes place
-    if (!rank_ref.prechargeEvent.scheduled()) {
-        schedule(rank_ref.prechargeEvent, pre_done_at);
-        // New event, increment count
-        ++rank_ref.outstandingEvents;
-    } else if (rank_ref.prechargeEvent.when() < pre_done_at) {
-        reschedule(rank_ref.prechargeEvent, pre_done_at);
-    }
-}
-
-std::pair<Tick, Tick>
-DRAMInterface::doBurstAccess(MemPacket* mem_pkt, Tick next_burst_at,
-                             const std::vector<MemPacketQueue>& queue)
-{
-    DPRINTF(DRAM, "Timing access to addr %#x, rank/bank/row %d %d %d\n",
-            mem_pkt->addr, mem_pkt->rank, mem_pkt->bank, mem_pkt->row);
-
-    // get the rank
-    Rank& rank_ref = *ranks[mem_pkt->rank];
-
-    assert(rank_ref.inRefIdleState());
-
-    // are we in or transitioning to a low-power state and have not scheduled
-    // a power-up event?
-    // if so, wake up from power down to issue RD/WR burst
-    if (rank_ref.inLowPowerState) {
-        assert(rank_ref.pwrState != PWR_SREF);
-        rank_ref.scheduleWakeUpEvent(tXP);
-    }
-
-    // get the bank
-    Bank& bank_ref = rank_ref.banks[mem_pkt->bank];
-
-    // for the state we need to track if it is a row hit or not
-    bool row_hit = true;
-
-    // Determine the access latency and update the bank state
-    if (bank_ref.openRow == mem_pkt->row) {
-        // nothing to do
-    } else {
-        row_hit = false;
-
-        // If there is a page open, precharge it.
-        if (bank_ref.openRow != Bank::NO_ROW) {
-            prechargeBank(rank_ref, bank_ref, std::max(bank_ref.preAllowedAt,
-                                                   curTick()));
-        }
-
-        // next we need to account for the delay in activating the page
-        Tick act_tick = std::max(bank_ref.actAllowedAt, curTick());
-
-        // Record the activation and deal with all the global timing
-        // constraints caused be a new activation (tRRD and tXAW)
-        activateBank(rank_ref, bank_ref, act_tick, mem_pkt->row);
-    }
-
-    // respect any constraints on the command (e.g. tRCD or tCCD)
-    const Tick col_allowed_at = mem_pkt->isRead() ?
-                                bank_ref.rdAllowedAt : bank_ref.wrAllowedAt;
-
-    // we need to wait until the bus is available before we can issue
-    // the command; need to ensure minimum bus delay requirement is met
-    Tick cmd_at = std::max({col_allowed_at, next_burst_at, curTick()});
-
-    // verify that we have command bandwidth to issue the burst
-    // if not, shift to next burst window
-    if (dataClockSync && ((cmd_at - rank_ref.lastBurstTick) > clkResyncDelay))
-        cmd_at = ctrl->verifyMultiCmd(cmd_at, maxCommandsPerWindow, tCK);
-    else
-        cmd_at = ctrl->verifySingleCmd(cmd_at, maxCommandsPerWindow);
-
-    // if we are interleaving bursts, ensure that
-    // 1) we don't double interleave on next burst issue
-    // 2) we are at an interleave boundary; if not, shift to next boundary
-    Tick burst_gap = tBURST_MIN;
-    if (burstInterleave) {
-        if (cmd_at == (rank_ref.lastBurstTick + tBURST_MIN)) {
-            // already interleaving, push next command to end of full burst
-            burst_gap = tBURST;
-        } else if (cmd_at < (rank_ref.lastBurstTick + tBURST)) {
-            // not at an interleave boundary after bandwidth check
-            // Shift command to tBURST boundary to avoid data contention
-            // Command will remain in the same burst window given that
-            // tBURST is less than tBURST_MAX
-            cmd_at = rank_ref.lastBurstTick + tBURST;
-        }
-    }
-    DPRINTF(DRAM, "Schedule RD/WR burst at tick %d\n", cmd_at);
-
-    // update the packet ready time
-    mem_pkt->readyTime = cmd_at + tCL + tBURST;
-
-    rank_ref.lastBurstTick = cmd_at;
-
-    // update the time for the next read/write burst for each
-    // bank (add a max with tCCD/tCCD_L/tCCD_L_WR here)
-    Tick dly_to_rd_cmd;
-    Tick dly_to_wr_cmd;
-    for (int j = 0; j < ranksPerChannel; j++) {
-        for (int i = 0; i < banksPerRank; i++) {
-            if (mem_pkt->rank == j) {
-                if (bankGroupArch &&
-                   (bank_ref.bankgr == ranks[j]->banks[i].bankgr)) {
-                    // bank group architecture requires longer delays between
-                    // RD/WR burst commands to the same bank group.
-                    // tCCD_L is default requirement for same BG timing
-                    // tCCD_L_WR is required for write-to-write
-                    // Need to also take bus turnaround delays into account
-                    dly_to_rd_cmd = mem_pkt->isRead() ?
-                                    tCCD_L : std::max(tCCD_L, wrToRdDlySameBG);
-                    dly_to_wr_cmd = mem_pkt->isRead() ?
-                                    std::max(tCCD_L, rdToWrDlySameBG) :
-                                    tCCD_L_WR;
-                } else {
-                    // tBURST is default requirement for diff BG timing
-                    // Need to also take bus turnaround delays into account
-                    dly_to_rd_cmd = mem_pkt->isRead() ? burst_gap :
-                                                       writeToReadDelay();
-                    dly_to_wr_cmd = mem_pkt->isRead() ? readToWriteDelay() :
-                                                       burst_gap;
-                }
-            } else {
-                // different rank is by default in a different bank group and
-                // doesn't require longer tCCD or additional RTW, WTR delays
-                // Need to account for rank-to-rank switching
-                dly_to_wr_cmd = rankToRankDelay();
-                dly_to_rd_cmd = rankToRankDelay();
-            }
-            ranks[j]->banks[i].rdAllowedAt = std::max(cmd_at + dly_to_rd_cmd,
-                                             ranks[j]->banks[i].rdAllowedAt);
-            ranks[j]->banks[i].wrAllowedAt = std::max(cmd_at + dly_to_wr_cmd,
-                                             ranks[j]->banks[i].wrAllowedAt);
-        }
-    }
-
-    // Save rank of current access
-    activeRank = mem_pkt->rank;
-
-    // If this is a write, we also need to respect the write recovery
-    // time before a precharge, in the case of a read, respect the
-    // read to precharge constraint
-    bank_ref.preAllowedAt = std::max(bank_ref.preAllowedAt,
-                                 mem_pkt->isRead() ? cmd_at + tRTP :
-                                 mem_pkt->readyTime + tWR);
-
-    // increment the bytes accessed and the accesses per row
-    bank_ref.bytesAccessed += burstSize;
-    ++bank_ref.rowAccesses;
-
-    // if we reached the max, then issue with an auto-precharge
-    bool auto_precharge = pageMgmt == enums::close ||
-        bank_ref.rowAccesses == maxAccessesPerRow;
-
-    // if we did not hit the limit, we might still want to
-    // auto-precharge
-    if (!auto_precharge &&
-        (pageMgmt == enums::open_adaptive ||
-         pageMgmt == enums::close_adaptive)) {
-        // a twist on the open and close page policies:
-        // 1) open_adaptive page policy does not blindly keep the
-        // page open, but close it if there are no row hits, and there
-        // are bank conflicts in the queue
-        // 2) close_adaptive page policy does not blindly close the
-        // page, but closes it only if there are no row hits in the queue.
-        // In this case, only force an auto precharge when there
-        // are no same page hits in the queue
-        bool got_more_hits = false;
-        bool got_bank_conflict = false;
-
-        for (uint8_t i = 0; i < ctrl->numPriorities(); ++i) {
-            auto p = queue[i].begin();
-            // keep on looking until we find a hit or reach the end of the
-            // queue
-            // 1) if a hit is found, then both open and close adaptive
-            //    policies keep the page open
-            // 2) if no hit is found, got_bank_conflict is set to true if a
-            //    bank conflict request is waiting in the queue
-            // 3) make sure we are not considering the packet that we are
-            //    currently dealing with
-            while (!got_more_hits && p != queue[i].end()) {
-                if (mem_pkt != (*p)) {
-                    bool same_rank_bank = (mem_pkt->rank == (*p)->rank) &&
-                                          (mem_pkt->bank == (*p)->bank);
-
-                    bool same_row = mem_pkt->row == (*p)->row;
-                    got_more_hits |= same_rank_bank && same_row;
-                    got_bank_conflict |= same_rank_bank && !same_row;
-                }
-                ++p;
-            }
-
-            if (got_more_hits)
-                break;
-        }
-
-        // auto pre-charge when either
-        // 1) open_adaptive policy, we have not got any more hits, and
-        //    have a bank conflict
-        // 2) close_adaptive policy and we have not got any more hits
-        auto_precharge = !got_more_hits &&
-            (got_bank_conflict || pageMgmt == enums::close_adaptive);
-    }
-
-    // DRAMPower trace command to be written
-    std::string mem_cmd = mem_pkt->isRead() ? "RD" : "WR";
-
-    // MemCommand required for DRAMPower library
-    MemCommand::cmds command = (mem_cmd == "RD") ? MemCommand::RD :
-                                                   MemCommand::WR;
-
-    rank_ref.cmdList.push_back(Command(command, mem_pkt->bank, cmd_at));
-
-    DPRINTF(DRAMPower, "%llu,%s,%d,%d\n", divCeil(cmd_at, tCK) -
-            timeStampOffset, mem_cmd, mem_pkt->bank, mem_pkt->rank);
-
-    // if this access should use auto-precharge, then we are
-    // closing the row after the read/write burst
-    if (auto_precharge) {
-        // if auto-precharge push a PRE command at the correct tick to the
-        // list used by DRAMPower library to calculate power
-        prechargeBank(rank_ref, bank_ref, std::max(curTick(),
-                      bank_ref.preAllowedAt), true);
-
-        DPRINTF(DRAM, "Auto-precharged bank: %d\n", mem_pkt->bankId);
-    }
-
-    // Update the stats and schedule the next request
-    if (mem_pkt->isRead()) {
-        // Every respQueue which will generate an event, increment count
-        ++rank_ref.outstandingEvents;
-
-        stats.readBursts++;
-        if (row_hit)
-            stats.readRowHits++;
-        stats.bytesRead += burstSize;
-        stats.perBankRdBursts[mem_pkt->bankId]++;
-
-        // Update latency stats
-        stats.totMemAccLat += mem_pkt->readyTime - mem_pkt->entryTime;
-        stats.totQLat += cmd_at - mem_pkt->entryTime;
-        stats.totBusLat += tBURST;
-    } else {
-        // Schedule write done event to decrement event count
-        // after the readyTime has been reached
-        // Only schedule latest write event to minimize events
-        // required; only need to ensure that final event scheduled covers
-        // the time that writes are outstanding and bus is active
-        // to holdoff power-down entry events
-        if (!rank_ref.writeDoneEvent.scheduled()) {
-            schedule(rank_ref.writeDoneEvent, mem_pkt->readyTime);
-            // New event, increment count
-            ++rank_ref.outstandingEvents;
-
-        } else if (rank_ref.writeDoneEvent.when() < mem_pkt->readyTime) {
-            reschedule(rank_ref.writeDoneEvent, mem_pkt->readyTime);
-        }
-        // will remove write from queue when returned to parent function
-        // decrement count for DRAM rank
-        --rank_ref.writeEntries;
-
-        stats.writeBursts++;
-        if (row_hit)
-            stats.writeRowHits++;
-        stats.bytesWritten += burstSize;
-        stats.perBankWrBursts[mem_pkt->bankId]++;
-
-    }
-    // Update bus state to reflect when previous command was issued
-    return std::make_pair(cmd_at, cmd_at + burst_gap);
-}
-
-void
-DRAMInterface::addRankToRankDelay(Tick cmd_at)
-{
-    // update timing for DRAM ranks due to bursts issued
-    // to ranks on other media interfaces
-    for (auto n : ranks) {
-        for (int i = 0; i < banksPerRank; i++) {
-            // different rank by default
-            // Need to only account for rank-to-rank switching
-            n->banks[i].rdAllowedAt = std::max(cmd_at + rankToRankDelay(),
-                                             n->banks[i].rdAllowedAt);
-            n->banks[i].wrAllowedAt = std::max(cmd_at + rankToRankDelay(),
-                                             n->banks[i].wrAllowedAt);
-        }
-    }
-}
-
-DRAMInterface::DRAMInterface(const DRAMInterfaceParams &_p)
-    : MemInterface(_p),
-      bankGroupsPerRank(_p.bank_groups_per_rank),
-      bankGroupArch(_p.bank_groups_per_rank > 0),
-      tCL(_p.tCL),
-      tBURST_MIN(_p.tBURST_MIN), tBURST_MAX(_p.tBURST_MAX),
-      tCCD_L_WR(_p.tCCD_L_WR), tCCD_L(_p.tCCD_L), tRCD(_p.tRCD),
-      tRP(_p.tRP), tRAS(_p.tRAS), tWR(_p.tWR), tRTP(_p.tRTP),
-      tRFC(_p.tRFC), tREFI(_p.tREFI), tRRD(_p.tRRD), tRRD_L(_p.tRRD_L),
-      tPPD(_p.tPPD), tAAD(_p.tAAD),
-      tXAW(_p.tXAW), tXP(_p.tXP), tXS(_p.tXS),
-      clkResyncDelay(tCL + _p.tBURST_MAX),
-      dataClockSync(_p.data_clock_sync),
-      burstInterleave(tBURST != tBURST_MIN),
-      twoCycleActivate(_p.two_cycle_activate),
-      activationLimit(_p.activation_limit),
-      wrToRdDlySameBG(tCL + _p.tBURST_MAX + _p.tWTR_L),
-      rdToWrDlySameBG(_p.tRTW + _p.tBURST_MAX),
-      pageMgmt(_p.page_policy),
-      maxAccessesPerRow(_p.max_accesses_per_row),
-      timeStampOffset(0), activeRank(0),
-      enableDRAMPowerdown(_p.enable_dram_powerdown),
-      lastStatsResetTick(0),
-      stats(*this)
-{
-    DPRINTF(DRAM, "Setting up DRAM Interface\n");
-
-    fatal_if(!isPowerOf2(burstSize), "DRAM burst size %d is not allowed, "
-             "must be a power of two\n", burstSize);
-
-    // sanity check the ranks since we rely on bit slicing for the
-    // address decoding
-    fatal_if(!isPowerOf2(ranksPerChannel), "DRAM rank count of %d is "
-             "not allowed, must be a power of two\n", ranksPerChannel);
-
-    for (int i = 0; i < ranksPerChannel; i++) {
-        DPRINTF(DRAM, "Creating DRAM rank %d \n", i);
-        Rank* rank = new Rank(_p, i, *this);
-        ranks.push_back(rank);
-    }
-
-    // determine the dram actual capacity from the DRAM config in Mbytes
-    uint64_t deviceCapacity = deviceSize / (1024 * 1024) * devicesPerRank *
-                              ranksPerChannel;
-
-    uint64_t capacity = 1ULL << ceilLog2(AbstractMemory::size());
-
-    DPRINTF(DRAM, "Memory capacity %lld (%lld) bytes\n", capacity,
-            AbstractMemory::size());
-
-    // if actual DRAM size does not match memory capacity in system warn!
-    if (deviceCapacity != capacity / (1024 * 1024))
-        warn("DRAM device capacity (%d Mbytes) does not match the "
-             "address range assigned (%d Mbytes)\n", deviceCapacity,
-             capacity / (1024 * 1024));
-
-    DPRINTF(DRAM, "Row buffer size %d bytes with %d bursts per row buffer\n",
-            rowBufferSize, burstsPerRowBuffer);
-
-    rowsPerBank = capacity / (rowBufferSize * banksPerRank * ranksPerChannel);
-
-    // some basic sanity checks
-    if (tREFI <= tRP || tREFI <= tRFC) {
-        fatal("tREFI (%d) must be larger than tRP (%d) and tRFC (%d)\n",
-              tREFI, tRP, tRFC);
-    }
-
-    // basic bank group architecture checks ->
-    if (bankGroupArch) {
-        // must have at least one bank per bank group
-        if (bankGroupsPerRank > banksPerRank) {
-            fatal("banks per rank (%d) must be equal to or larger than "
-                  "banks groups per rank (%d)\n",
-                  banksPerRank, bankGroupsPerRank);
-        }
-        // must have same number of banks in each bank group
-        if ((banksPerRank % bankGroupsPerRank) != 0) {
-            fatal("Banks per rank (%d) must be evenly divisible by bank "
-                  "groups per rank (%d) for equal banks per bank group\n",
-                  banksPerRank, bankGroupsPerRank);
-        }
-        // tCCD_L should be greater than minimal, back-to-back burst delay
-        if (tCCD_L <= tBURST) {
-            fatal("tCCD_L (%d) should be larger than the minimum bus delay "
-                  "(%d) when bank groups per rank (%d) is greater than 1\n",
-                  tCCD_L, tBURST, bankGroupsPerRank);
-        }
-        // tCCD_L_WR should be greater than minimal, back-to-back burst delay
-        if (tCCD_L_WR <= tBURST) {
-            fatal("tCCD_L_WR (%d) should be larger than the minimum bus delay "
-                  " (%d) when bank groups per rank (%d) is greater than 1\n",
-                  tCCD_L_WR, tBURST, bankGroupsPerRank);
-        }
-        // tRRD_L is greater than minimal, same bank group ACT-to-ACT delay
-        // some datasheets might specify it equal to tRRD
-        if (tRRD_L < tRRD) {
-            fatal("tRRD_L (%d) should be larger than tRRD (%d) when "
-                  "bank groups per rank (%d) is greater than 1\n",
-                  tRRD_L, tRRD, bankGroupsPerRank);
-        }
-    }
-}
-
-void
-DRAMInterface::init()
-{
-    AbstractMemory::init();
-
-    // a bit of sanity checks on the interleaving, save it for here to
-    // ensure that the system pointer is initialised
-    if (range.interleaved()) {
-        if (addrMapping == enums::RoRaBaChCo) {
-            if (rowBufferSize != range.granularity()) {
-                fatal("Channel interleaving of %s doesn't match RoRaBaChCo "
-                      "address map\n", name());
-            }
-        } else if (addrMapping == enums::RoRaBaCoCh ||
-                   addrMapping == enums::RoCoRaBaCh) {
-            // for the interleavings with channel bits in the bottom,
-            // if the system uses a channel striping granularity that
-            // is larger than the DRAM burst size, then map the
-            // sequential accesses within a stripe to a number of
-            // columns in the DRAM, effectively placing some of the
-            // lower-order column bits as the least-significant bits
-            // of the address (above the ones denoting the burst size)
-            assert(burstsPerStripe >= 1);
-
-            // channel striping has to be done at a granularity that
-            // is equal or larger to a cache line
-            if (system()->cacheLineSize() > range.granularity()) {
-                fatal("Channel interleaving of %s must be at least as large "
-                      "as the cache line size\n", name());
-            }
-
-            // ...and equal or smaller than the row-buffer size
-            if (rowBufferSize < range.granularity()) {
-                fatal("Channel interleaving of %s must be at most as large "
-                      "as the row-buffer size\n", name());
-            }
-            // this is essentially the check above, so just to be sure
-            assert(burstsPerStripe <= burstsPerRowBuffer);
-        }
-    }
-}
-
-void
-DRAMInterface::startup()
-{
-    if (system()->isTimingMode()) {
-        // timestamp offset should be in clock cycles for DRAMPower
-        timeStampOffset = divCeil(curTick(), tCK);
-
-        for (auto r : ranks) {
-            r->startup(curTick() + tREFI - tRP);
-        }
-    }
-}
-
-bool
-DRAMInterface::isBusy()
-{
-    int busy_ranks = 0;
-    for (auto r : ranks) {
-        if (!r->inRefIdleState()) {
-            if (r->pwrState != PWR_SREF) {
-                // rank is busy refreshing
-                DPRINTF(DRAMState, "Rank %d is not available\n", r->rank);
-                busy_ranks++;
-
-                // let the rank know that if it was waiting to drain, it
-                // is now done and ready to proceed
-                r->checkDrainDone();
-            }
-
-            // check if we were in self-refresh and haven't started
-            // to transition out
-            if ((r->pwrState == PWR_SREF) && r->inLowPowerState) {
-                DPRINTF(DRAMState, "Rank %d is in self-refresh\n", r->rank);
-                // if we have commands queued to this rank and we don't have
-                // a minimum number of active commands enqueued,
-                // exit self-refresh
-                if (r->forceSelfRefreshExit()) {
-                    DPRINTF(DRAMState, "rank %d was in self refresh and"
-                           " should wake up\n", r->rank);
-                    //wake up from self-refresh
-                    r->scheduleWakeUpEvent(tXS);
-                    // things are brought back into action once a refresh is
-                    // performed after self-refresh
-                    // continue with selection for other ranks
-                }
-            }
-        }
-    }
-    return (busy_ranks == ranksPerChannel);
-}
-
-void DRAMInterface::setupRank(const uint8_t rank, const bool is_read)
-{
-    // increment entry count of the rank based on packet type
-    if (is_read) {
-        ++ranks[rank]->readEntries;
-    } else {
-        ++ranks[rank]->writeEntries;
-    }
-}
-
-void
-DRAMInterface::respondEvent(uint8_t rank)
-{
-    Rank& rank_ref = *ranks[rank];
-
-    // if a read has reached its ready-time, decrement the number of reads
-    // At this point the packet has been handled and there is a possibility
-    // to switch to low-power mode if no other packet is available
-    --rank_ref.readEntries;
-    DPRINTF(DRAM, "number of read entries for rank %d is %d\n",
-            rank, rank_ref.readEntries);
-
-    // counter should at least indicate one outstanding request
-    // for this read
-    assert(rank_ref.outstandingEvents > 0);
-    // read response received, decrement count
-    --rank_ref.outstandingEvents;
-
-    // at this moment should not have transitioned to a low-power state
-    assert((rank_ref.pwrState != PWR_SREF) &&
-           (rank_ref.pwrState != PWR_PRE_PDN) &&
-           (rank_ref.pwrState != PWR_ACT_PDN));
-
-    // track if this is the last packet before idling
-    // and that there are no outstanding commands to this rank
-    if (rank_ref.isQueueEmpty() && rank_ref.outstandingEvents == 0 &&
-        rank_ref.inRefIdleState() && enableDRAMPowerdown) {
-        // verify that there are no events scheduled
-        assert(!rank_ref.activateEvent.scheduled());
-        assert(!rank_ref.prechargeEvent.scheduled());
-
-        // if coming from active state, schedule power event to
-        // active power-down else go to precharge power-down
-        DPRINTF(DRAMState, "Rank %d sleep at tick %d; current power state is "
-                "%d\n", rank, curTick(), rank_ref.pwrState);
-
-        // default to ACT power-down unless already in IDLE state
-        // could be in IDLE if PRE issued before data returned
-        PowerState next_pwr_state = PWR_ACT_PDN;
-        if (rank_ref.pwrState == PWR_IDLE) {
-            next_pwr_state = PWR_PRE_PDN;
-        }
-
-        rank_ref.powerDownSleep(next_pwr_state, curTick());
-    }
-}
-
-void
-DRAMInterface::checkRefreshState(uint8_t rank)
-{
-    Rank& rank_ref = *ranks[rank];
-
-    if ((rank_ref.refreshState == REF_PRE) &&
-        !rank_ref.prechargeEvent.scheduled()) {
-          // kick the refresh event loop into action again if banks already
-          // closed and just waiting for read to complete
-          schedule(rank_ref.refreshEvent, curTick());
-    }
-}
-
-void
-DRAMInterface::drainRanks()
-{
-    // also need to kick off events to exit self-refresh
-    for (auto r : ranks) {
-        // force self-refresh exit, which in turn will issue auto-refresh
-        if (r->pwrState == PWR_SREF) {
-            DPRINTF(DRAM,"Rank%d: Forcing self-refresh wakeup in drain\n",
-                    r->rank);
-            r->scheduleWakeUpEvent(tXS);
-        }
-    }
-}
-
-bool
-DRAMInterface::allRanksDrained() const
-{
-    // true until proven false
-    bool all_ranks_drained = true;
-    for (auto r : ranks) {
-        // then verify that the power state is IDLE ensuring all banks are
-        // closed and rank is not in a low power state. Also verify that rank
-        // is idle from a refresh point of view.
-        all_ranks_drained = r->inPwrIdleState() && r->inRefIdleState() &&
-            all_ranks_drained;
-    }
-    return all_ranks_drained;
-}
-
-void
-DRAMInterface::suspend()
-{
-    for (auto r : ranks) {
-        r->suspend();
-    }
-}
-
-std::pair<std::vector<uint32_t>, bool>
-DRAMInterface::minBankPrep(const MemPacketQueue& queue,
-                      Tick min_col_at) const
-{
-    Tick min_act_at = MaxTick;
-    std::vector<uint32_t> bank_mask(ranksPerChannel, 0);
-
-    // latest Tick for which ACT can occur without incurring additoinal
-    // delay on the data bus
-    const Tick hidden_act_max = std::max(min_col_at - tRCD, curTick());
-
-    // Flag condition when burst can issue back-to-back with previous burst
-    bool found_seamless_bank = false;
-
-    // Flag condition when bank can be opened without incurring additional
-    // delay on the data bus
-    bool hidden_bank_prep = false;
-
-    // determine if we have queued transactions targetting the
-    // bank in question
-    std::vector<bool> got_waiting(ranksPerChannel * banksPerRank, false);
-    for (const auto& p : queue) {
-        if (p->isDram() && ranks[p->rank]->inRefIdleState())
-            got_waiting[p->bankId] = true;
-    }
-
-    // Find command with optimal bank timing
-    // Will prioritize commands that can issue seamlessly.
-    for (int i = 0; i < ranksPerChannel; i++) {
-        for (int j = 0; j < banksPerRank; j++) {
-            uint16_t bank_id = i * banksPerRank + j;
-
-            // if we have waiting requests for the bank, and it is
-            // amongst the first available, update the mask
-            if (got_waiting[bank_id]) {
-                // make sure this rank is not currently refreshing.
-                assert(ranks[i]->inRefIdleState());
-                // simplistic approximation of when the bank can issue
-                // an activate, ignoring any rank-to-rank switching
-                // cost in this calculation
-                Tick act_at = ranks[i]->banks[j].openRow == Bank::NO_ROW ?
-                    std::max(ranks[i]->banks[j].actAllowedAt, curTick()) :
-                    std::max(ranks[i]->banks[j].preAllowedAt, curTick()) + tRP;
-
-                // When is the earliest the R/W burst can issue?
-                const Tick col_allowed_at = ctrl->inReadBusState(false) ?
-                                              ranks[i]->banks[j].rdAllowedAt :
-                                              ranks[i]->banks[j].wrAllowedAt;
-                Tick col_at = std::max(col_allowed_at, act_at + tRCD);
-
-                // bank can issue burst back-to-back (seamlessly) with
-                // previous burst
-                bool new_seamless_bank = col_at <= min_col_at;
-
-                // if we found a new seamless bank or we have no
-                // seamless banks, and got a bank with an earlier
-                // activate time, it should be added to the bit mask
-                if (new_seamless_bank ||
-                    (!found_seamless_bank && act_at <= min_act_at)) {
-                    // if we did not have a seamless bank before, and
-                    // we do now, reset the bank mask, also reset it
-                    // if we have not yet found a seamless bank and
-                    // the activate time is smaller than what we have
-                    // seen so far
-                    if (!found_seamless_bank &&
-                        (new_seamless_bank || act_at < min_act_at)) {
-                        std::fill(bank_mask.begin(), bank_mask.end(), 0);
-                    }
-
-                    found_seamless_bank |= new_seamless_bank;
-
-                    // ACT can occur 'behind the scenes'
-                    hidden_bank_prep = act_at <= hidden_act_max;
-
-                    // set the bit corresponding to the available bank
-                    replaceBits(bank_mask[i], j, j, 1);
-                    min_act_at = act_at;
-                }
-            }
-        }
-    }
-
-    return std::make_pair(bank_mask, hidden_bank_prep);
-}
-
-DRAMInterface::Rank::Rank(const DRAMInterfaceParams &_p,
-                         int _rank, DRAMInterface& _dram)
-    : EventManager(&_dram), dram(_dram),
-      pwrStateTrans(PWR_IDLE), pwrStatePostRefresh(PWR_IDLE),
-      pwrStateTick(0), refreshDueAt(0), pwrState(PWR_IDLE),
-      refreshState(REF_IDLE), inLowPowerState(false), rank(_rank),
-      readEntries(0), writeEntries(0), outstandingEvents(0),
-      wakeUpAllowedAt(0), power(_p, false), banks(_p.banks_per_rank),
-      numBanksActive(0), actTicks(_p.activation_limit, 0), lastBurstTick(0),
-      writeDoneEvent([this]{ processWriteDoneEvent(); }, name()),
-      activateEvent([this]{ processActivateEvent(); }, name()),
-      prechargeEvent([this]{ processPrechargeEvent(); }, name()),
-      refreshEvent([this]{ processRefreshEvent(); }, name()),
-      powerEvent([this]{ processPowerEvent(); }, name()),
-      wakeUpEvent([this]{ processWakeUpEvent(); }, name()),
-      stats(_dram, *this)
-{
-    for (int b = 0; b < _p.banks_per_rank; b++) {
-        banks[b].bank = b;
-        // GDDR addressing of banks to BG is linear.
-        // Here we assume that all DRAM generations address bank groups as
-        // follows:
-        if (_p.bank_groups_per_rank > 0) {
-            // Simply assign lower bits to bank group in order to
-            // rotate across bank groups as banks are incremented
-            // e.g. with 4 banks per bank group and 16 banks total:
-            //    banks 0,4,8,12  are in bank group 0
-            //    banks 1,5,9,13  are in bank group 1
-            //    banks 2,6,10,14 are in bank group 2
-            //    banks 3,7,11,15 are in bank group 3
-            banks[b].bankgr = b % _p.bank_groups_per_rank;
-        } else {
-            // No bank groups; simply assign to bank number
-            banks[b].bankgr = b;
-        }
-    }
-}
-
-void
-DRAMInterface::Rank::startup(Tick ref_tick)
-{
-    assert(ref_tick > curTick());
-
-    pwrStateTick = curTick();
-
-    // kick off the refresh, and give ourselves enough time to
-    // precharge
-    schedule(refreshEvent, ref_tick);
-}
-
-void
-DRAMInterface::Rank::suspend()
-{
-    deschedule(refreshEvent);
-
-    // Update the stats
-    updatePowerStats();
-
-    // don't automatically transition back to LP state after next REF
-    pwrStatePostRefresh = PWR_IDLE;
-}
-
-bool
-DRAMInterface::Rank::isQueueEmpty() const
-{
-    // check commmands in Q based on current bus direction
-    bool no_queued_cmds = (dram.ctrl->inReadBusState(true) &&
-                          (readEntries == 0))
-                       || (dram.ctrl->inWriteBusState(true) &&
-                          (writeEntries == 0));
-    return no_queued_cmds;
-}
-
-void
-DRAMInterface::Rank::checkDrainDone()
-{
-    // if this rank was waiting to drain it is now able to proceed to
-    // precharge
-    if (refreshState == REF_DRAIN) {
-        DPRINTF(DRAM, "Refresh drain done, now precharging\n");
-
-        refreshState = REF_PD_EXIT;
-
-        // hand control back to the refresh event loop
-        schedule(refreshEvent, curTick());
-    }
-}
-
-void
-DRAMInterface::Rank::flushCmdList()
-{
-    // at the moment sort the list of commands and update the counters
-    // for DRAMPower libray when doing a refresh
-    sort(cmdList.begin(), cmdList.end(), DRAMInterface::sortTime);
-
-    auto next_iter = cmdList.begin();
-    // push to commands to DRAMPower
-    for ( ; next_iter != cmdList.end() ; ++next_iter) {
-         Command cmd = *next_iter;
-         if (cmd.timeStamp <= curTick()) {
-             // Move all commands at or before curTick to DRAMPower
-             power.powerlib.doCommand(cmd.type, cmd.bank,
-                                      divCeil(cmd.timeStamp, dram.tCK) -
-                                      dram.timeStampOffset);
-         } else {
-             // done - found all commands at or before curTick()
-             // next_iter references the 1st command after curTick
-             break;
-         }
-    }
-    // reset cmdList to only contain commands after curTick
-    // if there are no commands after curTick, updated cmdList will be empty
-    // in this case, next_iter is cmdList.end()
-    cmdList.assign(next_iter, cmdList.end());
-}
-
-void
-DRAMInterface::Rank::processActivateEvent()
-{
-    // we should transition to the active state as soon as any bank is active
-    if (pwrState != PWR_ACT)
-        // note that at this point numBanksActive could be back at
-        // zero again due to a precharge scheduled in the future
-        schedulePowerEvent(PWR_ACT, curTick());
-}
-
-void
-DRAMInterface::Rank::processPrechargeEvent()
-{
-    // counter should at least indicate one outstanding request
-    // for this precharge
-    assert(outstandingEvents > 0);
-    // precharge complete, decrement count
-    --outstandingEvents;
-
-    // if we reached zero, then special conditions apply as we track
-    // if all banks are precharged for the power models
-    if (numBanksActive == 0) {
-        // no reads to this rank in the Q and no pending
-        // RD/WR or refresh commands
-        if (isQueueEmpty() && outstandingEvents == 0 &&
-            dram.enableDRAMPowerdown) {
-            // should still be in ACT state since bank still open
-            assert(pwrState == PWR_ACT);
-
-            // All banks closed - switch to precharge power down state.
-            DPRINTF(DRAMState, "Rank %d sleep at tick %d\n",
-                    rank, curTick());
-            powerDownSleep(PWR_PRE_PDN, curTick());
-        } else {
-            // we should transition to the idle state when the last bank
-            // is precharged
-            schedulePowerEvent(PWR_IDLE, curTick());
-        }
-    }
-}
-
-void
-DRAMInterface::Rank::processWriteDoneEvent()
-{
-    // counter should at least indicate one outstanding request
-    // for this write
-    assert(outstandingEvents > 0);
-    // Write transfer on bus has completed
-    // decrement per rank counter
-    --outstandingEvents;
-}
-
-void
-DRAMInterface::Rank::processRefreshEvent()
-{
-    // when first preparing the refresh, remember when it was due
-    if ((refreshState == REF_IDLE) || (refreshState == REF_SREF_EXIT)) {
-        // remember when the refresh is due
-        refreshDueAt = curTick();
-
-        // proceed to drain
-        refreshState = REF_DRAIN;
-
-        // make nonzero while refresh is pending to ensure
-        // power down and self-refresh are not entered
-        ++outstandingEvents;
-
-        DPRINTF(DRAM, "Refresh due\n");
-    }
-
-    // let any scheduled read or write to the same rank go ahead,
-    // after which it will
-    // hand control back to this event loop
-    if (refreshState == REF_DRAIN) {
-        // if a request is at the moment being handled and this request is
-        // accessing the current rank then wait for it to finish
-        if ((rank == dram.activeRank)
-            && (dram.ctrl->requestEventScheduled())) {
-            // hand control over to the request loop until it is
-            // evaluated next
-            DPRINTF(DRAM, "Refresh awaiting draining\n");
-
-            return;
-        } else {
-            refreshState = REF_PD_EXIT;
-        }
-    }
-
-    // at this point, ensure that rank is not in a power-down state
-    if (refreshState == REF_PD_EXIT) {
-        // if rank was sleeping and we have't started exit process,
-        // wake-up for refresh
-        if (inLowPowerState) {
-            DPRINTF(DRAM, "Wake Up for refresh\n");
-            // save state and return after refresh completes
-            scheduleWakeUpEvent(dram.tXP);
-            return;
-        } else {
-            refreshState = REF_PRE;
-        }
-    }
-
-    // at this point, ensure that all banks are precharged
-    if (refreshState == REF_PRE) {
-        // precharge any active bank
-        if (numBanksActive != 0) {
-            // at the moment, we use a precharge all even if there is
-            // only a single bank open
-            DPRINTF(DRAM, "Precharging all\n");
-
-            // first determine when we can precharge
-            Tick pre_at = curTick();
-
-            for (auto &b : banks) {
-                // respect both causality and any existing bank
-                // constraints, some banks could already have a
-                // (auto) precharge scheduled
-                pre_at = std::max(b.preAllowedAt, pre_at);
-            }
-
-            // make sure all banks per rank are precharged, and for those that
-            // already are, update their availability
-            Tick act_allowed_at = pre_at + dram.tRP;
-
-            for (auto &b : banks) {
-                if (b.openRow != Bank::NO_ROW) {
-                    dram.prechargeBank(*this, b, pre_at, true, false);
-                } else {
-                    b.actAllowedAt = std::max(b.actAllowedAt, act_allowed_at);
-                    b.preAllowedAt = std::max(b.preAllowedAt, pre_at);
-                }
-            }
-
-            // precharge all banks in rank
-            cmdList.push_back(Command(MemCommand::PREA, 0, pre_at));
-
-            DPRINTF(DRAMPower, "%llu,PREA,0,%d\n",
-                    divCeil(pre_at, dram.tCK) -
-                            dram.timeStampOffset, rank);
-        } else if ((pwrState == PWR_IDLE) && (outstandingEvents == 1))  {
-            // Banks are closed, have transitioned to IDLE state, and
-            // no outstanding ACT,RD/WR,Auto-PRE sequence scheduled
-            DPRINTF(DRAM, "All banks already precharged, starting refresh\n");
-
-            // go ahead and kick the power state machine into gear since
-            // we are already idle
-            schedulePowerEvent(PWR_REF, curTick());
-        } else {
-            // banks state is closed but haven't transitioned pwrState to IDLE
-            // or have outstanding ACT,RD/WR,Auto-PRE sequence scheduled
-            // should have outstanding precharge or read response event
-            assert(prechargeEvent.scheduled() ||
-                   dram.ctrl->respondEventScheduled());
-            // will start refresh when pwrState transitions to IDLE
-        }
-
-        assert(numBanksActive == 0);
-
-        // wait for all banks to be precharged or read to complete
-        // When precharge commands are done, power state machine will
-        // transition to the idle state, and automatically move to a
-        // refresh, at that point it will also call this method to get
-        // the refresh event loop going again
-        // Similarly, when read response completes, if all banks are
-        // precharged, will call this method to get loop re-started
-        return;
-    }
-
-    // last but not least we perform the actual refresh
-    if (refreshState == REF_START) {
-        // should never get here with any banks active
-        assert(numBanksActive == 0);
-        assert(pwrState == PWR_REF);
-
-        Tick ref_done_at = curTick() + dram.tRFC;
-
-        for (auto &b : banks) {
-            b.actAllowedAt = ref_done_at;
-        }
-
-        // at the moment this affects all ranks
-        cmdList.push_back(Command(MemCommand::REF, 0, curTick()));
-
-        // Update the stats
-        updatePowerStats();
-
-        DPRINTF(DRAMPower, "%llu,REF,0,%d\n", divCeil(curTick(), dram.tCK) -
-                dram.timeStampOffset, rank);
-
-        // Update for next refresh
-        refreshDueAt += dram.tREFI;
-
-        // make sure we did not wait so long that we cannot make up
-        // for it
-        if (refreshDueAt < ref_done_at) {
-            fatal("Refresh was delayed so long we cannot catch up\n");
-        }
-
-        // Run the refresh and schedule event to transition power states
-        // when refresh completes
-        refreshState = REF_RUN;
-        schedule(refreshEvent, ref_done_at);
-        return;
-    }
-
-    if (refreshState == REF_RUN) {
-        // should never get here with any banks active
-        assert(numBanksActive == 0);
-        assert(pwrState == PWR_REF);
-
-        assert(!powerEvent.scheduled());
-
-        if ((dram.ctrl->drainState() == DrainState::Draining) ||
-            (dram.ctrl->drainState() == DrainState::Drained)) {
-            // if draining, do not re-enter low-power mode.
-            // simply go to IDLE and wait
-            schedulePowerEvent(PWR_IDLE, curTick());
-        } else {
-            // At the moment, we sleep when the refresh ends and wait to be
-            // woken up again if previously in a low-power state.
-            if (pwrStatePostRefresh != PWR_IDLE) {
-                // power State should be power Refresh
-                assert(pwrState == PWR_REF);
-                DPRINTF(DRAMState, "Rank %d sleeping after refresh and was in "
-                        "power state %d before refreshing\n", rank,
-                        pwrStatePostRefresh);
-                powerDownSleep(pwrState, curTick());
-
-            // Force PRE power-down if there are no outstanding commands
-            // in Q after refresh.
-            } else if (isQueueEmpty() && dram.enableDRAMPowerdown) {
-                // still have refresh event outstanding but there should
-                // be no other events outstanding
-                assert(outstandingEvents == 1);
-                DPRINTF(DRAMState, "Rank %d sleeping after refresh but was NOT"
-                        " in a low power state before refreshing\n", rank);
-                powerDownSleep(PWR_PRE_PDN, curTick());
-
-            } else {
-                // move to the idle power state once the refresh is done, this
-                // will also move the refresh state machine to the refresh
-                // idle state
-                schedulePowerEvent(PWR_IDLE, curTick());
-            }
-        }
-
-        // At this point, we have completed the current refresh.
-        // In the SREF bypass case, we do not get to this state in the
-        // refresh STM and therefore can always schedule next event.
-        // Compensate for the delay in actually performing the refresh
-        // when scheduling the next one
-        schedule(refreshEvent, refreshDueAt - dram.tRP);
-
-        DPRINTF(DRAMState, "Refresh done at %llu and next refresh"
-                " at %llu\n", curTick(), refreshDueAt);
-    }
-}
-
-void
-DRAMInterface::Rank::schedulePowerEvent(PowerState pwr_state, Tick tick)
-{
-    // respect causality
-    assert(tick >= curTick());
-
-    if (!powerEvent.scheduled()) {
-        DPRINTF(DRAMState, "Scheduling power event at %llu to state %d\n",
-                tick, pwr_state);
-
-        // insert the new transition
-        pwrStateTrans = pwr_state;
-
-        schedule(powerEvent, tick);
-    } else {
-        panic("Scheduled power event at %llu to state %d, "
-              "with scheduled event at %llu to %d\n", tick, pwr_state,
-              powerEvent.when(), pwrStateTrans);
-    }
-}
-
-void
-DRAMInterface::Rank::powerDownSleep(PowerState pwr_state, Tick tick)
-{
-    // if low power state is active low, schedule to active low power state.
-    // in reality tCKE is needed to enter active low power. This is neglected
-    // here and could be added in the future.
-    if (pwr_state == PWR_ACT_PDN) {
-        schedulePowerEvent(pwr_state, tick);
-        // push command to DRAMPower
-        cmdList.push_back(Command(MemCommand::PDN_F_ACT, 0, tick));
-        DPRINTF(DRAMPower, "%llu,PDN_F_ACT,0,%d\n", divCeil(tick,
-                dram.tCK) - dram.timeStampOffset, rank);
-    } else if (pwr_state == PWR_PRE_PDN) {
-        // if low power state is precharge low, schedule to precharge low
-        // power state. In reality tCKE is needed to enter active low power.
-        // This is neglected here.
-        schedulePowerEvent(pwr_state, tick);
-        //push Command to DRAMPower
-        cmdList.push_back(Command(MemCommand::PDN_F_PRE, 0, tick));
-        DPRINTF(DRAMPower, "%llu,PDN_F_PRE,0,%d\n", divCeil(tick,
-                dram.tCK) - dram.timeStampOffset, rank);
-    } else if (pwr_state == PWR_REF) {
-        // if a refresh just occurred
-        // transition to PRE_PDN now that all banks are closed
-        // precharge power down requires tCKE to enter. For simplicity
-        // this is not considered.
-        schedulePowerEvent(PWR_PRE_PDN, tick);
-        //push Command to DRAMPower
-        cmdList.push_back(Command(MemCommand::PDN_F_PRE, 0, tick));
-        DPRINTF(DRAMPower, "%llu,PDN_F_PRE,0,%d\n", divCeil(tick,
-                dram.tCK) - dram.timeStampOffset, rank);
-    } else if (pwr_state == PWR_SREF) {
-        // should only enter SREF after PRE-PD wakeup to do a refresh
-        assert(pwrStatePostRefresh == PWR_PRE_PDN);
-        // self refresh requires time tCKESR to enter. For simplicity,
-        // this is not considered.
-        schedulePowerEvent(PWR_SREF, tick);
-        // push Command to DRAMPower
-        cmdList.push_back(Command(MemCommand::SREN, 0, tick));
-        DPRINTF(DRAMPower, "%llu,SREN,0,%d\n", divCeil(tick,
-                dram.tCK) - dram.timeStampOffset, rank);
-    }
-    // Ensure that we don't power-down and back up in same tick
-    // Once we commit to PD entry, do it and wait for at least 1tCK
-    // This could be replaced with tCKE if/when that is added to the model
-    wakeUpAllowedAt = tick + dram.tCK;
-
-    // Transitioning to a low power state, set flag
-    inLowPowerState = true;
-}
-
-void
-DRAMInterface::Rank::scheduleWakeUpEvent(Tick exit_delay)
-{
-    Tick wake_up_tick = std::max(curTick(), wakeUpAllowedAt);
-
-    DPRINTF(DRAMState, "Scheduling wake-up for rank %d at tick %d\n",
-            rank, wake_up_tick);
-
-    // if waking for refresh, hold previous state
-    // else reset state back to IDLE
-    if (refreshState == REF_PD_EXIT) {
-        pwrStatePostRefresh = pwrState;
-    } else {
-        // don't automatically transition back to LP state after next REF
-        pwrStatePostRefresh = PWR_IDLE;
-    }
-
-    // schedule wake-up with event to ensure entry has completed before
-    // we try to wake-up
-    schedule(wakeUpEvent, wake_up_tick);
-
-    for (auto &b : banks) {
-        // respect both causality and any existing bank
-        // constraints, some banks could already have a
-        // (auto) precharge scheduled
-        b.wrAllowedAt = std::max(wake_up_tick + exit_delay, b.wrAllowedAt);
-        b.rdAllowedAt = std::max(wake_up_tick + exit_delay, b.rdAllowedAt);
-        b.preAllowedAt = std::max(wake_up_tick + exit_delay, b.preAllowedAt);
-        b.actAllowedAt = std::max(wake_up_tick + exit_delay, b.actAllowedAt);
-    }
-    // Transitioning out of low power state, clear flag
-    inLowPowerState = false;
-
-    // push to DRAMPower
-    // use pwrStateTrans for cases where we have a power event scheduled
-    // to enter low power that has not yet been processed
-    if (pwrStateTrans == PWR_ACT_PDN) {
-        cmdList.push_back(Command(MemCommand::PUP_ACT, 0, wake_up_tick));
-        DPRINTF(DRAMPower, "%llu,PUP_ACT,0,%d\n", divCeil(wake_up_tick,
-                dram.tCK) - dram.timeStampOffset, rank);
-
-    } else if (pwrStateTrans == PWR_PRE_PDN) {
-        cmdList.push_back(Command(MemCommand::PUP_PRE, 0, wake_up_tick));
-        DPRINTF(DRAMPower, "%llu,PUP_PRE,0,%d\n", divCeil(wake_up_tick,
-                dram.tCK) - dram.timeStampOffset, rank);
-    } else if (pwrStateTrans == PWR_SREF) {
-        cmdList.push_back(Command(MemCommand::SREX, 0, wake_up_tick));
-        DPRINTF(DRAMPower, "%llu,SREX,0,%d\n", divCeil(wake_up_tick,
-                dram.tCK) - dram.timeStampOffset, rank);
-    }
-}
-
-void
-DRAMInterface::Rank::processWakeUpEvent()
-{
-    // Should be in a power-down or self-refresh state
-    assert((pwrState == PWR_ACT_PDN) || (pwrState == PWR_PRE_PDN) ||
-           (pwrState == PWR_SREF));
-
-    // Check current state to determine transition state
-    if (pwrState == PWR_ACT_PDN) {
-        // banks still open, transition to PWR_ACT
-        schedulePowerEvent(PWR_ACT, curTick());
-    } else {
-        // transitioning from a precharge power-down or self-refresh state
-        // banks are closed - transition to PWR_IDLE
-        schedulePowerEvent(PWR_IDLE, curTick());
-    }
-}
-
-void
-DRAMInterface::Rank::processPowerEvent()
-{
-    assert(curTick() >= pwrStateTick);
-    // remember where we were, and for how long
-    Tick duration = curTick() - pwrStateTick;
-    PowerState prev_state = pwrState;
-
-    // update the accounting
-    stats.pwrStateTime[prev_state] += duration;
-
-    // track to total idle time
-    if ((prev_state == PWR_PRE_PDN) || (prev_state == PWR_ACT_PDN) ||
-        (prev_state == PWR_SREF)) {
-        stats.totalIdleTime += duration;
-    }
-
-    pwrState = pwrStateTrans;
-    pwrStateTick = curTick();
-
-    // if rank was refreshing, make sure to start scheduling requests again
-    if (prev_state == PWR_REF) {
-        // bus IDLED prior to REF
-        // counter should be one for refresh command only
-        assert(outstandingEvents == 1);
-        // REF complete, decrement count and go back to IDLE
-        --outstandingEvents;
-        refreshState = REF_IDLE;
-
-        DPRINTF(DRAMState, "Was refreshing for %llu ticks\n", duration);
-        // if moving back to power-down after refresh
-        if (pwrState != PWR_IDLE) {
-            assert(pwrState == PWR_PRE_PDN);
-            DPRINTF(DRAMState, "Switching to power down state after refreshing"
-                    " rank %d at %llu tick\n", rank, curTick());
-        }
-
-        // completed refresh event, ensure next request is scheduled
-        if (!dram.ctrl->requestEventScheduled()) {
-            DPRINTF(DRAM, "Scheduling next request after refreshing"
-                           " rank %d\n", rank);
-            dram.ctrl->restartScheduler(curTick());
-        }
-    }
-
-    if ((pwrState == PWR_ACT) && (refreshState == REF_PD_EXIT)) {
-        // have exited ACT PD
-        assert(prev_state == PWR_ACT_PDN);
-
-        // go back to REF event and close banks
-        refreshState = REF_PRE;
-        schedule(refreshEvent, curTick());
-    } else if (pwrState == PWR_IDLE) {
-        DPRINTF(DRAMState, "All banks precharged\n");
-        if (prev_state == PWR_SREF) {
-            // set refresh state to REF_SREF_EXIT, ensuring inRefIdleState
-            // continues to return false during tXS after SREF exit
-            // Schedule a refresh which kicks things back into action
-            // when it finishes
-            refreshState = REF_SREF_EXIT;
-            schedule(refreshEvent, curTick() + dram.tXS);
-        } else {
-            // if we have a pending refresh, and are now moving to
-            // the idle state, directly transition to, or schedule refresh
-            if ((refreshState == REF_PRE) || (refreshState == REF_PD_EXIT)) {
-                // ensure refresh is restarted only after final PRE command.
-                // do not restart refresh if controller is in an intermediate
-                // state, after PRE_PDN exit, when banks are IDLE but an
-                // ACT is scheduled.
-                if (!activateEvent.scheduled()) {
-                    // there should be nothing waiting at this point
-                    assert(!powerEvent.scheduled());
-                    if (refreshState == REF_PD_EXIT) {
-                        // exiting PRE PD, will be in IDLE until tXP expires
-                        // and then should transition to PWR_REF state
-                        assert(prev_state == PWR_PRE_PDN);
-                        schedulePowerEvent(PWR_REF, curTick() + dram.tXP);
-                    } else if (refreshState == REF_PRE) {
-                        // can directly move to PWR_REF state and proceed below
-                        pwrState = PWR_REF;
-                    }
-                } else {
-                    // must have PRE scheduled to transition back to IDLE
-                    // and re-kick off refresh
-                    assert(prechargeEvent.scheduled());
-                }
-            }
-        }
-    }
-
-    // transition to the refresh state and re-start refresh process
-    // refresh state machine will schedule the next power state transition
-    if (pwrState == PWR_REF) {
-        // completed final PRE for refresh or exiting power-down
-        assert(refreshState == REF_PRE || refreshState == REF_PD_EXIT);
-
-        // exited PRE PD for refresh, with no pending commands
-        // bypass auto-refresh and go straight to SREF, where memory
-        // will issue refresh immediately upon entry
-        if (pwrStatePostRefresh == PWR_PRE_PDN && isQueueEmpty() &&
-           (dram.ctrl->drainState() != DrainState::Draining) &&
-           (dram.ctrl->drainState() != DrainState::Drained) &&
-           dram.enableDRAMPowerdown) {
-            DPRINTF(DRAMState, "Rank %d bypassing refresh and transitioning "
-                    "to self refresh at %11u tick\n", rank, curTick());
-            powerDownSleep(PWR_SREF, curTick());
-
-            // Since refresh was bypassed, remove event by decrementing count
-            assert(outstandingEvents == 1);
-            --outstandingEvents;
-
-            // reset state back to IDLE temporarily until SREF is entered
-            pwrState = PWR_IDLE;
-
-        // Not bypassing refresh for SREF entry
-        } else {
-            DPRINTF(DRAMState, "Refreshing\n");
-
-            // there should be nothing waiting at this point
-            assert(!powerEvent.scheduled());
-
-            // kick the refresh event loop into action again, and that
-            // in turn will schedule a transition to the idle power
-            // state once the refresh is done
-            schedule(refreshEvent, curTick());
-
-            // Banks transitioned to IDLE, start REF
-            refreshState = REF_START;
-        }
-    }
-
-}
-
-void
-DRAMInterface::Rank::updatePowerStats()
-{
-    // All commands up to refresh have completed
-    // flush cmdList to DRAMPower
-    flushCmdList();
-
-    // Call the function that calculates window energy at intermediate update
-    // events like at refresh, stats dump as well as at simulation exit.
-    // Window starts at the last time the calcWindowEnergy function was called
-    // and is upto current time.
-    power.powerlib.calcWindowEnergy(divCeil(curTick(), dram.tCK) -
-                                    dram.timeStampOffset);
-
-    // Get the energy from DRAMPower
-    Data::MemoryPowerModel::Energy energy = power.powerlib.getEnergy();
-
-    // The energy components inside the power lib are calculated over
-    // the window so accumulate into the corresponding gem5 stat
-    stats.actEnergy += energy.act_energy * dram.devicesPerRank;
-    stats.preEnergy += energy.pre_energy * dram.devicesPerRank;
-    stats.readEnergy += energy.read_energy * dram.devicesPerRank;
-    stats.writeEnergy += energy.write_energy * dram.devicesPerRank;
-    stats.refreshEnergy += energy.ref_energy * dram.devicesPerRank;
-    stats.actBackEnergy += energy.act_stdby_energy * dram.devicesPerRank;
-    stats.preBackEnergy += energy.pre_stdby_energy * dram.devicesPerRank;
-    stats.actPowerDownEnergy += energy.f_act_pd_energy * dram.devicesPerRank;
-    stats.prePowerDownEnergy += energy.f_pre_pd_energy * dram.devicesPerRank;
-    stats.selfRefreshEnergy += energy.sref_energy * dram.devicesPerRank;
-
-    // Accumulate window energy into the total energy.
-    stats.totalEnergy += energy.window_energy * dram.devicesPerRank;
-    // Average power must not be accumulated but calculated over the time
-    // since last stats reset. sim_clock::Frequency is tick period not tick
-    // frequency.
-    //              energy (pJ)     1e-9
-    // power (mW) = ----------- * ----------
-    //              time (tick)   tick_frequency
-    stats.averagePower = (stats.totalEnergy.value() /
-                    (curTick() - dram.lastStatsResetTick)) *
-                    (sim_clock::Frequency / 1000000000.0);
-}
-
-void
-DRAMInterface::Rank::computeStats()
-{
-    DPRINTF(DRAM,"Computing stats due to a dump callback\n");
-
-    // Update the stats
-    updatePowerStats();
-
-    // final update of power state times
-    stats.pwrStateTime[pwrState] += (curTick() - pwrStateTick);
-    pwrStateTick = curTick();
-}
-
-void
-DRAMInterface::Rank::resetStats() {
-    // The only way to clear the counters in DRAMPower is to call
-    // calcWindowEnergy function as that then calls clearCounters. The
-    // clearCounters method itself is private.
-    power.powerlib.calcWindowEnergy(divCeil(curTick(), dram.tCK) -
-                                    dram.timeStampOffset);
-
-}
-
-bool
-DRAMInterface::Rank::forceSelfRefreshExit() const {
-    return (readEntries != 0) ||
-           (dram.ctrl->inWriteBusState(true) && (writeEntries != 0));
-}
-
-void
-DRAMInterface::DRAMStats::resetStats()
-{
-    dram.lastStatsResetTick = curTick();
-}
-
-DRAMInterface::DRAMStats::DRAMStats(DRAMInterface &_dram)
-    : statistics::Group(&_dram),
-    dram(_dram),
-
-    ADD_STAT(readBursts, statistics::units::Count::get(),
-             "Number of DRAM read bursts"),
-    ADD_STAT(writeBursts, statistics::units::Count::get(),
-             "Number of DRAM write bursts"),
-
-    ADD_STAT(perBankRdBursts, statistics::units::Count::get(),
-             "Per bank write bursts"),
-    ADD_STAT(perBankWrBursts, statistics::units::Count::get(),
-             "Per bank write bursts"),
-
-    ADD_STAT(totQLat, statistics::units::Tick::get(), "Total ticks spent queuing"),
-    ADD_STAT(totBusLat, statistics::units::Tick::get(),
-             "Total ticks spent in databus transfers"),
-    ADD_STAT(totMemAccLat, statistics::units::Tick::get(),
-             "Total ticks spent from burst creation until serviced "
-             "by the DRAM"),
-
-    ADD_STAT(avgQLat, statistics::units::Rate<
-                statistics::units::Tick, statistics::units::Count>::get(),
-             "Average queueing delay per DRAM burst"),
-    ADD_STAT(avgBusLat, statistics::units::Rate<
-                statistics::units::Tick, statistics::units::Count>::get(),
-             "Average bus latency per DRAM burst"),
-    ADD_STAT(avgMemAccLat, statistics::units::Rate<
-                statistics::units::Tick, statistics::units::Count>::get(),
-             "Average memory access latency per DRAM burst"),
-
-    ADD_STAT(readRowHits, statistics::units::Count::get(),
-             "Number of row buffer hits during reads"),
-    ADD_STAT(writeRowHits, statistics::units::Count::get(),
-             "Number of row buffer hits during writes"),
-    ADD_STAT(readRowHitRate, statistics::units::Ratio::get(),
-             "Row buffer hit rate for reads"),
-    ADD_STAT(writeRowHitRate, statistics::units::Ratio::get(),
-             "Row buffer hit rate for writes"),
-
-    ADD_STAT(bytesPerActivate, statistics::units::Byte::get(),
-             "Bytes accessed per row activation"),
-
-    ADD_STAT(bytesRead, statistics::units::Byte::get(),
-            "Total bytes read"),
-    ADD_STAT(bytesWritten, statistics::units::Byte::get(),
-            "Total bytes written"),
-
-    ADD_STAT(avgRdBW, statistics::units::Rate<
-                statistics::units::Byte, statistics::units::Second>::get(),
-             "Average DRAM read bandwidth in MiBytes/s"),
-    ADD_STAT(avgWrBW, statistics::units::Rate<
-                statistics::units::Byte, statistics::units::Second>::get(),
-             "Average DRAM write bandwidth in MiBytes/s"),
-    ADD_STAT(peakBW,  statistics::units::Rate<
-                statistics::units::Byte, statistics::units::Second>::get(),
-             "Theoretical peak bandwidth in MiByte/s"),
-
-    ADD_STAT(busUtil, statistics::units::Ratio::get(),
-             "Data bus utilization in percentage"),
-    ADD_STAT(busUtilRead, statistics::units::Ratio::get(),
-             "Data bus utilization in percentage for reads"),
-    ADD_STAT(busUtilWrite, statistics::units::Ratio::get(),
-             "Data bus utilization in percentage for writes"),
-
-    ADD_STAT(pageHitRate, statistics::units::Ratio::get(),
-             "Row buffer hit rate, read and write combined")
-
-{
-}
-
-void
-DRAMInterface::DRAMStats::regStats()
-{
-    using namespace statistics;
-
-    avgQLat.precision(2);
-    avgBusLat.precision(2);
-    avgMemAccLat.precision(2);
-
-    readRowHitRate.precision(2);
-    writeRowHitRate.precision(2);
-
-    perBankRdBursts.init(dram.banksPerRank * dram.ranksPerChannel);
-    perBankWrBursts.init(dram.banksPerRank * dram.ranksPerChannel);
-
-    bytesPerActivate
-        .init(dram.maxAccessesPerRow ?
-              dram.maxAccessesPerRow : dram.rowBufferSize)
-        .flags(nozero);
-
-    peakBW.precision(2);
-    busUtil.precision(2);
-    busUtilWrite.precision(2);
-    busUtilRead.precision(2);
-
-    pageHitRate.precision(2);
-
-    // Formula stats
-    avgQLat = totQLat / readBursts;
-    avgBusLat = totBusLat / readBursts;
-    avgMemAccLat = totMemAccLat / readBursts;
-
-    readRowHitRate = (readRowHits / readBursts) * 100;
-    writeRowHitRate = (writeRowHits / writeBursts) * 100;
-
-    avgRdBW = (bytesRead / 1000000) / simSeconds;
-    avgWrBW = (bytesWritten / 1000000) / simSeconds;
-    peakBW = (sim_clock::Frequency / dram.burstDelay()) *
-              dram.bytesPerBurst() / 1000000;
-
-    busUtil = (avgRdBW + avgWrBW) / peakBW * 100;
-    busUtilRead = avgRdBW / peakBW * 100;
-    busUtilWrite = avgWrBW / peakBW * 100;
-
-    pageHitRate = (writeRowHits + readRowHits) /
-        (writeBursts + readBursts) * 100;
-}
-
-DRAMInterface::RankStats::RankStats(DRAMInterface &_dram, Rank &_rank)
-    : statistics::Group(&_dram, csprintf("rank%d", _rank.rank).c_str()),
-    rank(_rank),
-
-    ADD_STAT(actEnergy, statistics::units::Joule::get(),
-             "Energy for activate commands per rank (pJ)"),
-    ADD_STAT(preEnergy, statistics::units::Joule::get(),
-             "Energy for precharge commands per rank (pJ)"),
-    ADD_STAT(readEnergy, statistics::units::Joule::get(),
-             "Energy for read commands per rank (pJ)"),
-    ADD_STAT(writeEnergy, statistics::units::Joule::get(),
-             "Energy for write commands per rank (pJ)"),
-    ADD_STAT(refreshEnergy, statistics::units::Joule::get(),
-             "Energy for refresh commands per rank (pJ)"),
-    ADD_STAT(actBackEnergy, statistics::units::Joule::get(),
-             "Energy for active background per rank (pJ)"),
-    ADD_STAT(preBackEnergy, statistics::units::Joule::get(),
-             "Energy for precharge background per rank (pJ)"),
-    ADD_STAT(actPowerDownEnergy, statistics::units::Joule::get(),
-             "Energy for active power-down per rank (pJ)"),
-    ADD_STAT(prePowerDownEnergy, statistics::units::Joule::get(),
-             "Energy for precharge power-down per rank (pJ)"),
-    ADD_STAT(selfRefreshEnergy, statistics::units::Joule::get(),
-             "Energy for self refresh per rank (pJ)"),
-
-    ADD_STAT(totalEnergy, statistics::units::Joule::get(),
-             "Total energy per rank (pJ)"),
-    ADD_STAT(averagePower, statistics::units::Watt::get(),
-             "Core power per rank (mW)"),
-
-    ADD_STAT(totalIdleTime, statistics::units::Tick::get(),
-             "Total Idle time Per DRAM Rank"),
-    ADD_STAT(pwrStateTime, statistics::units::Tick::get(),
-             "Time in different power states")
-{
-}
-
-void
-DRAMInterface::RankStats::regStats()
-{
-    statistics::Group::regStats();
-
-    pwrStateTime
-        .init(6)
-        .subname(0, "IDLE")
-        .subname(1, "REF")
-        .subname(2, "SREF")
-        .subname(3, "PRE_PDN")
-        .subname(4, "ACT")
-        .subname(5, "ACT_PDN");
-}
-
-void
-DRAMInterface::RankStats::resetStats()
-{
-    statistics::Group::resetStats();
-
-    rank.resetStats();
-}
-
-void
-DRAMInterface::RankStats::preDumpStats()
-{
-    statistics::Group::preDumpStats();
-
-    rank.computeStats();
-}
-
-NVMInterface::NVMInterface(const NVMInterfaceParams &_p)
-    : MemInterface(_p),
-      maxPendingWrites(_p.max_pending_writes),
-      maxPendingReads(_p.max_pending_reads),
-      twoCycleRdWr(_p.two_cycle_rdwr),
-      tREAD(_p.tREAD), tWRITE(_p.tWRITE), tSEND(_p.tSEND),
-      stats(*this),
-      writeRespondEvent([this]{ processWriteRespondEvent(); }, name()),
-      readReadyEvent([this]{ processReadReadyEvent(); }, name()),
-      nextReadAt(0), numPendingReads(0), numReadDataReady(0),
-      numReadsToIssue(0), numWritesQueued(0)
-{
-    DPRINTF(NVM, "Setting up NVM Interface\n");
-
-    fatal_if(!isPowerOf2(burstSize), "NVM burst size %d is not allowed, "
-             "must be a power of two\n", burstSize);
-
-    // sanity check the ranks since we rely on bit slicing for the
-    // address decoding
-    fatal_if(!isPowerOf2(ranksPerChannel), "NVM rank count of %d is "
-             "not allowed, must be a power of two\n", ranksPerChannel);
-
-    for (int i =0; i < ranksPerChannel; i++) {
-        // Add NVM ranks to the system
-        DPRINTF(NVM, "Creating NVM rank %d \n", i);
-        Rank* rank = new Rank(_p, i, *this);
-        ranks.push_back(rank);
-    }
-
-    uint64_t capacity = 1ULL << ceilLog2(AbstractMemory::size());
-
-    DPRINTF(NVM, "NVM capacity %lld (%lld) bytes\n", capacity,
-            AbstractMemory::size());
-
-    rowsPerBank = capacity / (rowBufferSize *
-                    banksPerRank * ranksPerChannel);
-
-}
-
-NVMInterface::Rank::Rank(const NVMInterfaceParams &_p,
-                         int _rank, NVMInterface& _nvm)
-    : EventManager(&_nvm), rank(_rank), banks(_p.banks_per_rank)
-{
-    for (int b = 0; b < _p.banks_per_rank; b++) {
-        banks[b].bank = b;
-        // No bank groups; simply assign to bank number
-        banks[b].bankgr = b;
-    }
-}
-
-void
-NVMInterface::init()
-{
-    AbstractMemory::init();
-}
-
-void NVMInterface::setupRank(const uint8_t rank, const bool is_read)
-{
-    if (is_read) {
-        // increment count to trigger read and track number of reads in Q
-        numReadsToIssue++;
-    } else {
-        // increment count to track number of writes in Q
-        numWritesQueued++;
-    }
-}
-
-std::pair<MemPacketQueue::iterator, Tick>
-NVMInterface::chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const
-{
-    // remember if we found a hit, but one that cannit issue seamlessly
-    bool found_prepped_pkt = false;
-
-    auto selected_pkt_it = queue.end();
-    Tick selected_col_at = MaxTick;
-
-    for (auto i = queue.begin(); i != queue.end() ; ++i) {
-        MemPacket* pkt = *i;
-
-        // select optimal NVM packet in Q
-        if (!pkt->isDram()) {
-            const Bank& bank = ranks[pkt->rank]->banks[pkt->bank];
-            const Tick col_allowed_at = pkt->isRead() ? bank.rdAllowedAt :
-                                                        bank.wrAllowedAt;
-
-            // check if rank is not doing a refresh and thus is available,
-            // if not, jump to the next packet
-            if (burstReady(pkt)) {
-                DPRINTF(NVM, "%s bank %d - Rank %d available\n", __func__,
-                        pkt->bank, pkt->rank);
-
-                // no additional rank-to-rank or media delays
-                if (col_allowed_at <= min_col_at) {
-                    // FCFS within entries that can issue without
-                    // additional delay, such as same rank accesses
-                    // or media delay requirements
-                    selected_pkt_it = i;
-                    selected_col_at = col_allowed_at;
-                    // no need to look through the remaining queue entries
-                    DPRINTF(NVM, "%s Seamless buffer hit\n", __func__);
-                    break;
-                } else if (!found_prepped_pkt) {
-                    // packet is to prepped region but cannnot issue
-                    // seamlessly; remember this one and continue
-                    selected_pkt_it = i;
-                    selected_col_at = col_allowed_at;
-                    DPRINTF(NVM, "%s Prepped packet found \n", __func__);
-                    found_prepped_pkt = true;
-                }
-            } else {
-                DPRINTF(NVM, "%s bank %d - Rank %d not available\n", __func__,
-                        pkt->bank, pkt->rank);
-            }
-        }
-    }
-
-    if (selected_pkt_it == queue.end()) {
-        DPRINTF(NVM, "%s no available NVM ranks found\n", __func__);
-    }
-
-    return std::make_pair(selected_pkt_it, selected_col_at);
-}
-
-void
-NVMInterface::chooseRead(MemPacketQueue& queue)
-{
-    Tick cmd_at = std::max(curTick(), nextReadAt);
-
-    // This method does the arbitration between non-deterministic read
-    // requests to NVM. The chosen packet is not removed from the queue
-    // at this time. Removal from the queue will occur when the data is
-    // ready and a separate SEND command is issued to retrieve it via the
-    // chooseNext function in the top-level controller.
-    assert(!queue.empty());
-
-    assert(numReadsToIssue > 0);
-    numReadsToIssue--;
-    // For simplicity, issue non-deterministic reads in order (fcfs)
-    for (auto i = queue.begin(); i != queue.end() ; ++i) {
-        MemPacket* pkt = *i;
-
-        // Find 1st NVM read packet that hasn't issued read command
-        if (pkt->readyTime == MaxTick && !pkt->isDram() && pkt->isRead()) {
-           // get the bank
-           Bank& bank_ref = ranks[pkt->rank]->banks[pkt->bank];
-
-            // issueing a read, inc counter and verify we haven't overrun
-            numPendingReads++;
-            assert(numPendingReads <= maxPendingReads);
-
-            // increment the bytes accessed and the accesses per row
-            bank_ref.bytesAccessed += burstSize;
-
-            // Verify command bandiwth to issue
-            // Host can issue read immediately uith buffering closer
-            // to the NVM. The actual execution at the NVM may be delayed
-            // due to busy resources
-            if (twoCycleRdWr) {
-                cmd_at = ctrl->verifyMultiCmd(cmd_at,
-                                              maxCommandsPerWindow, tCK);
-            } else {
-                cmd_at = ctrl->verifySingleCmd(cmd_at,
-                                               maxCommandsPerWindow);
-            }
-
-            // Update delay to next read
-            // Ensures single read command issued per cycle
-            nextReadAt = cmd_at + tCK;
-
-            // If accessing a new location in this bank, update timing
-            // and stats
-            if (bank_ref.openRow != pkt->row) {
-                // update the open bank, re-using row field
-                bank_ref.openRow = pkt->row;
-
-                // sample the bytes accessed to a buffer in this bank
-                // here when we are re-buffering the data
-                stats.bytesPerBank.sample(bank_ref.bytesAccessed);
-                // start counting anew
-                bank_ref.bytesAccessed = 0;
-
-                // holdoff next command to this bank until the read completes
-                // and the data has been successfully buffered
-                // can pipeline accesses to the same bank, sending them
-                // across the interface B2B, but will incur full access
-                // delay between data ready responses to different buffers
-                // in a bank
-                bank_ref.actAllowedAt = std::max(cmd_at,
-                                        bank_ref.actAllowedAt) + tREAD;
-            }
-            // update per packet readyTime to holdoff burst read operation
-            // overloading readyTime, which will be updated again when the
-            // burst is issued
-            pkt->readyTime = std::max(cmd_at, bank_ref.actAllowedAt);
-
-            DPRINTF(NVM, "Issuing NVM Read to bank %d at tick %d. "
-                         "Data ready at %d\n",
-                         bank_ref.bank, cmd_at, pkt->readyTime);
-
-            // Insert into read ready queue. It will be handled after
-            // the media delay has been met
-            if (readReadyQueue.empty()) {
-                assert(!readReadyEvent.scheduled());
-                schedule(readReadyEvent, pkt->readyTime);
-            } else if (readReadyEvent.when() > pkt->readyTime) {
-                // move it sooner in time, to the first read with data
-                reschedule(readReadyEvent, pkt->readyTime);
-            } else {
-                assert(readReadyEvent.scheduled());
-            }
-            readReadyQueue.push_back(pkt->readyTime);
-
-            // found an NVM read to issue - break out
-            break;
-        }
-    }
-}
-
-void
-NVMInterface::processReadReadyEvent()
-{
-    // signal that there is read data ready to be transmitted
-    numReadDataReady++;
-
-    DPRINTF(NVM,
-            "processReadReadyEvent(): Data for an NVM read is ready. "
-            "numReadDataReady is %d\t numPendingReads is %d\n",
-             numReadDataReady, numPendingReads);
-
-    // Find lowest ready time and verify it is equal to curTick
-    // also find the next lowest to schedule next event
-    // Done with this response, erase entry
-    auto ready_it = readReadyQueue.begin();
-    Tick next_ready_at = MaxTick;
-    for (auto i = readReadyQueue.begin(); i != readReadyQueue.end() ; ++i) {
-        if (*ready_it > *i) {
-            next_ready_at = *ready_it;
-            ready_it = i;
-        } else if ((next_ready_at > *i) && (i != ready_it)) {
-            next_ready_at = *i;
-        }
-    }
-
-    // Verify we found the time of this event and remove it
-    assert(*ready_it == curTick());
-    readReadyQueue.erase(ready_it);
-
-    if (!readReadyQueue.empty()) {
-        assert(readReadyQueue.front() >= curTick());
-        assert(!readReadyEvent.scheduled());
-        schedule(readReadyEvent, next_ready_at);
-    }
-
-    // It is possible that a new command kicks things back into
-    // action before reaching this point but need to ensure that we
-    // continue to process new commands as read data becomes ready
-    // This will also trigger a drain if needed
-    if (!ctrl->requestEventScheduled()) {
-        DPRINTF(NVM, "Restart controller scheduler immediately\n");
-        ctrl->restartScheduler(curTick());
-    }
-}
-
-bool
-NVMInterface::burstReady(MemPacket* pkt) const {
-    bool read_rdy =  pkt->isRead() && (ctrl->inReadBusState(true)) &&
-               (pkt->readyTime <= curTick()) && (numReadDataReady > 0);
-    bool write_rdy =  !pkt->isRead() && !ctrl->inReadBusState(true) &&
-                !writeRespQueueFull();
-    return (read_rdy || write_rdy);
-}
-
-    std::pair<Tick, Tick>
-NVMInterface::doBurstAccess(MemPacket* pkt, Tick next_burst_at)
-{
-    DPRINTF(NVM, "NVM Timing access to addr %#x, rank/bank/row %d %d %d\n",
-            pkt->addr, pkt->rank, pkt->bank, pkt->row);
-
-    // get the bank
-    Bank& bank_ref = ranks[pkt->rank]->banks[pkt->bank];
-
-    // respect any constraints on the command
-    const Tick bst_allowed_at = pkt->isRead() ?
-                                bank_ref.rdAllowedAt : bank_ref.wrAllowedAt;
-
-    // we need to wait until the bus is available before we can issue
-    // the command; need minimum of tBURST between commands
-    Tick cmd_at = std::max(bst_allowed_at, curTick());
-
-    // we need to wait until the bus is available before we can issue
-    // the command; need minimum of tBURST between commands
-    cmd_at = std::max(cmd_at, next_burst_at);
-
-    // Verify there is command bandwidth to issue
-    // Read burst (send command) is a simple data access and only requires
-    // one command cycle
-    // Write command may require multiple cycles to enable larger address space
-    if (pkt->isRead() || !twoCycleRdWr) {
-        cmd_at = ctrl->verifySingleCmd(cmd_at, maxCommandsPerWindow);
-    } else {
-        cmd_at = ctrl->verifyMultiCmd(cmd_at, maxCommandsPerWindow, tCK);
-    }
-    // update the packet ready time to reflect when data will be transferred
-    // Use the same bus delays defined for NVM
-    pkt->readyTime = cmd_at + tSEND + tBURST;
-
-    Tick dly_to_rd_cmd;
-    Tick dly_to_wr_cmd;
-    for (auto n : ranks) {
-        for (int i = 0; i < banksPerRank; i++) {
-            // base delay is a function of tBURST and bus turnaround
-            dly_to_rd_cmd = pkt->isRead() ? tBURST : writeToReadDelay();
-            dly_to_wr_cmd = pkt->isRead() ? readToWriteDelay() : tBURST;
-
-            if (pkt->rank != n->rank) {
-                // adjust timing for different ranks
-                // Need to account for rank-to-rank switching with tCS
-                dly_to_wr_cmd = rankToRankDelay();
-                dly_to_rd_cmd = rankToRankDelay();
-            }
-            n->banks[i].rdAllowedAt = std::max(cmd_at + dly_to_rd_cmd,
-                                      n->banks[i].rdAllowedAt);
-
-            n->banks[i].wrAllowedAt = std::max(cmd_at + dly_to_wr_cmd,
-                                      n->banks[i].wrAllowedAt);
-        }
-    }
-
-    DPRINTF(NVM, "NVM Access to %#x, ready at %lld.\n",
-            pkt->addr, pkt->readyTime);
-
-    if (pkt->isRead()) {
-        // completed the read, decrement counters
-        assert(numPendingReads != 0);
-        assert(numReadDataReady != 0);
-
-        numPendingReads--;
-        numReadDataReady--;
-    } else {
-        // Adjust number of NVM writes in Q
-        assert(numWritesQueued > 0);
-        numWritesQueued--;
-
-        // increment the bytes accessed and the accesses per row
-        // only increment for writes as the reads are handled when
-        // the non-deterministic read is issued, before the data transfer
-        bank_ref.bytesAccessed += burstSize;
-
-        // Commands will be issued serially when accessing the same bank
-        // Commands can issue in parallel to different banks
-        if ((bank_ref.bank == pkt->bank) &&
-            (bank_ref.openRow != pkt->row)) {
-           // update the open buffer, re-using row field
-           bank_ref.openRow = pkt->row;
-
-           // sample the bytes accessed to a buffer in this bank
-           // here when we are re-buffering the data
-           stats.bytesPerBank.sample(bank_ref.bytesAccessed);
-           // start counting anew
-           bank_ref.bytesAccessed = 0;
-        }
-
-        // Determine when write will actually complete, assuming it is
-        // scheduled to push to NVM immediately
-        // update actAllowedAt to serialize next command completion that
-        // accesses this bank; must wait until this write completes
-        // Data accesses to the same buffer in this bank
-        // can issue immediately after actAllowedAt expires, without
-        // waiting additional delay of tWRITE. Can revisit this
-        // assumption/simplification in the future.
-        bank_ref.actAllowedAt = std::max(pkt->readyTime,
-                                bank_ref.actAllowedAt) + tWRITE;
-
-        // Need to track number of outstanding writes to
-        // ensure 'buffer' on media controller does not overflow
-        assert(!writeRespQueueFull());
-
-        // Insert into write done queue. It will be handled after
-        // the media delay has been met
-        if (writeRespQueueEmpty()) {
-            assert(!writeRespondEvent.scheduled());
-            schedule(writeRespondEvent, bank_ref.actAllowedAt);
-        } else {
-            assert(writeRespondEvent.scheduled());
-        }
-        writeRespQueue.push_back(bank_ref.actAllowedAt);
-        writeRespQueue.sort();
-        if (writeRespondEvent.when() > bank_ref.actAllowedAt) {
-            DPRINTF(NVM, "Rescheduled respond event from %lld to %11d\n",
-                writeRespondEvent.when(), bank_ref.actAllowedAt);
-            DPRINTF(NVM, "Front of response queue is %11d\n",
-                writeRespQueue.front());
-            reschedule(writeRespondEvent, bank_ref.actAllowedAt);
-        }
-
-    }
-
-    // Update the stats
-    if (pkt->isRead()) {
-        stats.readBursts++;
-        stats.bytesRead += burstSize;
-        stats.perBankRdBursts[pkt->bankId]++;
-        stats.pendingReads.sample(numPendingReads);
-
-        // Update latency stats
-        stats.totMemAccLat += pkt->readyTime - pkt->entryTime;
-        stats.totBusLat += tBURST;
-        stats.totQLat += cmd_at - pkt->entryTime;
-    } else {
-        stats.writeBursts++;
-        stats.bytesWritten += burstSize;
-        stats.perBankWrBursts[pkt->bankId]++;
-    }
-
-    return std::make_pair(cmd_at, cmd_at + tBURST);
-}
-
-void
-NVMInterface::processWriteRespondEvent()
-{
-    DPRINTF(NVM,
-            "processWriteRespondEvent(): A NVM write reached its readyTime.  "
-            "%d remaining pending NVM writes\n", writeRespQueue.size());
-
-    // Update stat to track histogram of pending writes
-    stats.pendingWrites.sample(writeRespQueue.size());
-
-    // Done with this response, pop entry
-    writeRespQueue.pop_front();
-
-    if (!writeRespQueue.empty()) {
-        assert(writeRespQueue.front() >= curTick());
-        assert(!writeRespondEvent.scheduled());
-        schedule(writeRespondEvent, writeRespQueue.front());
-    }
-
-    // It is possible that a new command kicks things back into
-    // action before reaching this point but need to ensure that we
-    // continue to process new commands as writes complete at the media and
-    // credits become available. This will also trigger a drain if needed
-    if (!ctrl->requestEventScheduled()) {
-        DPRINTF(NVM, "Restart controller scheduler immediately\n");
-        ctrl->restartScheduler(curTick());
-    }
-}
-
-void
-NVMInterface::addRankToRankDelay(Tick cmd_at)
-{
-    // update timing for NVM ranks due to bursts issued
-    // to ranks for other media interfaces
-    for (auto n : ranks) {
-        for (int i = 0; i < banksPerRank; i++) {
-            // different rank by default
-            // Need to only account for rank-to-rank switching
-            n->banks[i].rdAllowedAt = std::max(cmd_at + rankToRankDelay(),
-                                             n->banks[i].rdAllowedAt);
-            n->banks[i].wrAllowedAt = std::max(cmd_at + rankToRankDelay(),
-                                             n->banks[i].wrAllowedAt);
-        }
-    }
-}
-
-bool
-NVMInterface::isBusy(bool read_queue_empty, bool all_writes_nvm)
-{
-     DPRINTF(NVM,"isBusy: numReadDataReady = %d\n", numReadDataReady);
-     // Determine NVM is busy and cannot issue a burst
-     // A read burst cannot issue when data is not ready from the NVM
-     // Also check that we have reads queued to ensure we can change
-     // bus direction to service potential write commands.
-     // A write cannot issue once we've reached MAX pending writes
-     // Only assert busy for the write case when there are also
-     // no reads in Q and the write queue only contains NVM commands
-     // This allows the bus state to switch and service reads
-     return (ctrl->inReadBusState(true) ?
-                 (numReadDataReady == 0) && !read_queue_empty :
-                 writeRespQueueFull() && read_queue_empty &&
-                                         all_writes_nvm);
-}
-
-
-NVMInterface::NVMStats::NVMStats(NVMInterface &_nvm)
-    : statistics::Group(&_nvm),
-    nvm(_nvm),
-
-    ADD_STAT(readBursts, statistics::units::Count::get(),
-             "Number of NVM read bursts"),
-    ADD_STAT(writeBursts, statistics::units::Count::get(),
-             "Number of NVM write bursts"),
-
-    ADD_STAT(perBankRdBursts, statistics::units::Count::get(),
-             "Per bank write bursts"),
-    ADD_STAT(perBankWrBursts, statistics::units::Count::get(),
-             "Per bank write bursts"),
-
-    ADD_STAT(totQLat, statistics::units::Tick::get(), "Total ticks spent queuing"),
-    ADD_STAT(totBusLat, statistics::units::Tick::get(),
-             "Total ticks spent in databus transfers"),
-    ADD_STAT(totMemAccLat, statistics::units::Tick::get(),
-             "Total ticks spent from burst creation until serviced "
-             "by the NVM"),
-    ADD_STAT(avgQLat, statistics::units::Rate<
-                statistics::units::Tick, statistics::units::Count>::get(),
-             "Average queueing delay per NVM burst"),
-    ADD_STAT(avgBusLat, statistics::units::Rate<
-                statistics::units::Tick, statistics::units::Count>::get(),
-             "Average bus latency per NVM burst"),
-    ADD_STAT(avgMemAccLat, statistics::units::Rate<
-                statistics::units::Tick, statistics::units::Count>::get(),
-             "Average memory access latency per NVM burst"),
-
-    ADD_STAT(avgRdBW, statistics::units::Rate<
-                statistics::units::Byte, statistics::units::Second>::get(),
-             "Average DRAM read bandwidth in MiBytes/s"),
-    ADD_STAT(avgWrBW, statistics::units::Rate<
-                statistics::units::Byte, statistics::units::Second>::get(),
-             "Average DRAM write bandwidth in MiBytes/s"),
-    ADD_STAT(peakBW, statistics::units::Rate<
-                statistics::units::Byte, statistics::units::Second>::get(),
-             "Theoretical peak bandwidth in MiByte/s"),
-    ADD_STAT(busUtil, statistics::units::Ratio::get(),
-             "NVM Data bus utilization in percentage"),
-    ADD_STAT(busUtilRead, statistics::units::Ratio::get(),
-             "NVM Data bus read utilization in percentage"),
-    ADD_STAT(busUtilWrite, statistics::units::Ratio::get(),
-             "NVM Data bus write utilization in percentage"),
-
-    ADD_STAT(pendingReads, statistics::units::Count::get(),
-             "Reads issued to NVM for which data has not been transferred"),
-    ADD_STAT(pendingWrites, statistics::units::Count::get(),
-             "Number of outstanding writes to NVM"),
-    ADD_STAT(bytesPerBank, statistics::units::Byte::get(),
-             "Bytes read within a bank before loading new bank")
-
-{
-}
-
-void
-NVMInterface::NVMStats::regStats()
-{
-    using namespace statistics;
-
-    perBankRdBursts.init(nvm.ranksPerChannel == 0 ? 1 :
-              nvm.banksPerRank * nvm.ranksPerChannel);
-
-    perBankWrBursts.init(nvm.ranksPerChannel == 0 ? 1 :
-              nvm.banksPerRank * nvm.ranksPerChannel);
-
-    avgQLat.precision(2);
-    avgBusLat.precision(2);
-    avgMemAccLat.precision(2);
-
-    avgRdBW.precision(2);
-    avgWrBW.precision(2);
-    peakBW.precision(2);
-
-    busUtil.precision(2);
-    busUtilRead.precision(2);
-    busUtilWrite.precision(2);
-
-    pendingReads
-        .init(nvm.maxPendingReads)
-        .flags(nozero);
-
-    pendingWrites
-        .init(nvm.maxPendingWrites)
-        .flags(nozero);
-
-    bytesPerBank
-        .init(nvm.rowBufferSize)
-        .flags(nozero);
-
-    avgQLat = totQLat / readBursts;
-    avgBusLat = totBusLat / readBursts;
-    avgMemAccLat = totMemAccLat / readBursts;
-
-    avgRdBW = (bytesRead / 1000000) / simSeconds;
-    avgWrBW = (bytesWritten / 1000000) / simSeconds;
-    peakBW = (sim_clock::Frequency / nvm.tBURST) *
-              nvm.burstSize / 1000000;
-
-    busUtil = (avgRdBW + avgWrBW) / peakBW * 100;
-    busUtilRead = avgRdBW / peakBW * 100;
-    busUtilWrite = avgWrBW / peakBW * 100;
+    // setting the pseudo channel number for this interface
+    pseudoChannel = pseudo_channel;
 }
 
 } // namespace memory
diff --git a/src/mem/mem_interface.hh b/src/mem/mem_interface.hh
index a71f84f..8d6f4fe 100644
--- a/src/mem/mem_interface.hh
+++ b/src/mem/mem_interface.hh
@@ -57,11 +57,8 @@
 #include "enums/AddrMap.hh"
 #include "enums/PageManage.hh"
 #include "mem/abstract_mem.hh"
-#include "mem/drampower.hh"
 #include "mem/mem_ctrl.hh"
-#include "params/DRAMInterface.hh"
 #include "params/MemInterface.hh"
-#include "params/NVMInterface.hh"
 #include "sim/eventq.hh"
 
 namespace gem5
@@ -112,7 +109,7 @@
     };
 
     /**
-     * A pointer to the parent MemCtrl instance
+     * A pointer to the parent memory controller instance
      */
     MemCtrl* ctrl;
 
@@ -168,7 +165,6 @@
      */
     Tick rankToRankDelay() const { return tBURST + tCS; }
 
-
   public:
 
     /**
@@ -180,13 +176,33 @@
     const uint32_t readBufferSize;
     const uint32_t writeBufferSize;
 
+    /**
+     * NVM specific variable, but declaring it here allows
+     * treating different interfaces in a more genral way
+     * at the memory controller's end
+     */
+    uint32_t numWritesQueued;
+
+    /**
+     * Till when the controller must wait before issuing next RD/WR burst?
+     */
+    Tick nextBurstAt = 0;
+    Tick nextReqTime = 0;
+
+    /**
+     * pseudo channel number used for HBM modeling
+     */
+    uint8_t pseudoChannel;
+
     /** Set a pointer to the controller and initialize
      * interface based on controller parameters
      * @param _ctrl pointer to the parent controller
      * @param command_window size of command window used to
      *                       check command bandwidth
+     *  @param pseudo_channel pseudo channel number
      */
-    void setCtrl(MemCtrl* _ctrl, unsigned int command_window);
+    void setCtrl(MemCtrl* _ctrl, unsigned int command_window,
+                                    uint8_t pseudo_channel = 0);
 
     /**
      * Get an address in a dense range which starts from 0. The input
@@ -280,11 +296,16 @@
      * @param pkt_addr The starting address of the packet
      * @param size The size of the packet in bytes
      * @param is_read Is the request for a read or a write to memory
-     * @param is_dram Is the request to a DRAM interface
+     * @param pseudo_channel pseudo channel number of the packet
      * @return A MemPacket pointer with the decoded information
      */
-    MemPacket* decodePacket(const PacketPtr pkt, Addr pkt_addr,
-                           unsigned int size, bool is_read, bool is_dram);
+    virtual MemPacket* decodePacket(const PacketPtr pkt, Addr pkt_addr,
+                           unsigned int size, bool is_read,
+                           uint8_t pseudo_channel = 0)
+    {
+        panic("MemInterface decodePacket should not be executed from here.\n");
+        return nullptr;
+    }
 
     /**
      *  Add rank to rank delay to bus timing to all banks in all ranks
@@ -295,976 +316,84 @@
      */
     virtual void addRankToRankDelay(Tick cmd_at) = 0;
 
+    /**
+     * This function checks if ranks are busy.
+     */
+    virtual bool isBusy(bool read_queue_empty, bool all_writes_nvm) = 0;
+
+    /**
+     * This function performs the burst and update stats.
+     */
+    virtual std::pair<Tick, Tick>
+    doBurstAccess(MemPacket* mem_pkt, Tick next_burst_at,
+                  const std::vector<MemPacketQueue>& queue) = 0;
+
+    /**
+     * This function is DRAM specific.
+     */
+    virtual void respondEvent(uint8_t rank)
+    {
+        panic("MemInterface respondEvent should not be executed from here.\n");
+    };
+
+    /**
+     * This function is DRAM specific.
+     */
+    virtual void checkRefreshState(uint8_t rank)
+    {
+        panic("MemInterface checkRefreshState (DRAM) should "
+        "not be executed from here.\n");
+    };
+
+    /**
+     * This function is DRAM specific.
+     */
+    virtual void drainRanks()
+    {
+        panic("MemInterface drainRanks (DRAM) should "
+        "not be executed from here.\n");
+    }
+
+    /**
+     * This function is DRAM specific.
+     */
+    virtual void suspend()
+    {
+        panic("MemInterface suspend (DRAM) should "
+        "not be executed from here.\n");
+    }
+
+    /**
+     * This function is NVM specific.
+     */
+    virtual bool readsWaitingToIssue() const
+    {
+        panic("MemInterface readsWaitingToIssue (NVM) "
+        "should not be executed from here.\n");
+    };
+
+    /**
+     * This function is NVM specific.
+     */
+    virtual void chooseRead(MemPacketQueue& queue)
+    {
+        panic("MemInterface chooseRead (NVM) should "
+        "not be executed from here.\n");
+    };
+
+    /**
+     * This function is NVM specific.
+     */
+    virtual bool writeRespQueueFull() const
+    {
+        panic("MemInterface writeRespQueueFull (NVM) "
+        "should not be executed from here.\n");
+    }
+
     typedef MemInterfaceParams Params;
     MemInterface(const Params &_p);
 };
 
-/**
- * Interface to DRAM devices with media specific parameters,
- * statistics, and functions.
- * The DRAMInterface includes a class for individual ranks
- * and per rank functions.
- */
-class DRAMInterface : public MemInterface
-{
-  private:
-    /**
-     * Simple structure to hold the values needed to keep track of
-     * commands for DRAMPower
-     */
-    struct Command
-    {
-       Data::MemCommand::cmds type;
-       uint8_t bank;
-       Tick timeStamp;
-
-       constexpr Command(Data::MemCommand::cmds _type, uint8_t _bank,
-                         Tick time_stamp)
-            : type(_type), bank(_bank), timeStamp(time_stamp)
-        { }
-    };
-
-    /**
-     * The power state captures the different operational states of
-     * the DRAM and interacts with the bus read/write state machine,
-     * and the refresh state machine.
-     *
-     * PWR_IDLE      : The idle state in which all banks are closed
-     *                 From here can transition to:  PWR_REF, PWR_ACT,
-     *                 PWR_PRE_PDN
-     *
-     * PWR_REF       : Auto-refresh state.  Will transition when refresh is
-     *                 complete based on power state prior to PWR_REF
-     *                 From here can transition to:  PWR_IDLE, PWR_PRE_PDN,
-     *                 PWR_SREF
-     *
-     * PWR_SREF      : Self-refresh state.  Entered after refresh if
-     *                 previous state was PWR_PRE_PDN
-     *                 From here can transition to:  PWR_IDLE
-     *
-     * PWR_PRE_PDN   : Precharge power down state
-     *                 From here can transition to:  PWR_REF, PWR_IDLE
-     *
-     * PWR_ACT       : Activate state in which one or more banks are open
-     *                 From here can transition to:  PWR_IDLE, PWR_ACT_PDN
-     *
-     * PWR_ACT_PDN   : Activate power down state
-     *                 From here can transition to:  PWR_ACT
-     */
-    enum PowerState
-    {
-        PWR_IDLE = 0,
-        PWR_REF,
-        PWR_SREF,
-        PWR_PRE_PDN,
-        PWR_ACT,
-        PWR_ACT_PDN
-    };
-
-    /**
-     * The refresh state is used to control the progress of the
-     * refresh scheduling. When normal operation is in progress the
-     * refresh state is idle. Once tREFI has elasped, a refresh event
-     * is triggered to start the following STM transitions which are
-     * used to issue a refresh and return back to normal operation
-     *
-     * REF_IDLE      : IDLE state used during normal operation
-     *                 From here can transition to:  REF_DRAIN
-     *
-     * REF_SREF_EXIT : Exiting a self-refresh; refresh event scheduled
-     *                 after self-refresh exit completes
-     *                 From here can transition to:  REF_DRAIN
-     *
-     * REF_DRAIN     : Drain state in which on going accesses complete.
-     *                 From here can transition to:  REF_PD_EXIT
-     *
-     * REF_PD_EXIT   : Evaluate pwrState and issue wakeup if needed
-     *                 Next state dependent on whether banks are open
-     *                 From here can transition to:  REF_PRE, REF_START
-     *
-     * REF_PRE       : Close (precharge) all open banks
-     *                 From here can transition to:  REF_START
-     *
-     * REF_START     : Issue refresh command and update DRAMPower stats
-     *                 From here can transition to:  REF_RUN
-     *
-     * REF_RUN       : Refresh running, waiting for tRFC to expire
-     *                 From here can transition to:  REF_IDLE, REF_SREF_EXIT
-     */
-    enum RefreshState
-    {
-        REF_IDLE = 0,
-        REF_DRAIN,
-        REF_PD_EXIT,
-        REF_SREF_EXIT,
-        REF_PRE,
-        REF_START,
-        REF_RUN
-    };
-
-    class Rank;
-    struct RankStats : public statistics::Group
-    {
-        RankStats(DRAMInterface &dram, Rank &rank);
-
-        void regStats() override;
-        void resetStats() override;
-        void preDumpStats() override;
-
-        Rank &rank;
-
-        /*
-         * Command energies
-         */
-        statistics::Scalar actEnergy;
-        statistics::Scalar preEnergy;
-        statistics::Scalar readEnergy;
-        statistics::Scalar writeEnergy;
-        statistics::Scalar refreshEnergy;
-
-        /*
-         * Active Background Energy
-         */
-        statistics::Scalar actBackEnergy;
-
-        /*
-         * Precharge Background Energy
-         */
-        statistics::Scalar preBackEnergy;
-
-        /*
-         * Active Power-Down Energy
-         */
-        statistics::Scalar actPowerDownEnergy;
-
-        /*
-         * Precharge Power-Down Energy
-         */
-        statistics::Scalar prePowerDownEnergy;
-
-        /*
-         * self Refresh Energy
-         */
-        statistics::Scalar selfRefreshEnergy;
-
-        statistics::Scalar totalEnergy;
-        statistics::Scalar averagePower;
-
-        /**
-         * Stat to track total DRAM idle time
-         *
-         */
-        statistics::Scalar totalIdleTime;
-
-        /**
-         * Track time spent in each power state.
-         */
-        statistics::Vector pwrStateTime;
-    };
-
-    /**
-     * Rank class includes a vector of banks. Refresh and Power state
-     * machines are defined per rank. Events required to change the
-     * state of the refresh and power state machine are scheduled per
-     * rank. This class allows the implementation of rank-wise refresh
-     * and rank-wise power-down.
-     */
-    class Rank : public EventManager
-    {
-      private:
-
-        /**
-         * A reference to the parent DRAMInterface instance
-         */
-        DRAMInterface& dram;
-
-        /**
-         * Since we are taking decisions out of order, we need to keep
-         * track of what power transition is happening at what time
-         */
-        PowerState pwrStateTrans;
-
-        /**
-         * Previous low-power state, which will be re-entered after refresh.
-         */
-        PowerState pwrStatePostRefresh;
-
-        /**
-         * Track when we transitioned to the current power state
-         */
-        Tick pwrStateTick;
-
-        /**
-         * Keep track of when a refresh is due.
-         */
-        Tick refreshDueAt;
-
-        /**
-         * Function to update Power Stats
-         */
-        void updatePowerStats();
-
-        /**
-         * Schedule a power state transition in the future, and
-         * potentially override an already scheduled transition.
-         *
-         * @param pwr_state Power state to transition to
-         * @param tick Tick when transition should take place
-         */
-        void schedulePowerEvent(PowerState pwr_state, Tick tick);
-
-      public:
-
-        /**
-         * Current power state.
-         */
-        PowerState pwrState;
-
-       /**
-         * current refresh state
-         */
-        RefreshState refreshState;
-
-        /**
-         * rank is in or transitioning to power-down or self-refresh
-         */
-        bool inLowPowerState;
-
-        /**
-         * Current Rank index
-         */
-        uint8_t rank;
-
-       /**
-         * Track number of packets in read queue going to this rank
-         */
-        uint32_t readEntries;
-
-       /**
-         * Track number of packets in write queue going to this rank
-         */
-        uint32_t writeEntries;
-
-        /**
-         * Number of ACT, RD, and WR events currently scheduled
-         * Incremented when a refresh event is started as well
-         * Used to determine when a low-power state can be entered
-         */
-        uint8_t outstandingEvents;
-
-        /**
-         * delay low-power exit until this requirement is met
-         */
-        Tick wakeUpAllowedAt;
-
-        /**
-         * One DRAMPower instance per rank
-         */
-        DRAMPower power;
-
-        /**
-         * List of commands issued, to be sent to DRAMPpower at refresh
-         * and stats dump.  Keep commands here since commands to different
-         * banks are added out of order.  Will only pass commands up to
-         * curTick() to DRAMPower after sorting.
-         */
-        std::vector<Command> cmdList;
-
-        /**
-         * Vector of Banks. Each rank is made of several devices which in
-         * term are made from several banks.
-         */
-        std::vector<Bank> banks;
-
-        /**
-         *  To track number of banks which are currently active for
-         *  this rank.
-         */
-        unsigned int numBanksActive;
-
-        /** List to keep track of activate ticks */
-        std::deque<Tick> actTicks;
-
-        /**
-         * Track when we issued the last read/write burst
-         */
-        Tick lastBurstTick;
-
-        Rank(const DRAMInterfaceParams &_p, int _rank,
-             DRAMInterface& _dram);
-
-        const std::string name() const { return csprintf("%d", rank); }
-
-        /**
-         * Kick off accounting for power and refresh states and
-         * schedule initial refresh.
-         *
-         * @param ref_tick Tick for first refresh
-         */
-        void startup(Tick ref_tick);
-
-        /**
-         * Stop the refresh events.
-         */
-        void suspend();
-
-        /**
-         * Check if there is no refresh and no preparation of refresh ongoing
-         * i.e. the refresh state machine is in idle
-         *
-         * @param Return true if the rank is idle from a refresh point of view
-         */
-        bool inRefIdleState() const { return refreshState == REF_IDLE; }
-
-        /**
-         * Check if the current rank has all banks closed and is not
-         * in a low power state
-         *
-         * @param Return true if the rank is idle from a bank
-         *        and power point of view
-         */
-        bool inPwrIdleState() const { return pwrState == PWR_IDLE; }
-
-        /**
-         * Trigger a self-refresh exit if there are entries enqueued
-         * Exit if there are any read entries regardless of the bus state.
-         * If we are currently issuing write commands, exit if we have any
-         * write commands enqueued as well.
-         * Could expand this in the future to analyze state of entire queue
-         * if needed.
-         *
-         * @return boolean indicating self-refresh exit should be scheduled
-         */
-        bool forceSelfRefreshExit() const;
-
-        /**
-         * Check if the command queue of current rank is idle
-         *
-         * @param Return true if the there are no commands in Q.
-         *                    Bus direction determines queue checked.
-         */
-        bool isQueueEmpty() const;
-
-        /**
-         * Let the rank check if it was waiting for requests to drain
-         * to allow it to transition states.
-         */
-        void checkDrainDone();
-
-        /**
-         * Push command out of cmdList queue that are scheduled at
-         * or before curTick() to DRAMPower library
-         * All commands before curTick are guaranteed to be complete
-         * and can safely be flushed.
-         */
-        void flushCmdList();
-
-        /**
-         * Computes stats just prior to dump event
-         */
-        void computeStats();
-
-        /**
-         * Reset stats on a stats event
-         */
-        void resetStats();
-
-        /**
-         * Schedule a transition to power-down (sleep)
-         *
-         * @param pwr_state Power state to transition to
-         * @param tick Absolute tick when transition should take place
-         */
-        void powerDownSleep(PowerState pwr_state, Tick tick);
-
-       /**
-         * schedule and event to wake-up from power-down or self-refresh
-         * and update bank timing parameters
-         *
-         * @param exit_delay Relative tick defining the delay required between
-         *                   low-power exit and the next command
-         */
-        void scheduleWakeUpEvent(Tick exit_delay);
-
-        void processWriteDoneEvent();
-        EventFunctionWrapper writeDoneEvent;
-
-        void processActivateEvent();
-        EventFunctionWrapper activateEvent;
-
-        void processPrechargeEvent();
-        EventFunctionWrapper prechargeEvent;
-
-        void processRefreshEvent();
-        EventFunctionWrapper refreshEvent;
-
-        void processPowerEvent();
-        EventFunctionWrapper powerEvent;
-
-        void processWakeUpEvent();
-        EventFunctionWrapper wakeUpEvent;
-
-      protected:
-        RankStats stats;
-    };
-
-    /**
-     * Function for sorting Command structures based on timeStamp
-     *
-     * @param a Memory Command
-     * @param next Memory Command
-     * @return true if timeStamp of Command 1 < timeStamp of Command 2
-     */
-    static bool
-    sortTime(const Command& cmd, const Command& cmd_next)
-    {
-        return cmd.timeStamp < cmd_next.timeStamp;
-    }
-
-    /**
-     * DRAM specific device characteristics
-     */
-    const uint32_t bankGroupsPerRank;
-    const bool bankGroupArch;
-
-    /**
-     * DRAM specific timing requirements
-     */
-    const Tick tCL;
-    const Tick tBURST_MIN;
-    const Tick tBURST_MAX;
-    const Tick tCCD_L_WR;
-    const Tick tCCD_L;
-    const Tick tRCD;
-    const Tick tRP;
-    const Tick tRAS;
-    const Tick tWR;
-    const Tick tRTP;
-    const Tick tRFC;
-    const Tick tREFI;
-    const Tick tRRD;
-    const Tick tRRD_L;
-    const Tick tPPD;
-    const Tick tAAD;
-    const Tick tXAW;
-    const Tick tXP;
-    const Tick tXS;
-    const Tick clkResyncDelay;
-    const bool dataClockSync;
-    const bool burstInterleave;
-    const uint8_t twoCycleActivate;
-    const uint32_t activationLimit;
-    const Tick wrToRdDlySameBG;
-    const Tick rdToWrDlySameBG;
-
-
-    enums::PageManage pageMgmt;
-    /**
-     * Max column accesses (read and write) per row, before forefully
-     * closing it.
-     */
-    const uint32_t maxAccessesPerRow;
-
-    // timestamp offset
-    uint64_t timeStampOffset;
-
-    // Holds the value of the DRAM rank of burst issued
-    uint8_t activeRank;
-
-    /** Enable or disable DRAM powerdown states. */
-    bool enableDRAMPowerdown;
-
-    /** The time when stats were last reset used to calculate average power */
-    Tick lastStatsResetTick;
-
-    /**
-     * Keep track of when row activations happen, in order to enforce
-     * the maximum number of activations in the activation window. The
-     * method updates the time that the banks become available based
-     * on the current limits.
-     *
-     * @param rank_ref Reference to the rank
-     * @param bank_ref Reference to the bank
-     * @param act_tick Time when the activation takes place
-     * @param row Index of the row
-     */
-    void activateBank(Rank& rank_ref, Bank& bank_ref, Tick act_tick,
-                      uint32_t row);
-
-    /**
-     * Precharge a given bank and also update when the precharge is
-     * done. This will also deal with any stats related to the
-     * accesses to the open page.
-     *
-     * @param rank_ref The rank to precharge
-     * @param bank_ref The bank to precharge
-     * @param pre_tick Time when the precharge takes place
-     * @param auto_or_preall Is this an auto-precharge or precharge all command
-     * @param trace Is this an auto precharge then do not add to trace
-     */
-    void prechargeBank(Rank& rank_ref, Bank& bank_ref,
-                       Tick pre_tick, bool auto_or_preall = false,
-                       bool trace = true);
-
-    struct DRAMStats : public statistics::Group
-    {
-        DRAMStats(DRAMInterface &dram);
-
-        void regStats() override;
-        void resetStats() override;
-
-        DRAMInterface &dram;
-
-        /** total number of DRAM bursts serviced */
-        statistics::Scalar readBursts;
-        statistics::Scalar writeBursts;
-
-        /** DRAM per bank stats */
-        statistics::Vector perBankRdBursts;
-        statistics::Vector perBankWrBursts;
-
-        // Latencies summed over all requests
-        statistics::Scalar totQLat;
-        statistics::Scalar totBusLat;
-        statistics::Scalar totMemAccLat;
-
-        // Average latencies per request
-        statistics::Formula avgQLat;
-        statistics::Formula avgBusLat;
-        statistics::Formula avgMemAccLat;
-
-        // Row hit count and rate
-        statistics::Scalar readRowHits;
-        statistics::Scalar writeRowHits;
-        statistics::Formula readRowHitRate;
-        statistics::Formula writeRowHitRate;
-        statistics::Histogram bytesPerActivate;
-        // Number of bytes transferred to/from DRAM
-        statistics::Scalar bytesRead;
-        statistics::Scalar bytesWritten;
-
-        // Average bandwidth
-        statistics::Formula avgRdBW;
-        statistics::Formula avgWrBW;
-        statistics::Formula peakBW;
-        // bus utilization
-        statistics::Formula busUtil;
-        statistics::Formula busUtilRead;
-        statistics::Formula busUtilWrite;
-        statistics::Formula pageHitRate;
-    };
-
-    DRAMStats stats;
-
-    /**
-      * Vector of dram ranks
-      */
-    std::vector<Rank*> ranks;
-
-    /*
-     * @return delay between write and read commands
-     */
-    Tick writeToReadDelay() const override { return tBURST + tWTR + tCL; }
-
-    /**
-     * Find which are the earliest banks ready to issue an activate
-     * for the enqueued requests. Assumes maximum of 32 banks per rank
-     * Also checks if the bank is already prepped.
-     *
-     * @param queue Queued requests to consider
-     * @param min_col_at time of seamless burst command
-     * @return One-hot encoded mask of bank indices
-     * @return boolean indicating burst can issue seamlessly, with no gaps
-     */
-    std::pair<std::vector<uint32_t>, bool>
-    minBankPrep(const MemPacketQueue& queue, Tick min_col_at) const;
-
-    /*
-     * @return time to send a burst of data without gaps
-     */
-    Tick
-    burstDelay() const
-    {
-        return (burstInterleave ? tBURST_MAX / 2 : tBURST);
-    }
-
-  public:
-    /**
-     * Initialize the DRAM interface and verify parameters
-     */
-    void init() override;
-
-    /**
-     * Iterate through dram ranks and instantiate per rank startup routine
-     */
-    void startup() override;
-
-    /**
-     * Setup the rank based on packet received
-     *
-     * @param integer value of rank to be setup. used to index ranks vector
-     * @param are we setting up rank for read or write packet?
-     */
-    void setupRank(const uint8_t rank, const bool is_read) override;
-
-    /**
-     * Iterate through dram ranks to exit self-refresh in order to drain
-     */
-    void drainRanks();
-
-    /**
-     * Return true once refresh is complete for all ranks and there are no
-     * additional commands enqueued.  (only evaluated when draining)
-     * This will ensure that all banks are closed, power state is IDLE, and
-     * power stats have been updated
-     *
-     * @return true if all ranks have refreshed, with no commands enqueued
-     *
-     */
-    bool allRanksDrained() const override;
-
-    /**
-     * Iterate through DRAM ranks and suspend them
-     */
-    void suspend();
-
-    /*
-     * @return time to offset next command
-     */
-    Tick commandOffset() const override { return (tRP + tRCD); }
-
-    /*
-     * Function to calulate unloaded, closed bank access latency
-     */
-    Tick accessLatency() const override { return (tRP + tRCD + tCL); }
-
-    /**
-     * For FR-FCFS policy, find first DRAM command that can issue
-     *
-     * @param queue Queued requests to consider
-     * @param min_col_at Minimum tick for 'seamless' issue
-     * @return an iterator to the selected packet, else queue.end()
-     * @return the tick when the packet selected will issue
-     */
-    std::pair<MemPacketQueue::iterator, Tick>
-    chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const override;
-
-    /**
-     * Actually do the burst - figure out the latency it
-     * will take to service the req based on bank state, channel state etc
-     * and then update those states to account for this request. Based
-     * on this, update the packet's "readyTime" and move it to the
-     * response q from where it will eventually go back to the outside
-     * world.
-     *
-     * @param mem_pkt The packet created from the outside world pkt
-     * @param next_burst_at Minimum bus timing requirement from controller
-     * @param queue Reference to the read or write queue with the packet
-     * @return pair, tick when current burst is issued and
-     *               tick when next burst can issue
-     */
-    std::pair<Tick, Tick>
-    doBurstAccess(MemPacket* mem_pkt, Tick next_burst_at,
-                  const std::vector<MemPacketQueue>& queue);
-
-    /**
-     * Check if a burst operation can be issued to the DRAM
-     *
-     * @param Return true if RD/WR can issue
-     *                    This requires the DRAM to be in the
-     *                    REF IDLE state
-     */
-    bool
-    burstReady(MemPacket* pkt) const override
-    {
-        return ranks[pkt->rank]->inRefIdleState();
-    }
-
-    /**
-     * This function checks if ranks are actively refreshing and
-     * therefore busy. The function also checks if ranks are in
-     * the self-refresh state, in which case, a self-refresh exit
-     * is initiated.
-     *
-     * return boolean if all ranks are in refresh and therefore busy
-     */
-    bool isBusy();
-
-    /**
-     *  Add rank to rank delay to bus timing to all DRAM banks in alli ranks
-     *  when access to an alternate interface is issued
-     *
-     *  param cmd_at Time of current command used as starting point for
-     *               addition of rank-to-rank delay
-     */
-    void addRankToRankDelay(Tick cmd_at) override;
-
-    /**
-     * Complete response process for DRAM when read burst is complete
-     * This will update the counters and check if a power down state
-     * can be entered.
-     *
-     * @param rank Specifies rank associated with read burst
-     */
-    void respondEvent(uint8_t rank);
-
-    /**
-     * Check the refresh state to determine if refresh needs
-     * to be kicked back into action after a read response
-     *
-     * @param rank Specifies rank associated with read burst
-     */
-    void checkRefreshState(uint8_t rank);
-
-    DRAMInterface(const DRAMInterfaceParams &_p);
-};
-
-/**
- * Interface to NVM devices with media specific parameters,
- * statistics, and functions.
- * The NVMInterface includes a class for individual ranks
- * and per rank functions.
- */
-class NVMInterface : public MemInterface
-{
-  private:
-    /**
-     * NVM rank class simply includes a vector of banks.
-     */
-    class Rank : public EventManager
-    {
-      public:
-
-        /**
-         * Current Rank index
-         */
-        uint8_t rank;
-
-        /**
-         * Vector of NVM banks. Each rank is made of several banks
-         * that can be accessed in parallel.
-         */
-        std::vector<Bank> banks;
-
-        Rank(const NVMInterfaceParams &_p, int _rank,
-             NVMInterface& _nvm);
-    };
-
-    /**
-     * NVM specific device and channel characteristics
-     */
-    const uint32_t maxPendingWrites;
-    const uint32_t maxPendingReads;
-    const bool twoCycleRdWr;
-
-    /**
-     * NVM specific timing requirements
-     */
-    const Tick tREAD;
-    const Tick tWRITE;
-    const Tick tSEND;
-
-    struct NVMStats : public statistics::Group
-    {
-        NVMStats(NVMInterface &nvm);
-
-        void regStats() override;
-
-        NVMInterface &nvm;
-
-        /** NVM stats */
-        statistics::Scalar readBursts;
-        statistics::Scalar writeBursts;
-
-        statistics::Vector perBankRdBursts;
-        statistics::Vector perBankWrBursts;
-
-        // Latencies summed over all requests
-        statistics::Scalar totQLat;
-        statistics::Scalar totBusLat;
-        statistics::Scalar totMemAccLat;
-
-        // Average latencies per request
-        statistics::Formula avgQLat;
-        statistics::Formula avgBusLat;
-        statistics::Formula avgMemAccLat;
-
-        statistics::Scalar bytesRead;
-        statistics::Scalar bytesWritten;
-
-        // Average bandwidth
-        statistics::Formula avgRdBW;
-        statistics::Formula avgWrBW;
-        statistics::Formula peakBW;
-        statistics::Formula busUtil;
-        statistics::Formula busUtilRead;
-        statistics::Formula busUtilWrite;
-
-        /** NVM stats */
-        statistics::Histogram pendingReads;
-        statistics::Histogram pendingWrites;
-        statistics::Histogram bytesPerBank;
-    };
-    NVMStats stats;
-
-    void processWriteRespondEvent();
-    EventFunctionWrapper writeRespondEvent;
-
-    void processReadReadyEvent();
-    EventFunctionWrapper readReadyEvent;
-
-    /**
-      * Vector of nvm ranks
-      */
-    std::vector<Rank*> ranks;
-
-    /**
-     * Holding queue for non-deterministic write commands, which
-     * maintains writes that have been issued but have not completed
-     * Stored seperately mostly to keep the code clean and help with
-     * events scheduling.
-     * This mimics a buffer on the media controller and therefore is
-     * not added to the main write queue for sizing
-     */
-    std::list<Tick> writeRespQueue;
-
-    std::deque<Tick> readReadyQueue;
-
-    /**
-     * Check if the write response queue is empty
-     *
-     * @param Return true if empty
-     */
-    bool writeRespQueueEmpty() const { return writeRespQueue.empty(); }
-
-    /**
-     * Till when must we wait before issuing next read command?
-     */
-    Tick nextReadAt;
-
-    // keep track of reads that have issued for which data is either
-    // not yet ready or has not yet been transferred to the ctrl
-    uint16_t numPendingReads;
-    uint16_t numReadDataReady;
-
-  public:
-    // keep track of the number of reads that have yet to be issued
-    uint16_t numReadsToIssue;
-
-    // number of writes in the writeQueue for the NVM interface
-    uint32_t numWritesQueued;
-
-    /**
-     * Initialize the NVM interface and verify parameters
-     */
-    void init() override;
-
-    /**
-     * Setup the rank based on packet received
-     *
-     * @param integer value of rank to be setup. used to index ranks vector
-     * @param are we setting up rank for read or write packet?
-     */
-    void setupRank(const uint8_t rank, const bool is_read) override;
-
-    /**
-     * Check drain state of NVM interface
-     *
-     * @return true if write response queue is empty
-     *
-     */
-    bool allRanksDrained() const override { return writeRespQueueEmpty(); }
-
-    /*
-     * @return time to offset next command
-     */
-    Tick commandOffset() const override { return tBURST; }
-
-    /**
-     * Check if a burst operation can be issued to the NVM
-     *
-     * @param Return true if RD/WR can issue
-     *                    for reads, also verfy that ready count
-     *                    has been updated to a non-zero value to
-     *                    account for race conditions between events
-     */
-    bool burstReady(MemPacket* pkt) const override;
-
-    /**
-     * This function checks if ranks are busy.
-     * This state is true when either:
-     * 1) There is no command with read data ready to transmit or
-     * 2) The NVM inteface has reached the maximum number of outstanding
-     *    writes commands.
-     * @param read_queue_empty There are no read queued
-     * @param all_writes_nvm   All writes in queue are for NVM interface
-     * @return true of NVM is busy
-     *
-     */
-    bool isBusy(bool read_queue_empty, bool all_writes_nvm);
-    /**
-     * For FR-FCFS policy, find first NVM command that can issue
-     * default to first command to prepped region
-     *
-     * @param queue Queued requests to consider
-     * @param min_col_at Minimum tick for 'seamless' issue
-     * @return an iterator to the selected packet, else queue.end()
-     * @return the tick when the packet selected will issue
-     */
-    std::pair<MemPacketQueue::iterator, Tick>
-    chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const override;
-
-    /**
-     *  Add rank to rank delay to bus timing to all NVM banks in alli ranks
-     *  when access to an alternate interface is issued
-     *
-     *  param cmd_at Time of current command used as starting point for
-     *               addition of rank-to-rank delay
-     */
-    void addRankToRankDelay(Tick cmd_at) override;
-
-
-    /**
-     * Select read command to issue asynchronously
-     */
-    void chooseRead(MemPacketQueue& queue);
-
-    /*
-     * Function to calulate unloaded access latency
-     */
-    Tick accessLatency() const override { return (tREAD + tSEND); }
-
-    /**
-     * Check if the write response queue has reached defined threshold
-     *
-     * @param Return true if full
-     */
-    bool
-    writeRespQueueFull() const
-    {
-        return writeRespQueue.size() == maxPendingWrites;
-    }
-
-    bool
-    readsWaitingToIssue() const
-    {
-        return ((numReadsToIssue != 0) &&
-                (numPendingReads < maxPendingReads));
-    }
-
-    /**
-     * Actually do the burst and update stats.
-     *
-     * @param pkt The packet created from the outside world pkt
-     * @param next_burst_at Minimum bus timing requirement from controller
-     * @return pair, tick when current burst is issued and
-     *               tick when next burst can issue
-     */
-    std::pair<Tick, Tick>
-    doBurstAccess(MemPacket* pkt, Tick next_burst_at);
-
-    NVMInterface(const NVMInterfaceParams &_p);
-};
 
 } // namespace memory
 } // namespace gem5
diff --git a/src/mem/nvm_interface.cc b/src/mem/nvm_interface.cc
new file mode 100644
index 0000000..b2c4073
--- /dev/null
+++ b/src/mem/nvm_interface.cc
@@ -0,0 +1,729 @@
+/*
+ * Copyright (c) 2010-2020 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2013 Amin Farmahini-Farahani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mem/nvm_interface.hh"
+
+#include "base/bitfield.hh"
+#include "base/cprintf.hh"
+#include "base/trace.hh"
+#include "debug/NVM.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+
+namespace memory
+{
+
+NVMInterface::NVMInterface(const NVMInterfaceParams &_p)
+    : MemInterface(_p),
+      maxPendingWrites(_p.max_pending_writes),
+      maxPendingReads(_p.max_pending_reads),
+      twoCycleRdWr(_p.two_cycle_rdwr),
+      tREAD(_p.tREAD), tWRITE(_p.tWRITE), tSEND(_p.tSEND),
+      stats(*this),
+      writeRespondEvent([this]{ processWriteRespondEvent(); }, name()),
+      readReadyEvent([this]{ processReadReadyEvent(); }, name()),
+      nextReadAt(0), numPendingReads(0), numReadDataReady(0),
+      numReadsToIssue(0)
+{
+    DPRINTF(NVM, "Setting up NVM Interface\n");
+
+    fatal_if(!isPowerOf2(burstSize), "NVM burst size %d is not allowed, "
+             "must be a power of two\n", burstSize);
+
+    // sanity check the ranks since we rely on bit slicing for the
+    // address decoding
+    fatal_if(!isPowerOf2(ranksPerChannel), "NVM rank count of %d is "
+             "not allowed, must be a power of two\n", ranksPerChannel);
+
+    for (int i =0; i < ranksPerChannel; i++) {
+        // Add NVM ranks to the system
+        DPRINTF(NVM, "Creating NVM rank %d \n", i);
+        Rank* rank = new Rank(_p, i, *this);
+        ranks.push_back(rank);
+    }
+
+    uint64_t capacity = 1ULL << ceilLog2(AbstractMemory::size());
+
+    DPRINTF(NVM, "NVM capacity %lld (%lld) bytes\n", capacity,
+            AbstractMemory::size());
+
+    rowsPerBank = capacity / (rowBufferSize *
+                    banksPerRank * ranksPerChannel);
+}
+
+NVMInterface::Rank::Rank(const NVMInterfaceParams &_p,
+                         int _rank, NVMInterface& _nvm)
+    : EventManager(&_nvm), rank(_rank), banks(_p.banks_per_rank)
+{
+    for (int b = 0; b < _p.banks_per_rank; b++) {
+        banks[b].bank = b;
+        // No bank groups; simply assign to bank number
+        banks[b].bankgr = b;
+    }
+}
+
+void
+NVMInterface::init()
+{
+    AbstractMemory::init();
+}
+
+void NVMInterface::setupRank(const uint8_t rank, const bool is_read)
+{
+    if (is_read) {
+        // increment count to trigger read and track number of reads in Q
+        numReadsToIssue++;
+    } else {
+        // increment count to track number of writes in Q
+        numWritesQueued++;
+    }
+}
+
+MemPacket*
+NVMInterface::decodePacket(const PacketPtr pkt, Addr pkt_addr,
+                       unsigned size, bool is_read, uint8_t pseudo_channel)
+{
+    // decode the address based on the address mapping scheme, with
+    // Ro, Ra, Co, Ba and Ch denoting row, rank, column, bank and
+    // channel, respectively
+    uint8_t rank;
+    uint8_t bank;
+    // use a 64-bit unsigned during the computations as the row is
+    // always the top bits, and check before creating the packet
+    uint64_t row;
+
+    // Get packed address, starting at 0
+    Addr addr = getCtrlAddr(pkt_addr);
+
+    // truncate the address to a memory burst, which makes it unique to
+    // a specific buffer, row, bank, rank and channel
+    addr = addr / burstSize;
+
+    // we have removed the lowest order address bits that denote the
+    // position within the column
+    if (addrMapping == enums::RoRaBaChCo || addrMapping == enums::RoRaBaCoCh) {
+        // the lowest order bits denote the column to ensure that
+        // sequential cache lines occupy the same row
+        addr = addr / burstsPerRowBuffer;
+
+        // after the channel bits, get the bank bits to interleave
+        // over the banks
+        bank = addr % banksPerRank;
+        addr = addr / banksPerRank;
+
+        // after the bank, we get the rank bits which thus interleaves
+        // over the ranks
+        rank = addr % ranksPerChannel;
+        addr = addr / ranksPerChannel;
+
+        // lastly, get the row bits, no need to remove them from addr
+        row = addr % rowsPerBank;
+    } else if (addrMapping == enums::RoCoRaBaCh) {
+        // with emerging technologies, could have small page size with
+        // interleaving granularity greater than row buffer
+        if (burstsPerStripe > burstsPerRowBuffer) {
+            // remove column bits which are a subset of burstsPerStripe
+            addr = addr / burstsPerRowBuffer;
+        } else {
+            // remove lower column bits below channel bits
+            addr = addr / burstsPerStripe;
+        }
+
+        // start with the bank bits, as this provides the maximum
+        // opportunity for parallelism between requests
+        bank = addr % banksPerRank;
+        addr = addr / banksPerRank;
+
+        // next get the rank bits
+        rank = addr % ranksPerChannel;
+        addr = addr / ranksPerChannel;
+
+        // next, the higher-order column bites
+        if (burstsPerStripe < burstsPerRowBuffer) {
+            addr = addr / (burstsPerRowBuffer / burstsPerStripe);
+        }
+
+        // lastly, get the row bits, no need to remove them from addr
+        row = addr % rowsPerBank;
+    } else
+        panic("Unknown address mapping policy chosen!");
+
+    assert(rank < ranksPerChannel);
+    assert(bank < banksPerRank);
+    assert(row < rowsPerBank);
+    assert(row < Bank::NO_ROW);
+
+    DPRINTF(NVM, "Address: %#x Rank %d Bank %d Row %d\n",
+            pkt_addr, rank, bank, row);
+
+    // create the corresponding memory packet with the entry time and
+    // ready time set to the current tick, the latter will be updated
+    // later
+    uint16_t bank_id = banksPerRank * rank + bank;
+
+    return new MemPacket(pkt, is_read, false, pseudo_channel, rank, bank, row,
+                   bank_id, pkt_addr, size);
+}
+
+std::pair<MemPacketQueue::iterator, Tick>
+NVMInterface::chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const
+{
+    // remember if we found a hit, but one that cannit issue seamlessly
+    bool found_prepped_pkt = false;
+
+    auto selected_pkt_it = queue.end();
+    Tick selected_col_at = MaxTick;
+
+    for (auto i = queue.begin(); i != queue.end() ; ++i) {
+        MemPacket* pkt = *i;
+
+        // select optimal NVM packet in Q
+        if (!pkt->isDram()) {
+            const Bank& bank = ranks[pkt->rank]->banks[pkt->bank];
+            const Tick col_allowed_at = pkt->isRead() ? bank.rdAllowedAt :
+                                                        bank.wrAllowedAt;
+
+            // check if rank is not doing a refresh and thus is available,
+            // if not, jump to the next packet
+            if (burstReady(pkt)) {
+                DPRINTF(NVM, "%s bank %d - Rank %d available\n", __func__,
+                        pkt->bank, pkt->rank);
+
+                // no additional rank-to-rank or media delays
+                if (col_allowed_at <= min_col_at) {
+                    // FCFS within entries that can issue without
+                    // additional delay, such as same rank accesses
+                    // or media delay requirements
+                    selected_pkt_it = i;
+                    selected_col_at = col_allowed_at;
+                    // no need to look through the remaining queue entries
+                    DPRINTF(NVM, "%s Seamless buffer hit\n", __func__);
+                    break;
+                } else if (!found_prepped_pkt) {
+                    // packet is to prepped region but cannnot issue
+                    // seamlessly; remember this one and continue
+                    selected_pkt_it = i;
+                    selected_col_at = col_allowed_at;
+                    DPRINTF(NVM, "%s Prepped packet found \n", __func__);
+                    found_prepped_pkt = true;
+                }
+            } else {
+                DPRINTF(NVM, "%s bank %d - Rank %d not available\n", __func__,
+                        pkt->bank, pkt->rank);
+            }
+        }
+    }
+
+    if (selected_pkt_it == queue.end()) {
+        DPRINTF(NVM, "%s no available NVM ranks found\n", __func__);
+    }
+
+    return std::make_pair(selected_pkt_it, selected_col_at);
+}
+
+void
+NVMInterface::chooseRead(MemPacketQueue& queue)
+{
+    Tick cmd_at = std::max(curTick(), nextReadAt);
+
+    // This method does the arbitration between non-deterministic read
+    // requests to NVM. The chosen packet is not removed from the queue
+    // at this time. Removal from the queue will occur when the data is
+    // ready and a separate SEND command is issued to retrieve it via the
+    // chooseNext function in the top-level controller.
+    assert(!queue.empty());
+
+    assert(numReadsToIssue > 0);
+    numReadsToIssue--;
+    // For simplicity, issue non-deterministic reads in order (fcfs)
+    for (auto i = queue.begin(); i != queue.end() ; ++i) {
+        MemPacket* pkt = *i;
+
+        // Find 1st NVM read packet that hasn't issued read command
+        if (pkt->readyTime == MaxTick && !pkt->isDram() && pkt->isRead()) {
+           // get the bank
+           Bank& bank_ref = ranks[pkt->rank]->banks[pkt->bank];
+
+            // issueing a read, inc counter and verify we haven't overrun
+            numPendingReads++;
+            assert(numPendingReads <= maxPendingReads);
+
+            // increment the bytes accessed and the accesses per row
+            bank_ref.bytesAccessed += burstSize;
+
+            // Verify command bandiwth to issue
+            // Host can issue read immediately uith buffering closer
+            // to the NVM. The actual execution at the NVM may be delayed
+            // due to busy resources
+            if (twoCycleRdWr) {
+                cmd_at = ctrl->verifyMultiCmd(cmd_at,
+                                              maxCommandsPerWindow, tCK);
+            } else {
+                cmd_at = ctrl->verifySingleCmd(cmd_at,
+                                              maxCommandsPerWindow, false);
+            }
+
+            // Update delay to next read
+            // Ensures single read command issued per cycle
+            nextReadAt = cmd_at + tCK;
+
+            // If accessing a new location in this bank, update timing
+            // and stats
+            if (bank_ref.openRow != pkt->row) {
+                // update the open bank, re-using row field
+                bank_ref.openRow = pkt->row;
+
+                // sample the bytes accessed to a buffer in this bank
+                // here when we are re-buffering the data
+                stats.bytesPerBank.sample(bank_ref.bytesAccessed);
+                // start counting anew
+                bank_ref.bytesAccessed = 0;
+
+                // holdoff next command to this bank until the read completes
+                // and the data has been successfully buffered
+                // can pipeline accesses to the same bank, sending them
+                // across the interface B2B, but will incur full access
+                // delay between data ready responses to different buffers
+                // in a bank
+                bank_ref.actAllowedAt = std::max(cmd_at,
+                                        bank_ref.actAllowedAt) + tREAD;
+            }
+            // update per packet readyTime to holdoff burst read operation
+            // overloading readyTime, which will be updated again when the
+            // burst is issued
+            pkt->readyTime = std::max(cmd_at, bank_ref.actAllowedAt);
+
+            DPRINTF(NVM, "Issuing NVM Read to bank %d at tick %d. "
+                         "Data ready at %d\n",
+                         bank_ref.bank, cmd_at, pkt->readyTime);
+
+            // Insert into read ready queue. It will be handled after
+            // the media delay has been met
+            if (readReadyQueue.empty()) {
+                assert(!readReadyEvent.scheduled());
+                schedule(readReadyEvent, pkt->readyTime);
+            } else if (readReadyEvent.when() > pkt->readyTime) {
+                // move it sooner in time, to the first read with data
+                reschedule(readReadyEvent, pkt->readyTime);
+            } else {
+                assert(readReadyEvent.scheduled());
+            }
+            readReadyQueue.push_back(pkt->readyTime);
+
+            // found an NVM read to issue - break out
+            break;
+        }
+    }
+}
+
+void
+NVMInterface::processReadReadyEvent()
+{
+    // signal that there is read data ready to be transmitted
+    numReadDataReady++;
+
+    DPRINTF(NVM,
+            "processReadReadyEvent(): Data for an NVM read is ready. "
+            "numReadDataReady is %d\t numPendingReads is %d\n",
+             numReadDataReady, numPendingReads);
+
+    // Find lowest ready time and verify it is equal to curTick
+    // also find the next lowest to schedule next event
+    // Done with this response, erase entry
+    auto ready_it = readReadyQueue.begin();
+    Tick next_ready_at = MaxTick;
+    for (auto i = readReadyQueue.begin(); i != readReadyQueue.end() ; ++i) {
+        if (*ready_it > *i) {
+            next_ready_at = *ready_it;
+            ready_it = i;
+        } else if ((next_ready_at > *i) && (i != ready_it)) {
+            next_ready_at = *i;
+        }
+    }
+
+    // Verify we found the time of this event and remove it
+    assert(*ready_it == curTick());
+    readReadyQueue.erase(ready_it);
+
+    if (!readReadyQueue.empty()) {
+        assert(readReadyQueue.front() >= curTick());
+        assert(!readReadyEvent.scheduled());
+        schedule(readReadyEvent, next_ready_at);
+    }
+
+    // It is possible that a new command kicks things back into
+    // action before reaching this point but need to ensure that we
+    // continue to process new commands as read data becomes ready
+    // This will also trigger a drain if needed
+    if (!ctrl->requestEventScheduled()) {
+        DPRINTF(NVM, "Restart controller scheduler immediately\n");
+        ctrl->restartScheduler(curTick());
+    }
+}
+
+bool
+NVMInterface::burstReady(MemPacket* pkt) const {
+    bool read_rdy =  pkt->isRead() && (ctrl->inReadBusState(true)) &&
+               (pkt->readyTime <= curTick()) && (numReadDataReady > 0);
+    bool write_rdy =  !pkt->isRead() && !ctrl->inReadBusState(true) &&
+                !writeRespQueueFull();
+    return (read_rdy || write_rdy);
+}
+
+    std::pair<Tick, Tick>
+NVMInterface::doBurstAccess(MemPacket* pkt, Tick next_burst_at,
+                  const std::vector<MemPacketQueue>& queue)
+{
+    DPRINTF(NVM, "NVM Timing access to addr %#x, rank/bank/row %d %d %d\n",
+            pkt->addr, pkt->rank, pkt->bank, pkt->row);
+
+    // get the bank
+    Bank& bank_ref = ranks[pkt->rank]->banks[pkt->bank];
+
+    // respect any constraints on the command
+    const Tick bst_allowed_at = pkt->isRead() ?
+                                bank_ref.rdAllowedAt : bank_ref.wrAllowedAt;
+
+    // we need to wait until the bus is available before we can issue
+    // the command; need minimum of tBURST between commands
+    Tick cmd_at = std::max(bst_allowed_at, curTick());
+
+    // we need to wait until the bus is available before we can issue
+    // the command; need minimum of tBURST between commands
+    cmd_at = std::max(cmd_at, next_burst_at);
+
+    // Verify there is command bandwidth to issue
+    // Read burst (send command) is a simple data access and only requires
+    // one command cycle
+    // Write command may require multiple cycles to enable larger address space
+    if (pkt->isRead() || !twoCycleRdWr) {
+        cmd_at = ctrl->verifySingleCmd(cmd_at, maxCommandsPerWindow, false);
+    } else {
+        cmd_at = ctrl->verifyMultiCmd(cmd_at, maxCommandsPerWindow, tCK);
+    }
+    // update the packet ready time to reflect when data will be transferred
+    // Use the same bus delays defined for NVM
+    pkt->readyTime = cmd_at + tSEND + tBURST;
+
+    Tick dly_to_rd_cmd;
+    Tick dly_to_wr_cmd;
+    for (auto n : ranks) {
+        for (int i = 0; i < banksPerRank; i++) {
+            // base delay is a function of tBURST and bus turnaround
+            dly_to_rd_cmd = pkt->isRead() ? tBURST : writeToReadDelay();
+            dly_to_wr_cmd = pkt->isRead() ? readToWriteDelay() : tBURST;
+
+            if (pkt->rank != n->rank) {
+                // adjust timing for different ranks
+                // Need to account for rank-to-rank switching with tCS
+                dly_to_wr_cmd = rankToRankDelay();
+                dly_to_rd_cmd = rankToRankDelay();
+            }
+            n->banks[i].rdAllowedAt = std::max(cmd_at + dly_to_rd_cmd,
+                                      n->banks[i].rdAllowedAt);
+
+            n->banks[i].wrAllowedAt = std::max(cmd_at + dly_to_wr_cmd,
+                                      n->banks[i].wrAllowedAt);
+        }
+    }
+
+    DPRINTF(NVM, "NVM Access to %#x, ready at %lld.\n",
+            pkt->addr, pkt->readyTime);
+
+    if (pkt->isRead()) {
+        // completed the read, decrement counters
+        assert(numPendingReads != 0);
+        assert(numReadDataReady != 0);
+
+        numPendingReads--;
+        numReadDataReady--;
+    } else {
+        // Adjust number of NVM writes in Q
+        assert(numWritesQueued > 0);
+        numWritesQueued--;
+
+        // increment the bytes accessed and the accesses per row
+        // only increment for writes as the reads are handled when
+        // the non-deterministic read is issued, before the data transfer
+        bank_ref.bytesAccessed += burstSize;
+
+        // Commands will be issued serially when accessing the same bank
+        // Commands can issue in parallel to different banks
+        if ((bank_ref.bank == pkt->bank) &&
+            (bank_ref.openRow != pkt->row)) {
+           // update the open buffer, re-using row field
+           bank_ref.openRow = pkt->row;
+
+           // sample the bytes accessed to a buffer in this bank
+           // here when we are re-buffering the data
+           stats.bytesPerBank.sample(bank_ref.bytesAccessed);
+           // start counting anew
+           bank_ref.bytesAccessed = 0;
+        }
+
+        // Determine when write will actually complete, assuming it is
+        // scheduled to push to NVM immediately
+        // update actAllowedAt to serialize next command completion that
+        // accesses this bank; must wait until this write completes
+        // Data accesses to the same buffer in this bank
+        // can issue immediately after actAllowedAt expires, without
+        // waiting additional delay of tWRITE. Can revisit this
+        // assumption/simplification in the future.
+        bank_ref.actAllowedAt = std::max(pkt->readyTime,
+                                bank_ref.actAllowedAt) + tWRITE;
+
+        // Need to track number of outstanding writes to
+        // ensure 'buffer' on media controller does not overflow
+        assert(!writeRespQueueFull());
+
+        // Insert into write done queue. It will be handled after
+        // the media delay has been met
+        if (writeRespQueueEmpty()) {
+            assert(!writeRespondEvent.scheduled());
+            schedule(writeRespondEvent, bank_ref.actAllowedAt);
+        } else {
+            assert(writeRespondEvent.scheduled());
+        }
+        writeRespQueue.push_back(bank_ref.actAllowedAt);
+        writeRespQueue.sort();
+        if (writeRespondEvent.when() > bank_ref.actAllowedAt) {
+            DPRINTF(NVM, "Rescheduled respond event from %lld to %11d\n",
+                writeRespondEvent.when(), bank_ref.actAllowedAt);
+            DPRINTF(NVM, "Front of response queue is %11d\n",
+                writeRespQueue.front());
+            reschedule(writeRespondEvent, bank_ref.actAllowedAt);
+        }
+
+    }
+
+    // Update the stats
+    if (pkt->isRead()) {
+        stats.readBursts++;
+        stats.bytesRead += burstSize;
+        stats.perBankRdBursts[pkt->bankId]++;
+        stats.pendingReads.sample(numPendingReads);
+
+        // Update latency stats
+        stats.totMemAccLat += pkt->readyTime - pkt->entryTime;
+        stats.totBusLat += tBURST;
+        stats.totQLat += cmd_at - pkt->entryTime;
+    } else {
+        stats.writeBursts++;
+        stats.bytesWritten += burstSize;
+        stats.perBankWrBursts[pkt->bankId]++;
+    }
+
+    return std::make_pair(cmd_at, cmd_at + tBURST);
+}
+
+void
+NVMInterface::processWriteRespondEvent()
+{
+    DPRINTF(NVM,
+            "processWriteRespondEvent(): A NVM write reached its readyTime.  "
+            "%d remaining pending NVM writes\n", writeRespQueue.size());
+
+    // Update stat to track histogram of pending writes
+    stats.pendingWrites.sample(writeRespQueue.size());
+
+    // Done with this response, pop entry
+    writeRespQueue.pop_front();
+
+    if (!writeRespQueue.empty()) {
+        assert(writeRespQueue.front() >= curTick());
+        assert(!writeRespondEvent.scheduled());
+        schedule(writeRespondEvent, writeRespQueue.front());
+    }
+
+    // It is possible that a new command kicks things back into
+    // action before reaching this point but need to ensure that we
+    // continue to process new commands as writes complete at the media and
+    // credits become available. This will also trigger a drain if needed
+    if (!ctrl->requestEventScheduled()) {
+        DPRINTF(NVM, "Restart controller scheduler immediately\n");
+        ctrl->restartScheduler(curTick());
+    }
+}
+
+void
+NVMInterface::addRankToRankDelay(Tick cmd_at)
+{
+    // update timing for NVM ranks due to bursts issued
+    // to ranks for other media interfaces
+    for (auto n : ranks) {
+        for (int i = 0; i < banksPerRank; i++) {
+            // different rank by default
+            // Need to only account for rank-to-rank switching
+            n->banks[i].rdAllowedAt = std::max(cmd_at + rankToRankDelay(),
+                                             n->banks[i].rdAllowedAt);
+            n->banks[i].wrAllowedAt = std::max(cmd_at + rankToRankDelay(),
+                                             n->banks[i].wrAllowedAt);
+        }
+    }
+}
+
+bool
+NVMInterface::isBusy(bool read_queue_empty, bool all_writes_nvm)
+{
+     DPRINTF(NVM,"isBusy: numReadDataReady = %d\n", numReadDataReady);
+     // Determine NVM is busy and cannot issue a burst
+     // A read burst cannot issue when data is not ready from the NVM
+     // Also check that we have reads queued to ensure we can change
+     // bus direction to service potential write commands.
+     // A write cannot issue once we've reached MAX pending writes
+     // Only assert busy for the write case when there are also
+     // no reads in Q and the write queue only contains NVM commands
+     // This allows the bus state to switch and service reads
+     return (ctrl->inReadBusState(true) ?
+                 (numReadDataReady == 0) && !read_queue_empty :
+                 writeRespQueueFull() && read_queue_empty &&
+                                         all_writes_nvm);
+}
+
+NVMInterface::NVMStats::NVMStats(NVMInterface &_nvm)
+    : statistics::Group(&_nvm),
+    nvm(_nvm),
+
+    ADD_STAT(readBursts, statistics::units::Count::get(),
+             "Number of NVM read bursts"),
+    ADD_STAT(writeBursts, statistics::units::Count::get(),
+             "Number of NVM write bursts"),
+
+    ADD_STAT(perBankRdBursts, statistics::units::Count::get(),
+             "Per bank write bursts"),
+    ADD_STAT(perBankWrBursts, statistics::units::Count::get(),
+             "Per bank write bursts"),
+
+    ADD_STAT(totQLat, statistics::units::Tick::get(),
+             "Total ticks spent queuing"),
+    ADD_STAT(totBusLat, statistics::units::Tick::get(),
+             "Total ticks spent in databus transfers"),
+    ADD_STAT(totMemAccLat, statistics::units::Tick::get(),
+             "Total ticks spent from burst creation until serviced "
+             "by the NVM"),
+    ADD_STAT(avgQLat, statistics::units::Rate<
+                statistics::units::Tick, statistics::units::Count>::get(),
+             "Average queueing delay per NVM burst"),
+    ADD_STAT(avgBusLat, statistics::units::Rate<
+                statistics::units::Tick, statistics::units::Count>::get(),
+             "Average bus latency per NVM burst"),
+    ADD_STAT(avgMemAccLat, statistics::units::Rate<
+                statistics::units::Tick, statistics::units::Count>::get(),
+             "Average memory access latency per NVM burst"),
+
+    ADD_STAT(avgRdBW, statistics::units::Rate<
+                statistics::units::Byte, statistics::units::Second>::get(),
+             "Average DRAM read bandwidth in MiBytes/s"),
+    ADD_STAT(avgWrBW, statistics::units::Rate<
+                statistics::units::Byte, statistics::units::Second>::get(),
+             "Average DRAM write bandwidth in MiBytes/s"),
+    ADD_STAT(peakBW, statistics::units::Rate<
+                statistics::units::Byte, statistics::units::Second>::get(),
+             "Theoretical peak bandwidth in MiByte/s"),
+    ADD_STAT(busUtil, statistics::units::Ratio::get(),
+             "NVM Data bus utilization in percentage"),
+    ADD_STAT(busUtilRead, statistics::units::Ratio::get(),
+             "NVM Data bus read utilization in percentage"),
+    ADD_STAT(busUtilWrite, statistics::units::Ratio::get(),
+             "NVM Data bus write utilization in percentage"),
+
+    ADD_STAT(pendingReads, statistics::units::Count::get(),
+             "Reads issued to NVM for which data has not been transferred"),
+    ADD_STAT(pendingWrites, statistics::units::Count::get(),
+             "Number of outstanding writes to NVM"),
+    ADD_STAT(bytesPerBank, statistics::units::Byte::get(),
+             "Bytes read within a bank before loading new bank")
+
+{
+}
+
+void
+NVMInterface::NVMStats::regStats()
+{
+    using namespace statistics;
+
+    perBankRdBursts.init(nvm.ranksPerChannel == 0 ? 1 :
+              nvm.banksPerRank * nvm.ranksPerChannel);
+
+    perBankWrBursts.init(nvm.ranksPerChannel == 0 ? 1 :
+              nvm.banksPerRank * nvm.ranksPerChannel);
+
+    avgQLat.precision(2);
+    avgBusLat.precision(2);
+    avgMemAccLat.precision(2);
+
+    avgRdBW.precision(2);
+    avgWrBW.precision(2);
+    peakBW.precision(2);
+
+    busUtil.precision(2);
+    busUtilRead.precision(2);
+    busUtilWrite.precision(2);
+
+    pendingReads
+        .init(nvm.maxPendingReads)
+        .flags(nozero);
+
+    pendingWrites
+        .init(nvm.maxPendingWrites)
+        .flags(nozero);
+
+    bytesPerBank
+        .init(nvm.rowBufferSize)
+        .flags(nozero);
+
+    avgQLat = totQLat / readBursts;
+    avgBusLat = totBusLat / readBursts;
+    avgMemAccLat = totMemAccLat / readBursts;
+
+    avgRdBW = (bytesRead / 1000000) / simSeconds;
+    avgWrBW = (bytesWritten / 1000000) / simSeconds;
+    peakBW = (sim_clock::Frequency / nvm.tBURST) *
+              nvm.burstSize / 1000000;
+
+    busUtil = (avgRdBW + avgWrBW) / peakBW * 100;
+    busUtilRead = avgRdBW / peakBW * 100;
+    busUtilWrite = avgWrBW / peakBW * 100;
+}
+
+} // namespace memory
+} // namespace gem5
diff --git a/src/mem/nvm_interface.hh b/src/mem/nvm_interface.hh
new file mode 100644
index 0000000..cc41587
--- /dev/null
+++ b/src/mem/nvm_interface.hh
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2012-2020 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2013 Amin Farmahini-Farahani
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * NVMInterface declaration
+ */
+
+#ifndef __NVM_INTERFACE_HH__
+#define __NVM_INTERFACE_HH__
+
+#include "mem/mem_interface.hh"
+#include "params/NVMInterface.hh"
+
+namespace gem5
+{
+
+namespace memory
+{
+
+/**
+ * Interface to NVM devices with media specific parameters,
+ * statistics, and functions.
+ * The NVMInterface includes a class for individual ranks
+ * and per rank functions.
+ */
+class NVMInterface : public MemInterface
+{
+  private:
+    /**
+     * NVM rank class simply includes a vector of banks.
+     */
+    class Rank : public EventManager
+    {
+      public:
+
+        /**
+         * Current Rank index
+         */
+        uint8_t rank;
+
+        /**
+         * Vector of NVM banks. Each rank is made of several banks
+         * that can be accessed in parallel.
+         */
+        std::vector<Bank> banks;
+
+        Rank(const NVMInterfaceParams &_p, int _rank,
+             NVMInterface& _nvm);
+    };
+
+    /**
+     * NVM specific device and channel characteristics
+     */
+    const uint32_t maxPendingWrites;
+    const uint32_t maxPendingReads;
+    const bool twoCycleRdWr;
+
+    /**
+     * NVM specific timing requirements
+     */
+    const Tick tREAD;
+    const Tick tWRITE;
+    const Tick tSEND;
+
+    struct NVMStats : public statistics::Group
+    {
+        NVMStats(NVMInterface &nvm);
+
+        void regStats() override;
+
+        NVMInterface &nvm;
+
+        /** NVM stats */
+        statistics::Scalar readBursts;
+        statistics::Scalar writeBursts;
+
+        statistics::Vector perBankRdBursts;
+        statistics::Vector perBankWrBursts;
+
+        // Latencies summed over all requests
+        statistics::Scalar totQLat;
+        statistics::Scalar totBusLat;
+        statistics::Scalar totMemAccLat;
+
+        // Average latencies per request
+        statistics::Formula avgQLat;
+        statistics::Formula avgBusLat;
+        statistics::Formula avgMemAccLat;
+
+        statistics::Scalar bytesRead;
+        statistics::Scalar bytesWritten;
+
+        // Average bandwidth
+        statistics::Formula avgRdBW;
+        statistics::Formula avgWrBW;
+        statistics::Formula peakBW;
+        statistics::Formula busUtil;
+        statistics::Formula busUtilRead;
+        statistics::Formula busUtilWrite;
+
+        /** NVM stats */
+        statistics::Histogram pendingReads;
+        statistics::Histogram pendingWrites;
+        statistics::Histogram bytesPerBank;
+    };
+    NVMStats stats;
+
+    void processWriteRespondEvent();
+    EventFunctionWrapper writeRespondEvent;
+
+    void processReadReadyEvent();
+    EventFunctionWrapper readReadyEvent;
+
+    /**
+      * Vector of nvm ranks
+      */
+    std::vector<Rank*> ranks;
+
+    /**
+     * Holding queue for non-deterministic write commands, which
+     * maintains writes that have been issued but have not completed
+     * Stored seperately mostly to keep the code clean and help with
+     * events scheduling.
+     * This mimics a buffer on the media controller and therefore is
+     * not added to the main write queue for sizing
+     */
+    std::list<Tick> writeRespQueue;
+
+    std::deque<Tick> readReadyQueue;
+
+    /**
+     * Check if the write response queue is empty
+     *
+     * @param Return true if empty
+     */
+    bool writeRespQueueEmpty() const { return writeRespQueue.empty(); }
+
+    /**
+     * Till when must we wait before issuing next read command?
+     */
+    Tick nextReadAt;
+
+    // keep track of reads that have issued for which data is either
+    // not yet ready or has not yet been transferred to the ctrl
+    uint16_t numPendingReads;
+    uint16_t numReadDataReady;
+
+  public:
+    // keep track of the number of reads that have yet to be issued
+    uint16_t numReadsToIssue;
+
+    /**
+     * Initialize the NVM interface and verify parameters
+     */
+    void init() override;
+
+    /**
+     * Setup the rank based on packet received
+     *
+     * @param integer value of rank to be setup. used to index ranks vector
+     * @param are we setting up rank for read or write packet?
+     */
+    void setupRank(const uint8_t rank, const bool is_read) override;
+
+    MemPacket* decodePacket(const PacketPtr pkt, Addr pkt_addr,
+                           unsigned int size, bool is_read,
+                           uint8_t pseudo_channel = 0) override;
+
+    /**
+     * Check drain state of NVM interface
+     *
+     * @return true if write response queue is empty
+     *
+     */
+    bool allRanksDrained() const override { return writeRespQueueEmpty(); }
+
+    /*
+     * @return time to offset next command
+     */
+    Tick commandOffset() const override { return tBURST; }
+
+    /**
+     * Check if a burst operation can be issued to the NVM
+     *
+     * @param Return true if RD/WR can issue
+     *                    for reads, also verfy that ready count
+     *                    has been updated to a non-zero value to
+     *                    account for race conditions between events
+     */
+    bool burstReady(MemPacket* pkt) const override;
+
+    /**
+     * This function checks if ranks are busy.
+     * This state is true when either:
+     * 1) There is no command with read data ready to transmit or
+     * 2) The NVM inteface has reached the maximum number of outstanding
+     *    writes commands.
+     * @param read_queue_empty There are no read queued
+     * @param all_writes_nvm   All writes in queue are for NVM interface
+     * @return true of NVM is busy
+     *
+     */
+    bool isBusy(bool read_queue_empty, bool all_writes_nvm) override;
+    /**
+     * For FR-FCFS policy, find first NVM command that can issue
+     * default to first command to prepped region
+     *
+     * @param queue Queued requests to consider
+     * @param min_col_at Minimum tick for 'seamless' issue
+     * @return an iterator to the selected packet, else queue.end()
+     * @return the tick when the packet selected will issue
+     */
+    std::pair<MemPacketQueue::iterator, Tick>
+    chooseNextFRFCFS(MemPacketQueue& queue, Tick min_col_at) const override;
+
+    /**
+     *  Add rank to rank delay to bus timing to all NVM banks in alli ranks
+     *  when access to an alternate interface is issued
+     *
+     *  param cmd_at Time of current command used as starting point for
+     *               addition of rank-to-rank delay
+     */
+    void addRankToRankDelay(Tick cmd_at) override;
+
+    /**
+     * Following two functions are not required for nvm interface
+     */
+    void respondEvent(uint8_t rank) override { };
+
+    void checkRefreshState(uint8_t rank) override { };
+
+    /**
+     * Select read command to issue asynchronously
+     */
+    void chooseRead(MemPacketQueue& queue) override;
+
+    /*
+     * Function to calulate unloaded access latency
+     */
+    Tick accessLatency() const override { return (tREAD + tSEND); }
+
+    /**
+     * Check if the write response queue has reached defined threshold
+     *
+     * @param Return true if full
+     */
+    bool
+    writeRespQueueFull() const override
+    {
+        return writeRespQueue.size() == maxPendingWrites;
+    }
+
+    bool
+    readsWaitingToIssue() const override
+    {
+        return ((numReadsToIssue != 0) &&
+                (numPendingReads < maxPendingReads));
+    }
+
+    /**
+     * Actually do the burst and update stats.
+     *
+     * @param pkt The packet created from the outside world pkt
+     * @param next_burst_at Minimum bus timing requirement from controller
+     * @return pair, tick when current burst is issued and
+     *               tick when next burst can issue
+     */
+    std::pair<Tick, Tick>
+    doBurstAccess(MemPacket* pkt, Tick next_burst_at,
+                  const std::vector<MemPacketQueue>& queue) override;
+
+    /**
+     * The next three functions are DRAM-specific and will be ignored by NVM.
+     */
+    void drainRanks() override { }
+    void suspend() override { }
+    void startup() override { }
+
+    NVMInterface(const NVMInterfaceParams &_p);
+};
+
+} // namespace memory
+} // namespace gem5
+
+#endif //__NVM_INTERFACE_HH__
diff --git a/src/mem/packet.cc b/src/mem/packet.cc
index 219bc76..3cd1bb9 100644
--- a/src/mem/packet.cc
+++ b/src/mem/packet.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2019 ARM Limited
+ * Copyright (c) 2011-2019, 2021 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -57,6 +57,7 @@
 #include "base/logging.hh"
 #include "base/trace.hh"
 #include "mem/packet_access.hh"
+#include "sim/bufval.hh"
 
 namespace gem5
 {
@@ -164,6 +165,18 @@
     /* StoreCondResp */
     { {IsWrite, IsLlsc, IsResponse},
             InvalidCmd, "StoreCondResp" },
+    /* LockedRMWReadReq */
+    { {IsRead, IsLockedRMW, NeedsWritable, IsRequest, NeedsResponse},
+            LockedRMWReadResp, "LockedRMWReadReq" },
+    /* LockedRMWReadResp */
+    { {IsRead, IsLockedRMW, NeedsWritable, IsResponse, HasData},
+            InvalidCmd, "LockedRMWReadResp" },
+    /* LockedRMWWriteReq */
+    { {IsWrite, IsLockedRMW, NeedsWritable, IsRequest, NeedsResponse,
+           HasData}, LockedRMWWriteResp, "LockedRMWWriteReq" },
+    /* LockedRMWWriteResp */
+    { {IsWrite, IsLockedRMW, NeedsWritable, IsResponse},
+            InvalidCmd, "LockedRMWWriteResp" },
     /* SwapReq -- for Swap ldstub type operations */
     { {IsRead, IsWrite, NeedsWritable, IsRequest, HasData, NeedsResponse},
         SwapResp, "SwapReq" },
@@ -219,6 +232,7 @@
     { {IsRead, IsRequest, NeedsResponse}, HTMReqResp, "HTMReq" },
     { {IsRead, IsResponse}, InvalidCmd, "HTMReqResp" },
     { {IsRead, IsRequest}, InvalidCmd, "HTMAbort" },
+    { {IsRequest}, InvalidCmd, "TlbiExtSync" },
 };
 
 AddrRange
@@ -333,40 +347,17 @@
 uint64_t
 Packet::getUintX(ByteOrder endian) const
 {
-    switch(getSize()) {
-      case 1:
-        return (uint64_t)get<uint8_t>(endian);
-      case 2:
-        return (uint64_t)get<uint16_t>(endian);
-      case 4:
-        return (uint64_t)get<uint32_t>(endian);
-      case 8:
-        return (uint64_t)get<uint64_t>(endian);
-      default:
-        panic("%i isn't a supported word size.\n", getSize());
-    }
+    auto [val, success] =
+        gem5::getUintX(getConstPtr<void>(), getSize(), endian);
+    panic_if(!success, "%i isn't a supported word size.\n", getSize());
+    return val;
 }
 
 void
 Packet::setUintX(uint64_t w, ByteOrder endian)
 {
-    switch(getSize()) {
-      case 1:
-        set<uint8_t>((uint8_t)w, endian);
-        break;
-      case 2:
-        set<uint16_t>((uint16_t)w, endian);
-        break;
-      case 4:
-        set<uint32_t>((uint32_t)w, endian);
-        break;
-      case 8:
-        set<uint64_t>((uint64_t)w, endian);
-        break;
-      default:
-        panic("%i isn't a supported word size.\n", getSize());
-    }
-
+    bool success = gem5::setUintX(w, getPtr<void>(), getSize(), endian);
+    panic_if(!success, "%i isn't a supported word size.\n", getSize());
 }
 
 void
diff --git a/src/mem/packet.hh b/src/mem/packet.hh
index 88995f1..7d32634 100644
--- a/src/mem/packet.hh
+++ b/src/mem/packet.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2019 ARM Limited
+ * Copyright (c) 2012-2019, 2021 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -112,6 +112,10 @@
         StoreCondReq,
         StoreCondFailReq,       // Failed StoreCondReq in MSHR (never sent)
         StoreCondResp,
+        LockedRMWReadReq,
+        LockedRMWReadResp,
+        LockedRMWWriteReq,
+        LockedRMWWriteResp,
         SwapReq,
         SwapResp,
         // MessageReq and MessageResp are deprecated.
@@ -140,6 +144,8 @@
         HTMReq,
         HTMReqResp,
         HTMAbort,
+        // Tlb shootdown
+        TlbiExtSync,
         NUM_MEM_CMDS
     };
 
@@ -162,6 +168,7 @@
         IsSWPrefetch,
         IsHWPrefetch,
         IsLlsc,         //!< Alpha/MIPS LL or SC access
+        IsLockedRMW,    //!< x86 locked RMW access
         HasData,        //!< There is an associated payload
         IsError,        //!< Error response
         IsPrint,        //!< Print state matching address (for debugging)
@@ -239,6 +246,7 @@
      */
     bool hasData() const        { return testCmdAttrib(HasData); }
     bool isLLSC() const         { return testCmdAttrib(IsLlsc); }
+    bool isLockedRMW() const    { return testCmdAttrib(IsLockedRMW); }
     bool isSWPrefetch() const   { return testCmdAttrib(IsSWPrefetch); }
     bool isHWPrefetch() const   { return testCmdAttrib(IsHWPrefetch); }
     bool isPrefetch() const     { return testCmdAttrib(IsSWPrefetch) ||
@@ -607,6 +615,7 @@
         return resp_cmd.hasData();
     }
     bool isLLSC() const              { return cmd.isLLSC(); }
+    bool isLockedRMW() const         { return cmd.isLockedRMW(); }
     bool isError() const             { return cmd.isError(); }
     bool isPrint() const             { return cmd.isPrint(); }
     bool isFlush() const             { return cmd.isFlush(); }
@@ -976,6 +985,8 @@
             return MemCmd::SoftPFExReq;
         else if (req->isPrefetch())
             return MemCmd::SoftPFReq;
+        else if (req->isLockedRMW())
+            return MemCmd::LockedRMWReadReq;
         else
             return MemCmd::ReadReq;
     }
@@ -995,6 +1006,8 @@
               MemCmd::InvalidateReq;
         } else if (req->isCacheClean()) {
             return MemCmd::CleanSharedReq;
+        } else if (req->isLockedRMW()) {
+            return MemCmd::LockedRMWWriteReq;
         } else
             return MemCmd::WriteReq;
     }
diff --git a/src/mem/physical.cc b/src/mem/physical.cc
index ae20fb6..06f2cdc 100644
--- a/src/mem/physical.cc
+++ b/src/mem/physical.cc
@@ -50,11 +50,13 @@
 #include <iostream>
 #include <string>
 
+#include "base/intmath.hh"
 #include "base/trace.hh"
 #include "debug/AddrRanges.hh"
 #include "debug/Checkpoint.hh"
 #include "mem/abstract_mem.hh"
 #include "sim/serialize.hh"
+#include "sim/sim_exit.hh"
 
 /**
  * On Linux, MAP_NORESERVE allow us to simulate a very large memory
@@ -77,10 +79,17 @@
 PhysicalMemory::PhysicalMemory(const std::string& _name,
                                const std::vector<AbstractMemory*>& _memories,
                                bool mmap_using_noreserve,
-                               const std::string& shared_backstore) :
+                               const std::string& shared_backstore,
+                               bool auto_unlink_shared_backstore) :
     _name(_name), size(0), mmapUsingNoReserve(mmap_using_noreserve),
-    sharedBackstore(shared_backstore)
+    sharedBackstore(shared_backstore), sharedBackstoreSize(0),
+    pageSize(sysconf(_SC_PAGE_SIZE))
 {
+    // Register cleanup callback if requested.
+    if (auto_unlink_shared_backstore && !sharedBackstore.empty()) {
+        registerExitCallback([=]() { shm_unlink(shared_backstore.c_str()); });
+    }
+
     if (mmap_using_noreserve)
         warn("Not reserving swap space. May cause SIGSEGV on actual usage\n");
 
@@ -201,17 +210,24 @@
 
     int shm_fd;
     int map_flags;
+    off_t map_offset;
 
     if (sharedBackstore.empty()) {
         shm_fd = -1;
         map_flags =  MAP_ANON | MAP_PRIVATE;
+        map_offset = 0;
     } else {
-        DPRINTF(AddrRanges, "Sharing backing store as %s\n",
-                sharedBackstore.c_str());
+        // Newly create backstore will be located after previous one.
+        map_offset = sharedBackstoreSize;
+        // mmap requires the offset to be multiple of page, so we need to
+        // upscale the range size.
+        sharedBackstoreSize += roundUp(range.size(), pageSize);
+        DPRINTF(AddrRanges, "Sharing backing store as %s at offset %llu\n",
+                sharedBackstore.c_str(), (uint64_t)map_offset);
         shm_fd = shm_open(sharedBackstore.c_str(), O_CREAT | O_RDWR, 0666);
         if (shm_fd == -1)
                panic("Shared memory failed");
-        if (ftruncate(shm_fd, range.size()))
+        if (ftruncate(shm_fd, sharedBackstoreSize))
                panic("Setting size of shared memory failed");
         map_flags = MAP_SHARED;
     }
@@ -224,7 +240,7 @@
 
     uint8_t* pmem = (uint8_t*) mmap(NULL, range.size(),
                                     PROT_READ | PROT_WRITE,
-                                    map_flags, shm_fd, 0);
+                                    map_flags, shm_fd, map_offset);
 
     if (pmem == (uint8_t*) MAP_FAILED) {
         perror("mmap");
@@ -235,7 +251,8 @@
     // remember this backing store so we can checkpoint it and unmap
     // it appropriately
     backingStore.emplace_back(range, pmem,
-                              conf_table_reported, in_addr_map, kvm_map);
+                              conf_table_reported, in_addr_map, kvm_map,
+                              shm_fd, map_offset);
 
     // point the memories to their backing store
     for (const auto& m : _memories) {
diff --git a/src/mem/physical.hh b/src/mem/physical.hh
index b7ec8eb..4997d80 100644
--- a/src/mem/physical.hh
+++ b/src/mem/physical.hh
@@ -70,9 +70,11 @@
      * pointers, because PhysicalMemory is responsible for that.
      */
     BackingStoreEntry(AddrRange range, uint8_t* pmem,
-                      bool conf_table_reported, bool in_addr_map, bool kvm_map)
+                      bool conf_table_reported, bool in_addr_map, bool kvm_map,
+                      int shm_fd=-1, off_t shm_offset=0)
         : range(range), pmem(pmem), confTableReported(conf_table_reported),
-          inAddrMap(in_addr_map), kvmMap(kvm_map)
+          inAddrMap(in_addr_map), kvmMap(kvm_map), shmFd(shm_fd),
+          shmOffset(shm_offset)
         {}
 
     /**
@@ -101,6 +103,18 @@
       * acceleration.
       */
      bool kvmMap;
+
+     /**
+      * If this backing store is based on a shared memory, this is the fd to
+      * the shared memory. Otherwise, it should be -1.
+      */
+     int shmFd;
+
+     /**
+      * If this backing store is based on a shared memory, this is the offset
+      * of this backing store in the share memory. Otherwise, the value is 0.
+      */
+     off_t shmOffset;
 };
 
 /**
@@ -140,6 +154,9 @@
     const bool mmapUsingNoReserve;
 
     const std::string sharedBackstore;
+    uint64_t sharedBackstoreSize;
+
+    long pageSize;
 
     // The physical memory used to provide the memory in the simulated
     // system
@@ -173,7 +190,8 @@
     PhysicalMemory(const std::string& _name,
                    const std::vector<AbstractMemory*>& _memories,
                    bool mmap_using_noreserve,
-                   const std::string& shared_backstore);
+                   const std::string& shared_backstore,
+                   bool auto_unlink_shared_backstore);
 
     /**
      * Unmap all the backing store we have used.
diff --git a/src/mem/probes/SConscript b/src/mem/probes/SConscript
index 6df807f..0498b11 100644
--- a/src/mem/probes/SConscript
+++ b/src/mem/probes/SConscript
@@ -47,6 +47,5 @@
 Source('mem_footprint.cc')
 
 # Packet tracing requires protobuf support
-if env['HAVE_PROTOBUF']:
-    SimObject('MemTraceProbe.py', sim_objects=['MemTraceProbe'])
-    Source('mem_trace.cc')
+SimObject('MemTraceProbe.py', sim_objects=['MemTraceProbe'], tags='protobuf')
+Source('mem_trace.cc', tags='protobuf')
diff --git a/src/mem/request.hh b/src/mem/request.hh
index 3b884a9..39d9d72 100644
--- a/src/mem/request.hh
+++ b/src/mem/request.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013,2017-2020 ARM Limited
+ * Copyright (c) 2012-2013,2017-2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -237,6 +237,20 @@
         // This separation is necessary to ensure the disjoint components
         // of the system work correctly together.
 
+        /** The Request is a TLB shootdown */
+        TLBI                        = 0x0000100000000000,
+
+        /** The Request is a TLB shootdown sync */
+        TLBI_SYNC                   = 0x0000200000000000,
+
+        /** The Request tells the CPU model that a
+            remote TLB Sync has been requested */
+        TLBI_EXT_SYNC               = 0x0000400000000000,
+
+        /** The Request tells the interconnect that a
+            remote TLB Sync request has completed */
+        TLBI_EXT_SYNC_COMP          = 0x0000800000000000,
+
         /**
          * These flags are *not* cleared when a Request object is
          * reused (assigned a new address).
@@ -249,6 +263,9 @@
     static const FlagsType HTM_CMD = HTM_START | HTM_COMMIT |
         HTM_CANCEL | HTM_ABORT;
 
+    static const FlagsType TLBI_CMD = TLBI | TLBI_SYNC |
+        TLBI_EXT_SYNC | TLBI_EXT_SYNC_COMP;
+
     /** Requestor Ids that are statically allocated
      * @{*/
     enum : RequestorID
@@ -419,6 +436,12 @@
      */
     uint32_t _substreamId = 0;
 
+    /**
+     * For fullsystem GPU simulation, this determines if a requests
+     * destination is system (host) memory or dGPU (device) memory.
+     */
+    bool _systemReq = 0;
+
     /** The virtual address of the request. */
     Addr _vaddr = MaxAddr;
 
@@ -500,6 +523,22 @@
     ~Request() {}
 
     /**
+     * Factory method for creating memory management requests, with
+     * unspecified addr and size.
+     */
+    static RequestPtr
+    createMemManagement(Flags flags, RequestorID id)
+    {
+        auto mgmt_req = std::make_shared<Request>();
+        mgmt_req->_flags.set(flags);
+        mgmt_req->_requestorId = id;
+        mgmt_req->_time = curTick();
+
+        assert(mgmt_req->isMemMgmt());
+        return mgmt_req;
+    }
+
+    /**
      * Set up Context numbers.
      */
     void
@@ -761,6 +800,13 @@
     }
 
     void
+    clearFlags(Flags flags)
+    {
+        assert(hasPaddr() || hasVaddr());
+        _flags.clear(flags);
+    }
+
+    void
     setCacheCoherenceFlags(CacheCoherenceFlags extraFlags)
     {
         // TODO: do mem_sync_op requests have valid paddr/vaddr?
@@ -768,6 +814,14 @@
         _cacheCoherenceFlags.set(extraFlags);
     }
 
+    void
+    clearCacheCoherenceFlags(CacheCoherenceFlags extraFlags)
+    {
+        // TODO: do mem_sync_op requests have valid paddr/vaddr?
+        assert(hasPaddr() || hasVaddr());
+        _cacheCoherenceFlags.clear(extraFlags);
+    }
+
     /** Accessor function for vaddr.*/
     bool
     hasVaddr() const
@@ -789,6 +843,12 @@
         return _requestorId;
     }
 
+    void
+    requestorId(RequestorID rid)
+    {
+        _requestorId = rid;
+    }
+
     uint32_t
     taskId() const
     {
@@ -845,6 +905,10 @@
         return _contextId;
     }
 
+    /* For GPU fullsystem mark this request is not to device memory. */
+    void setSystemReq(bool sysReq) { _systemReq = sysReq; }
+    bool systemReq() const { return _systemReq; }
+
     bool
     hasStreamId() const
     {
@@ -975,6 +1039,18 @@
                 isHTMCancel() || isHTMAbort());
     }
 
+    bool isTlbi() const { return _flags.isSet(TLBI); }
+    bool isTlbiSync() const { return _flags.isSet(TLBI_SYNC); }
+    bool isTlbiExtSync() const { return _flags.isSet(TLBI_EXT_SYNC); }
+    bool isTlbiExtSyncComp() const { return _flags.isSet(TLBI_EXT_SYNC_COMP); }
+    bool
+    isTlbiCmd() const
+    {
+        return (isTlbi() || isTlbiSync() ||
+                isTlbiExtSync() || isTlbiExtSyncComp());
+    }
+    bool isMemMgmt() const { return isTlbiCmd() || isHTMCmd(); }
+
     bool
     isAtomic() const
     {
diff --git a/src/mem/ruby/SConscript b/src/mem/ruby/SConscript
index b3ea716..5062efd 100644
--- a/src/mem/ruby/SConscript
+++ b/src/mem/ruby/SConscript
@@ -1,5 +1,17 @@
 # -*- mode:python -*-
 
+# Copyright (c) 2021 Arm Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
 # Copyright (c) 2009 The Hewlett-Packard Development Company
 # All rights reserved.
 #
@@ -37,7 +49,7 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 DebugFlag('ProtocolTrace')
@@ -55,11 +67,12 @@
 DebugFlag('RubyTester')
 DebugFlag('RubyStats')
 DebugFlag('RubyResourceStalls')
+DebugFlag('RubyProtocol')
 
 CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester',
     'RubyGenerated', 'RubySlicc', 'RubySystem', 'RubyCache',
     'RubyDma', 'RubyPort', 'RubySequencer', 'RubyCacheTrace',
-    'RubyPrefetcher'])
+    'RubyPrefetcher', 'RubyProtocol'])
 
 #
 # Link includes
@@ -98,6 +111,9 @@
 MakeInclude('structures/PersistentTable.hh')
 MakeInclude('structures/RubyPrefetcher.hh')
 MakeInclude('structures/TBEStorage.hh')
+if env['PROTOCOL'] == 'CHI':
+    MakeInclude('structures/MN_TBEStorage.hh')
+    MakeInclude('structures/MN_TBETable.hh')
 MakeInclude('structures/TBETable.hh')
 MakeInclude('structures/TimerTable.hh')
 MakeInclude('structures/WireBuffer.hh')
diff --git a/src/mem/ruby/SConsopts b/src/mem/ruby/SConsopts
index 80713c4..f26b6d0 100644
--- a/src/mem/ruby/SConsopts
+++ b/src/mem/ruby/SConsopts
@@ -34,5 +34,3 @@
 
 sticky_vars.Add(('NUMBER_BITS_PER_SET', 'Max elements in set (default 64)',
                  64))
-
-export_vars.extend(['PROTOCOL', 'NUMBER_BITS_PER_SET'])
diff --git a/src/mem/ruby/common/Consumer.cc b/src/mem/ruby/common/Consumer.cc
index 8017ed7..4ac8292 100644
--- a/src/mem/ruby/common/Consumer.cc
+++ b/src/mem/ruby/common/Consumer.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 ARM Limited
+ * Copyright (c) 2020-2021 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -46,9 +46,9 @@
 namespace ruby
 {
 
-Consumer::Consumer(ClockedObject *_em)
+Consumer::Consumer(ClockedObject *_em, Event::Priority ev_prio)
     : m_wakeup_event([this]{ processCurrentEvent(); },
-                    "Consumer Event", false),
+                    "Consumer Event", false, ev_prio),
       em(_em)
 { }
 
diff --git a/src/mem/ruby/common/Consumer.hh b/src/mem/ruby/common/Consumer.hh
index 8849294..9e8e89a 100644
--- a/src/mem/ruby/common/Consumer.hh
+++ b/src/mem/ruby/common/Consumer.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 ARM Limited
+ * Copyright (c) 2020-2021 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -61,7 +61,8 @@
 class Consumer
 {
   public:
-    Consumer(ClockedObject *_em);
+    Consumer(ClockedObject *em,
+             Event::Priority ev_prio = Event::Default_Pri);
 
     virtual
     ~Consumer()
diff --git a/src/mem/ruby/common/SConscript b/src/mem/ruby/common/SConscript
index 60cbe92..9f683cb 100644
--- a/src/mem/ruby/common/SConscript
+++ b/src/mem/ruby/common/SConscript
@@ -28,10 +28,11 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
-env.Append(CPPDEFINES={'NUMBER_BITS_PER_SET': env['NUMBER_BITS_PER_SET']})
+env.Append(CPPDEFINES={'NUMBER_BITS_PER_SET':
+    env['CONF']['NUMBER_BITS_PER_SET']})
 
 Source('Address.cc')
 Source('BoolVec.cc')
diff --git a/src/mem/ruby/common/TypeDefines.hh b/src/mem/ruby/common/TypeDefines.hh
index 8f877be..8cc3e91 100644
--- a/src/mem/ruby/common/TypeDefines.hh
+++ b/src/mem/ruby/common/TypeDefines.hh
@@ -30,6 +30,8 @@
 #ifndef __MEM_RUBY_COMMON_TYPEDEFINES_HH__
 #define __MEM_RUBY_COMMON_TYPEDEFINES_HH__
 
+#include <string>
+
 namespace gem5
 {
 
@@ -39,6 +41,7 @@
 typedef unsigned int LinkID;
 typedef unsigned int NodeID;
 typedef unsigned int SwitchID;
+typedef std::string PortDirection;
 
 } // namespace ruby
 } // namespace gem5
diff --git a/src/mem/ruby/network/BasicLink.py b/src/mem/ruby/network/BasicLink.py
index 39f2282..5c5fcca 100644
--- a/src/mem/ruby/network/BasicLink.py
+++ b/src/mem/ruby/network/BasicLink.py
@@ -37,6 +37,10 @@
     # Width of the link in bytes
     # Only used by simple network.
     # Garnet models this by flit size
+    # For the simple links, the bandwidth factor translates to the
+    # bandwidth multiplier.  The multipiler, in combination with the
+    # endpoint bandwidth multiplier - message size multiplier ratio,
+    # determines the link bandwidth in bytes
     bandwidth_factor = Param.Int("generic bandwidth factor, usually in bytes")
     weight = Param.Int(1, "used to restrict routing in shortest path analysis")
     supported_vnets = VectorParam.Int([], "Vnets supported Default:All([])")
diff --git a/src/mem/ruby/network/MessageBuffer.cc b/src/mem/ruby/network/MessageBuffer.cc
index f4b2bb5..9a65009 100644
--- a/src/mem/ruby/network/MessageBuffer.cc
+++ b/src/mem/ruby/network/MessageBuffer.cc
@@ -58,19 +58,31 @@
 using stl_helpers::operator<<;
 
 MessageBuffer::MessageBuffer(const Params &p)
-    : SimObject(p), m_stall_map_size(0),
-    m_max_size(p.buffer_size), m_time_last_time_size_checked(0),
+    : SimObject(p), m_stall_map_size(0), m_max_size(p.buffer_size),
+    m_max_dequeue_rate(p.max_dequeue_rate), m_dequeues_this_cy(0),
+    m_time_last_time_size_checked(0),
     m_time_last_time_enqueue(0), m_time_last_time_pop(0),
     m_last_arrival_time(0), m_strict_fifo(p.ordered),
     m_randomization(p.randomization),
     m_allow_zero_latency(p.allow_zero_latency),
-    ADD_STAT(m_not_avail_count, "Number of times this buffer did not have "
-                                "N slots available"),
-    ADD_STAT(m_buf_msgs, "Average number of messages in buffer"),
-    ADD_STAT(m_stall_time, "Average number of cycles messages are stalled in "
-                           "this MB"),
-    ADD_STAT(m_stall_count, "Number of times messages were stalled"),
-    ADD_STAT(m_occupancy, "Average occupancy of buffer capacity")
+    m_routing_priority(p.routing_priority),
+    ADD_STAT(m_not_avail_count, statistics::units::Count::get(),
+             "Number of times this buffer did not have N slots available"),
+    ADD_STAT(m_msg_count, statistics::units::Count::get(),
+             "Number of messages passed the buffer"),
+    ADD_STAT(m_buf_msgs, statistics::units::Rate<
+                statistics::units::Count, statistics::units::Tick>::get(),
+             "Average number of messages in buffer"),
+    ADD_STAT(m_stall_time, statistics::units::Tick::get(),
+             "Total number of ticks messages were stalled in this buffer"),
+    ADD_STAT(m_stall_count, statistics::units::Count::get(),
+             "Number of times messages were stalled"),
+    ADD_STAT(m_avg_stall_time, statistics::units::Rate<
+                statistics::units::Tick, statistics::units::Count>::get(),
+             "Average stall ticks per message"),
+    ADD_STAT(m_occupancy, statistics::units::Rate<
+                statistics::units::Ratio, statistics::units::Tick>::get(),
+             "Average occupancy of buffer capacity")
 {
     m_msg_counter = 0;
     m_consumer = NULL;
@@ -93,12 +105,18 @@
     m_not_avail_count
         .flags(statistics::nozero);
 
+    m_msg_count
+        .flags(statistics::nozero);
+
     m_buf_msgs
         .flags(statistics::nozero);
 
     m_stall_count
         .flags(statistics::nozero);
 
+    m_avg_stall_time
+        .flags(statistics::nozero | statistics::nonan);
+
     m_occupancy
         .flags(statistics::nozero);
 
@@ -110,6 +128,8 @@
     } else {
         m_occupancy = 0;
     }
+
+    m_avg_stall_time = m_stall_time / m_msg_count;
 }
 
 unsigned int
@@ -288,19 +308,23 @@
     message->updateDelayedTicks(current_time);
     Tick delay = message->getDelayedTicks();
 
-    m_stall_time = curTick() - message->getTime();
-
     // record previous size and time so the current buffer size isn't
     // adjusted until schd cycle
     if (m_time_last_time_pop < current_time) {
         m_size_at_cycle_start = m_prio_heap.size();
         m_stalled_at_cycle_start = m_stall_map_size;
         m_time_last_time_pop = current_time;
+        m_dequeues_this_cy = 0;
     }
+    ++m_dequeues_this_cy;
 
     pop_heap(m_prio_heap.begin(), m_prio_heap.end(), std::greater<MsgPtr>());
     m_prio_heap.pop_back();
     if (decrement_messages) {
+        // Record how much time is passed since the message was enqueued
+        m_stall_time += curTick() - message->getLastEnqueueTime();
+        m_msg_count++;
+
         // If the message will be removed from the queue, decrement the
         // number of message in the queue.
         m_buf_msgs--;
@@ -487,8 +511,26 @@
 bool
 MessageBuffer::isReady(Tick current_time) const
 {
-    return ((m_prio_heap.size() > 0) &&
-        (m_prio_heap.front()->getLastEnqueueTime() <= current_time));
+    assert(m_time_last_time_pop <= current_time);
+    bool can_dequeue = (m_max_dequeue_rate == 0) ||
+                       (m_time_last_time_pop < current_time) ||
+                       (m_dequeues_this_cy < m_max_dequeue_rate);
+    bool is_ready = (m_prio_heap.size() > 0) &&
+                   (m_prio_heap.front()->getLastEnqueueTime() <= current_time);
+    if (!can_dequeue && is_ready) {
+        // Make sure the Consumer executes next cycle to dequeue the ready msg
+        m_consumer->scheduleEvent(Cycles(1));
+    }
+    return can_dequeue && is_ready;
+}
+
+Tick
+MessageBuffer::readyTime() const
+{
+    if (m_prio_heap.empty())
+        return MaxTick;
+    else
+        return m_prio_heap.front()->getLastEnqueueTime();
 }
 
 uint32_t
diff --git a/src/mem/ruby/network/MessageBuffer.hh b/src/mem/ruby/network/MessageBuffer.hh
index ff2a4dd..2795993 100644
--- a/src/mem/ruby/network/MessageBuffer.hh
+++ b/src/mem/ruby/network/MessageBuffer.hh
@@ -86,6 +86,9 @@
     // TRUE if head of queue timestamp <= SystemTime
     bool isReady(Tick current_time) const;
 
+    // earliest tick the head of queue will be ready, or MaxTick if empty
+    Tick readyTime() const;
+
     void
     delayHead(Tick current_time, Tick delta)
     {
@@ -155,6 +158,9 @@
     void setIncomingLink(int link_id) { m_input_link_id = link_id; }
     void setVnet(int net) { m_vnet_id = net; }
 
+    int getIncomingLink() const { return m_input_link_id; }
+    int getVnet() const { return m_vnet_id; }
+
     Port &
     getPort(const std::string &, PortID idx=InvalidPortID) override
     {
@@ -184,6 +190,8 @@
         return functionalAccess(pkt, true, &mask) == 1;
     }
 
+    int routingPriority() const { return m_routing_priority; }
+
   private:
     void reanalyzeList(std::list<MsgPtr> &, Tick);
 
@@ -240,6 +248,14 @@
      */
     const unsigned int m_max_size;
 
+    /**
+     * When != 0, isReady returns false once m_max_dequeue_rate
+     * messages have been dequeued in the same cycle.
+     */
+    const unsigned int m_max_dequeue_rate;
+
+    unsigned int m_dequeues_this_cy;
+
     Tick m_time_last_time_size_checked;
     unsigned int m_size_last_time_size_checked;
 
@@ -259,14 +275,18 @@
     const MessageRandomization m_randomization;
     const bool m_allow_zero_latency;
 
+    const int m_routing_priority;
+
     int m_input_link_id;
     int m_vnet_id;
 
     // Count the # of times I didn't have N slots available
     statistics::Scalar m_not_avail_count;
+    statistics::Scalar m_msg_count;
     statistics::Average m_buf_msgs;
-    statistics::Average m_stall_time;
+    statistics::Scalar m_stall_time;
     statistics::Scalar m_stall_count;
+    statistics::Formula m_avg_stall_time;
     statistics::Formula m_occupancy;
 };
 
diff --git a/src/mem/ruby/network/MessageBuffer.py b/src/mem/ruby/network/MessageBuffer.py
index b30726e..b776196 100644
--- a/src/mem/ruby/network/MessageBuffer.py
+++ b/src/mem/ruby/network/MessageBuffer.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 ARM Limited
+# Copyright (c) 2020-2021 ARM Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -67,3 +67,9 @@
     master = DeprecatedParam(out_port, '`master` is now called `out_port`')
     in_port = ResponsePort("Response port from MessageBuffer sender")
     slave = DeprecatedParam(in_port, '`slave` is now called `in_port`')
+    max_dequeue_rate = Param.Unsigned(0, "Maximum number of messages that can \
+                                          be dequeued per cycle \
+                                    (0 allows dequeueing all ready messages)")
+    routing_priority = Param.Int(0, "Buffer priority when messages are \
+                                     consumed by the network. Smaller value \
+                                     means higher priority")
diff --git a/src/mem/ruby/network/SConscript b/src/mem/ruby/network/SConscript
index a1ea977..b5a4e79 100644
--- a/src/mem/ruby/network/SConscript
+++ b/src/mem/ruby/network/SConscript
@@ -28,7 +28,7 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('BasicLink.py', sim_objects=[
diff --git a/src/mem/ruby/network/Topology.hh b/src/mem/ruby/network/Topology.hh
index 5ce654f..301811e 100644
--- a/src/mem/ruby/network/Topology.hh
+++ b/src/mem/ruby/network/Topology.hh
@@ -42,7 +42,6 @@
 #define __MEM_RUBY_NETWORK_TOPOLOGY_HH__
 
 #include <iostream>
-#include <string>
 #include <vector>
 
 #include "mem/ruby/common/TypeDefines.hh"
@@ -65,7 +64,6 @@
  * represent the source ID, destination ID, and vnet number.
  */
 typedef std::vector<std::vector<std::vector<int>>> Matrix;
-typedef std::string PortDirection;
 
 struct LinkEntry
 {
diff --git a/src/mem/ruby/network/fault_model/SConscript b/src/mem/ruby/network/fault_model/SConscript
index 3aff28e..701b59a 100644
--- a/src/mem/ruby/network/fault_model/SConscript
+++ b/src/mem/ruby/network/fault_model/SConscript
@@ -33,7 +33,7 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('FaultModel.py', sim_objects=['FaultModel'])
diff --git a/src/mem/ruby/network/garnet/Credit.cc b/src/mem/ruby/network/garnet/Credit.cc
index 65ff488..74d6611 100644
--- a/src/mem/ruby/network/garnet/Credit.cc
+++ b/src/mem/ruby/network/garnet/Credit.cc
@@ -46,7 +46,7 @@
 // and m_is_free_signal (whether VC is free or not)
 
 Credit::Credit(int vc, bool is_free_signal, Tick curTime)
-    : flit(0, vc, 0, RouteInfo(), 0, nullptr, 0, 0, curTime)
+    : flit(0, 0, vc, 0, RouteInfo(), 0, nullptr, 0, 0, curTime)
 {
     m_is_free_signal = is_free_signal;
     m_type = CREDIT_;
diff --git a/src/mem/ruby/network/garnet/GarnetNetwork.cc b/src/mem/ruby/network/garnet/GarnetNetwork.cc
index de566a5..01b2473 100644
--- a/src/mem/ruby/network/garnet/GarnetNetwork.cc
+++ b/src/mem/ruby/network/garnet/GarnetNetwork.cc
@@ -70,6 +70,7 @@
     m_buffers_per_data_vc = p.buffers_per_data_vc;
     m_buffers_per_ctrl_vc = p.buffers_per_ctrl_vc;
     m_routing_algorithm = p.routing_algorithm;
+    m_next_packet_id = 0;
 
     m_enable_fault_model = p.enable_fault_model;
     if (m_enable_fault_model)
diff --git a/src/mem/ruby/network/garnet/GarnetNetwork.hh b/src/mem/ruby/network/garnet/GarnetNetwork.hh
index 912445b..d18caae 100644
--- a/src/mem/ruby/network/garnet/GarnetNetwork.hh
+++ b/src/mem/ruby/network/garnet/GarnetNetwork.hh
@@ -153,6 +153,7 @@
     }
 
     void update_traffic_distribution(RouteInfo route);
+    int getNextPacketID() { return m_next_packet_id++; }
 
   protected:
     // Configuration
@@ -209,6 +210,7 @@
     std::vector<NetworkLink *> m_networklinks; // All flit links in the network
     std::vector<CreditLink *> m_creditlinks; // All credit links in the network
     std::vector<NetworkInterface *> m_nis;   // All NI's in Network
+    int m_next_packet_id; // static vairable for packet id allocation
 };
 
 inline std::ostream&
diff --git a/src/mem/ruby/network/garnet/NetworkInterface.cc b/src/mem/ruby/network/garnet/NetworkInterface.cc
index a33102a..1154718 100644
--- a/src/mem/ruby/network/garnet/NetworkInterface.cc
+++ b/src/mem/ruby/network/garnet/NetworkInterface.cc
@@ -436,9 +436,11 @@
 
         m_net_ptr->increment_injected_packets(vnet);
         m_net_ptr->update_traffic_distribution(route);
+        int packet_id = m_net_ptr->getNextPacketID();
         for (int i = 0; i < num_flits; i++) {
             m_net_ptr->increment_injected_flits(vnet);
-            flit *fl = new flit(i, vc, vnet, route, num_flits, new_msg_ptr,
+            flit *fl = new flit(packet_id,
+                i, vc, vnet, route, num_flits, new_msg_ptr,
                 m_net_ptr->MessageSizeType_to_int(
                 net_msg_ptr->getMessageSize()),
                 oPort->bitWidth(), curTick());
diff --git a/src/mem/ruby/network/garnet/SConscript b/src/mem/ruby/network/garnet/SConscript
index af6e758..9e6e19e 100644
--- a/src/mem/ruby/network/garnet/SConscript
+++ b/src/mem/ruby/network/garnet/SConscript
@@ -28,7 +28,7 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('GarnetLink.py', enums=['CDCType'], sim_objects=[
diff --git a/src/mem/ruby/network/garnet/flit.cc b/src/mem/ruby/network/garnet/flit.cc
index 6e6ed5f..b65297c 100644
--- a/src/mem/ruby/network/garnet/flit.cc
+++ b/src/mem/ruby/network/garnet/flit.cc
@@ -43,7 +43,7 @@
 {
 
 // Constructor for the flit
-flit::flit(int id, int  vc, int vnet, RouteInfo route, int size,
+flit::flit(int packet_id, int id, int  vc, int vnet, RouteInfo route, int size,
     MsgPtr msg_ptr, int MsgSize, uint32_t bWidth, Tick curTime)
 {
     m_size = size;
@@ -51,6 +51,7 @@
     m_enqueue_time = curTime;
     m_dequeue_time = curTime;
     m_time = curTime;
+    m_packet_id = id;
     m_id = id;
     m_vnet = vnet;
     m_vc = vc;
@@ -82,7 +83,7 @@
     int new_size = (int)divCeil((float)msgSize, (float)bWidth);
     assert(new_id < new_size);
 
-    flit *fl = new flit(new_id, m_vc, m_vnet, m_route,
+    flit *fl = new flit(m_packet_id, new_id, m_vc, m_vnet, m_route,
                     new_size, m_msg_ptr, msgSize, bWidth, m_time);
     fl->set_enqueue_time(m_enqueue_time);
     fl->set_src_delay(src_delay);
@@ -97,7 +98,7 @@
     int new_size = (int)divCeil((float)msgSize, (float)bWidth);
     assert(new_id < new_size);
 
-    flit *fl = new flit(new_id, m_vc, m_vnet, m_route,
+    flit *fl = new flit(m_packet_id, new_id, m_vc, m_vnet, m_route,
                     new_size, m_msg_ptr, msgSize, bWidth, m_time);
     fl->set_enqueue_time(m_enqueue_time);
     fl->set_src_delay(src_delay);
@@ -109,6 +110,7 @@
 flit::print(std::ostream& out) const
 {
     out << "[flit:: ";
+    out << "PacketId=" << m_packet_id << " ";
     out << "Id=" << m_id << " ";
     out << "Type=" << m_type << " ";
     out << "Size=" << m_size << " ";
diff --git a/src/mem/ruby/network/garnet/flit.hh b/src/mem/ruby/network/garnet/flit.hh
index 8814d3c..a84dc57 100644
--- a/src/mem/ruby/network/garnet/flit.hh
+++ b/src/mem/ruby/network/garnet/flit.hh
@@ -51,7 +51,7 @@
 {
   public:
     flit() {}
-    flit(int id, int vc, int vnet, RouteInfo route, int size,
+    flit(int packet_id, int id, int vc, int vnet, RouteInfo route, int size,
          MsgPtr msg_ptr, int MsgSize, uint32_t bWidth, Tick curTime);
 
     virtual ~flit(){};
@@ -60,6 +60,7 @@
     int get_size() { return m_size; }
     Tick get_enqueue_time() { return m_enqueue_time; }
     Tick get_dequeue_time() { return m_dequeue_time; }
+    int getPacketID() { return m_packet_id; }
     int get_id() { return m_id; }
     Tick get_time() { return m_time; }
     int get_vnet() { return m_vnet; }
@@ -114,6 +115,7 @@
     uint32_t m_width;
     int msgSize;
   protected:
+    int m_packet_id;
     int m_id;
     int m_vnet;
     int m_vc;
diff --git a/src/mem/ruby/network/simple/PerfectSwitch.cc b/src/mem/ruby/network/simple/PerfectSwitch.cc
index ff0400d..74d78e3 100644
--- a/src/mem/ruby/network/simple/PerfectSwitch.cc
+++ b/src/mem/ruby/network/simple/PerfectSwitch.cc
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2020-2021 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
  * All rights reserved.
  *
@@ -47,17 +59,10 @@
 
 const int PRIORITY_SWITCH_LIMIT = 128;
 
-// Operator for helper class
-bool
-operator<(const LinkOrder& l1, const LinkOrder& l2)
-{
-    return (l1.m_value < l2.m_value);
-}
-
 PerfectSwitch::PerfectSwitch(SwitchID sid, Switch *sw, uint32_t virt_nets)
-    : Consumer(sw), m_switch_id(sid), m_switch(sw)
+    : Consumer(sw, Switch::PERFECTSWITCH_EV_PRI),
+      m_switch_id(sid), m_switch(sw)
 {
-    m_round_robin_start = 0;
     m_wakeups_wo_switch = 0;
     m_virtual_networks = virt_nets;
 }
@@ -83,151 +88,132 @@
             in[i]->setConsumer(this);
             in[i]->setIncomingLink(port);
             in[i]->setVnet(i);
+            updatePriorityGroups(i, in[i]);
         }
     }
 }
 
 void
-PerfectSwitch::addOutPort(const std::vector<MessageBuffer*>& out,
-                          const NetDest& routing_table_entry)
+PerfectSwitch::updatePriorityGroups(int vnet, MessageBuffer* in_buf)
 {
-    // Setup link order
-    LinkOrder l;
-    l.m_value = 0;
-    l.m_link = m_out.size();
-    m_link_order.push_back(l);
+    while (m_in_prio.size() <= vnet) {
+        m_in_prio.emplace_back();
+        m_in_prio_groups.emplace_back();
+    }
 
-    // Add to routing table
-    m_out.push_back(out);
-    m_routing_table.push_back(routing_table_entry);
+    m_in_prio[vnet].push_back(in_buf);
+
+    std::sort(m_in_prio[vnet].begin(), m_in_prio[vnet].end(),
+        [](const MessageBuffer* i, const MessageBuffer* j)
+        { return i->routingPriority() < j->routingPriority(); });
+
+    // reset groups
+    m_in_prio_groups[vnet].clear();
+    int cur_prio = m_in_prio[vnet].front()->routingPriority();
+    m_in_prio_groups[vnet].emplace_back();
+    for (auto buf : m_in_prio[vnet]) {
+        if (buf->routingPriority() != cur_prio)
+            m_in_prio_groups[vnet].emplace_back();
+        m_in_prio_groups[vnet].back().push_back(buf);
+    }
+}
+
+void
+PerfectSwitch::addOutPort(const std::vector<MessageBuffer*>& out,
+                          const NetDest& routing_table_entry,
+                          const PortDirection &dst_inport,
+                          Tick routing_latency,
+                          int link_weight)
+{
+    // Add to routing unit
+    m_switch->getRoutingUnit().addOutPort(m_out.size(),
+                                          out,
+                                          routing_table_entry,
+                                          dst_inport,
+                                          link_weight);
+    m_out.push_back({routing_latency, out});
 }
 
 PerfectSwitch::~PerfectSwitch()
 {
 }
 
+MessageBuffer*
+PerfectSwitch::inBuffer(int in_port, int vnet) const
+{
+    if (m_in[in_port].size() <= vnet) {
+        return nullptr;
+    }
+    else {
+        return m_in[in_port][vnet];
+    }
+}
+
 void
 PerfectSwitch::operateVnet(int vnet)
 {
-    // This is for round-robin scheduling
-    int incoming = m_round_robin_start;
-    m_round_robin_start++;
-    if (m_round_robin_start >= m_in.size()) {
-        m_round_robin_start = 0;
-    }
+    if (m_pending_message_count[vnet] == 0)
+        return;
 
-    if (m_pending_message_count[vnet] > 0) {
-        // for all input ports, use round robin scheduling
-        for (int counter = 0; counter < m_in.size(); counter++) {
-            // Round robin scheduling
-            incoming++;
-            if (incoming >= m_in.size()) {
-                incoming = 0;
+    for (auto &in : m_in_prio_groups[vnet]) {
+        // first check the port with the oldest message
+        unsigned start_in_port = 0;
+        Tick lowest_tick = MaxTick;
+        for (int i = 0; i < in.size(); ++i) {
+            MessageBuffer *buffer = in[i];
+            if (buffer) {
+                Tick ready_time = buffer->readyTime();
+                if (ready_time < lowest_tick){
+                    lowest_tick = ready_time;
+                    start_in_port = i;
+                }
             }
-
-            // Is there a message waiting?
-            if (m_in[incoming].size() <= vnet) {
-                continue;
-            }
-
-            MessageBuffer *buffer = m_in[incoming][vnet];
-            if (buffer == nullptr) {
-                continue;
-            }
-
-            operateMessageBuffer(buffer, incoming, vnet);
+        }
+        DPRINTF(RubyNetwork, "vnet %d: %d pending msgs. "
+                            "Checking port %d first\n",
+                vnet, m_pending_message_count[vnet], start_in_port);
+        // check all ports starting with the one with the oldest message
+        for (int i = 0; i < in.size(); ++i) {
+            int in_port = (i + start_in_port) % in.size();
+            MessageBuffer *buffer = in[in_port];
+            if (buffer)
+                operateMessageBuffer(buffer, vnet);
         }
     }
 }
 
 void
-PerfectSwitch::operateMessageBuffer(MessageBuffer *buffer, int incoming,
-                                    int vnet)
+PerfectSwitch::operateMessageBuffer(MessageBuffer *buffer, int vnet)
 {
     MsgPtr msg_ptr;
     Message *net_msg_ptr = NULL;
 
     // temporary vectors to store the routing results
-    std::vector<LinkID> output_links;
-    std::vector<NetDest> output_link_destinations;
+    static thread_local std::vector<BaseRoutingUnit::RouteInfo> output_links;
+
     Tick current_time = m_switch->clockEdge();
 
     while (buffer->isReady(current_time)) {
-        DPRINTF(RubyNetwork, "incoming: %d\n", incoming);
+        DPRINTF(RubyNetwork, "incoming: %d\n", buffer->getIncomingLink());
 
         // Peek at message
         msg_ptr = buffer->peekMsgPtr();
         net_msg_ptr = msg_ptr.get();
         DPRINTF(RubyNetwork, "Message: %s\n", (*net_msg_ptr));
 
+
         output_links.clear();
-        output_link_destinations.clear();
-        NetDest msg_dsts = net_msg_ptr->getDestination();
-
-        // Unfortunately, the token-protocol sends some
-        // zero-destination messages, so this assert isn't valid
-        // assert(msg_dsts.count() > 0);
-
-        assert(m_link_order.size() == m_routing_table.size());
-        assert(m_link_order.size() == m_out.size());
-
-        if (m_network_ptr->getAdaptiveRouting()) {
-            if (m_network_ptr->isVNetOrdered(vnet)) {
-                // Don't adaptively route
-                for (int out = 0; out < m_out.size(); out++) {
-                    m_link_order[out].m_link = out;
-                    m_link_order[out].m_value = 0;
-                }
-            } else {
-                // Find how clogged each link is
-                for (int out = 0; out < m_out.size(); out++) {
-                    int out_queue_length = 0;
-                    for (int v = 0; v < m_virtual_networks; v++) {
-                        out_queue_length += m_out[out][v]->getSize(current_time);
-                    }
-                    int value =
-                        (out_queue_length << 8) |
-                        random_mt.random(0, 0xff);
-                    m_link_order[out].m_link = out;
-                    m_link_order[out].m_value = value;
-                }
-
-                // Look at the most empty link first
-                sort(m_link_order.begin(), m_link_order.end());
-            }
-        }
-
-        for (int i = 0; i < m_routing_table.size(); i++) {
-            // pick the next link to look at
-            int link = m_link_order[i].m_link;
-            NetDest dst = m_routing_table[link];
-            DPRINTF(RubyNetwork, "dst: %s\n", dst);
-
-            if (!msg_dsts.intersectionIsNotEmpty(dst))
-                continue;
-
-            // Remember what link we're using
-            output_links.push_back(link);
-
-            // Need to remember which destinations need this message in
-            // another vector.  This Set is the intersection of the
-            // routing_table entry and the current destination set.  The
-            // intersection must not be empty, since we are inside "if"
-            output_link_destinations.push_back(msg_dsts.AND(dst));
-
-            // Next, we update the msg_destination not to include
-            // those nodes that were already handled by this link
-            msg_dsts.removeNetDest(dst);
-        }
-
-        assert(msg_dsts.count() == 0);
+        m_switch->getRoutingUnit().route(*net_msg_ptr, vnet,
+                                         m_network_ptr->isVNetOrdered(vnet),
+                                         output_links);
 
         // Check for resources - for all outgoing queues
         bool enough = true;
         for (int i = 0; i < output_links.size(); i++) {
-            int outgoing = output_links[i];
+            int outgoing = output_links[i].m_link_id;
+            OutputPort &out_port = m_out[outgoing];
 
-            if (!m_out[outgoing][vnet]->areNSlotsAvailable(1, current_time))
+            if (!out_port.buffers[vnet]->areNSlotsAvailable(1, current_time))
                 enough = false;
 
             DPRINTF(RubyNetwork, "Checking if node is blocked ..."
@@ -263,7 +249,8 @@
 
         // Enqueue it - for all outgoing queues
         for (int i=0; i<output_links.size(); i++) {
-            int outgoing = output_links[i];
+            int outgoing = output_links[i].m_link_id;
+            OutputPort &out_port = m_out[outgoing];
 
             if (i > 0) {
                 // create a private copy of the unmodified message
@@ -273,15 +260,15 @@
             // Change the internal destination set of the message so it
             // knows which destinations this link is responsible for.
             net_msg_ptr = msg_ptr.get();
-            net_msg_ptr->getDestination() = output_link_destinations[i];
+            net_msg_ptr->getDestination() = output_links[i].m_destinations;
 
             // Enqeue msg
             DPRINTF(RubyNetwork, "Enqueuing net msg from "
                     "inport[%d][%d] to outport [%d][%d].\n",
-                    incoming, vnet, outgoing, vnet);
+                    buffer->getIncomingLink(), vnet, outgoing, vnet);
 
-            m_out[outgoing][vnet]->enqueue(msg_ptr, current_time,
-                                           m_switch->cyclesToTicks(Cycles(1)));
+            out_port.buffers[vnet]->enqueue(msg_ptr, current_time,
+                                           out_port.latency);
         }
     }
 }
diff --git a/src/mem/ruby/network/simple/PerfectSwitch.hh b/src/mem/ruby/network/simple/PerfectSwitch.hh
index 32c5383..589bca1 100644
--- a/src/mem/ruby/network/simple/PerfectSwitch.hh
+++ b/src/mem/ruby/network/simple/PerfectSwitch.hh
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
  * All rights reserved.
  *
@@ -54,14 +66,6 @@
 class SimpleNetwork;
 class Switch;
 
-struct LinkOrder
-{
-    int m_link;
-    int m_value;
-};
-
-bool operator<(const LinkOrder& l1, const LinkOrder& l2);
-
 class PerfectSwitch : public Consumer
 {
   public:
@@ -74,7 +78,10 @@
     void init(SimpleNetwork *);
     void addInPort(const std::vector<MessageBuffer*>& in);
     void addOutPort(const std::vector<MessageBuffer*>& out,
-                    const NetDest& routing_table_entry);
+                    const NetDest& routing_table_entry,
+                    const PortDirection &dst_inport,
+                    Tick routing_latency,
+                    int link_weight);
 
     int getInLinks() const { return m_in.size(); }
     int getOutLinks() const { return m_out.size(); }
@@ -92,24 +99,36 @@
     PerfectSwitch& operator=(const PerfectSwitch& obj);
 
     void operateVnet(int vnet);
-    void operateMessageBuffer(MessageBuffer *b, int incoming, int vnet);
+    void operateMessageBuffer(MessageBuffer *b, int vnet);
 
     const SwitchID m_switch_id;
     Switch * const m_switch;
 
-    // vector of queues from the components
+    // Vector of queues associated to each port.
     std::vector<std::vector<MessageBuffer*> > m_in;
-    std::vector<std::vector<MessageBuffer*> > m_out;
 
-    std::vector<NetDest> m_routing_table;
-    std::vector<LinkOrder> m_link_order;
+    // Each output port also has a latency for routing to that port
+    struct OutputPort
+    {
+        Tick latency;
+        std::vector<MessageBuffer*> buffers;
+    };
+    std::vector<OutputPort> m_out;
+
+    // input ports ordered by priority; indexed by vnet first
+    std::vector<std::vector<MessageBuffer*> > m_in_prio;
+    // input ports grouped by priority; indexed by vnet,prio_lv
+    std::vector<std::vector<std::vector<MessageBuffer*>>> m_in_prio_groups;
+
+    void updatePriorityGroups(int vnet, MessageBuffer* buf);
 
     uint32_t m_virtual_networks;
-    int m_round_robin_start;
     int m_wakeups_wo_switch;
 
     SimpleNetwork* m_network_ptr;
     std::vector<int> m_pending_message_count;
+
+    MessageBuffer* inBuffer(int in_port, int vnet) const;
 };
 
 inline std::ostream&
diff --git a/src/mem/ruby/network/simple/SConscript b/src/mem/ruby/network/simple/SConscript
index 32c5082..97055f5 100644
--- a/src/mem/ruby/network/simple/SConscript
+++ b/src/mem/ruby/network/simple/SConscript
@@ -1,5 +1,17 @@
 # -*- mode:python -*-
 
+# Copyright (c) 2021 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
 # Copyright (c) 2009 The Hewlett-Packard Development Company
 # All rights reserved.
 #
@@ -28,14 +40,17 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('SimpleLink.py', sim_objects=['SimpleExtLink', 'SimpleIntLink'])
-SimObject('SimpleNetwork.py', sim_objects=['SimpleNetwork', 'Switch'])
+SimObject('SimpleNetwork.py', sim_objects=['SimpleNetwork', 'BaseRoutingUnit',
+    'WeightBased', 'Switch'])
 
 Source('PerfectSwitch.cc')
 Source('SimpleLink.cc')
 Source('SimpleNetwork.cc')
 Source('Switch.cc')
 Source('Throttle.cc')
+
+Source('routing/WeightBased.cc')
diff --git a/src/mem/ruby/network/simple/SimpleLink.cc b/src/mem/ruby/network/simple/SimpleLink.cc
index 0f55545..8aea3f3 100644
--- a/src/mem/ruby/network/simple/SimpleLink.cc
+++ b/src/mem/ruby/network/simple/SimpleLink.cc
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2011 Advanced Micro Devices, Inc.
  * All rights reserved.
  *
@@ -51,13 +63,11 @@
 }
 
 SimpleIntLink::SimpleIntLink(const Params &p)
-    : BasicIntLink(p)
+    : BasicIntLink(p),
+      m_bw_multiplier(p.bandwidth_factor),
+      m_buffers(p.buffers)
 {
-    // For the simple links, the bandwidth factor translates to the
-    // bandwidth multiplier.  The multipiler, in combination with the
-    // endpoint bandwidth multiplier - message size multiplier ratio,
-    // determines the link bandwidth in bytes
-    m_bw_multiplier = p.bandwidth_factor;
+
 }
 
 void
diff --git a/src/mem/ruby/network/simple/SimpleLink.hh b/src/mem/ruby/network/simple/SimpleLink.hh
index 2f2582c..a311392 100644
--- a/src/mem/ruby/network/simple/SimpleLink.hh
+++ b/src/mem/ruby/network/simple/SimpleLink.hh
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2011 Advanced Micro Devices, Inc.
  * All rights reserved.
  *
@@ -73,6 +85,7 @@
     void print(std::ostream& out) const;
 
     int m_bw_multiplier;
+    const std::vector<MessageBuffer*> m_buffers;
 };
 
 inline std::ostream&
diff --git a/src/mem/ruby/network/simple/SimpleLink.py b/src/mem/ruby/network/simple/SimpleLink.py
index de9eb76..0497594 100644
--- a/src/mem/ruby/network/simple/SimpleLink.py
+++ b/src/mem/ruby/network/simple/SimpleLink.py
@@ -1,3 +1,15 @@
+# Copyright (c) 2021 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
 # Copyright (c) 2011 Advanced Micro Devices, Inc.
 # All rights reserved.
 #
@@ -26,8 +38,10 @@
 
 from m5.params import *
 from m5.proxy import *
+from m5.util import fatal
 from m5.SimObject import SimObject
 from m5.objects.BasicLink import BasicIntLink, BasicExtLink
+from m5.objects.MessageBuffer import MessageBuffer
 
 class SimpleExtLink(BasicExtLink):
     type = 'SimpleExtLink'
@@ -38,3 +52,40 @@
     type = 'SimpleIntLink'
     cxx_header = "mem/ruby/network/simple/SimpleLink.hh"
     cxx_class = 'gem5::ruby::SimpleIntLink'
+
+    # Buffers for this internal link.
+    # One buffer is allocated per vnet when setup_buffers is called.
+    # These are created by setup_buffers and the user should not
+    # set these manually.
+    buffers = VectorParam.MessageBuffer([], "Buffers for int_links")
+
+    def setup_buffers(self, network):
+        if len(self.buffers) > 0:
+            fatal("User should not manually set links' \
+                   in_buffers or out_buffers")
+
+        # The network needs number_of_virtual_networks buffers per
+        # in and out port
+        buffers = []
+        for i in range(int(network.number_of_virtual_networks)):
+            buffers.append(MessageBuffer(ordered = True))
+
+        # If physical_vnets_channels is set we adjust the buffer sizes and
+        # the max_dequeue_rate in order to achieve the expected thoughput
+        # assuming a fully pipelined link, i.e., throughput of 1 msg per cycle
+        # per channel (assuming the channels width matches the protocol
+        # logical message size, otherwise maximum thoughput may be smaller).
+        # In MessageBuffer, an entry occupied by a dequeued message at cycle
+        # X will available for enqueuing another message at cycle X+1. So
+        # for a 1 cy enqueue latency, 2 entries are needed. For any latency,
+        # the size should be at least latency+1.
+        if len(network.physical_vnets_channels) != 0:
+            assert(len(network.physical_vnets_channels) == \
+                   int(network.number_of_virtual_networks))
+            for i in range(int(network.number_of_virtual_networks)):
+                buffers[i].buffer_size = \
+                    network.physical_vnets_channels[i] * (self.latency + 1)
+                buffers[i].max_dequeue_rate = \
+                    network.physical_vnets_channels[i]
+
+        self.buffers = buffers
diff --git a/src/mem/ruby/network/simple/SimpleNetwork.cc b/src/mem/ruby/network/simple/SimpleNetwork.cc
index 38d9f79..1ddd53e 100644
--- a/src/mem/ruby/network/simple/SimpleNetwork.cc
+++ b/src/mem/ruby/network/simple/SimpleNetwork.cc
@@ -61,19 +61,33 @@
 SimpleNetwork::SimpleNetwork(const Params &p)
     : Network(p), m_buffer_size(p.buffer_size),
       m_endpoint_bandwidth(p.endpoint_bandwidth),
-      m_adaptive_routing(p.adaptive_routing),
       networkStats(this)
 {
     // record the routers
     for (std::vector<BasicRouter*>::const_iterator i = p.routers.begin();
          i != p.routers.end(); ++i) {
-        Switch* s = safe_cast<Switch*>(*i);
-        m_switches.push_back(s);
+        auto* s = safe_cast<Switch*>(*i);
         s->init_net_ptr(this);
+        auto id = static_cast<size_t>(s->params().router_id);
+        m_switches[id] = s;
     }
 
-    m_int_link_buffers = p.int_link_buffers;
-    m_num_connected_buffers = 0;
+    const std::vector<int> &physical_vnets_channels =
+        p.physical_vnets_channels;
+    const std::vector<int> &physical_vnets_bandwidth =
+        p.physical_vnets_bandwidth;
+    bool physical_vnets = physical_vnets_channels.size() > 0;
+    int vnets = p.number_of_virtual_networks;
+
+    fatal_if(physical_vnets && (physical_vnets_channels.size() != vnets),
+        "physical_vnets_channels must provide channel count for all vnets");
+
+    fatal_if(!physical_vnets && (physical_vnets_bandwidth.size() != 0),
+        "physical_vnets_bandwidth also requires physical_vnets_channels");
+
+    fatal_if((physical_vnets_bandwidth.size() != vnets) &&
+             (physical_vnets_bandwidth.size() != 0),
+        "physical_vnets_bandwidth must provide BW for all vnets");
 }
 
 void
@@ -95,14 +109,20 @@
 {
     NodeID local_dest = getLocalNodeID(global_dest);
     assert(local_dest < m_nodes);
-    assert(src < m_switches.size());
     assert(m_switches[src] != NULL);
 
     SimpleExtLink *simple_link = safe_cast<SimpleExtLink*>(link);
 
+    // some destinations don't use all vnets, but Switch requires the size
+    // output buffer list to match the number of vnets
+    int num_vnets = params().number_of_virtual_networks;
+    gem5_assert(num_vnets >= m_fromNetQueues[local_dest].size());
+    m_fromNetQueues[local_dest].resize(num_vnets, nullptr);
+
     m_switches[src]->addOutPort(m_fromNetQueues[local_dest],
-                                routing_table_entry[0], simple_link->m_latency,
-                                simple_link->m_bw_multiplier);
+                                routing_table_entry[0],
+                                simple_link->m_latency, 0,
+                                simple_link->m_bw_multiplier, true);
 }
 
 // From an endpoint node to a switch
@@ -122,24 +142,19 @@
                                 PortDirection src_outport,
                                 PortDirection dst_inport)
 {
-    // Create a set of new MessageBuffers
-    std::vector<MessageBuffer*> queues(m_virtual_networks);
-
-    for (int i = 0; i < m_virtual_networks; i++) {
-        // allocate a buffer
-        assert(m_num_connected_buffers < m_int_link_buffers.size());
-        MessageBuffer* buffer_ptr = m_int_link_buffers[m_num_connected_buffers];
-        m_num_connected_buffers++;
-        queues[i] = buffer_ptr;
-    }
-
     // Connect it to the two switches
     SimpleIntLink *simple_link = safe_cast<SimpleIntLink*>(link);
 
-    m_switches[dest]->addInPort(queues);
-    m_switches[src]->addOutPort(queues, routing_table_entry[0],
+    m_switches[dest]->addInPort(simple_link->m_buffers);
+    m_switches[src]->addOutPort(simple_link->m_buffers, routing_table_entry[0],
                                 simple_link->m_latency,
-                                simple_link->m_bw_multiplier);
+                                simple_link->m_weight,
+                                simple_link->m_bw_multiplier,
+                                false,
+                                dst_inport);
+    // Maitain a global list of buffers (used for functional accesses only)
+    m_int_link_buffers.insert(m_int_link_buffers.end(),
+            simple_link->m_buffers.begin(), simple_link->m_buffers.end());
 }
 
 void
@@ -164,9 +179,9 @@
             ;
 
         // Now state what the formula is.
-        for (int i = 0; i < m_switches.size(); i++) {
+        for (auto& it : m_switches) {
             *(networkStats.m_msg_counts[(unsigned int) type]) +=
-                sum(m_switches[i]->getMsgCount(type));
+                sum(it.second->getMsgCount(type));
         }
 
         *(networkStats.m_msg_bytes[(unsigned int) type]) =
@@ -178,8 +193,8 @@
 void
 SimpleNetwork::collateStats()
 {
-    for (int i = 0; i < m_switches.size(); i++) {
-        m_switches[i]->collateStats();
+    for (auto& it : m_switches) {
+        it.second->collateStats();
     }
 }
 
@@ -197,8 +212,8 @@
 bool
 SimpleNetwork::functionalRead(Packet *pkt)
 {
-    for (unsigned int i = 0; i < m_switches.size(); i++) {
-        if (m_switches[i]->functionalRead(pkt))
+    for (auto& it : m_switches) {
+        if (it.second->functionalRead(pkt))
             return true;
     }
     for (unsigned int i = 0; i < m_int_link_buffers.size(); ++i) {
@@ -213,8 +228,8 @@
 SimpleNetwork::functionalRead(Packet *pkt, WriteMask &mask)
 {
     bool read = false;
-    for (unsigned int i = 0; i < m_switches.size(); i++) {
-        if (m_switches[i]->functionalRead(pkt, mask))
+    for (auto& it : m_switches) {
+        if (it.second->functionalRead(pkt, mask))
             read = true;
     }
     for (unsigned int i = 0; i < m_int_link_buffers.size(); ++i) {
@@ -229,8 +244,8 @@
 {
     uint32_t num_functional_writes = 0;
 
-    for (unsigned int i = 0; i < m_switches.size(); i++) {
-        num_functional_writes += m_switches[i]->functionalWrite(pkt);
+    for (auto& it : m_switches) {
+        num_functional_writes += it.second->functionalWrite(pkt);
     }
 
     for (unsigned int i = 0; i < m_int_link_buffers.size(); ++i) {
diff --git a/src/mem/ruby/network/simple/SimpleNetwork.hh b/src/mem/ruby/network/simple/SimpleNetwork.hh
index 4c003a7..b90ee33 100644
--- a/src/mem/ruby/network/simple/SimpleNetwork.hh
+++ b/src/mem/ruby/network/simple/SimpleNetwork.hh
@@ -61,7 +61,8 @@
 class SimpleNetwork : public Network
 {
   public:
-    typedef SimpleNetworkParams Params;
+    PARAMS(SimpleNetwork);
+
     SimpleNetwork(const Params &p);
     ~SimpleNetwork() = default;
 
@@ -69,7 +70,6 @@
 
     int getBufferSize() { return m_buffer_size; }
     int getEndpointBandwidth() { return m_endpoint_bandwidth; }
-    bool getAdaptiveRouting() {return m_adaptive_routing; }
 
     void collateStats();
     void regStats();
@@ -102,12 +102,10 @@
     SimpleNetwork(const SimpleNetwork& obj);
     SimpleNetwork& operator=(const SimpleNetwork& obj);
 
-    std::vector<Switch*> m_switches;
+    std::unordered_map<int, Switch*> m_switches;
     std::vector<MessageBuffer*> m_int_link_buffers;
-    int m_num_connected_buffers;
     const int m_buffer_size;
     const int m_endpoint_bandwidth;
-    const bool m_adaptive_routing;
 
 
     struct NetworkStats : public statistics::Group
diff --git a/src/mem/ruby/network/simple/SimpleNetwork.py b/src/mem/ruby/network/simple/SimpleNetwork.py
index cfd95f7..42484a1 100644
--- a/src/mem/ruby/network/simple/SimpleNetwork.py
+++ b/src/mem/ruby/network/simple/SimpleNetwork.py
@@ -1,3 +1,15 @@
+# Copyright (c) 2021 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
 # Copyright (c) 2009 Advanced Micro Devices, Inc.
 # All rights reserved.
 #
@@ -27,6 +39,8 @@
 from m5.params import *
 from m5.proxy import *
 
+from m5.util import fatal
+from m5.SimObject import SimObject
 from m5.objects.Network import RubyNetwork
 from m5.objects.BasicRouter import BasicRouter
 from m5.objects.MessageBuffer import MessageBuffer
@@ -36,40 +50,46 @@
     cxx_header = "mem/ruby/network/simple/SimpleNetwork.hh"
     cxx_class = 'gem5::ruby::SimpleNetwork'
 
-    buffer_size = Param.Int(0,
-        "default buffer size; 0 indicates infinite buffering");
-    endpoint_bandwidth = Param.Int(1000, "bandwidth adjustment factor");
-    adaptive_routing = Param.Bool(False, "enable adaptive routing");
-    int_link_buffers = VectorParam.MessageBuffer("Buffers for int_links")
+    buffer_size = Param.Int(0, "default internal buffer size for links and\
+                                routers; 0 indicates infinite buffering")
+    endpoint_bandwidth = Param.Int(1000, "bandwidth adjustment factor")
+
+    physical_vnets_channels = VectorParam.Int([],
+        "Set to emulate multiple channels for each vnet."
+        "If not set, all vnets share the same physical channel.")
+
+    physical_vnets_bandwidth = VectorParam.Int([],
+        "Assign a different link bandwidth factor for each vnet channels."
+        "Only valid when physical_vnets_channels is set. This overrides the"
+        "bandwidth_factor parameter set for the  individual links.")
 
     def setup_buffers(self):
-        # Note that all SimpleNetwork MessageBuffers are currently ordered
-        network_buffers = []
+        # Setup internal buffers for links and routers
         for link in self.int_links:
-            # The network needs number_of_virtual_networks buffers per
-            # int_link port
-            for i in range(int(self.number_of_virtual_networks)):
-                network_buffers.append(MessageBuffer(ordered = True))
-                network_buffers.append(MessageBuffer(ordered = True))
-        self.int_link_buffers = network_buffers
-
-        # Also add buffers for all router-link connections
+            link.setup_buffers(self)
         for router in self.routers:
-            router_buffers = []
-            # Add message buffers to routers at the end of each
-            # unidirectional internal link
-            for link in self.int_links:
-                if link.dst_node == router:
-                    for i in range(int(self.number_of_virtual_networks)):
-                        router_buffers.append(MessageBuffer(ordered = True))
+            router.setup_buffers(self)
 
-            # Add message buffers to routers for each external link connection
-            for link in self.ext_links:
-                # Routers can only be int_nodes on ext_links
-                if link.int_node in self.routers:
-                    for i in range(int(self.number_of_virtual_networks)):
-                        router_buffers.append(MessageBuffer(ordered = True))
-            router.port_buffers = router_buffers
+
+class BaseRoutingUnit(SimObject):
+    type = 'BaseRoutingUnit'
+    abstract = True
+    cxx_header = 'mem/ruby/network/simple/routing/BaseRoutingUnit.hh'
+    cxx_class = 'gem5::ruby::BaseRoutingUnit'
+
+
+class WeightBased(BaseRoutingUnit):
+    type = 'WeightBased'
+    cxx_header = 'mem/ruby/network/simple/routing/WeightBased.hh'
+    cxx_class = 'gem5::ruby::WeightBased'
+
+    adaptive_routing = Param.Bool(False, "enable adaptive routing")
+
+
+class SwitchPortBuffer(MessageBuffer):
+    """MessageBuffer type used internally by the Switch port buffers"""
+    ordered = True
+    allow_zero_latency = True
 
 class Switch(BasicRouter):
     type = 'Switch'
@@ -78,4 +98,55 @@
 
     virt_nets = Param.Int(Parent.number_of_virtual_networks,
                           "number of virtual networks")
-    port_buffers = VectorParam.MessageBuffer("Port buffers")
+
+    int_routing_latency = Param.Cycles(BasicRouter.latency,
+                                    "Routing latency to internal links")
+    ext_routing_latency = Param.Cycles(BasicRouter.latency,
+                                    "Routing latency to external links")
+
+    # Internal port buffers used between the PerfectSwitch and
+    # Throttle objects. There is one buffer per virtual network
+    # and per output port.
+    # These are created by setup_buffers and the user should not
+    # set these manually.
+    port_buffers = VectorParam.MessageBuffer([], "Port buffers")
+
+    routing_unit = Param.BaseRoutingUnit(
+                        WeightBased(adaptive_routing = False),
+                        "Routing strategy to be used")
+
+    def setup_buffers(self, network):
+        def vnet_buffer_size(vnet):
+            """
+            Gets the size of the message buffers associated to a vnet
+            If physical_vnets_channels is set we just multiply the size of the
+            buffers as SimpleNetwork does not actually creates multiple phy
+            channels per vnet.
+            """
+            if len(network.physical_vnets_channels) == 0:
+                return network.buffer_size
+            else:
+                return network.buffer_size * \
+                       network.physical_vnets_channels[vnet]
+
+        if len(self.port_buffers) > 0:
+            fatal("User should not manually set routers' port_buffers")
+
+        router_buffers = []
+        # Add message buffers to routers at the end of each
+        # unidirectional internal link
+        for link in network.int_links:
+            if link.dst_node == self:
+                for i in range(int(network.number_of_virtual_networks)):
+                    router_buffers.append(SwitchPortBuffer(
+                                    buffer_size = vnet_buffer_size(i)))
+
+        # Add message buffers to routers for each external link connection
+        for link in network.ext_links:
+            # Routers can only be int_nodes on ext_links
+            if link.int_node == self:
+                for i in range(int(network.number_of_virtual_networks)):
+                    router_buffers.append(SwitchPortBuffer(
+                                    buffer_size = vnet_buffer_size(i)))
+
+        self.port_buffers = router_buffers
diff --git a/src/mem/ruby/network/simple/Switch.cc b/src/mem/ruby/network/simple/Switch.cc
index 668ea89..c74246e 100644
--- a/src/mem/ruby/network/simple/Switch.cc
+++ b/src/mem/ruby/network/simple/Switch.cc
@@ -58,7 +58,10 @@
 
 Switch::Switch(const Params &p)
   : BasicRouter(p),
-    perfectSwitch(m_id, this, p.virt_nets), m_num_connected_buffers(0),
+    perfectSwitch(m_id, this, p.virt_nets),
+    m_int_routing_latency(p.int_routing_latency),
+    m_ext_routing_latency(p.ext_routing_latency),
+    m_routing_unit(*p.routing_unit), m_num_connected_buffers(0),
     switchStats(this)
 {
     m_port_buffers.reserve(p.port_buffers.size());
@@ -72,6 +75,7 @@
 {
     BasicRouter::init();
     perfectSwitch.init(m_network_ptr);
+    m_routing_unit.init_parent(this);
 }
 
 void
@@ -83,12 +87,31 @@
 void
 Switch::addOutPort(const std::vector<MessageBuffer*>& out,
                    const NetDest& routing_table_entry,
-                   Cycles link_latency, int bw_multiplier)
+                   Cycles link_latency, int link_weight,
+                   int bw_multiplier,
+                   bool is_external,
+                   PortDirection dst_inport)
 {
+    const std::vector<int> &physical_vnets_channels =
+        m_network_ptr->params().physical_vnets_channels;
+
     // Create a throttle
-    throttles.emplace_back(m_id, m_network_ptr->params().ruby_system,
-        throttles.size(), link_latency, bw_multiplier,
-        m_network_ptr->getEndpointBandwidth(), this);
+    if (physical_vnets_channels.size() > 0 && !out.empty()) {
+        // Assign a different bandwith for each vnet channel if specified by
+        // physical_vnets_bandwidth, otherwise all channels use bw_multiplier
+        std::vector<int> physical_vnets_bandwidth =
+            m_network_ptr->params().physical_vnets_bandwidth;
+        physical_vnets_bandwidth.resize(out.size(), bw_multiplier);
+
+        throttles.emplace_back(m_id, m_network_ptr->params().ruby_system,
+            throttles.size(), link_latency,
+            physical_vnets_channels, physical_vnets_bandwidth,
+            m_network_ptr->getEndpointBandwidth(), this);
+    } else {
+        throttles.emplace_back(m_id, m_network_ptr->params().ruby_system,
+            throttles.size(), link_latency, bw_multiplier,
+            m_network_ptr->getEndpointBandwidth(), this);
+    }
 
     // Create one buffer per vnet (these are intermediaryQueues)
     std::vector<MessageBuffer*> intermediateBuffers;
@@ -101,8 +124,11 @@
         intermediateBuffers.push_back(buffer_ptr);
     }
 
+    Tick routing_latency = is_external ? cyclesToTicks(m_ext_routing_latency) :
+                                         cyclesToTicks(m_int_routing_latency);
     // Hook the queues to the PerfectSwitch
-    perfectSwitch.addOutPort(intermediateBuffers, routing_table_entry);
+    perfectSwitch.addOutPort(intermediateBuffers, routing_table_entry,
+                             dst_inport, routing_latency, link_weight);
 
     // Hook the queues to the Throttle
     throttles.back().addLinks(intermediateBuffers, out);
@@ -113,10 +139,6 @@
 {
     BasicRouter::regStats();
 
-    for (auto& throttle : throttles) {
-        throttle.regStats();
-    }
-
     for (const auto& throttle : throttles) {
         switchStats.m_avg_utilization += throttle.getUtilization();
     }
@@ -151,18 +173,12 @@
 Switch::resetStats()
 {
     perfectSwitch.clearStats();
-    for (auto& throttle : throttles) {
-        throttle.clearStats();
-    }
 }
 
 void
 Switch::collateStats()
 {
     perfectSwitch.collateStats();
-    for (auto& throttle : throttles) {
-        throttle.collateStats();
-    }
 }
 
 void
diff --git a/src/mem/ruby/network/simple/Switch.hh b/src/mem/ruby/network/simple/Switch.hh
index 1498df6..86abfda 100644
--- a/src/mem/ruby/network/simple/Switch.hh
+++ b/src/mem/ruby/network/simple/Switch.hh
@@ -61,6 +61,7 @@
 #include "mem/ruby/network/BasicRouter.hh"
 #include "mem/ruby/network/simple/PerfectSwitch.hh"
 #include "mem/ruby/network/simple/Throttle.hh"
+#include "mem/ruby/network/simple/routing/BaseRoutingUnit.hh"
 #include "mem/ruby/protocol/MessageSizeType.hh"
 #include "params/Switch.hh"
 
@@ -77,6 +78,12 @@
 class Switch : public BasicRouter
 {
   public:
+
+    // Makes sure throttle sends messages to the links after the switch is
+    // done forwarding the messages in the same cycle
+    static constexpr Event::Priority PERFECTSWITCH_EV_PRI = Event::Default_Pri;
+    static constexpr Event::Priority THROTTLE_EV_PRI = Event::Default_Pri + 1;
+
     typedef SwitchParams Params;
     Switch(const Params &p);
     ~Switch() = default;
@@ -85,7 +92,9 @@
     void addInPort(const std::vector<MessageBuffer*>& in);
     void addOutPort(const std::vector<MessageBuffer*>& out,
                     const NetDest& routing_table_entry,
-                    Cycles link_latency, int bw_multiplier);
+                    Cycles link_latency, int link_weight, int bw_multiplier,
+                    bool is_external,
+                    PortDirection dst_inport = "");
 
     void resetStats();
     void collateStats();
@@ -100,6 +109,8 @@
     bool functionalRead(Packet *, WriteMask&);
     uint32_t functionalWrite(Packet *);
 
+    BaseRoutingUnit& getRoutingUnit() { return m_routing_unit; }
+
   private:
     // Private copy constructor and assignment operator
     Switch(const Switch& obj);
@@ -109,6 +120,11 @@
     SimpleNetwork* m_network_ptr;
     std::list<Throttle> throttles;
 
+    const Cycles m_int_routing_latency;
+    const Cycles m_ext_routing_latency;
+
+    BaseRoutingUnit &m_routing_unit;
+
     unsigned m_num_connected_buffers;
     std::vector<MessageBuffer*> m_port_buffers;
 
diff --git a/src/mem/ruby/network/simple/Throttle.cc b/src/mem/ruby/network/simple/Throttle.cc
index 20d269f..20cebcc 100644
--- a/src/mem/ruby/network/simple/Throttle.cc
+++ b/src/mem/ruby/network/simple/Throttle.cc
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
  * All rights reserved.
  *
@@ -38,6 +50,7 @@
 #include "mem/ruby/network/simple/Switch.hh"
 #include "mem/ruby/slicc_interface/Message.hh"
 #include "mem/ruby/system/RubySystem.hh"
+#include "sim/stats.hh"
 
 namespace gem5
 {
@@ -53,23 +66,45 @@
 static int network_message_to_size(Message* net_msg_ptr);
 
 Throttle::Throttle(int sID, RubySystem *rs, NodeID node, Cycles link_latency,
-                   int link_bandwidth_multiplier, int endpoint_bandwidth,
-                   Switch *em)
-    : Consumer(em),
+                   int endpoint_bandwidth, Switch *em)
+    : Consumer(em,  Switch::THROTTLE_EV_PRI),
       m_switch_id(sID), m_switch(em), m_node(node),
-      m_ruby_system(rs),
+      m_physical_vnets(false), m_ruby_system(rs),
       throttleStats(em, node)
 {
     m_vnets = 0;
 
-    assert(link_bandwidth_multiplier > 0);
-    m_link_bandwidth_multiplier = link_bandwidth_multiplier;
-
     m_link_latency = link_latency;
     m_endpoint_bandwidth = endpoint_bandwidth;
 
     m_wakeups_wo_switch = 0;
-    m_link_utilization_proxy = 0;
+}
+
+Throttle::Throttle(int sID, RubySystem *rs, NodeID node, Cycles link_latency,
+                   int link_bandwidth_multiplier, int endpoint_bandwidth,
+                   Switch *em)
+    : Throttle(sID, rs, node, link_latency, endpoint_bandwidth, em)
+{
+    gem5_assert(link_bandwidth_multiplier > 0);
+    m_link_bandwidth_multiplier.push_back(link_bandwidth_multiplier);
+}
+
+Throttle::Throttle(int sID, RubySystem *rs, NodeID node, Cycles link_latency,
+                   const std::vector<int> &vnet_channels,
+                   const std::vector<int> &vnet_bandwidth_multiplier,
+                   int endpoint_bandwidth, Switch *em)
+    : Throttle(sID, rs, node, link_latency, endpoint_bandwidth, em)
+{
+    m_physical_vnets = true;
+    for (auto link_bandwidth_multiplier : vnet_bandwidth_multiplier){
+        gem5_assert(link_bandwidth_multiplier > 0);
+        m_link_bandwidth_multiplier.push_back(link_bandwidth_multiplier);
+    }
+    for (auto channels : vnet_channels){
+        gem5_assert(channels > 0);
+        m_vnet_channels.push_back(channels);
+    }
+    gem5_assert(m_link_bandwidth_multiplier.size() == m_vnet_channels.size());
 }
 
 void
@@ -82,8 +117,7 @@
         MessageBuffer *in_ptr = in_vec[vnet];
         MessageBuffer *out_ptr = out_vec[vnet];
 
-        m_vnets++;
-        m_units_remaining.push_back(0);
+        m_units_remaining.emplace_back(getChannelCnt(vnet),0);
         m_in.push_back(in_ptr);
         m_out.push_back(out_ptr);
 
@@ -92,34 +126,74 @@
         std::string desc = "[Queue to Throttle " +
             std::to_string(m_switch_id) + " " + std::to_string(m_node) + "]";
     }
+
+    m_vnets = in_vec.size();
+
+    gem5_assert(m_physical_vnets ?
+           (m_link_bandwidth_multiplier.size() == m_vnets) :
+           (m_link_bandwidth_multiplier.size() == 1));
+}
+
+int
+Throttle::getLinkBandwidth(int vnet) const
+{
+    int bw = m_physical_vnets ?
+                m_link_bandwidth_multiplier[vnet] :
+                m_link_bandwidth_multiplier[0];
+    gem5_assert(bw > 0);
+    return m_endpoint_bandwidth * bw;
+}
+
+int
+Throttle::getTotalLinkBandwidth() const
+{
+    int sum = getLinkBandwidth(0) * getChannelCnt(0);
+    if (m_physical_vnets) {
+        for (unsigned i = 1; i < m_vnets; ++i)
+            sum += getLinkBandwidth(i) * getChannelCnt(i);
+    }
+    return sum;
+}
+
+int
+Throttle::getChannelCnt(int vnet) const
+{
+    return m_physical_vnets ? m_vnet_channels[vnet] : 1;
 }
 
 void
-Throttle::operateVnet(int vnet, int &bw_remaining, bool &schedule_wakeup,
+Throttle::operateVnet(int vnet, int channel, int &total_bw_remaining,
+                      bool &bw_saturated, bool &output_blocked,
                       MessageBuffer *in, MessageBuffer *out)
 {
     if (out == nullptr || in == nullptr) {
         return;
     }
 
-    assert(m_units_remaining[vnet] >= 0);
+    int &units_remaining = m_units_remaining[vnet][channel];
+
+    gem5_assert(units_remaining >= 0);
     Tick current_time = m_switch->clockEdge();
 
-    while (bw_remaining > 0 && (in->isReady(current_time) ||
-                                m_units_remaining[vnet] > 0) &&
+    int bw_remaining = m_physical_vnets ?
+                getLinkBandwidth(vnet) : total_bw_remaining;
+
+    auto hasPendingWork = [&]{ return in->isReady(current_time) ||
+                                      units_remaining > 0; };
+    while ((bw_remaining > 0) && hasPendingWork() &&
            out->areNSlotsAvailable(1, current_time)) {
         // See if we are done transferring the previous message on
         // this virtual network
-        if (m_units_remaining[vnet] == 0 && in->isReady(current_time)) {
+        if (units_remaining == 0 && in->isReady(current_time)) {
             // Find the size of the message we are moving
             MsgPtr msg_ptr = in->peekMsgPtr();
             Message *net_msg_ptr = msg_ptr.get();
-            m_units_remaining[vnet] +=
-                network_message_to_size(net_msg_ptr);
+            Tick msg_enqueue_time = msg_ptr->getLastEnqueueTime();
+            units_remaining = network_message_to_size(net_msg_ptr);
 
             DPRINTF(RubyNetwork, "throttle: %d my bw %d bw spent "
                     "enqueueing net msg %d time: %lld.\n",
-                    m_node, getLinkBandwidth(), m_units_remaining[vnet],
+                    m_node, getLinkBandwidth(vnet), units_remaining,
                     m_ruby_system->curCycle());
 
             // Move the message
@@ -129,24 +203,39 @@
 
             // Count the message
             (*(throttleStats.
-                m_msg_counts[net_msg_ptr->getMessageSize()]))[vnet]++;
+                msg_counts[net_msg_ptr->getMessageSize()]))[vnet]++;
+            throttleStats.total_msg_count += 1;
+            uint32_t total_size =
+                Network::MessageSizeType_to_int(net_msg_ptr->getMessageSize());
+            throttleStats.total_msg_bytes += total_size;
+            total_size -=
+                Network::MessageSizeType_to_int(MessageSizeType_Control);
+            throttleStats.total_data_msg_bytes += total_size;
+            throttleStats.total_msg_wait_time +=
+                current_time - msg_enqueue_time;
             DPRINTF(RubyNetwork, "%s\n", *out);
         }
 
         // Calculate the amount of bandwidth we spent on this message
-        int diff = m_units_remaining[vnet] - bw_remaining;
-        m_units_remaining[vnet] = std::max(0, diff);
-        bw_remaining = std::max(0, -diff);
+        int spent = std::min(units_remaining, bw_remaining);
+        units_remaining -= spent;
+        bw_remaining -= spent;
+        total_bw_remaining -= spent;
     }
 
-    if (bw_remaining > 0 && (in->isReady(current_time) ||
-                             m_units_remaining[vnet] > 0) &&
-        !out->areNSlotsAvailable(1, current_time)) {
-        DPRINTF(RubyNetwork, "vnet: %d", vnet);
+    gem5_assert(units_remaining >= 0);
+    gem5_assert(bw_remaining >= 0);
+    gem5_assert(total_bw_remaining >= 0);
 
-        // schedule me to wakeup again because I'm waiting for my
-        // output queue to become available
-        schedule_wakeup = true;
+    // Notify caller if
+    //  - we ran out of bandwith and still have stuff to do
+    //  - we had something to do but output queue was unavailable
+    if (hasPendingWork()) {
+        gem5_assert((bw_remaining == 0) ||
+                    !out->areNSlotsAvailable(1, current_time));
+        bw_saturated = bw_saturated || (bw_remaining == 0);
+        output_blocked = output_blocked ||
+            !out->areNSlotsAvailable(1, current_time);
     }
 }
 
@@ -154,11 +243,12 @@
 Throttle::wakeup()
 {
     // Limits the number of message sent to a limited number of bytes/cycle.
-    assert(getLinkBandwidth() > 0);
-    int bw_remaining = getLinkBandwidth();
+    assert(getTotalLinkBandwidth() > 0);
+    int bw_remaining = getTotalLinkBandwidth();
 
     m_wakeups_wo_switch++;
-    bool schedule_wakeup = false;
+    bool bw_saturated = false;
+    bool output_blocked = false;
 
     // variable for deciding the direction in which to iterate
     bool iteration_direction = false;
@@ -172,13 +262,19 @@
 
     if (iteration_direction) {
         for (int vnet = 0; vnet < m_vnets; ++vnet) {
-            operateVnet(vnet, bw_remaining, schedule_wakeup,
-                        m_in[vnet], m_out[vnet]);
+            for (int channel = 0; channel < getChannelCnt(vnet); ++channel) {
+                operateVnet(vnet, channel, bw_remaining,
+                            bw_saturated, output_blocked,
+                            m_in[vnet], m_out[vnet]);
+            }
         }
     } else {
         for (int vnet = m_vnets-1; vnet >= 0; --vnet) {
-            operateVnet(vnet, bw_remaining, schedule_wakeup,
-                        m_in[vnet], m_out[vnet]);
+            for (int channel = 0; channel < getChannelCnt(vnet); ++channel) {
+                operateVnet(vnet, channel, bw_remaining,
+                            bw_saturated, output_blocked,
+                            m_in[vnet], m_out[vnet]);
+            }
         }
     }
 
@@ -187,71 +283,34 @@
     // assert(bw_remaining != getLinkBandwidth());
 
     // Record that we used some or all of the link bandwidth this cycle
-    double ratio = 1.0 - (double(bw_remaining) / double(getLinkBandwidth()));
+    double ratio = 1.0 - (double(bw_remaining) /
+                         double(getTotalLinkBandwidth()));
 
     // If ratio = 0, we used no bandwidth, if ratio = 1, we used all
-    m_link_utilization_proxy += ratio;
+    throttleStats.acc_link_utilization += ratio;
 
-    if (bw_remaining > 0 && !schedule_wakeup) {
-        // We have extra bandwidth and our output buffer was
-        // available, so we must not have anything else to do until
-        // another message arrives.
-        DPRINTF(RubyNetwork, "%s not scheduled again\n", *this);
-    } else {
-        DPRINTF(RubyNetwork, "%s scheduled again\n", *this);
+    if (bw_saturated) throttleStats.total_bw_sat_cy += 1;
+    if (output_blocked) throttleStats.total_stall_cy += 1;
 
+    if (bw_saturated || output_blocked) {
         // We are out of bandwidth for this cycle, so wakeup next
         // cycle and continue
+        DPRINTF(RubyNetwork, "%s scheduled again\n", *this);
         scheduleEvent(Cycles(1));
     }
 }
 
 void
-Throttle::regStats()
-{
-    for (MessageSizeType type = MessageSizeType_FIRST;
-         type < MessageSizeType_NUM; ++type) {
-        throttleStats.m_msg_counts[(unsigned int)type] =
-            new statistics::Vector(&throttleStats,
-            csprintf("msg_count.%s", MessageSizeType_to_string(type)).c_str());
-        throttleStats.m_msg_counts[(unsigned int)type]
-            ->init(Network::getNumberOfVirtualNetworks())
-            .flags(statistics::nozero)
-            ;
-
-        throttleStats.m_msg_bytes[(unsigned int) type] =
-            new statistics::Formula(&throttleStats,
-            csprintf("msg_bytes.%s", MessageSizeType_to_string(type)).c_str());
-        throttleStats.m_msg_bytes[(unsigned int) type]
-            ->flags(statistics::nozero)
-            ;
-
-        *(throttleStats.m_msg_bytes[(unsigned int) type]) =
-            *(throttleStats.m_msg_counts[type]) * statistics::constant(
-                Network::MessageSizeType_to_int(type));
-    }
-}
-
-void
-Throttle::clearStats()
-{
-    m_link_utilization_proxy = 0;
-}
-
-void
-Throttle::collateStats()
-{
-    double time_delta = double(m_ruby_system->curCycle() -
-                               m_ruby_system->getStartCycle());
-
-    throttleStats.m_link_utilization =
-        100.0 * m_link_utilization_proxy / time_delta;
-}
-
-void
 Throttle::print(std::ostream& out) const
 {
-    ccprintf(out,  "[%i bw: %i]", m_node, getLinkBandwidth());
+    ccprintf(out,  "[%i bw:", m_node);
+    if (m_physical_vnets) {
+        for (unsigned i = 0; i < m_vnets; ++i)
+            ccprintf(out,  " vnet%d=%i", i, getLinkBandwidth(i));
+    } else {
+        ccprintf(out,  " %i", getTotalLinkBandwidth());
+    }
+    ccprintf(out,  "]");
 }
 
 int
@@ -270,11 +329,65 @@
 }
 
 Throttle::
-ThrottleStats::ThrottleStats(statistics::Group *parent, const NodeID &nodeID)
+ThrottleStats::ThrottleStats(Switch *parent, const NodeID &nodeID)
     : statistics::Group(parent, csprintf("throttle%02i", nodeID).c_str()),
-      m_link_utilization(this, "link_utilization")
+      ADD_STAT(acc_link_utilization, statistics::units::Count::get(),
+        "Accumulated link utilization"),
+      ADD_STAT(link_utilization, statistics::units::Ratio::get(),
+        "Average link utilization"),
+      ADD_STAT(total_msg_count, statistics::units::Count::get(),
+        "Total number of messages forwarded by this switch"),
+      ADD_STAT(total_msg_bytes, statistics::units::Byte::get(),
+        "Total number of bytes forwarded by this switch"),
+      ADD_STAT(total_data_msg_bytes, statistics::units::Byte::get(),
+        "Total number of data bytes forwarded by this switch"),
+      ADD_STAT(total_msg_wait_time, statistics::units::Tick::get(),
+        "Total time spend forwarding messages"),
+      ADD_STAT(total_stall_cy, statistics::units::Cycle::get(),
+        "Total time spent blocked on any output link"),
+      ADD_STAT(total_bw_sat_cy, statistics::units::Cycle::get(),
+        "Total time bandwidth was saturated on any output link"),
+      ADD_STAT(avg_msg_wait_time, statistics::units::Ratio::get(),
+        "Average time a message took to be forwarded"),
+      ADD_STAT(avg_bandwidth, statistics::units::Ratio::get(),
+        "Average bandwidth (GB/s)"),
+      ADD_STAT(avg_useful_bandwidth, statistics::units::Ratio::get(),
+        "Average usefull (only data) bandwidth (GB/s)")
 {
+    link_utilization = 100 * acc_link_utilization /
+                        (simTicks / parent->clockPeriod());
 
+    avg_msg_wait_time = total_msg_wait_time / total_msg_count;
+
+    avg_bandwidth.precision(2);
+    avg_bandwidth = (total_msg_bytes / simSeconds) /
+                      statistics::constant(1024*1024*1024);
+
+    avg_useful_bandwidth.precision(2);
+    avg_useful_bandwidth = (total_data_msg_bytes / simSeconds) /
+                             statistics::constant(1024*1024*1024);
+
+    for (MessageSizeType type = MessageSizeType_FIRST;
+         type < MessageSizeType_NUM; ++type) {
+        msg_counts[(unsigned int)type] =
+            new statistics::Vector(this,
+            csprintf("msg_count.%s", MessageSizeType_to_string(type)).c_str());
+        msg_counts[(unsigned int)type]
+            ->init(Network::getNumberOfVirtualNetworks())
+            .flags(statistics::nozero)
+            ;
+
+        msg_bytes[(unsigned int) type] =
+            new statistics::Formula(this,
+            csprintf("msg_bytes.%s", MessageSizeType_to_string(type)).c_str());
+        msg_bytes[(unsigned int) type]
+            ->flags(statistics::nozero)
+            ;
+
+        *(msg_bytes[(unsigned int) type]) =
+            *(msg_counts[type]) * statistics::constant(
+                Network::MessageSizeType_to_int(type));
+    }
 }
 
 } // namespace ruby
diff --git a/src/mem/ruby/network/simple/Throttle.hh b/src/mem/ruby/network/simple/Throttle.hh
index 6689407..83454b7 100644
--- a/src/mem/ruby/network/simple/Throttle.hh
+++ b/src/mem/ruby/network/simple/Throttle.hh
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
  * All rights reserved.
  *
@@ -57,10 +69,17 @@
 
 class Throttle : public Consumer
 {
+  private:
+    Throttle(int sID, RubySystem *rs, NodeID node, Cycles link_latency,
+             int endpoint_bandwidth, Switch *em);
   public:
     Throttle(int sID, RubySystem *rs, NodeID node, Cycles link_latency,
              int link_bandwidth_multiplier, int endpoint_bandwidth,
              Switch *em);
+    Throttle(int sID, RubySystem *rs, NodeID node, Cycles link_latency,
+             const std::vector<int> &vnet_channels,
+             const std::vector<int> &vnet_bandwidth_multiplier,
+             int endpoint_bandwidth, Switch *em);
     ~Throttle() {}
 
     std::string name()
@@ -71,25 +90,26 @@
     void wakeup();
 
     // The average utilization (a fraction) since last clearStats()
-    const statistics::Scalar & getUtilization() const
-    { return throttleStats.m_link_utilization; }
+    const statistics::Formula & getUtilization() const
+    { return throttleStats.link_utilization; }
     const statistics::Vector & getMsgCount(unsigned int type) const
-    { return *(throttleStats.m_msg_counts[type]); }
+    { return *(throttleStats.msg_counts[type]); }
 
-    int getLinkBandwidth() const
-    { return m_endpoint_bandwidth * m_link_bandwidth_multiplier; }
+    int getLinkBandwidth(int vnet) const;
+
+    int getTotalLinkBandwidth() const;
+
+    int getChannelCnt(int vnet) const;
 
     Cycles getLatency() const { return m_link_latency; }
 
-    void clearStats();
-    void collateStats();
-    void regStats();
     void print(std::ostream& out) const;
 
   private:
     void init(NodeID node, Cycles link_latency, int link_bandwidth_multiplier,
               int endpoint_bandwidth);
-    void operateVnet(int vnet, int &bw_remainin, bool &schedule_wakeup,
+    void operateVnet(int vnet, int channel, int &total_bw_remaining,
+                     bool &bw_saturated, bool &output_blocked,
                      MessageBuffer *in, MessageBuffer *out);
 
     // Private copy constructor and assignment operator
@@ -99,29 +119,39 @@
     std::vector<MessageBuffer*> m_in;
     std::vector<MessageBuffer*> m_out;
     unsigned int m_vnets;
-    std::vector<int> m_units_remaining;
+    std::vector<std::vector<int>> m_units_remaining;
 
     const int m_switch_id;
     Switch *m_switch;
     NodeID m_node;
 
-    int m_link_bandwidth_multiplier;
+    bool m_physical_vnets;
+    std::vector<int> m_link_bandwidth_multiplier;
+    std::vector<int> m_vnet_channels;
     Cycles m_link_latency;
     int m_wakeups_wo_switch;
     int m_endpoint_bandwidth;
     RubySystem *m_ruby_system;
 
-    double m_link_utilization_proxy;
-
-
     struct ThrottleStats : public statistics::Group
     {
-        ThrottleStats(statistics::Group *parent, const NodeID &nodeID);
+        ThrottleStats(Switch *parent, const NodeID &nodeID);
 
         // Statistical variables
-        statistics::Scalar m_link_utilization;
-        statistics::Vector* m_msg_counts[MessageSizeType_NUM];
-        statistics::Formula* m_msg_bytes[MessageSizeType_NUM];
+        statistics::Scalar acc_link_utilization;
+        statistics::Formula link_utilization;
+        statistics::Vector* msg_counts[MessageSizeType_NUM];
+        statistics::Formula* msg_bytes[MessageSizeType_NUM];
+
+        statistics::Scalar total_msg_count;
+        statistics::Scalar total_msg_bytes;
+        statistics::Scalar total_data_msg_bytes;
+        statistics::Scalar total_msg_wait_time;
+        statistics::Scalar total_stall_cy;
+        statistics::Scalar total_bw_sat_cy;
+        statistics::Formula avg_msg_wait_time;
+        statistics::Formula avg_bandwidth;
+        statistics::Formula avg_useful_bandwidth;
     } throttleStats;
 };
 
diff --git a/src/mem/ruby/network/simple/routing/BaseRoutingUnit.hh b/src/mem/ruby/network/simple/routing/BaseRoutingUnit.hh
new file mode 100644
index 0000000..9ebf59d
--- /dev/null
+++ b/src/mem/ruby/network/simple/routing/BaseRoutingUnit.hh
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEM_RUBY_NETWORK_SIMPLE_BASEROUTINGUNIT_HH__
+#define __MEM_RUBY_NETWORK_SIMPLE_BASEROUTINGUNIT_HH__
+
+#include <vector>
+
+#include "mem/ruby/network/Network.hh"
+#include "mem/ruby/slicc_interface/Message.hh"
+#include "params/BaseRoutingUnit.hh"
+#include "sim/sim_object.hh"
+
+namespace gem5
+{
+
+namespace ruby
+{
+
+class Switch;
+
+class BaseRoutingUnit : public SimObject
+{
+  public:
+    PARAMS(BaseRoutingUnit);
+
+    BaseRoutingUnit(const Params &p)
+      :SimObject(p)
+    {
+    }
+
+    virtual void addOutPort(LinkID link_id,
+                           const std::vector<MessageBuffer*>& m_out_buffer,
+                           const NetDest& routing_table_entry,
+                           const PortDirection &direction,
+                           int link_weight) = 0;
+
+    struct RouteInfo
+    {
+        RouteInfo(const NetDest &dests, const LinkID link_id)
+          :m_destinations(dests), m_link_id(link_id)
+        {}
+        const NetDest m_destinations;
+        const LinkID m_link_id;
+    };
+
+    virtual void route(const Message &msg,
+                       int vnet,
+                       bool deterministic,
+                       std::vector<RouteInfo> &out_links) = 0;
+
+    void init_parent(Switch *parent_switch)
+    { m_parent_switch = parent_switch; }
+
+  protected:
+
+    Switch *m_parent_switch;
+
+};
+
+} // namespace ruby
+} // namespace gem5
+
+#endif // __MEM_RUBY_NETWORK_SIMPLE_BASEROUTINGUNIT_HH__
diff --git a/src/mem/ruby/network/simple/routing/WeightBased.cc b/src/mem/ruby/network/simple/routing/WeightBased.cc
new file mode 100644
index 0000000..a63b0fa
--- /dev/null
+++ b/src/mem/ruby/network/simple/routing/WeightBased.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mem/ruby/network/simple/routing/WeightBased.hh"
+
+#include <algorithm>
+
+#include "base/random.hh"
+#include "mem/ruby/network/simple/Switch.hh"
+
+namespace gem5
+{
+
+namespace ruby
+{
+
+WeightBased::WeightBased(const Params &p)
+    :BaseRoutingUnit(p)
+{
+}
+
+void
+WeightBased::addOutPort(LinkID link_id,
+                    const std::vector<MessageBuffer*>& m_out_buffer,
+                    const NetDest& routing_table_entry,
+                    const PortDirection &direction,
+                    int link_weight)
+{
+    gem5_assert(link_id == m_links.size());
+    m_links.emplace_back(new LinkInfo{link_id,
+                        routing_table_entry,
+                        m_out_buffer,
+                        0, link_weight});
+    sortLinks();
+}
+
+void
+WeightBased::route(const Message &msg,
+                   int vnet,
+                   bool deterministic,
+                   std::vector<RouteInfo> &out_links)
+{
+    // Makes sure ordering was reset adaptive option was set
+    if (params().adaptive_routing) {
+        if (deterministic) {
+            // Don't adaptively route
+            // Makes sure ordering is reset
+            for (auto &link : m_links)
+                link->m_order = 0;
+        } else {
+            // Find how clogged each link is
+            for (auto &link : m_links) {
+                int out_queue_length = 0;
+                Tick current_time = m_parent_switch->clockEdge();
+                for (auto buffer : link->m_out_buffers) {
+                    out_queue_length += buffer->getSize(current_time);
+                }
+                // improve load distribution by randomizing order of links
+                // with the same queue length
+                link->m_order =
+                    (out_queue_length << 8) | random_mt.random(0, 0xff);
+            }
+        }
+        sortLinks();
+    }
+
+    findRoute(msg, out_links);
+}
+
+void
+WeightBased::findRoute(const Message &msg,
+                       std::vector<RouteInfo> &out_links) const
+{
+    NetDest msg_dsts = msg.getDestination();
+    assert(out_links.size() == 0);
+    for (auto &link : m_links) {
+        const NetDest &dst = link->m_routing_entry;
+        if (msg_dsts.intersectionIsNotEmpty(dst)) {
+            // Need to remember which destinations need this message in
+            // another vector.  This Set is the intersection of the
+            // routing_table entry and the current destination set.
+            out_links.emplace_back(msg_dsts.AND(dst), link->m_link_id);
+
+            // Next, we update the msg_destination not to include
+            // those nodes that were already handled by this link
+            msg_dsts.removeNetDest(dst);
+        }
+    }
+
+    gem5_assert(msg_dsts.count() == 0);
+}
+
+} // namespace ruby
+} // namespace gem5
diff --git a/src/mem/ruby/network/simple/routing/WeightBased.hh b/src/mem/ruby/network/simple/routing/WeightBased.hh
new file mode 100644
index 0000000..a723e9b
--- /dev/null
+++ b/src/mem/ruby/network/simple/routing/WeightBased.hh
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEM_RUBY_NETWORK_SIMPLE_WEIGHTBASEDROUTINGUNIT_HH__
+#define __MEM_RUBY_NETWORK_SIMPLE_WEIGHTBASEDROUTINGUNIT_HH__
+
+#include "mem/ruby/network/simple/routing/BaseRoutingUnit.hh"
+#include "params/WeightBased.hh"
+
+namespace gem5
+{
+
+namespace ruby
+{
+
+class WeightBased : public BaseRoutingUnit
+{
+  public:
+    PARAMS(WeightBased);
+
+    WeightBased(const Params &p);
+
+    void addOutPort(LinkID link_id,
+                    const std::vector<MessageBuffer*>& m_out_buffer,
+                    const NetDest& routing_table_entry,
+                    const PortDirection &direction,
+                    int link_weight) override;
+
+    void route(const Message &msg,
+                int vnet,
+                bool deterministic,
+                std::vector<RouteInfo> &out_links) override;
+
+  private:
+
+    struct LinkInfo {
+        const LinkID m_link_id;
+        const NetDest m_routing_entry;
+        const std::vector<MessageBuffer*> m_out_buffers;
+        int m_order;
+        int m_weight;
+    };
+
+    std::vector<std::unique_ptr<LinkInfo>> m_links;
+
+    void findRoute(const Message &msg,
+                   std::vector<RouteInfo> &out_links) const;
+
+    void sortLinks() {
+        std::sort(m_links.begin(), m_links.end(),
+            [](const auto &a, const auto &b) {
+                auto tup = [](const auto &li)
+                { return std::make_tuple(li->m_order,
+                                         li->m_weight,
+                                         li->m_link_id);};
+                return tup(a) < tup(b);
+            });
+    }
+};
+
+} // namespace ruby
+} // namespace gem5
+
+#endif // __MEM_RUBY_NETWORK_SIMPLE_WEIGHTBASEDROUTINGUNIT_HH__
diff --git a/src/mem/ruby/profiler/SConscript b/src/mem/ruby/profiler/SConscript
index 424c53e..0c493e9 100644
--- a/src/mem/ruby/profiler/SConscript
+++ b/src/mem/ruby/profiler/SConscript
@@ -28,7 +28,7 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 Source('AccessTraceForAddress.cc')
diff --git a/src/mem/ruby/protocol/GPU_VIPER-TCC.sm b/src/mem/ruby/protocol/GPU_VIPER-TCC.sm
index 0d92565..032a64c 100644
--- a/src/mem/ruby/protocol/GPU_VIPER-TCC.sm
+++ b/src/mem/ruby/protocol/GPU_VIPER-TCC.sm
@@ -801,6 +801,7 @@
 
   transition(WI, WBAck,I) {
     dt_deallocateTBE;
+    wada_wakeUpAllDependentsAddr;
     pr_popResponseQueue;
   }
 }
diff --git a/src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm b/src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm
index e44d8db..3b38e3b 100644
--- a/src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm
+++ b/src/mem/ruby/protocol/MOESI_AMD_Base-dir.sm
@@ -61,26 +61,28 @@
 {
   // STATES
   state_declaration(State, desc="Directory states", default="Directory_State_U") {
-    U, AccessPermission:Backing_Store,                 desc="unblocked";
+    U, AccessPermission:Backing_Store,          desc="unblocked";
     BL, AccessPermission:Busy,                  desc="got L3 WB request";
     // BL is Busy because it's possible for the data only to be in the network
     // in the WB, L3 has sent it and gone on with its business in possibly I
     // state.
-    BDR_M, AccessPermission:Backing_Store,  desc="DMA read, blocked waiting for memory";
-    BS_M, AccessPermission:Backing_Store,                 desc="blocked waiting for memory";
-    BM_M, AccessPermission:Backing_Store,                 desc="blocked waiting for memory";
-    B_M, AccessPermission:Backing_Store,                 desc="blocked waiting for memory";
-    BP, AccessPermission:Backing_Store,                 desc="blocked waiting for probes, no need for memory";
-    BDR_PM, AccessPermission:Backing_Store, desc="DMA read, blocked waiting for probes and memory";
-    BS_PM, AccessPermission:Backing_Store,                desc="blocked waiting for probes and Memory";
-    BM_PM, AccessPermission:Backing_Store,                desc="blocked waiting for probes and Memory";
-    B_PM, AccessPermission:Backing_Store,                desc="blocked waiting for probes and Memory";
-    BDW_P, AccessPermission:Backing_Store, desc="DMA write, blocked waiting for probes, no need for memory";
-    BDR_Pm, AccessPermission:Backing_Store, desc="DMA read, blocked waiting for probes, already got memory";
-    BS_Pm, AccessPermission:Backing_Store,                desc="blocked waiting for probes, already got memory";
-    BM_Pm, AccessPermission:Backing_Store,                desc="blocked waiting for probes, already got memory";
-    B_Pm, AccessPermission:Backing_Store,                desc="blocked waiting for probes, already got memory";
-    B, AccessPermission:Backing_Store,                  desc="sent response, Blocked til ack";
+    BDR_M, AccessPermission:Backing_Store,      desc="DMA read, blocked waiting for memory";
+    BDW_M, AccessPermission:Backing_Store,      desc="DMA write, blocked waiting for memory";
+    BS_M, AccessPermission:Backing_Store,       desc="blocked waiting for memory";
+    BM_M, AccessPermission:Backing_Store,       desc="blocked waiting for memory";
+    B_M, AccessPermission:Backing_Store,        desc="blocked waiting for memory";
+    BP, AccessPermission:Backing_Store,         desc="blocked waiting for probes, no need for memory";
+    BDR_PM, AccessPermission:Backing_Store,     desc="DMA read, blocked waiting for probes and memory";
+    BDW_PM, AccessPermission:Backing_Store,     desc="DMA write, blocked waiting for probes and memory";
+    BS_PM, AccessPermission:Backing_Store,      desc="blocked waiting for probes and Memory";
+    BM_PM, AccessPermission:Backing_Store,      desc="blocked waiting for probes and Memory";
+    B_PM, AccessPermission:Backing_Store,       desc="blocked waiting for probes and Memory";
+    BDR_Pm, AccessPermission:Backing_Store,     desc="DMA read, blocked waiting for probes, already got memory";
+    BDW_Pm, AccessPermission:Backing_Store,     desc="DMA write, blocked waiting for probes, already got memory";
+    BS_Pm, AccessPermission:Backing_Store,      desc="blocked waiting for probes, already got memory";
+    BM_Pm, AccessPermission:Backing_Store,      desc="blocked waiting for probes, already got memory";
+    B_Pm, AccessPermission:Backing_Store,       desc="blocked waiting for probes, already got memory";
+    B, AccessPermission:Backing_Store,          desc="sent response, Blocked til ack";
   }
 
   // Events
@@ -132,7 +134,6 @@
   // DirectoryEntry
   structure(Entry, desc="...", interface="AbstractCacheEntry", main="false") {
     State DirectoryState,          desc="Directory state";
-    DataBlock DataBlk,             desc="data for the block";
     NetDest VicDirtyIgnore,  desc="VicDirty coming from whom to ignore";
   }
 
@@ -195,16 +196,6 @@
     return dir_entry;
   }
 
-  DataBlock getDataBlock(Addr addr), return_by_ref="yes" {
-    TBE tbe := TBEs.lookup(addr);
-    if (is_valid(tbe) && tbe.MemData) {
-      DPRINTF(RubySlicc, "Returning DataBlk from TBE %s:%s\n", addr, tbe);
-      return tbe.DataBlk;
-    }
-    DPRINTF(RubySlicc, "Returning DataBlk from Dir %s:%s\n", addr, getDirectoryEntry(addr));
-    return getDirectoryEntry(addr).DataBlk;
-  }
-
   State getState(TBE tbe, CacheEntry entry, Addr addr) {
     return getDirectoryEntry(addr).DirectoryState;
   }
@@ -590,7 +581,13 @@
           out_msg.Type := TriggerType:L3Hit;
         }
         CacheEntry entry := static_cast(CacheEntry, "pointer", L3CacheMemory.lookup(address));
-        tbe.DataBlk := entry.DataBlk;
+
+        // tbe.DataBlk may have partial data (e.g., for DMA writes). Make sure
+        // not to clobber the data before merging it with the L3 cache data.
+        DataBlock tmpBlk := entry.DataBlk;
+        tmpBlk.copyPartial(tbe.DataBlk, tbe.writeMask);
+        tbe.DataBlk := tmpBlk;
+
         tbe.L3Hit := true;
         tbe.MemData := true;
         L3CacheMemory.deallocate(address);
@@ -646,209 +643,287 @@
 
   action(icd_probeInvCoreDataForDMA, "icd", desc="Probe inv cores, return data for DMA") {
     peek(dmaRequestQueue_in, DMARequestMsg) {
-      enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
-        out_msg.addr := address;
-        out_msg.Type := ProbeRequestType:PrbInv;
-        out_msg.ReturnData := true;
-        out_msg.MessageSize := MessageSizeType:Control;
+      NetDest probe_dests;
+      // Add relevant machine types based on CPUonly, GPUonly and noTCCdir:
+      //  CPUonly &&  GPUonly -> Invalid
+      //  CPUonly && !GPUonly -> Add CorePairs
+      // !CPUonly &&  GPUonly -> Add TCCs or TCC dirs
+      // !CPUonly && !GPUonly -> Add CorePairs and TCCs or TCC dirs
+      if (CPUonly) {
+        assert(!GPUonly);
+        probe_dests.broadcast(MachineType:CorePair);
+      } else {
+        // CPU + GPU system
         if (!GPUonly) {
-            out_msg.Destination.broadcast(MachineType:CorePair);
+          probe_dests.broadcast(MachineType:CorePair);
         }
 
-        // Add relevant TCC node to list. This replaces all TCPs and SQCs
-        if (CPUonly) {
-            // CPU only has neither TCC nor TCC directory to add.
-        } else if (noTCCdir) {
-          out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC,
-                                  TCC_select_low_bit, TCC_select_num_bits));
+        // CPU + GPU or GPU only system
+        if (noTCCdir) {
+          probe_dests.add(mapAddressToRange(address, MachineType:TCC,
+                                            TCC_select_low_bit,
+                                            TCC_select_num_bits));
         } else {
-          out_msg.Destination.add(mapAddressToRange(address,
-                                                    MachineType:TCCdir,
-                            TCC_select_low_bit, TCC_select_num_bits));
+          probe_dests.add(mapAddressToRange(address, MachineType:TCCdir,
+                                            TCC_select_low_bit,
+                                            TCC_select_num_bits));
         }
-        out_msg.Destination.remove(in_msg.Requestor);
-        tbe.NumPendingAcks := out_msg.Destination.count();
-        if (tbe.NumPendingAcks == 0) {
-          enqueue(triggerQueue_out, TriggerMsg, 1) {
-            out_msg.addr := address;
-            out_msg.Type := TriggerType:AcksComplete;
-          }
+      }
+      probe_dests.remove(in_msg.Requestor);
+
+      if (probe_dests.count() > 0) {
+        enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
+          out_msg.addr := address;
+          out_msg.Type := ProbeRequestType:PrbInv;
+          out_msg.ReturnData := true;
+          out_msg.MessageSize := MessageSizeType:Control;
+          out_msg.Destination := probe_dests;
+          tbe.NumPendingAcks := out_msg.Destination.count();
+          DPRINTF(RubySlicc, "%s\n", out_msg);
+          APPEND_TRANSITION_COMMENT(" dc: Acks remaining: ");
+          APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
+          tbe.ProbeRequestStartTime := curCycle();
+          assert(out_msg.Destination.count() > 0);
         }
-        DPRINTF(RubySlicc, "%s\n", out_msg);
-        APPEND_TRANSITION_COMMENT(" dc: Acks remaining: ");
-        APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
-        tbe.ProbeRequestStartTime := curCycle();
-        assert(out_msg.Destination.count() > 0);
+      }
+
+      if (probe_dests.count() == 0) {
+        enqueue(triggerQueue_out, TriggerMsg, 1) {
+          out_msg.addr := address;
+          out_msg.Type := TriggerType:AcksComplete;
+        }
       }
     }
   }
 
   action(dc_probeInvCoreData, "dc", desc="probe inv cores, return data") {
     peek(requestNetwork_in, CPURequestMsg) {
-      enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
-        out_msg.addr := address;
-        out_msg.Type := ProbeRequestType:PrbInv;
-        out_msg.ReturnData := true;
-        out_msg.MessageSize := MessageSizeType:Control;
+      NetDest probe_dests;
+      // Add relevant machine types based on CPUonly, GPUonly and noTCCdir:
+      //  CPUonly &&  GPUonly -> Invalid
+      //  CPUonly && !GPUonly -> Add CorePairs
+      // !CPUonly &&  GPUonly -> Add TCCs or TCC dirs if no write conflict
+      // !CPUonly && !GPUonly -> Add CorePairs and TCCs or TCC dirs
+      //                         if no write conflict
+      if (CPUonly) {
+        assert(!GPUonly);
+        probe_dests.broadcast(MachineType:CorePair);
+      } else {
+        // CPU + GPU system
         if (!GPUonly) {
-            // won't be realistic for multisocket
-            out_msg.Destination.broadcast(MachineType:CorePair);
+          probe_dests.broadcast(MachineType:CorePair);
         }
 
-        // add relevant TCC node to list. This replaces all TCPs and SQCs
-        if (((in_msg.Type == CoherenceRequestType:WriteThrough ||
-              in_msg.Type == CoherenceRequestType:Atomic) &&
-             in_msg.NoWriteConflict) ||
-            CPUonly) {
-        } else if (noTCCdir) {
-          out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC,
-                                  TCC_select_low_bit, TCC_select_num_bits));
-        } else {
-	      out_msg.Destination.add(mapAddressToRange(address,
-                                                    MachineType:TCCdir,
-                            TCC_select_low_bit, TCC_select_num_bits));
-        }
-        out_msg.Destination.remove(in_msg.Requestor);
-        tbe.NumPendingAcks := out_msg.Destination.count();
-        if (tbe.NumPendingAcks == 0) {
-          enqueue(triggerQueue_out, TriggerMsg, 1) {
-            out_msg.addr := address;
-            out_msg.Type := TriggerType:AcksComplete;
+        // CPU + GPU or GPU only system
+        if ((in_msg.Type != CoherenceRequestType:WriteThrough &&
+             in_msg.Type != CoherenceRequestType:Atomic) ||
+             !in_msg.NoWriteConflict) {
+          if (noTCCdir) {
+            probe_dests.add(mapAddressToRange(address, MachineType:TCC,
+                                              TCC_select_low_bit,
+                                              TCC_select_num_bits));
+          } else {
+            probe_dests.add(mapAddressToRange(address, MachineType:TCCdir,
+                                              TCC_select_low_bit,
+                                              TCC_select_num_bits));
           }
         }
-        DPRINTF(RubySlicc, "%s\n", out_msg);
-        APPEND_TRANSITION_COMMENT(" dc: Acks remaining: ");
-        APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
-        tbe.ProbeRequestStartTime := curCycle();
-        assert(out_msg.Destination.count() > 0);
+      }
+      probe_dests.remove(in_msg.Requestor);
+
+      if (probe_dests.count() > 0) {
+        enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
+          out_msg.addr := address;
+          out_msg.Type := ProbeRequestType:PrbInv;
+          out_msg.ReturnData := true;
+          out_msg.MessageSize := MessageSizeType:Control;
+          out_msg.Destination := probe_dests;
+          tbe.NumPendingAcks := out_msg.Destination.count();
+          DPRINTF(RubySlicc, "%s\n", out_msg);
+          APPEND_TRANSITION_COMMENT(" dc: Acks remaining: ");
+          APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
+          tbe.ProbeRequestStartTime := curCycle();
+          assert(out_msg.Destination.count() > 0);
+        }
+      }
+
+      if (probe_dests.count() == 0) {
+        enqueue(triggerQueue_out, TriggerMsg, 1) {
+          out_msg.addr := address;
+          out_msg.Type := TriggerType:AcksComplete;
+        }
       }
     }
   }
 
   action(scd_probeShrCoreDataForDma, "dsc", desc="probe shared cores, return data for DMA") {
     peek(dmaRequestQueue_in, DMARequestMsg) {
-      enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
-        out_msg.addr := address;
-        out_msg.Type := ProbeRequestType:PrbDowngrade;
-        out_msg.ReturnData := true;
-        out_msg.MessageSize := MessageSizeType:Control;
+      NetDest probe_dests;
+      // Add relevant machine types based on CPUonly, GPUonly and noTCCdir:
+      //  CPUonly &&  GPUonly -> Invalid
+      //  CPUonly && !GPUonly -> Add CorePairs
+      // !CPUonly &&  GPUonly -> Add TCCs or TCC dirs
+      // !CPUonly && !GPUonly -> Add CorePairs and TCCs or TCC dirs
+      if (CPUonly) {
+        assert(!GPUonly);
+        probe_dests.broadcast(MachineType:CorePair);
+      } else {
+        // CPU + GPU system
         if (!GPUonly) {
-            out_msg.Destination.broadcast(MachineType:CorePair);
+          probe_dests.broadcast(MachineType:CorePair);
         }
-        // add relevant TCC node to the list. This replaces all TCPs and SQCs
-        if (noTCCdir || CPUonly) {
-          //Don't need to notify TCC about reads
-        } else {
-          out_msg.Destination.add(mapAddressToRange(address,
-                                                    MachineType:TCCdir,
-                            TCC_select_low_bit, TCC_select_num_bits));
+
+        // CPU + GPU or GPU only system
+        // We don't need to notify TCC about reads
+        if (!noTCCdir) {
+          probe_dests.add(mapAddressToRange(address, MachineType:TCCdir,
+                                            TCC_select_low_bit,
+                                            TCC_select_num_bits));
         }
-        if (noTCCdir && !CPUonly) {
-          out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC,
-                                  TCC_select_low_bit, TCC_select_num_bits));
+      }
+      probe_dests.remove(in_msg.Requestor);
+
+      if (probe_dests.count() > 0) {
+        enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
+          out_msg.addr := address;
+          out_msg.Type := ProbeRequestType:PrbDowngrade;
+          out_msg.ReturnData := true;
+          out_msg.MessageSize := MessageSizeType:Control;
+          out_msg.Destination := probe_dests;
+          tbe.NumPendingAcks := out_msg.Destination.count();
+          DPRINTF(RubySlicc, "%s\n", (out_msg));
+          APPEND_TRANSITION_COMMENT(" sc: Acks remaining: ");
+          APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
+          tbe.ProbeRequestStartTime := curCycle();
+          assert(out_msg.Destination.count() > 0);
         }
-        out_msg.Destination.remove(in_msg.Requestor);
-        tbe.NumPendingAcks := out_msg.Destination.count();
-        if (tbe.NumPendingAcks == 0) {
-          enqueue(triggerQueue_out, TriggerMsg, 1) {
-            out_msg.addr := address;
-            out_msg.Type := TriggerType:AcksComplete;
-          }
+      }
+
+      if (probe_dests.count() == 0) {
+        enqueue(triggerQueue_out, TriggerMsg, 1) {
+          out_msg.addr := address;
+          out_msg.Type := TriggerType:AcksComplete;
         }
-        DPRINTF(RubySlicc, "%s\n", (out_msg));
-        APPEND_TRANSITION_COMMENT(" sc: Acks remaining: ");
-        APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
-        tbe.ProbeRequestStartTime := curCycle();
-        assert(out_msg.Destination.count() > 0);
       }
     }
   }
 
   action(sc_probeShrCoreData, "sc", desc="probe shared cores, return data") {
     peek(requestNetwork_in, CPURequestMsg) { // not the right network?
-      enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
-        out_msg.addr := address;
-        out_msg.Type := ProbeRequestType:PrbDowngrade;
-        out_msg.ReturnData := true;
-        out_msg.MessageSize := MessageSizeType:Control;
+      NetDest probe_dests;
+      // Add relevant machine types based on CPUonly, GPUonly and noTCCdir:
+      //  CPUonly &&  GPUonly -> Invalid
+      //  CPUonly && !GPUonly -> Add CorePairs
+      // !CPUonly &&  GPUonly -> Add TCCs or TCC dirs
+      // !CPUonly && !GPUonly -> Add CorePairs and TCCs or TCC dirs
+      if (CPUonly) {
+        assert(!GPUonly);
+        probe_dests.broadcast(MachineType:CorePair);
+      } else {
+        // CPU + GPU system
         if (!GPUonly) {
-            // won't be realistic for multisocket
-            out_msg.Destination.broadcast(MachineType:CorePair);
+          probe_dests.broadcast(MachineType:CorePair);
         }
-        // add relevant TCC node to the list. This replaces all TCPs and SQCs
-        if (noTCCdir || CPUonly) {
-          //Don't need to notify TCC about reads
-        } else {
-	      out_msg.Destination.add(mapAddressToRange(address,
-                                                    MachineType:TCCdir,
-                            TCC_select_low_bit, TCC_select_num_bits));
-          tbe.NumPendingAcks := tbe.NumPendingAcks + 1;
+
+        // CPU + GPU or GPU only system
+        // We don't need to notify TCC about reads
+        if (!noTCCdir) {
+          probe_dests.add(mapAddressToRange(address, MachineType:TCCdir,
+                                            TCC_select_low_bit,
+                                            TCC_select_num_bits));
         }
-        if (noTCCdir && !CPUonly) {
-          out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC,
-                                  TCC_select_low_bit, TCC_select_num_bits));
+      }
+      probe_dests.remove(in_msg.Requestor);
+
+      if (probe_dests.count() > 0) {
+        enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
+          out_msg.addr := address;
+          out_msg.Type := ProbeRequestType:PrbDowngrade;
+          out_msg.ReturnData := true;
+          out_msg.MessageSize := MessageSizeType:Control;
+          out_msg.Destination := probe_dests;
+          tbe.NumPendingAcks := out_msg.Destination.count();
+          DPRINTF(RubySlicc, "%s\n", (out_msg));
+          APPEND_TRANSITION_COMMENT(" sc: Acks remaining: ");
+          APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
+          tbe.ProbeRequestStartTime := curCycle();
+          assert(out_msg.Destination.count() > 0);
         }
-        out_msg.Destination.remove(in_msg.Requestor);
-        tbe.NumPendingAcks := out_msg.Destination.count();
-        if (tbe.NumPendingAcks == 0) {
-          enqueue(triggerQueue_out, TriggerMsg, 1) {
-            out_msg.addr := address;
-            out_msg.Type := TriggerType:AcksComplete;
-          }
+      }
+
+      if (probe_dests.count() == 0) {
+        enqueue(triggerQueue_out, TriggerMsg, 1) {
+          out_msg.addr := address;
+          out_msg.Type := TriggerType:AcksComplete;
         }
-        DPRINTF(RubySlicc, "%s\n", (out_msg));
-        APPEND_TRANSITION_COMMENT(" sc: Acks remaining: ");
-        APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
-        tbe.ProbeRequestStartTime := curCycle();
-        assert(out_msg.Destination.count() > 0);
       }
     }
   }
 
   action(ic_probeInvCore, "ic", desc="probe invalidate core, no return data needed") {
     peek(requestNetwork_in, CPURequestMsg) { // not the right network?
-      enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
-        out_msg.addr := address;
-        out_msg.Type := ProbeRequestType:PrbInv;
-        out_msg.ReturnData := false;
-        out_msg.MessageSize := MessageSizeType:Control;
+      NetDest probe_dests;
+      // Add relevant machine types based on CPUonly, GPUonly and noTCCdir:
+      //  CPUonly &&  GPUonly -> Invalid
+      //  CPUonly && !GPUonly -> Add CorePairs
+      // !CPUonly &&  GPUonly -> Add TCCs or TCC dirs
+      // !CPUonly && !GPUonly -> Add CorePairs and TCCs or TCC dirs
+      if (CPUonly) {
+        assert(!GPUonly);
+        probe_dests.broadcast(MachineType:CorePair);
+      } else {
+        // CPU + GPU system
         if (!GPUonly) {
-            // won't be realistic for multisocket
-            out_msg.Destination.broadcast(MachineType:CorePair);
+          probe_dests.broadcast(MachineType:CorePair);
         }
 
-        // add relevant TCC node to the list. This replaces all TCPs and SQCs
-        if (noTCCdir && !CPUonly) {
-            out_msg.Destination.add(mapAddressToRange(address,MachineType:TCC,
-                              TCC_select_low_bit, TCC_select_num_bits));
+        // CPU + GPU or GPU only system
+        if (noTCCdir) {
+          probe_dests.add(mapAddressToRange(address, MachineType:TCC,
+                                            TCC_select_low_bit,
+                                            TCC_select_num_bits));
         } else {
-            if (!noTCCdir) {
-                out_msg.Destination.add(mapAddressToRange(address,
-                                                          MachineType:TCCdir,
-                                                          TCC_select_low_bit,
-                                                          TCC_select_num_bits));
-            }
+          probe_dests.add(mapAddressToRange(address, MachineType:TCCdir,
+                                            TCC_select_low_bit,
+                                            TCC_select_num_bits));
         }
-        out_msg.Destination.remove(in_msg.Requestor);
-        tbe.NumPendingAcks := out_msg.Destination.count();
-        if (tbe.NumPendingAcks == 0) {
-          enqueue(triggerQueue_out, TriggerMsg, 1) {
-            out_msg.addr := address;
-            out_msg.Type := TriggerType:AcksComplete;
-          }
+      }
+      probe_dests.remove(in_msg.Requestor);
+
+      if (probe_dests.count() > 0) {
+        enqueue(probeNetwork_out, NBProbeRequestMsg, response_latency) {
+          out_msg.addr := address;
+          out_msg.Type := ProbeRequestType:PrbInv;
+          out_msg.ReturnData := false;
+          out_msg.MessageSize := MessageSizeType:Control;
+          out_msg.Destination := probe_dests;
+          tbe.NumPendingAcks := out_msg.Destination.count();
+          APPEND_TRANSITION_COMMENT(" ic: Acks remaining: ");
+          APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
+          DPRINTF(RubySlicc, "%s\n", out_msg);
+          tbe.ProbeRequestStartTime := curCycle();
+          assert(out_msg.Destination.count() > 0);
         }
-        APPEND_TRANSITION_COMMENT(" ic: Acks remaining: ");
-        APPEND_TRANSITION_COMMENT(tbe.NumPendingAcks);
-        DPRINTF(RubySlicc, "%s\n", out_msg);
-        tbe.ProbeRequestStartTime := curCycle();
-        assert(out_msg.Destination.count() > 0);
+      }
+
+      if (probe_dests.count() == 0) {
+        enqueue(triggerQueue_out, TriggerMsg, 1) {
+          out_msg.addr := address;
+          out_msg.Type := TriggerType:AcksComplete;
+        }
       }
     }
   }
 
   action(d_writeDataToMemory, "d", desc="Write data to memory") {
     peek(responseNetwork_in, ResponseMsg) {
-      getDirectoryEntry(address).DataBlk := in_msg.DataBlk;
+      enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) {
+        out_msg.addr := address;
+        out_msg.Type := MemoryRequestType:MEMORY_WB;
+        out_msg.Sender := machineID;
+        out_msg.MessageSize := MessageSizeType:Writeback_Data;
+        out_msg.DataBlk := in_msg.DataBlk;
+      }
       if (tbe.Dirty == false) {
           // have to update the TBE, too, because of how this
           // directory deals with functional writes
@@ -870,7 +945,14 @@
         tbe.wtData := true;
         tbe.Dirty := true;
         tbe.DataBlk := in_msg.DataBlk;
-        tbe.writeMask.fillMask();
+        // DMAs can be partial cache line. This is indicated by a "Len" value
+        // which is non-zero. In this case make sure to set the writeMask
+        // appropriately. This can occur for either reads or writes.
+        if (in_msg.Len != 0) {
+          tbe.writeMask.setMask(addressOffset(in_msg.PhysicalAddress, address), in_msg.Len);
+        } else {
+          tbe.writeMask.fillMask();
+        }
       }
     }
   }
@@ -894,7 +976,6 @@
         tbe.WTRequestor := in_msg.WTRequestor;
         tbe.LastSender := in_msg.Requestor;
       }
-      tbe.DataBlk := getDirectoryEntry(address).DataBlk; // Data only for WBs
       tbe.Dirty := false;
       if (in_msg.Type == CoherenceRequestType:WriteThrough) {
         tbe.DataBlk.copyPartial(in_msg.DataBlk,in_msg.writeMask);
@@ -908,30 +989,38 @@
   }
 
   action(dt_deallocateTBE, "dt", desc="deallocate TBE Entry") {
-    if (tbe.Dirty == false) {
-        getDirectoryEntry(address).DataBlk := tbe.DataBlk;
-    }
     TBEs.deallocate(address);
     unset_tbe();
   }
 
   action(wd_writeBackData, "wd", desc="Write back data if needed") {
-    if (tbe.wtData) {
-      getDirectoryEntry(address).DataBlk.copyPartial(tbe.DataBlk, tbe.writeMask);
-    } else if (tbe.atomicData) {
-      tbe.DataBlk.atomicPartial(getDirectoryEntry(address).DataBlk,tbe.writeMask);
-      getDirectoryEntry(address).DataBlk := tbe.DataBlk;
-    } else if (tbe.Dirty == false) {
-      getDirectoryEntry(address).DataBlk := tbe.DataBlk;
+    if (tbe.wtData || tbe.atomicData || tbe.Dirty == false) {
+      if (tbe.atomicData) {
+        tbe.DataBlk.atomicPartial(tbe.DataBlk, tbe.writeMask);
+      }
+      enqueue(memQueue_out, MemoryMsg, to_memory_controller_latency) {
+        out_msg.addr := address;
+        out_msg.Type := MemoryRequestType:MEMORY_WB;
+        out_msg.Sender := machineID;
+        out_msg.MessageSize := MessageSizeType:Writeback_Data;
+        out_msg.DataBlk := tbe.DataBlk;
+        DPRINTF(ProtocolTrace, "%s\n", out_msg);
+      }
     }
   }
 
   action(mt_writeMemDataToTBE, "mt", desc="write Mem data to TBE") {
     peek(memQueue_in, MemoryMsg) {
       if (tbe.wtData == true) {
-          // do nothing
+        // Keep the write-through data based on mask, but use the memory block
+        // for the masked-off data. If we received a probe with data, the mask
+        // will be filled and the tbe data will fully overwrite the memory
+        // data in the temp block.
+        DataBlock tmpBlk := in_msg.DataBlk;
+        tmpBlk.copyPartial(tbe.DataBlk, tbe.writeMask);
+        tbe.DataBlk := tmpBlk;
       } else if (tbe.Dirty == false) {
-        tbe.DataBlk := getDirectoryEntry(address).DataBlk;
+        tbe.DataBlk := in_msg.DataBlk;
       }
       tbe.MemData := true;
     }
@@ -1127,7 +1216,7 @@
   }
 
   // TRANSITIONS
-  transition({BL, BDR_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_P, BS_PM, BM_PM, B_PM, BDR_Pm, BS_Pm, BM_Pm, B_Pm, B}, {RdBlkS, RdBlkM, RdBlk, CtoD}) {
+  transition({BL, BDR_M, BDW_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_PM, BS_PM, BM_PM, B_PM, BDR_Pm, BDW_Pm, BS_Pm, BM_Pm, B_Pm, B}, {RdBlkS, RdBlkM, RdBlk, CtoD}) {
       st_stallAndWaitRequest;
   }
 
@@ -1138,7 +1227,7 @@
 
   // The exit state is always going to be U, so wakeUpDependents logic should be covered in all the
   // transitions which are flowing into U.
-  transition({BL, BDR_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_P, BS_PM, BM_PM, B_PM, BDR_Pm, BS_Pm, BM_Pm, B_Pm, B}, {DmaRead,DmaWrite}){
+  transition({BL, BDR_M, BDW_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_PM, BS_PM, BM_PM, B_PM, BDR_Pm, BDW_Pm, BS_Pm, BM_Pm, B_Pm, B}, {DmaRead,DmaWrite}){
     sd_stallAndWaitRequest;
   }
 
@@ -1159,9 +1248,10 @@
     p_popRequestQueue;
   }
 
-  transition(U, DmaWrite, BDW_P) {L3TagArrayRead} {
+  transition(U, DmaWrite, BDW_PM) {L3TagArrayRead} {
     atd_allocateTBEforDMA;
-    da_sendResponseDmaAck;
+    qdr_queueDmaRdReq;
+    pr_profileL3HitMiss; //Must come after qdr_queueDmaRdReq
     icd_probeInvCoreDataForDMA;
     pd_popDmaRequestQueue;
   }
@@ -1236,15 +1326,15 @@
     pr_popResponseQueue;
   }
 
-  transition({B, BDR_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_P, BS_PM, BM_PM, B_PM, BDR_Pm, BS_Pm, BM_Pm, B_Pm}, {VicDirty, VicClean}) {
+  transition({B, BDR_M, BDW_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_PM, BS_PM, BM_PM, B_PM, BDR_Pm, BDW_Pm, BS_Pm, BM_Pm, B_Pm}, {VicDirty, VicClean}) {
     z_stall;
   }
 
-  transition({U, BL, BDR_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_P, BS_PM, BM_PM, B_PM, BDR_Pm, BS_Pm, BM_Pm, B_Pm, B}, WBAck) {
+  transition({U, BL, BDR_M, BDW_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_PM, BS_PM, BM_PM, B_PM, BDR_Pm, BDW_Pm, BS_Pm, BM_Pm, B_Pm, B}, WBAck) {
     pm_popMemQueue;
   }
 
-  transition({U, BL, BDR_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_P, BS_PM, BM_PM, B_PM, BDR_Pm, BS_Pm, BM_Pm, B_Pm, B}, StaleVicDirty) {
+  transition({U, BL, BDR_M, BDW_M, BS_M, BM_M, B_M, BP, BDR_PM, BDW_PM, BS_PM, BM_PM, B_PM, BDR_Pm, BDW_Pm, BS_Pm, BM_Pm, B_Pm, B}, StaleVicDirty) {
     rv_removeVicDirtyIgnore;
     w_sendResponseWBAck;
     p_popRequestQueue;
@@ -1265,6 +1355,11 @@
     pm_popMemQueue;
   }
 
+  transition(BDW_PM, MemData, BDW_Pm) {
+    mt_writeMemDataToTBE;
+    pm_popMemQueue;
+  }
+
   transition(BS_PM, MemData, BS_Pm) {} {
     mt_writeMemDataToTBE;
     pm_popMemQueue;
@@ -1284,6 +1379,10 @@
     ptl_popTriggerQueue;
   }
 
+  transition(BDW_PM, L3Hit, BDW_Pm) {
+    ptl_popTriggerQueue;
+  }
+
   transition(BS_PM, L3Hit, BS_Pm) {} {
     ptl_popTriggerQueue;
   }
@@ -1304,6 +1403,15 @@
     pm_popMemQueue;
   }
 
+  transition(BDW_M, MemData, U) {
+    mt_writeMemDataToTBE;
+    da_sendResponseDmaAck;
+    wd_writeBackData;
+    wada_wakeUpAllDependentsAddr;
+    dt_deallocateTBE;
+    pm_popMemQueue;
+  }
+
   transition(BS_M, MemData, B){L3TagArrayWrite, L3DataArrayWrite} {
     mt_writeMemDataToTBE;
     s_sendResponseS;
@@ -1355,7 +1463,7 @@
     ptl_popTriggerQueue;
   }
 
-  transition({BDR_PM, BS_PM, BDW_P, BM_PM, B_PM, BDR_Pm, BS_Pm, BM_Pm, B_Pm, BP}, CPUPrbResp) {
+  transition({BDR_PM, BDW_PM, BS_PM, BM_PM, B_PM, BDR_Pm, BDW_Pm, BS_Pm, BM_Pm, B_Pm, BP}, CPUPrbResp) {
     y_writeProbeDataToTBE;
     x_decrementAcks;
     o_checkForCompletion;
@@ -1366,6 +1474,10 @@
     pt_popTriggerQueue;
   }
 
+  transition(BDW_PM, ProbeAcksComplete, BDW_M) {
+    pt_popTriggerQueue;
+  }
+
   transition(BS_PM, ProbeAcksComplete, BS_M) {} {
     sf_setForwardReqTime;
     pt_popTriggerQueue;
@@ -1381,7 +1493,8 @@
     pt_popTriggerQueue;
   }
 
-  transition(BDW_P, ProbeAcksComplete, U) {
+  transition(BDR_Pm, ProbeAcksComplete, U) {
+    dd_sendResponseDmaData;
     // Check for pending requests from the core we put to sleep while waiting
     // for a response
     wada_wakeUpAllDependentsAddr;
@@ -1389,8 +1502,9 @@
     pt_popTriggerQueue;
   }
 
-  transition(BDR_Pm, ProbeAcksComplete, U) {
-    dd_sendResponseDmaData;
+  transition(BDW_Pm, ProbeAcksComplete, U) {
+    da_sendResponseDmaAck;
+    wd_writeBackData;
     // Check for pending requests from the core we put to sleep while waiting
     // for a response
     wada_wakeUpAllDependentsAddr;
diff --git a/src/mem/ruby/protocol/MOESI_AMD_Base-dma.sm b/src/mem/ruby/protocol/MOESI_AMD_Base-dma.sm
index dbecabd..993387d 100644
--- a/src/mem/ruby/protocol/MOESI_AMD_Base-dma.sm
+++ b/src/mem/ruby/protocol/MOESI_AMD_Base-dma.sm
@@ -139,6 +139,7 @@
         out_msg.Len := in_msg.Len;
         out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory));
         out_msg.MessageSize := MessageSizeType:Writeback_Control;
+        DPRINTF(ProtocolTrace, "%s\n", out_msg);
       }
     }
   }
@@ -154,6 +155,7 @@
           out_msg.Len := in_msg.Len;
           out_msg.Destination.add(mapAddressToMachine(address, MachineType:Directory));
           out_msg.MessageSize := MessageSizeType:Writeback_Control;
+          DPRINTF(ProtocolTrace, "%s\n", out_msg);
         }
       }
   }
diff --git a/src/mem/ruby/protocol/RubySlicc_Exports.sm b/src/mem/ruby/protocol/RubySlicc_Exports.sm
index cea6c04..a32983a 100644
--- a/src/mem/ruby/protocol/RubySlicc_Exports.sm
+++ b/src/mem/ruby/protocol/RubySlicc_Exports.sm
@@ -183,16 +183,23 @@
   HTM_Commit,        desc="hardware memory transaction: commit";
   HTM_Cancel,        desc="hardware memory transaction: cancel";
   HTM_Abort,         desc="hardware memory transaction: abort";
+  TLBI,              desc="TLB Invalidation - Initiation";
+  TLBI_SYNC,         desc="TLB Invalidation Sync operation - Potential initiation";
+  TLBI_EXT_SYNC,      desc="TLB Invalidation Sync operation - External Sync has been requested";
+  TLBI_EXT_SYNC_COMP, desc="TLB Invalidation Sync operation - External Sync has been completed";
 }
 
 bool isWriteRequest(RubyRequestType type);
 bool isDataReadRequest(RubyRequestType type);
 bool isReadRequest(RubyRequestType type);
 bool isHtmCmdRequest(RubyRequestType type);
+bool isTlbiRequest(RubyRequestType type);
 
 // hardware transactional memory
 RubyRequestType htmCmdToRubyRequestType(Packet *pkt);
 
+RubyRequestType tlbiCmdToRubyRequestType(Packet *pkt);
+
 enumeration(HtmCallbackMode, desc="...", default="HtmCallbackMode_NULL") {
   HTM_CMD,          desc="htm command";
   LD_FAIL,          desc="htm transaction failed - inform via read";
@@ -265,6 +272,7 @@
     RegionBuffer, desc="Region buffer for CPU and GPU";
     Cache,       desc="Generic coherent cache controller";
     Memory,      desc="Memory controller interface";
+    MiscNode,    desc="CHI protocol Misc Node";
     NULL,        desc="null mach type";
 }
 
diff --git a/src/mem/ruby/protocol/RubySlicc_Types.sm b/src/mem/ruby/protocol/RubySlicc_Types.sm
index e5ecb00..8d76f78 100644
--- a/src/mem/ruby/protocol/RubySlicc_Types.sm
+++ b/src/mem/ruby/protocol/RubySlicc_Types.sm
@@ -139,6 +139,11 @@
                      Cycles, Cycles, Cycles);
   void writeUniqueCallback(Addr, DataBlock);
 
+  void unaddressedCallback(Addr, RubyRequestType);
+  void unaddressedCallback(Addr, RubyRequestType, MachineType);
+  void unaddressedCallback(Addr, RubyRequestType, MachineType,
+                    Cycles, Cycles, Cycles);
+
   // ll/sc support
   void writeCallbackScFail(Addr, DataBlock);
   bool llscCheckMonitor(Addr);
@@ -170,6 +175,8 @@
   PacketPtr pkt,             desc="Packet associated with this request";
   bool htmFromTransaction,   desc="Memory request originates within a HTM transaction";
   int htmTransactionUid,     desc="Used to identify the unique HTM transaction that produced this request";
+  bool isTlbi,               desc="Memory request is a TLB shootdown (invalidation) operation";
+  Addr tlbiTransactionUid,   desc="Unique identifier of the TLB shootdown operation that produced this request";
 
   RequestPtr getRequestPtr();
 }
diff --git a/src/mem/ruby/protocol/SConscript b/src/mem/ruby/protocol/SConscript
index f137021..238ce2f 100644
--- a/src/mem/ruby/protocol/SConscript
+++ b/src/mem/ruby/protocol/SConscript
@@ -36,7 +36,7 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 output_dir = Dir('.')
@@ -60,14 +60,16 @@
                         r'''include[ \t]["'](.*)["'];''')
 env.Append(SCANNERS=slicc_scanner)
 
+slicc_includes = ['mem/ruby/slicc_interface/RubySlicc_includes.hh'] + \
+        env['SLICC_INCLUDES']
 def slicc_emitter(target, source, env):
     assert len(source) == 1
     filepath = source[0].srcnode().abspath
 
     slicc = SLICC(filepath, protocol_base.abspath, verbose=False)
     slicc.process()
-    slicc.writeCodeFiles(output_dir.abspath, env['SLICC_INCLUDES'])
-    if env['SLICC_HTML']:
+    slicc.writeCodeFiles(output_dir.abspath, slicc_includes)
+    if env['CONF']['SLICC_HTML']:
         slicc.writeHTMLFiles(html_dir.abspath)
 
     target.extend([output_dir.File(f) for f in sorted(slicc.files())])
@@ -79,14 +81,14 @@
 
     slicc = SLICC(filepath, protocol_base.abspath, verbose=True)
     slicc.process()
-    slicc.writeCodeFiles(output_dir.abspath, env['SLICC_INCLUDES'])
-    if env['SLICC_HTML']:
+    slicc.writeCodeFiles(output_dir.abspath, slicc_includes)
+    if env['CONF']['SLICC_HTML']:
         slicc.writeHTMLFiles(html_dir.abspath)
 
 slicc_builder = Builder(action=MakeAction(slicc_action, Transform("SLICC")),
                         emitter=slicc_emitter)
 
-protocol = env['PROTOCOL']
+protocol = env['CONF']['PROTOCOL']
 protocol_dir = None
 for path in env['PROTOCOL_DIRS']:
     if os.path.exists(path.File("%s.slicc" % protocol).abspath):
diff --git a/src/mem/ruby/protocol/SConsopts b/src/mem/ruby/protocol/SConsopts
index 03b87b4..2fcc57a 100644
--- a/src/mem/ruby/protocol/SConsopts
+++ b/src/mem/ruby/protocol/SConsopts
@@ -51,5 +51,3 @@
 
 protocol_base = Dir('.')
 Export('protocol_base')
-
-main.Append(SLICC_INCLUDES=['mem/ruby/slicc_interface/RubySlicc_includes.hh'])
diff --git a/src/mem/ruby/protocol/chi/CHI-cache-actions.sm b/src/mem/ruby/protocol/chi/CHI-cache-actions.sm
index b1a7d99..2c47ac9 100644
--- a/src/mem/ruby/protocol/chi/CHI-cache-actions.sm
+++ b/src/mem/ruby/protocol/chi/CHI-cache-actions.sm
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 ARM Limited
+ * Copyright (c) 2021-2022 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -60,9 +60,10 @@
       assert(in_msg.allowRetry);
       enqueue(retryTriggerOutPort, RetryTriggerMsg, 0) {
         out_msg.addr := in_msg.addr;
+        out_msg.usesTxnId := false;
         out_msg.event := Event:SendRetryAck;
         out_msg.retryDest := in_msg.requestor;
-        retryQueue.emplace(in_msg.addr,in_msg.requestor);
+        retryQueue.emplace(in_msg.addr,false,in_msg.requestor);
       }
     }
   }
@@ -106,6 +107,23 @@
   snpInPort.dequeue(clockEdge());
 }
 
+action(AllocateTBE_DvmSnoop, desc="") {
+  // No retry for snoop requests; just create resource stall
+  check_allocate(storDvmSnpTBEs);
+
+  storDvmSnpTBEs.incrementReserved();
+
+  // Move request to rdy queue
+  peek(snpInPort, CHIRequestMsg) {
+    enqueue(snpRdyOutPort, CHIRequestMsg, allocation_latency) {
+      assert(in_msg.usesTxnId);
+      assert(in_msg.addr == address);
+      out_msg := in_msg;
+    }
+  }
+  snpInPort.dequeue(clockEdge());
+}
+
 action(AllocateTBE_SeqRequest, desc="") {
   // No retry for sequencer requests; just create resource stall
   check_allocate(storTBEs);
@@ -145,6 +163,49 @@
   seqInPort.dequeue(clockEdge());
 }
 
+action(AllocateTBE_SeqDvmRequest, desc="") {
+  // No retry for sequencer requests; just create resource stall
+  check_allocate(storDvmTBEs);
+
+  // reserve a slot for this request
+  storDvmTBEs.incrementReserved();
+
+  // Move request to rdy queue
+  peek(seqInPort, RubyRequest) {
+    enqueue(reqRdyOutPort, CHIRequestMsg, allocation_latency) {
+      // DVM operations do not relate to memory addresses
+      // Use the DVM transaction ID instead
+      out_msg.usesTxnId := true;
+      out_msg.txnId := in_msg.tlbiTransactionUid;
+
+      // TODO - zero these out?
+      out_msg.addr := in_msg.tlbiTransactionUid;
+      out_msg.accAddr := in_msg.tlbiTransactionUid;
+      out_msg.accSize := blockSize;
+      assert(in_msg.Prefetch == PrefetchBit:No);
+      out_msg.is_local_pf := false;
+      out_msg.is_remote_pf := false;
+
+      out_msg.requestor := machineID;
+      out_msg.fwdRequestor := machineID;
+      out_msg.seqReq := in_msg.getRequestPtr();
+      out_msg.isSeqReqValid := true;
+
+
+      if (in_msg.Type == RubyRequestType:TLBI) {
+        out_msg.type := CHIRequestType:DvmTlbi_Initiate;
+      } else if (in_msg.Type == RubyRequestType:TLBI_SYNC) {
+        out_msg.type := CHIRequestType:DvmSync_Initiate;
+      } else if (in_msg.Type == RubyRequestType:TLBI_EXT_SYNC_COMP) {
+        out_msg.type := CHIRequestType:DvmSync_ExternCompleted;
+      } else {
+        error("Invalid RubyRequestType");
+      }
+    }
+  }
+  seqInPort.dequeue(clockEdge());
+}
+
 action(AllocateTBE_PfRequest, desc="Allocate TBE for prefetch request") {
   // No retry for prefetch requests; just create resource stall
   check_allocate(storTBEs);
@@ -211,6 +272,14 @@
   incomingTransactionStart(address, curTransitionEvent(), initial, was_retried);
 }
 
+action(Initiate_Request_DVM, desc="") {
+  peek(reqRdyPort, CHIRequestMsg) {
+    // "address" for DVM = transaction ID
+    TBE tbe := allocateDvmRequestTBE(address, in_msg);
+    set_tbe(tbe);
+  }
+}
+
 action(Initiate_Request_Stale, desc="") {
   State initial := getState(tbe, cache_entry, address);
   bool was_retried := false;
@@ -219,6 +288,17 @@
     was_retried := in_msg.allowRetry == false;
   }
   copyCacheAndDir(cache_entry, getDirEntry(address), tbe, initial);
+
+  // usually we consider data locally invalid on RU states even if we
+  // have a copy; so it override it to valid so we can comeback to UD_RU/UC_RU
+  // at the end of this transaction
+  if (tbe.dir_ownerExists && tbe.dir_ownerIsExcl && is_valid(cache_entry)) {
+    // treat the data we got from the cache as valid
+    tbe.dataBlk := cache_entry.DataBlk;
+    tbe.dataBlkValid.fillMask();
+    tbe.dataValid := true;
+  }
+
   incomingTransactionStart(address, curTransitionEvent(), initial, was_retried);
 }
 
@@ -288,6 +368,8 @@
     tbe.is_stale := (tbe.dataValid && tbe.dataUnique) == false;
   } else if (hazard_tbe.pendReqType == CHIRequestType:Evict) {
     tbe.is_stale := tbe.dataValid == false;
+  } else if (hazard_tbe.pendReqType == CHIRequestType:CleanUnique) {
+    tbe.is_stale := tbe.dataValid == false;
   }
 
   // a pending action from the original request may have been stalled during
@@ -472,6 +554,15 @@
   }
   tbe.updateDirOnCompAck := false;
   // no need to update or access tags/data on ReadOnce served from upstream
+
+  if (is_invalid(cache_entry)) {
+    // if we receive data, invalidate at the end so it can be dropped
+    tbe.dataToBeInvalid := true;
+  } else if (tbe.dataValid == false) {
+    // possible on UD_RU,UC_RU where cache_entry valid but tbe.dataValid == false
+    // this prevents going to RU if no data is received from snoop
+    tbe.dataValid := true;
+  }
 }
 
 
@@ -575,71 +666,61 @@
 action(Initiate_CleanUnique, desc="") {
   tbe.actions.push(Event:ReadMissPipe); // TODO need another latency pipe ??
 
+  // invalidates everyone except requestor
+  if (tbe.dir_sharers.count() > 1) {
+    tbe.actions.push(Event:SendSnpCleanInvalidNoReq);
+  }
+  // auto upgrade if HN
+  tbe.dataUnique := tbe.dataUnique || is_HN;
+  // get unique permission
+  if (tbe.dataUnique == false) {
+    tbe.actions.push(Event:SendCleanUnique);
+    tbe.actions.push(Event:CheckUpgrade_FromCU);
+  }
+  // next actions will depend on the data state after snoops+CleanUnique
+  tbe.actions.push(Event:FinishCleanUnique);
+}
+
+action(Initiate_CleanUnique_Stale, desc="") {
   // requestor don't have the line anymore; send response but don't update the
   // directory on CompAck. The requestor knows we are not tracking it and will
   // send a ReadUnique later
-  if (tbe.dir_sharers.isElement(tbe.requestor) == false) {
-    tbe.actions.push(Event:SendCompUCResp);
-    tbe.actions.push(Event:WaitCompAck);
-    tbe.updateDirOnCompAck := false;
-  } else {
-    // invalidates everyone except requestor
-    if (tbe.dir_sharers.count() > 1) {
-      tbe.actions.push(Event:SendSnpCleanInvalidNoReq);
-    }
-    // auto upgrade if HN
-    tbe.dataUnique := tbe.dataUnique || is_HN;
-    // get unique permission
-    if (tbe.dataUnique == false) {
-      tbe.actions.push(Event:SendCleanUnique);
-      tbe.actions.push(Event:CheckUpgrade_FromCU);
-    }
-    // next actions will depend on the data state after snoops+CleanUnique
-    tbe.actions.push(Event:FinishCleanUnique);
-  }
+  tbe.actions.push(Event:SendCompUCRespStale);
+  tbe.actions.push(Event:WaitCompAck);
+  tbe.updateDirOnCompAck := false;
 }
 
 action(Finish_CleanUnique, desc="") {
   // This is should be executed at the end of a transaction
   assert(tbe.actions.empty());
-  tbe.actions.push(Event:SendCompUCResp);
-  tbe.actions.push(Event:WaitCompAck);
 
   // everyone may have been hit by an invalidation so check again
   if (tbe.dir_sharers.isElement(tbe.requestor) == false) {
     tbe.updateDirOnCompAck := false;
     assert(tbe.dataValid == false);
+    assert(tbe.is_stale);
+    tbe.is_stale := false;
+    tbe.actions.push(Event:SendCompUCRespStale);
+    tbe.actions.push(Event:WaitCompAck);
+    tbe.actions.push(Event:TagArrayWrite);
   } else {
     // must be the only one in sharers map
     assert(tbe.dir_sharers.count() == 1);
     assert(tbe.dataUnique);
 
-    // similar to Initiate_MaitainCoherence; writeback if the owner has data as
-    // clean data and we have it dirty and cannot keep it
-    bool fill_pipeline := tbe.dataValid && tbe.dataDirty;
-    bool req_has_dirty := tbe.dir_ownerExists && (tbe.dir_owner == tbe.requestor);
-    if (tbe.dataValid && tbe.dataDirty && tbe.dataToBeInvalid &&
-        (req_has_dirty == false)) {
-      fill_pipeline := false;
-      if (is_HN) {
-        tbe.actions.push(Event:SendWriteNoSnp);
-      } else {
-        tbe.actions.push(Event:SendWriteClean);
-      }
-      tbe.actions.push(Event:WriteBEPipe);
-      tbe.actions.push(Event:SendWBData);
-    }
-
     // needed by UpdateDirState_FromReqResp triggered by the expected CompAck
     tbe.dataMaybeDirtyUpstream := true;
     tbe.requestorToBeExclusiveOwner := true;
     tbe.dir_ownerExists := false;
 
-    if (fill_pipeline) {
-      tbe.actions.push(Event:CheckCacheFill);
-    }
+    tbe.actions.push(Event:SendCompUCResp);
+    tbe.actions.push(Event:WaitCompAck);
+
+    // Ensure we writeback or update the cache if the owner has data as
+    // clean data and we have it dirty.
+    // MaintainCoherence queues the TagArrayWrite
+    tbe.actions.push(Event:MaintainCoherence);
   }
-  tbe.actions.push(Event:TagArrayWrite);
 }
 
 
@@ -770,16 +851,9 @@
 }
 
 action(Initiate_WriteUnique_Forward, desc="") {
-  if (comp_wu) {
-    tbe.actions.push(Event:SendDBIDResp_WU);
-    tbe.actions.pushNB(Event:WriteFEPipe);
-    tbe.actions.pushNB(Event:SendWriteUnique);
-    tbe.actions.pushNB(Event:SendComp_WU);
-  } else {
-    tbe.actions.push(Event:SendCompDBIDResp_WU);
-    tbe.actions.pushNB(Event:WriteFEPipe);
-    tbe.actions.pushNB(Event:SendWriteUnique);
-  }
+  tbe.actions.push(Event:WriteFEPipe);
+  tbe.actions.push(Event:SendWriteUnique);
+  tbe.actions.push(Event:SendCompDBIDResp_WU);
   tbe.actions.push(Event:WriteBEPipe);
   tbe.actions.push(Event:SendWUData);
   tbe.dataToBeInvalid := true;
@@ -817,67 +891,72 @@
 
   tbe.actions.pushNB(Event:SendCompDBIDRespStale);
   tbe.actions.pushNB(Event:WriteFEPipe);
+  tbe.actions.push(Event:FinishCopyBack_Stale);
 
+  assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != tbe.requestor));
+}
+
+action(Finish_CopyBack_Stale, desc="") {
   // if it was the last known sharer and we don't have the data do the same
   // the Initiate_Evict
-  if ((is_HN == false) && (tbe.dir_sharers.count() == 1) &&
+  if ((is_HN == false) && (tbe.dir_sharers.count() == 0) &&
       tbe.dir_sharers.isElement(tbe.requestor) && (tbe.dataValid == false)) {
     tbe.actions.push(Event:SendEvict);
   }
-
-  tbe.dir_sharers.remove(tbe.requestor);
-  assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != tbe.requestor));
-
-  // usually we consider data locally invalid on RU states even if we
-  // have a copy; consider it valid for this transition only so we can
-  // comeback to UD_RU/UC_RU
-  if (is_valid(cache_entry) && (tbe.dataValid == false) &&
-      tbe.dir_ownerExists && tbe.dir_ownerIsExcl) {
-    tbe.dataValid := true;
-  }
 }
 
 action(Initiate_Evict, desc="") {
-  tbe.actions.push(Event:SendCompIResp);
-
-  assert(tbe.dir_sharers.isElement(tbe.requestor));
-  assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != tbe.requestor));
-  tbe.dir_sharers.remove(tbe.requestor);
-
-  if ((is_HN == false) && (tbe.dir_sharers.count() == 0) &&
+  if ((is_HN == false) && (tbe.dir_sharers.count() == 1) &&
       (tbe.dataValid == false)) {
-    tbe.actions.push(Event:SendEvict);
+    // last sharer and we also don't have a copy the line, so we also need to
+    // send an eviction downstream
+    if (tbe.dataUnique) {
+      // we need to send a WriteEvictFull so need the upstream data before
+      // we ack the evict
+      tbe.actions.push(Event:SendSnpOnce);
+      tbe.actions.push(Event:SendCompIResp);
+      tbe.actions.push(Event:SendWriteBackOrWriteEvict);
+      tbe.actions.push(Event:WriteBEPipe);
+      tbe.actions.push(Event:SendWBData);
+    } else {
+      tbe.actions.push(Event:SendCompIResp);
+      tbe.actions.push(Event:SendEvict);
+    }
+  } else {
+    tbe.actions.push(Event:SendCompIResp);
   }
-
-  tbe.actions.pushNB(Event:TagArrayWrite);
+  tbe.actions.push(Event:TagArrayWrite);
 }
 
-action(Initiate_MaitainCoherence, desc="") {
+action(Initiate_MaintainCoherence, desc="") {
   // issue a copy back if necessary to maintain coherence for data we are
   // droping. This is should be executed at the end of a transaction
   assert(tbe.actions.empty());
   // go through either the fill or the writeback pipeline
   if (tbe.dataValid && tbe.dataToBeInvalid) {
+    // we don't need to WB if the upstream is SD, because the
+    // owner is responsible for the WB
+    bool has_non_ex_owner := tbe.dir_ownerExists && !tbe.dir_ownerIsExcl;
     if (is_HN) {
-      if (tbe.dataDirty && (tbe.dataMaybeDirtyUpstream == false)) {
+      if (tbe.dataDirty && !has_non_ex_owner) {
         tbe.actions.push(Event:SendWriteNoSnp);
         tbe.actions.push(Event:WriteBEPipe);
         tbe.actions.push(Event:SendWBData);
       }
     } else {
       if (tbe.dir_sharers.isEmpty() && (tbe.dataDirty || tbe.dataUnique)) {
+        assert(!has_non_ex_owner);
         tbe.actions.push(Event:SendWriteBackOrWriteEvict);
         tbe.actions.push(Event:WriteBEPipe);
         tbe.actions.push(Event:SendWBData);
-      } else if ((tbe.dir_sharers.isEmpty() == false) && tbe.dataDirty &&
-                 (tbe.dataMaybeDirtyUpstream == false)) {
+      } else if (tbe.dataDirty && !has_non_ex_owner) {
+        assert(!tbe.dir_sharers.isEmpty());
         tbe.actions.push(Event:SendWriteClean);
         tbe.actions.push(Event:WriteBEPipe);
         tbe.actions.push(Event:SendWBData);
       }
     }
-  }
-  else if (tbe.dataValid) {
+  } else if (tbe.dataValid) {
     tbe.actions.push(Event:CheckCacheFill);
   }
   tbe.actions.push(Event:TagArrayWrite);
@@ -1217,7 +1296,6 @@
   assert(tbe.dataValid);
   assert(is_HN == false);
   assert(tbe.dataDirty);
-  assert(tbe.dataMaybeDirtyUpstream == false);
 
   enqueue(reqOutPort, CHIRequestMsg, request_latency) {
     prepareRequest(tbe, CHIRequestType:WriteCleanFull, out_msg);
@@ -1555,6 +1633,12 @@
   tbe.expected_req_resp.addExpectedCount(1);
 }
 
+action(ExpectComp, desc="") {
+  assert(is_valid(tbe));
+  tbe.expected_req_resp.addExpectedRespType(CHIResponseType:Comp);
+  tbe.expected_req_resp.addExpectedCount(1);
+}
+
 action(Receive_ReqDataResp, desc="") {
   assert(is_valid(tbe));
   assert(tbe.expected_req_resp.hasExpected());
@@ -1662,7 +1746,7 @@
       } else if (in_msg.type == CHIDataType:CBWrData_UD_PD) {
         assert(tbe.dir_ownerExists && tbe.dir_ownerIsExcl && (tbe.dir_owner == in_msg.responder));
         assert(tbe.dir_sharers.isElement(in_msg.responder));
-        if (tbe.pendReqType != CHIRequestType:WriteCleanFull) {
+        if (tbe.reqType != CHIRequestType:WriteCleanFull) {
           tbe.dir_ownerExists := false;
           tbe.dir_ownerIsExcl := false;
           tbe.dir_sharers.remove(in_msg.responder);
@@ -1670,14 +1754,17 @@
 
       } else if (in_msg.type == CHIDataType:CBWrData_SC) {
         assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != in_msg.responder));
-        tbe.dir_sharers.remove(in_msg.responder);
+        // Do not remove the responder in case of stale WriteCleanFull
+        if (tbe.reqType != CHIRequestType:WriteCleanFull) {
+          tbe.dir_sharers.remove(in_msg.responder);
+        }
 
       } else if (in_msg.type == CHIDataType:CBWrData_SD_PD) {
         assert(tbe.dir_ownerExists && (tbe.dir_ownerIsExcl == false) && (tbe.dir_owner == in_msg.responder));
         assert(tbe.dir_sharers.isElement(in_msg.responder));
         tbe.dir_ownerExists := false;
         tbe.dir_ownerIsExcl := false;
-        if (tbe.pendReqType != CHIRequestType:WriteCleanFull) {
+        if (tbe.reqType != CHIRequestType:WriteCleanFull) {
           tbe.dir_sharers.remove(in_msg.responder);
         }
 
@@ -1824,7 +1911,13 @@
         assert(tbe.dataUnique);
         tbe.dataDirty := true;
         tbe.dataValid := true;
-        tbe.dataMaybeDirtyUpstream := false;
+        if (tbe.reqType == CHIRequestType:WriteCleanFull) {
+          // upstream data can still be UC if this is a WriteCleanFull
+          assert(tbe.dir_ownerExists && tbe.dir_ownerIsExcl);
+          tbe.dataMaybeDirtyUpstream := true;
+        } else {
+          tbe.dataMaybeDirtyUpstream := false;
+        }
 
       } else if (in_msg.type == CHIDataType:CBWrData_SD_PD) {
         tbe.dataDirty := true;
@@ -1851,7 +1944,12 @@
     peek(datInPort, CHIDataMsg) {
       assert(in_msg.type == CHIDataType:NCBWrData);
       tbe.dataDirty := true;
-      tbe.dataValid := tbe.accSize == blockSize;
+      if (tbe.reqType == CHIRequestType:WriteUniquePtl) {
+        // we are just updating any valid data we already had
+        tbe.dataValid := tbe.dataValid || (tbe.accSize == blockSize);
+      } else {
+        tbe.dataValid := tbe.accSize == blockSize;
+      }
     }
   }
   printTBEState(tbe);
@@ -2064,6 +2162,16 @@
   }
 }
 
+action(Send_Retry_DVM, desc="") {
+  assert(tbe.pendReqAllowRetry);
+  assert(tbe.rcvdRetryCredit);
+  assert(tbe.rcvdRetryAck);
+  enqueue(reqOutPort, CHIRequestMsg, request_latency) {
+    prepareRequestRetryDVM(tbe, out_msg);
+  }
+  destsWaitingRetry.removeNetDest(tbe.pendReqDest);
+}
+
 action(Receive_RetryAck_Hazard, desc="") {
   TBE hazard_tbe := getHazardTBE(tbe);
   assert(hazard_tbe.pendReqAllowRetry);
@@ -2101,27 +2209,15 @@
   bool is_rd_nsd := tbe.reqType == CHIRequestType:ReadNotSharedDirty;
   bool is_rd_unique := tbe.reqType == CHIRequestType:ReadUnique;
 
+  // if the config allows (or not caching the data) and line has no sharers
+  bool snd_unique_on_rs := (fwd_unique_on_readshared || tbe.dataToBeInvalid)
+                          && tbe.dataUnique && tbe.dir_sharers.isEmpty();
+  // if the request type allows and we won't be caching the data
+  bool snd_dirty_on_rs := is_rd_shared && !is_rd_nsd && tbe.dataToBeInvalid;
+
   if (is_rd_once) {
     tbe.snd_msgType := CHIDataType:CompData_I;
-  } else if (tbe.dataToBeInvalid) {
-    // We will drop the data so propagate it's coherent state upstream
-    if (tbe.dataUnique && tbe.dataDirty) {
-      tbe.snd_msgType := CHIDataType:CompData_UD_PD;
-    } else if (tbe.dataUnique) {
-      tbe.snd_msgType := CHIDataType:CompData_UC;
-    } else if (tbe.dataDirty) {
-      if (is_rd_nsd) {
-        tbe.snd_msgType := CHIDataType:CompData_SC;
-      } else {
-        tbe.snd_msgType := CHIDataType:CompData_SD_PD;
-      }
-    } else {
-      tbe.snd_msgType := CHIDataType:CompData_SC;
-    }
-  } else if (is_rd_unique ||
-             (is_rd_shared && tbe.dataUnique &&
-              fwd_unique_on_readshared && (tbe.dir_ownerExists == false))) {
-    // propagates dirtyness
+  } else if (is_rd_unique || (is_rd_shared && snd_unique_on_rs)) {
     assert(tbe.dataUnique);
     if (tbe.dataDirty) {
       tbe.snd_msgType := CHIDataType:CompData_UD_PD;
@@ -2129,8 +2225,13 @@
       tbe.snd_msgType := CHIDataType:CompData_UC;
     }
   } else if (is_rd_shared) {
-    // still keeping a copy so can send as SC
-    tbe.snd_msgType := CHIDataType:CompData_SC;
+    if (tbe.dataDirty && snd_dirty_on_rs) {
+      tbe.snd_msgType := CHIDataType:CompData_SD_PD;
+    } else {
+      // notice the MaintainCoherence will send WriteClean if the line
+      // is dirty and we won't be caching the data
+      tbe.snd_msgType := CHIDataType:CompData_SC;
+    }
   } else {
     error("Invalid request type");
   }
@@ -2434,6 +2535,13 @@
 
 action(Send_CompI, desc="") {
   assert(is_valid(tbe));
+
+  // Used to ack Evict request
+  assert(tbe.dir_sharers.isElement(tbe.requestor));
+  assert((tbe.dir_ownerExists == false) || (tbe.dir_owner != tbe.requestor));
+
+  tbe.dir_sharers.remove(tbe.requestor);
+
   enqueue(rspOutPort, CHIResponseMsg, response_latency) {
     out_msg.addr := address;
     out_msg.type := CHIResponseType:Comp_I;
@@ -2452,6 +2560,19 @@
   }
 }
 
+action(Send_CompUC_Stale, desc="") {
+  assert(is_valid(tbe));
+  enqueue(rspOutPort, CHIResponseMsg, response_latency) {
+    out_msg.addr := address;
+    out_msg.type := CHIResponseType:Comp_UC;
+    out_msg.responder := machineID;
+    out_msg.Destination.add(tbe.requestor);
+    // We don't know if this is a stale clean unique or a bug, so flag the
+    // reponse so the requestor can make further checks
+    out_msg.stale := true;
+  }
+}
+
 action(Send_CompAck, desc="") {
   assert(is_valid(tbe));
   enqueue(rspOutPort, CHIResponseMsg, response_latency) {
@@ -2521,6 +2642,10 @@
 action(Send_SnpRespI, desc="") {
   enqueue(rspOutPort, CHIResponseMsg, response_latency) {
     out_msg.addr := address;
+    if (tbe.is_dvm_tbe || tbe.is_dvm_snp_tbe) {
+      out_msg.usesTxnId := true;
+      out_msg.txnId := tbe.addr;
+    }
     out_msg.type := CHIResponseType:SnpResp_I;
     out_msg.responder := machineID;
     out_msg.Destination.add(tbe.requestor);
@@ -2531,6 +2656,7 @@
   peek(retryTriggerInPort, RetryTriggerMsg) {
     enqueue(rspOutPort, CHIResponseMsg, response_latency) {
       out_msg.addr := in_msg.addr;
+      out_msg.usesTxnId := in_msg.usesTxnId;
       out_msg.type := CHIResponseType:RetryAck;
       out_msg.responder := machineID;
       out_msg.Destination.add(in_msg.retryDest);
@@ -2542,6 +2668,7 @@
   peek(retryTriggerInPort, RetryTriggerMsg) {
     enqueue(rspOutPort, CHIResponseMsg, response_latency) {
       out_msg.addr := in_msg.addr;
+      out_msg.usesTxnId := in_msg.usesTxnId;
       out_msg.type := CHIResponseType:PCrdGrant;
       out_msg.responder := machineID;
       out_msg.Destination.add(in_msg.retryDest);
@@ -2549,20 +2676,25 @@
   }
 }
 
-// Note on CheckUpgrade_FromStore/CheckUpgrade_FromCU/CheckUpgrade_FromRU
+// Note on CheckUpgrade_FromStoreOrRU/CheckUpgrade_FromCU
 // We will always get Comp_UC; but if our data is invalidated before
 // Comp_UC we would need to go to UCE. Since we don't use the UCE state
 // we remain in the transient state and follow-up with ReadUnique.
 // Note this assumes the responder knows we have invalid data when sending
 // us Comp_UC and does not register us as owner.
 
-action(CheckUpgrade_FromStore, desc="") {
+action(CheckUpgrade_FromStoreOrRU, desc="") {
   assert(is_HN == false);
   if (tbe.dataUnique) {
     // success, just send CompAck next
     assert(tbe.dataValid);
   } else {
+    // will need to get data instead
     tbe.actions.pushFront(Event:SendReadUnique);
+    // we must have received an invalidation snoop that marked
+    // the req as stale
+    assert(tbe.is_stale);
+    tbe.is_stale := false;
   }
   tbe.actions.pushFront(Event:SendCompAck);
 }
@@ -2579,17 +2711,6 @@
   tbe.actions.pushFront(Event:SendCompAck);
 }
 
-action(CheckUpgrade_FromRU, desc="") {
-  assert(is_HN == false);
-  if (tbe.dataUnique) {
-    // success, just send CompAck next
-    assert(tbe.dataValid);
-  } else {
-    // will need to get data instead
-    tbe.actions.pushFront(Event:SendReadUnique);
-  }
-  tbe.actions.pushFront(Event:SendCompAck);
-}
 
 action(Finalize_UpdateCacheFromTBE, desc="") {
   assert(is_valid(tbe));
@@ -2742,6 +2863,37 @@
   incomingTransactionEnd(address, curTransitionNextState());
 }
 
+action(Finalize_DeallocateDvmRequest, desc="") {
+  assert(is_valid(tbe));
+  assert(tbe.actions.empty());
+  wakeupPendingReqs(tbe);
+  wakeupPendingSnps(tbe);
+  wakeupPendingTgrs(tbe);
+
+  // Don't call processRetryQueue() because DVM ops don't interact with the retry queue
+
+  assert(tbe.is_dvm_tbe);
+  deallocateDvmTBE(tbe);
+  unset_tbe();
+}
+
+action(Finalize_DeallocateDvmSnoop, desc="") {
+  assert(is_valid(tbe));
+  assert(tbe.actions.empty());
+  wakeupPendingReqs(tbe);
+  wakeupPendingSnps(tbe);
+  wakeupPendingTgrs(tbe);
+
+  // Don't call processRetryQueue() because DVM ops don't interact with the retry queue
+
+  assert(tbe.is_dvm_snp_tbe);
+  deallocateDvmSnoopTBE(tbe);
+  unset_tbe();
+
+  // Last argument = false, so it uses a "unique ID" rather than an address
+  incomingTransactionEnd(address, curTransitionNextState(), false);
+}
+
 action(Pop_ReqRdyQueue, desc="") {
   reqRdyPort.dequeue(clockEdge());
 }
@@ -2775,6 +2927,13 @@
   retryTriggerInPort.dequeue(clockEdge());
 }
 
+action(Pop_SnpInPort, desc="") {
+  snpInPort.dequeue(clockEdge());
+}
+action(Pop_SeqInPort, desc="") {
+  seqInPort.dequeue(clockEdge());
+}
+
 action(ProcessNextState, desc="") {
   assert(is_valid(tbe));
   processNextState(address, tbe, cache_entry);
@@ -3057,3 +3216,190 @@
   assert(is_valid(tbe));
   tbe.delayNextAction := curTick() + cyclesToTicks(snp_latency);
 }
+
+//////////////////////////////////
+// DVM Actions
+
+action(Send_DvmTlbi, desc="") {
+  enqueue(reqOutPort, CHIRequestMsg, request_latency) {
+    prepareRequest(tbe, CHIRequestType:DvmOpNonSync, out_msg);
+    DPRINTF(RubyProtocol, "Sending DvmOpNonSync to %d\n", getMiscNodeMachine());
+
+    out_msg.usesTxnId := true;
+    out_msg.txnId := tbe.addr; // for DVM TBEs addr = txnId
+
+    out_msg.Destination.clear();
+    out_msg.Destination.add(getMiscNodeMachine());
+    out_msg.dataToFwdRequestor := false;
+
+    // Don't set message size, we don't use the data inside the messages
+
+    allowRequestRetry(tbe, out_msg);
+  }
+
+  // TLBIs can be ended early if the MN chooses to send CompDBIDResp.
+  // Otherwise, the MN sends a plain DBIDResp, and then sends a Comp later.
+  // => We add two possible response types, then add 1 to the count
+  // e.g. "expect exactly 1 (CompDBIDResp OR DBIDResp)"
+  clearExpectedReqResp(tbe);
+  tbe.expected_req_resp.addExpectedRespType(CHIResponseType:CompDBIDResp);
+  tbe.expected_req_resp.addExpectedRespType(CHIResponseType:DBIDResp);
+  tbe.expected_req_resp.addExpectedCount(1);
+  // If a plain DBIDResp is recieved, then Comp will be manually expected.
+  // (expect_sep_wu_comp also sort of handles this, but it's WU specific,
+  // and ProcessNextState doesn't respect it).
+
+  // Push a value to the list of pending NonSyncs
+  // The actual value doesn't matter, but we have to pick
+  // a type which already has function signatures
+  // e.g. TriggerQueue has push(Event) specified in SLICC but not push(addr)
+  DPRINTF(RubyProtocol, "Pushing pending nonsync to blocklist %16x\n", tbe.addr);
+  dvmPendingNonSyncsBlockingSync.push(Event:DvmTlbi_Initiate);
+}
+
+// Try to send a DVM Sync, but put it in the pending slot
+// if there are pending Non-Syncs blocking it.
+action(Try_Send_DvmSync, desc="") {
+  if (dvmPendingNonSyncsBlockingSync.empty()){
+    DPRINTF(RubyProtocol, "Nonsync queue is empty so %016x can proceed\n", tbe.addr);
+    tbe.actions.push(Event:DvmSync_Send);
+  } else {
+    assert(!dvmHasPendingSyncOp);
+    DPRINTF(RubyProtocol, "Nonsync queue is not empty so %016x is now pending\n", tbe.addr);
+    dvmHasPendingSyncOp := true;
+    dvmPendingSyncOp := address;
+  }
+}
+
+// Try to send a DVM sync that was put in the pending slot
+// due to pending Non-Syncs blocking it. Those Non-Syncs may not be
+// blocking it anymore.
+action(Try_Send_Pending_DvmSync, desc="") {
+  // Pop an element off the list of pending NonSyncs
+  // It won't necessarily be ours, but that doesn't matter.
+  assert(!dvmPendingNonSyncsBlockingSync.empty());
+  DPRINTF(RubyProtocol, "Popping nonsync from blocklist %16x\n", tbe.addr);
+  dvmPendingNonSyncsBlockingSync.pop();
+
+  if (dvmPendingNonSyncsBlockingSync.empty() && dvmHasPendingSyncOp) {
+    DPRINTF(RubyProtocol, "Blocklist now empty, pending op %16x can proceed\n", dvmPendingSyncOp);
+    TBE syncTBE := getDvmTBE(dvmPendingSyncOp);
+    assert(is_valid(syncTBE));
+    syncTBE.actions.push(Event:DvmSync_Send);
+
+    dvmHasPendingSyncOp := false;
+  }
+}
+
+action(Send_DvmSync, desc="") {
+  enqueue(reqOutPort, CHIRequestMsg, request_latency) {
+    prepareRequest(tbe, CHIRequestType:DvmOpSync, out_msg);
+    DPRINTF(RubyProtocol, "Sending DvmOpSync to %d\n", getMiscNodeMachine());
+
+    out_msg.usesTxnId := true;
+    out_msg.txnId := tbe.addr; // for DVM TBEs addr = txnId
+
+    out_msg.Destination.clear();
+    out_msg.Destination.add(getMiscNodeMachine());
+    out_msg.dataToFwdRequestor := false;
+
+    // Don't set message size, we don't use the data inside the messages
+
+    allowRequestRetry(tbe, out_msg);
+  }
+
+  clearExpectedReqResp(tbe);
+  tbe.expected_req_resp.addExpectedRespType(CHIResponseType:DBIDResp);
+  tbe.expected_req_resp.addExpectedCount(1);
+  // Comp will be expected later
+}
+
+action(Send_DvmTlbi_NCBWrData, desc="") {
+  enqueue(datOutPort, CHIDataMsg, data_latency) {
+    out_msg.addr := tbe.addr;
+    out_msg.type := CHIDataType:NCBWrData;
+
+    out_msg.usesTxnId := true;
+    out_msg.txnId := tbe.addr; // for DVM TBEs addr = txnId
+
+    // Set dataBlk to all 0 - we don't actually use the contents
+    out_msg.dataBlk.clear();
+    // Data should be 8 bytes - this function is (offset, range)
+    out_msg.bitMask.setMask(0, 8);
+
+    out_msg.responder := machineID;
+
+    out_msg.Destination.clear();
+    out_msg.Destination.add(getMiscNodeMachine());
+  }
+}
+
+action(Send_DvmSync_NCBWrData, desc="") {
+  enqueue(datOutPort, CHIDataMsg, data_latency) {
+    out_msg.addr := tbe.addr;
+    out_msg.type := CHIDataType:NCBWrData;
+
+    out_msg.usesTxnId := true;
+    out_msg.txnId := tbe.addr; // for DVM TBEs addr = txnId
+
+    // Set dataBlk to all 0 - we don't actually use the contents
+    out_msg.dataBlk.clear();
+    // Data should be 8 bytes - this function is (offset, range)
+    // I assume the range is in bytes...
+    out_msg.bitMask.setMask(0, 8);
+
+    out_msg.responder := machineID;
+
+    out_msg.Destination.clear();
+    out_msg.Destination.add(getMiscNodeMachine());
+  }
+}
+
+action(DvmTlbi_CompCallback, desc="") {
+  assert(is_valid(tbe));
+  assert(tbe.is_dvm_tbe);
+  assert(tbe.reqType == CHIRequestType:DvmTlbi_Initiate);
+  sequencer.unaddressedCallback(tbe.addr, RubyRequestType:TLBI);
+}
+
+action(DvmSync_CompCallback, desc="") {
+  assert(is_valid(tbe));
+  assert(tbe.is_dvm_tbe);
+  assert(tbe.reqType == CHIRequestType:DvmSync_Initiate);
+  sequencer.unaddressedCallback(tbe.addr, RubyRequestType:TLBI_SYNC);
+}
+
+//////////////////////////////////
+// DVM Snoop Actions
+
+action(Initiate_DvmSnoop, desc="") {
+  // DvmSnoop cannot be retried
+  bool was_retried := false;
+  peek(snpRdyPort, CHIRequestMsg) {
+    set_tbe(allocateDvmSnoopTBE(address, in_msg));
+  }
+  // Last argument = false, so it uses a "unique ID" rather than an address
+  // "Incoming" transactions for DVM = time between receiving a Snooped DVM op
+  // and sending the SnpResp_I
+  incomingTransactionStart(address, curTransitionEvent(), State:I, was_retried, false);
+}
+
+action(DvmExtTlbi_EnqueueSnpResp, desc=""){
+  tbe.delayNextAction := curTick() + cyclesToTicks(dvm_ext_tlbi_latency);
+  tbe.actions.push(Event:SendSnpIResp);
+}
+
+action(DvmExtSync_TriggerCallback, desc=""){
+  assert(is_valid(tbe));
+  assert(tbe.is_dvm_snp_tbe);
+  sequencer.unaddressedCallback(tbe.addr, RubyRequestType:TLBI_EXT_SYNC);
+}
+
+action(Profile_OutgoingStart_DVM, desc="") {
+  outgoingTransactionStart(address, curTransitionEvent(), false);
+}
+
+action(Profile_OutgoingEnd_DVM, desc="") {
+  assert(is_valid(tbe));
+  outgoingTransactionEnd(address, tbe.rcvdRetryAck, false);
+}
diff --git a/src/mem/ruby/protocol/chi/CHI-cache-funcs.sm b/src/mem/ruby/protocol/chi/CHI-cache-funcs.sm
index adf4e1c..f990c0b 100644
--- a/src/mem/ruby/protocol/chi/CHI-cache-funcs.sm
+++ b/src/mem/ruby/protocol/chi/CHI-cache-funcs.sm
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 ARM Limited
+ * Copyright (c) 2021-2022 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -52,11 +52,19 @@
 void set_tbe(TBE b);
 void unset_tbe();
 MachineID mapAddressToDownstreamMachine(Addr addr);
+MachineID mapAddressToMachine(Addr addr, MachineType mtype);
 
 void incomingTransactionStart(Addr, Event, State, bool);
 void incomingTransactionEnd(Addr, State);
 void outgoingTransactionStart(Addr, Event);
 void outgoingTransactionEnd(Addr, bool);
+// Overloads for transaction-measuring functions
+// final bool = isAddressed
+// if false, uses a "unaddressed" table with unique IDs
+void incomingTransactionStart(Addr, Event, State, bool, bool);
+void incomingTransactionEnd(Addr, State, bool);
+void outgoingTransactionStart(Addr, Event, bool);
+void outgoingTransactionEnd(Addr, bool, bool);
 Event curTransitionEvent();
 State curTransitionNextState();
 
@@ -74,6 +82,10 @@
   return static_cast(CacheEntry, "pointer", cache.lookup(addr));
 }
 
+CacheEntry nullCacheEntry(), return_by_pointer="yes" {
+  return OOD;
+}
+
 DirEntry getDirEntry(Addr addr), return_by_pointer = "yes" {
   if (directory.isTagPresent(addr)) {
     return directory.lookup(addr);
@@ -110,6 +122,22 @@
   }
 }
 
+TBE nullTBE(), return_by_pointer="yes" {
+  return OOD;
+}
+
+TBE getDvmTBE(Addr txnId), return_by_pointer="yes" {
+  TBE dvm_tbe := dvmTBEs[txnId];
+  if (is_valid(dvm_tbe)) {
+    return dvm_tbe;
+  }
+  TBE dvm_snp_tbe := dvmSnpTBEs[txnId];
+  if (is_valid(dvm_snp_tbe)) {
+    return dvm_snp_tbe;
+  }
+  return OOD;
+}
+
 TBE getCurrentActiveTBE(Addr addr), return_by_pointer="yes" {
   // snoops take precedence over wbs and reqs
   // it's invalid to have a replacement and a req active at the same time
@@ -135,8 +163,15 @@
   TBE tbe := getCurrentActiveTBE(addr);
   if(is_valid(tbe)) {
     assert(Cache_State_to_permission(tbe.state) == AccessPermission:Busy);
-    if (tbe.expected_req_resp.hasExpected() ||
-        tbe.expected_snp_resp.hasExpected()) {
+    // It's assumed that if all caches are in transient state, the latest data
+    // is 1) within an inflight message, then 2 ) in memory. But with
+    // CleanUnique there may be no inflight messages with data, so it needs
+    // special handling.
+    bool cu_requester_or_responder :=
+      (tbe.reqType == CHIRequestType:CleanUnique) ||
+      (tbe.pendReqType == CHIRequestType:CleanUnique);
+    if ((tbe.expected_req_resp.hasExpected() ||
+        tbe.expected_snp_resp.hasExpected()) && !cu_requester_or_responder) {
       DPRINTF(RubySlicc, "%x %s,%s\n", addr, tbe.state, AccessPermission:Busy);
       return AccessPermission:Busy;
     }
@@ -373,6 +408,8 @@
 
   assert(tbe.is_snp_tbe == false);
   assert(tbe.is_repl_tbe == false);
+  assert(tbe.is_dvm_tbe == false);
+  assert(tbe.is_dvm_snp_tbe == false);
   tbe.is_req_tbe := true;
 
   tbe.accAddr := in_msg.accAddr;
@@ -393,6 +430,41 @@
   return tbe;
 }
 
+TBE allocateDvmRequestTBE(Addr txnId, CHIRequestMsg in_msg), return_by_pointer="yes" {
+  // We must have reserved resources for this allocation
+  storDvmTBEs.decrementReserved();
+  assert(storDvmTBEs.areNSlotsAvailable(1));
+
+  dvmTBEs.allocate(txnId);
+  TBE tbe := dvmTBEs[txnId];
+
+  // Setting .addr = txnId
+  initializeTBE(tbe, txnId, storDvmTBEs.addEntryToNewSlot());
+
+  assert(tbe.is_snp_tbe == false);
+  assert(tbe.is_repl_tbe == false);
+  assert(tbe.is_req_tbe == false);
+  assert(tbe.is_dvm_snp_tbe == false);
+  tbe.is_dvm_tbe := true;
+
+  // TODO - zero these out?
+  tbe.accAddr := txnId;
+  tbe.accSize := blockSize;
+  tbe.requestor := in_msg.requestor;
+  tbe.reqType := in_msg.type;
+
+  tbe.isSeqReqValid := in_msg.isSeqReqValid;
+  tbe.seqReq := in_msg.seqReq;
+  tbe.is_local_pf := in_msg.is_local_pf;
+  tbe.is_remote_pf := in_msg.is_remote_pf;
+
+  tbe.use_DMT := false;
+  tbe.use_DCT := false;
+
+  tbe.hasUseTimeout := false;
+
+  return tbe;
+}
 
 TBE allocateSnoopTBE(Addr addr, CHIRequestMsg in_msg), return_by_pointer="yes" {
   // We must have reserved resources for this allocation
@@ -405,6 +477,8 @@
 
   assert(tbe.is_req_tbe == false);
   assert(tbe.is_repl_tbe == false);
+  assert(tbe.is_dvm_tbe == false);
+  assert(tbe.is_dvm_snp_tbe == false);
   tbe.is_snp_tbe := true;
 
   tbe.accAddr := addr;
@@ -421,6 +495,35 @@
   return tbe;
 }
 
+TBE allocateDvmSnoopTBE(Addr txnId, CHIRequestMsg in_msg), return_by_pointer="yes" {
+  // We must have reserved resources for this allocation
+  storDvmSnpTBEs.decrementReserved();
+  assert(storDvmSnpTBEs.areNSlotsAvailable(1));
+
+  dvmSnpTBEs.allocate(txnId);
+  TBE tbe := dvmSnpTBEs[txnId];
+  initializeTBE(tbe, txnId, storDvmSnpTBEs.addEntryToNewSlot());
+
+  assert(tbe.is_req_tbe == false);
+  assert(tbe.is_repl_tbe == false);
+  assert(tbe.is_dvm_tbe == false);
+  assert(tbe.is_snp_tbe == false);
+  tbe.is_dvm_snp_tbe := true;
+
+  // TODO - zero these out?
+  tbe.accAddr := txnId;
+  tbe.accSize := blockSize;
+  tbe.requestor := in_msg.requestor;
+  tbe.fwdRequestor := in_msg.fwdRequestor;
+  tbe.reqType := in_msg.type;
+
+  tbe.snpNeedsData := in_msg.retToSrc;
+
+  tbe.use_DMT := false;
+  tbe.use_DCT := false;
+
+  return tbe;
+}
 
 TBE _allocateReplacementTBE(Addr addr, int storSlot), return_by_pointer="yes" {
   TBE tbe := replTBEs[addr];
@@ -428,6 +531,7 @@
 
   assert(tbe.is_req_tbe == false);
   assert(tbe.is_snp_tbe == false);
+  assert(tbe.is_dvm_tbe == false);
   tbe.is_repl_tbe := true;
 
   tbe.accAddr := addr;
@@ -552,10 +656,33 @@
   out_msg.is_remote_pf := tbe.is_local_pf || tbe.is_remote_pf;
 }
 
+void prepareRequestRetryDVM(TBE tbe, CHIRequestMsg & out_msg) {
+  assert(tbe.pendReqAllowRetry);
+  tbe.pendReqAllowRetry := false;
+  out_msg.allowRetry := false;
+
+  out_msg.addr := tbe.addr;
+  out_msg.usesTxnId := true;
+  out_msg.txnId := tbe.addr;
+  out_msg.requestor := machineID;
+  out_msg.fwdRequestor := tbe.requestor;
+  out_msg.accAddr := tbe.pendReqAccAddr;
+  out_msg.accSize := tbe.pendReqAccSize;
+  out_msg.type := tbe.pendReqType;
+  out_msg.Destination := tbe.pendReqDest;
+  out_msg.dataToFwdRequestor := tbe.pendReqD2OrigReq;
+  out_msg.retToSrc := tbe.pendReqRetToSrc;
+  out_msg.isSeqReqValid := tbe.isSeqReqValid;
+  out_msg.seqReq := tbe.seqReq;
+  out_msg.is_local_pf := false;
+  out_msg.is_remote_pf := tbe.is_local_pf || tbe.is_remote_pf;
+}
+
 void enqueueDoRetry(TBE tbe) {
   if (tbe.rcvdRetryAck && tbe.rcvdRetryCredit) {
     enqueue(retryTriggerOutPort, RetryTriggerMsg, 0) {
       out_msg.addr := tbe.addr;
+      out_msg.usesTxnId := tbe.is_dvm_tbe || tbe.is_dvm_snp_tbe;
       out_msg.event := Event:DoRetry;
     }
     destsWaitingRetry.removeNetDest(tbe.pendReqDest);
@@ -573,6 +700,7 @@
     retryQueue.pop();
     enqueue(retryTriggerOutPort, RetryTriggerMsg, 0) {
       out_msg.addr := e.addr;
+      out_msg.usesTxnId := e.usesTxnId;
       out_msg.retryDest := e.retryDest;
       out_msg.event := Event:SendPCrdGrant;
     }
@@ -583,15 +711,17 @@
   if (unify_repl_TBEs) {
     assert(storReplTBEs.size() == 0);
     assert(storReplTBEs.reserved() == 0);
-    DPRINTF(RubySlicc, "Resources(used/rsvd/max): TBEs=%d/%d/%d snpTBEs=%d/%d/%d replTBEs=%d/%d/%d\n",
+    DPRINTF(RubySlicc, "Resources(used/rsvd/max): TBEs=%d/%d/%d snpTBEs=%d/%d/%d replTBEs=%d/%d/%d dvmTBEs=%d/%d/%d\n",
                   storTBEs.size(), storTBEs.reserved(), storTBEs.capacity(),
                   storSnpTBEs.size(), storSnpTBEs.reserved(), storSnpTBEs.capacity(),
-                  storTBEs.size(), storTBEs.reserved(), storTBEs.capacity());
+                  storTBEs.size(), storTBEs.reserved(), storTBEs.capacity(),
+                  storDvmTBEs.size(), storDvmTBEs.reserved(), storDvmTBEs.capacity());
   } else {
-    DPRINTF(RubySlicc, "Resources(used/rsvd/max): TBEs=%d/%d/%d snpTBEs=%d/%d/%d replTBEs=%d/%d/%d\n",
+    DPRINTF(RubySlicc, "Resources(used/rsvd/max): TBEs=%d/%d/%d snpTBEs=%d/%d/%d replTBEs=%d/%d/%d dvmTBEs=%d/%d/%d\n",
                   storTBEs.size(), storTBEs.reserved(), storTBEs.capacity(),
                   storSnpTBEs.size(), storSnpTBEs.reserved(), storSnpTBEs.capacity(),
-                  storReplTBEs.size(), storReplTBEs.reserved(), storReplTBEs.capacity());
+                  storReplTBEs.size(), storReplTBEs.reserved(), storReplTBEs.capacity(),
+                  storDvmTBEs.size(), storDvmTBEs.reserved(), storDvmTBEs.capacity());
   }
   DPRINTF(RubySlicc, "Resources(in/out size): req=%d/%d rsp=%d/%d dat=%d/%d snp=%d/%d trigger=%d\n",
                 reqIn.getSize(curTick()), reqOut.getSize(curTick()),
@@ -659,6 +789,17 @@
   DPRINTF(RubySlicc, "dataBlkValid = %s\n", tbe.dataBlkValid);
 }
 
+void printDvmTBEState(TBE tbe) {
+  DPRINTF(RubySlicc, "STATE: addr=%#x reqType=%d state=%d pendAction=%s isDvmTBE=%d isReplTBE=%d isReqTBE=%d isSnpTBE=%d\n",
+                      tbe.addr, tbe.reqType, tbe.state, tbe.pendAction,
+                      tbe.is_dvm_tbe, tbe.is_repl_tbe, tbe.is_req_tbe, tbe.is_snp_tbe);
+}
+
+MachineID getMiscNodeMachine() {
+  // return the MachineID of the misc node
+  return mapAddressToMachine(intToAddress(0), MachineType:MiscNode);
+}
+
 void copyCacheAndDir(CacheEntry cache_entry, DirEntry dir_entry,
                      TBE tbe, State initialState) {
   assert(is_valid(tbe));
@@ -785,6 +926,20 @@
   replTBEs.deallocate(tbe.addr);
 }
 
+void deallocateDvmTBE(TBE tbe) {
+  assert(is_valid(tbe));
+  assert(tbe.is_dvm_tbe);
+  storDvmTBEs.removeEntryFromSlot(tbe.storSlot);
+  dvmTBEs.deallocate(tbe.addr);
+}
+
+void deallocateDvmSnoopTBE(TBE tbe) {
+  assert(is_valid(tbe));
+  assert(tbe.is_dvm_snp_tbe);
+  storDvmSnpTBEs.removeEntryFromSlot(tbe.storSlot);
+  dvmSnpTBEs.deallocate(tbe.addr);
+}
+
 void setDataToBeStates(TBE tbe) {
   assert(is_valid(tbe));
   if (tbe.dataToBeInvalid) {
@@ -1037,6 +1192,10 @@
     } else {
       return Event:WriteUnique; // all WriteUnique handled the same when ~PoC
     }
+  } else if (type == CHIRequestType:DvmTlbi_Initiate) {
+    return Event:DvmTlbi_Initiate;
+  } else if (type == CHIRequestType:DvmSync_Initiate) {
+    return Event:DvmSync_Initiate;
   } else {
     error("Invalid CHIRequestType");
   }
@@ -1188,6 +1347,14 @@
     return Event:SnpOnce;
   } else if (type == CHIRequestType:SnpOnceFwd) {
     return Event:SnpOnceFwd;
+  } else if (type == CHIRequestType:SnpDvmOpSync_P1) {
+    return Event:SnpDvmOpSync_P1;
+  } else if (type == CHIRequestType:SnpDvmOpSync_P2) {
+    return Event:SnpDvmOpSync_P2;
+  } else if (type == CHIRequestType:SnpDvmOpNonSync_P1) {
+    return Event:SnpDvmOpNonSync_P1;
+  } else if (type == CHIRequestType:SnpDvmOpNonSync_P2) {
+    return Event:SnpDvmOpNonSync_P2;
   } else {
     error("Invalid CHIRequestType");
   }
diff --git a/src/mem/ruby/protocol/chi/CHI-cache-ports.sm b/src/mem/ruby/protocol/chi/CHI-cache-ports.sm
index efba9bc..8bb76fd 100644
--- a/src/mem/ruby/protocol/chi/CHI-cache-ports.sm
+++ b/src/mem/ruby/protocol/chi/CHI-cache-ports.sm
@@ -78,9 +78,18 @@
   if (rspInPort.isReady(clockEdge())) {
     printResources();
     peek(rspInPort, CHIResponseMsg) {
-      TBE tbe := getCurrentActiveTBE(in_msg.addr);
-      trigger(respToEvent(in_msg.type, tbe), in_msg.addr,
-              getCacheEntry(in_msg.addr), tbe);
+      if (in_msg.usesTxnId) {
+        // A ResponseMsg that uses transaction ID
+        // is separate from the memory system,
+        // uses a separate TBE table and doesn't have a cache entry
+        TBE tbe := getDvmTBE(in_msg.txnId);
+        trigger(respToEvent(in_msg.type, tbe), in_msg.txnId,
+                nullCacheEntry(), tbe);
+      } else {
+        TBE tbe := getCurrentActiveTBE(in_msg.addr);
+        trigger(respToEvent(in_msg.type, tbe), in_msg.addr,
+                getCacheEntry(in_msg.addr), tbe);
+      }
     }
   }
 }
@@ -96,8 +105,9 @@
   if (datInPort.isReady(clockEdge())) {
     printResources();
     peek(datInPort, CHIDataMsg) {
-      assert((in_msg.bitMask.count() <= data_channel_size)
-              && (in_msg.bitMask.count() > 0));
+      // We don't have any transactions that use data requests
+      assert(!in_msg.usesTxnId);
+      assert((in_msg.bitMask.count() <= data_channel_size) && (in_msg.bitMask.count() > 0));
       trigger(dataToEvent(in_msg.type), in_msg.addr,
               getCacheEntry(in_msg.addr), getCurrentActiveTBE(in_msg.addr));
     }
@@ -116,16 +126,26 @@
     printResources();
     peek(snpRdyPort, CHIRequestMsg) {
       assert(in_msg.allowRetry == false);
-      TBE tbe := getCurrentActiveTBE(in_msg.addr);
-      if (is_valid(tbe) && tbe.hasUseTimeout) {
-        // we may be in the BUSY_INTR waiting for a cache block, but if
-        // the timeout is set the snoop must still wait, so trigger the
-        // stall form here to prevent creating other states
-        trigger(Event:SnpStalled, in_msg.addr,
-                getCacheEntry(in_msg.addr), tbe);
+      if (in_msg.usesTxnId) {
+        TBE tbe := getDvmTBE(in_msg.txnId);
+        if (is_valid(tbe)) {
+          assert(tbe.is_dvm_snp_tbe);
+        }
+        // TBE may be valid or invalid
+        trigger(snpToEvent(in_msg.type), in_msg.txnId,
+                nullCacheEntry(), tbe);
       } else {
-        trigger(snpToEvent(in_msg.type), in_msg.addr,
-                getCacheEntry(in_msg.addr), tbe);
+        TBE tbe := getCurrentActiveTBE(in_msg.addr);
+        if (is_valid(tbe) && tbe.hasUseTimeout) {
+          // we may be in the BUSY_INTR waiting for a cache block, but if
+          // the timeout is set the snoop must still wait, so trigger the
+          // stall form here to prevent creating other states
+          trigger(Event:SnpStalled, in_msg.addr,
+                  getCacheEntry(in_msg.addr), tbe);
+        } else {
+          trigger(snpToEvent(in_msg.type), in_msg.addr,
+                  getCacheEntry(in_msg.addr), tbe);
+        }
       }
     }
   }
@@ -152,8 +172,21 @@
     printResources();
     peek(snpInPort, CHIRequestMsg) {
       assert(in_msg.allowRetry == false);
-      trigger(Event:AllocSnoop, in_msg.addr,
-              getCacheEntry(in_msg.addr), getCurrentActiveTBE(in_msg.addr));
+      if (in_msg.usesTxnId) {
+        TBE preexistingTBE := getDvmTBE(in_msg.txnId);
+        // If this is just building on another transaction invoke the thing directly
+        if (is_valid(preexistingTBE)){
+          assert(preexistingTBE.is_dvm_snp_tbe);
+          trigger(snpToEvent(in_msg.type), in_msg.txnId,
+                  nullCacheEntry(), preexistingTBE);
+        } else {
+          trigger(Event:AllocDvmSnoop, in_msg.txnId,
+                  nullCacheEntry(), nullTBE());
+        }
+      } else {
+        trigger(Event:AllocSnoop, in_msg.addr,
+                getCacheEntry(in_msg.addr), getCurrentActiveTBE(in_msg.addr));
+      }
     }
   }
 }
@@ -169,16 +202,24 @@
     printResources();
     peek(retryTriggerInPort, RetryTriggerMsg) {
       Event ev := in_msg.event;
-      TBE tbe := getCurrentActiveTBE(in_msg.addr);
       assert((ev == Event:SendRetryAck) || (ev == Event:SendPCrdGrant) ||
               (ev == Event:DoRetry));
-      if (ev == Event:DoRetry) {
+      if (in_msg.usesTxnId) {
+        TBE tbe := getDvmTBE(in_msg.addr);
+        CacheEntry entry := nullCacheEntry();
         assert(is_valid(tbe));
-        if (tbe.is_req_hazard || tbe.is_repl_hazard) {
-          ev := Event:DoRetry_Hazard;
+        trigger(ev, in_msg.addr, entry, tbe);
+      } else {
+        TBE tbe := getCurrentActiveTBE(in_msg.addr);
+        CacheEntry entry := getCacheEntry(in_msg.addr);
+        if (ev == Event:DoRetry) {
+          assert(is_valid(tbe));
+          if (tbe.is_req_hazard || tbe.is_repl_hazard) {
+            ev := Event:DoRetry_Hazard;
+          }
         }
+        trigger(ev, in_msg.addr, entry, tbe);
       }
-      trigger(ev, in_msg.addr, getCacheEntry(in_msg.addr), tbe);
     }
   }
 }
@@ -195,18 +236,24 @@
   if (triggerInPort.isReady(clockEdge())) {
     printResources();
     peek(triggerInPort, TriggerMsg) {
-      TBE tbe := getCurrentActiveTBE(in_msg.addr);
-      assert(is_valid(tbe));
-      if (in_msg.from_hazard != (tbe.is_req_hazard || tbe.is_repl_hazard)) {
-        // possible when handling a snoop hazard and an action from the
-        // the initial transaction got woken up. Stall the action until the
-        // hazard ends
-        assert(in_msg.from_hazard == false);
-        assert(tbe.is_req_hazard || tbe.is_repl_hazard);
-        trigger(Event:ActionStalledOnHazard, in_msg.addr,
-                getCacheEntry(in_msg.addr), tbe);
+      if (in_msg.usesTxnId) {
+        TBE tbe := getDvmTBE(in_msg.addr);
+        assert(is_valid(tbe));
+        trigger(tbe.pendAction, in_msg.addr, nullCacheEntry(), tbe);
       } else {
-        trigger(tbe.pendAction, in_msg.addr, getCacheEntry(in_msg.addr), tbe);
+        TBE tbe := getCurrentActiveTBE(in_msg.addr);
+        assert(is_valid(tbe));
+        if (in_msg.from_hazard != (tbe.is_req_hazard || tbe.is_repl_hazard)) {
+          // possible when handling a snoop hazard and an action from the
+          // the initial transaction got woken up. Stall the action until the
+          // hazard ends
+          assert(in_msg.from_hazard == false);
+          assert(tbe.is_req_hazard || tbe.is_repl_hazard);
+          trigger(Event:ActionStalledOnHazard, in_msg.addr,
+                  getCacheEntry(in_msg.addr), tbe);
+        } else {
+          trigger(tbe.pendAction, in_msg.addr, getCacheEntry(in_msg.addr), tbe);
+        }
       }
     }
   }
@@ -258,36 +305,53 @@
   if (reqRdyPort.isReady(clockEdge())) {
     printResources();
     peek(reqRdyPort, CHIRequestMsg) {
-      CacheEntry cache_entry := getCacheEntry(in_msg.addr);
-      TBE tbe := getCurrentActiveTBE(in_msg.addr);
+      if (in_msg.usesTxnId) {
+        TBE preexistingTBE := getDvmTBE(in_msg.txnId);
 
-      DirEntry dir_entry := getDirEntry(in_msg.addr);
+        // DVM transactions do not directly relate to the Cache,
+        // and do not have cache entries
+        trigger(
+          reqToEvent(in_msg.type, in_msg.is_local_pf),
+          in_msg.txnId,
+          nullCacheEntry(),
+          preexistingTBE);
+      } else {
+        CacheEntry cache_entry := getCacheEntry(in_msg.addr);
+        TBE tbe := getCurrentActiveTBE(in_msg.addr);
 
-      // Special case for possibly stale writebacks or evicts
-      if (in_msg.type == CHIRequestType:WriteBackFull) {
-        if (is_invalid(dir_entry) || (dir_entry.ownerExists == false) ||
-            (dir_entry.owner != in_msg.requestor)) {
-          trigger(Event:WriteBackFull_Stale, in_msg.addr, cache_entry, tbe);
+        DirEntry dir_entry := getDirEntry(in_msg.addr);
+
+        // Special case for possibly stale writebacks or evicts
+        if (in_msg.type == CHIRequestType:WriteBackFull) {
+          if (is_invalid(dir_entry) || (dir_entry.ownerExists == false) ||
+              (dir_entry.owner != in_msg.requestor)) {
+            trigger(Event:WriteBackFull_Stale, in_msg.addr, cache_entry, tbe);
+          }
+        } else if (in_msg.type == CHIRequestType:WriteEvictFull) {
+          if (is_invalid(dir_entry) || (dir_entry.ownerExists == false) ||
+              (dir_entry.ownerIsExcl == false) || (dir_entry.owner != in_msg.requestor)) {
+            trigger(Event:WriteEvictFull_Stale, in_msg.addr, cache_entry, tbe);
+          }
+        } else if (in_msg.type == CHIRequestType:WriteCleanFull) {
+          if (is_invalid(dir_entry) || (dir_entry.ownerExists == false) ||
+              (dir_entry.owner != in_msg.requestor)) {
+            trigger(Event:WriteCleanFull_Stale, in_msg.addr, cache_entry, tbe);
+          }
+        } else if (in_msg.type == CHIRequestType:Evict) {
+          if (is_invalid(dir_entry) ||
+              (dir_entry.sharers.isElement(in_msg.requestor) == false)) {
+            trigger(Event:Evict_Stale, in_msg.addr, cache_entry, tbe);
+          }
+        } else if (in_msg.type == CHIRequestType:CleanUnique) {
+          if (is_invalid(dir_entry) ||
+              (dir_entry.sharers.isElement(in_msg.requestor) == false)) {
+            trigger(Event:CleanUnique_Stale, in_msg.addr, cache_entry, tbe);
+          }
         }
-      } else if (in_msg.type == CHIRequestType:WriteEvictFull) {
-        if (is_invalid(dir_entry) || (dir_entry.ownerExists == false) ||
-            (dir_entry.ownerIsExcl == false) || (dir_entry.owner != in_msg.requestor)) {
-          trigger(Event:WriteEvictFull_Stale, in_msg.addr, cache_entry, tbe);
-        }
-      } else if (in_msg.type == CHIRequestType:WriteCleanFull) {
-        if (is_invalid(dir_entry) || (dir_entry.ownerExists == false) ||
-            (dir_entry.ownerIsExcl == false) || (dir_entry.owner != in_msg.requestor)) {
-          trigger(Event:WriteCleanFull_Stale, in_msg.addr, cache_entry, tbe);
-        }
-      } else if (in_msg.type == CHIRequestType:Evict) {
-        if (is_invalid(dir_entry) ||
-            (dir_entry.sharers.isElement(in_msg.requestor) == false)) {
-          trigger(Event:Evict_Stale, in_msg.addr, cache_entry, tbe);
-        }
+
+        // Normal request path
+        trigger(reqToEvent(in_msg.type, in_msg.is_local_pf), in_msg.addr, cache_entry, tbe);
       }
-
-      // Normal request path
-      trigger(reqToEvent(in_msg.type, in_msg.is_local_pf), in_msg.addr, cache_entry, tbe);
     }
   }
 }
@@ -311,6 +375,9 @@
   if (reqInPort.isReady(clockEdge())) {
     printResources();
     peek(reqInPort, CHIRequestMsg) {
+      // DVM Sync and TLBIs from external sources will use txnId requests,
+      // but they aren't implemented yet.
+      assert(!in_msg.usesTxnId);
       if (in_msg.allowRetry) {
         trigger(Event:AllocRequest, in_msg.addr,
               getCacheEntry(in_msg.addr), getCurrentActiveTBE(in_msg.addr));
@@ -332,9 +399,32 @@
   if (seqInPort.isReady(clockEdge())) {
     printResources();
     peek(seqInPort, RubyRequest) {
-      trigger(Event:AllocSeqRequest, in_msg.LineAddress,
-              getCacheEntry(in_msg.LineAddress),
-              getCurrentActiveTBE(in_msg.LineAddress));
+      if (in_msg.isTlbi) {
+        TBE tbe := getDvmTBE(in_msg.tlbiTransactionUid);
+
+        if (in_msg.Type == RubyRequestType:TLBI_EXT_SYNC_COMP) {
+          assert(is_valid(tbe));
+          // Trigger the relevant event on the TBE for this ID
+          trigger(Event:DvmSync_ExternCompleted,
+            in_msg.tlbiTransactionUid,  // "Address" equivalent
+            nullCacheEntry(), // no cache entry
+            tbe, // TBE exists already, the event should be invoked on it
+          );
+        } else {
+          // There shouldn't be a transaction with the same ID
+          assert(!is_valid(tbe));
+          // Allocate a new TBE
+          trigger(Event:AllocSeqDvmRequest,
+            in_msg.tlbiTransactionUid,  // "Address" equivalent
+            nullCacheEntry(), // no cache entry
+            nullTBE(), // TBE isn't allocated yet
+          );
+        }
+      } else {
+        trigger(Event:AllocSeqRequest, in_msg.LineAddress,
+                getCacheEntry(in_msg.LineAddress),
+                getCurrentActiveTBE(in_msg.LineAddress));
+      }
     }
   }
 }
@@ -386,6 +476,8 @@
     assert(tbe.pendAction != Event:null);
     enqueue(triggerOutPort, TriggerMsg, trigger_latency) {
       out_msg.addr := tbe.addr;
+      // TODO - put usesTxnId on the TBE?
+      out_msg.usesTxnId := tbe.is_dvm_tbe || tbe.is_dvm_snp_tbe;
       out_msg.from_hazard := tbe.is_req_hazard || tbe.is_repl_hazard;
     }
   }
diff --git a/src/mem/ruby/protocol/chi/CHI-cache-transitions.sm b/src/mem/ruby/protocol/chi/CHI-cache-transitions.sm
index da944d5..4c93988 100644
--- a/src/mem/ruby/protocol/chi/CHI-cache-transitions.sm
+++ b/src/mem/ruby/protocol/chi/CHI-cache-transitions.sm
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 ARM Limited
+ * Copyright (c) 2021-2022 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -67,10 +67,20 @@
   AllocateTBE_Snoop;
 }
 
+transition({I}, AllocDvmSnoop) {
+  AllocateTBE_DvmSnoop;
+}
+
 transition({UD,UD_T,SD,UC,SC,I,BUSY_INTR,BUSY_BLKD}, AllocSeqRequest) {
   AllocateTBE_SeqRequest;
 }
 
+// You can't allocate a DVM request on the same TBE as another DVM request,
+// so we don't need a long "Transition-from" list and we can change the output state.
+transition({I}, AllocSeqDvmRequest) {
+  AllocateTBE_SeqDvmRequest;
+}
+
 transition({I,SC,UC,SD,UD,UD_T,RU,RSC,RSD,RUSD,SC_RSC,SD_RSC,SD_RSD,UC_RSC,UC_RU,UD_RU,UD_RSD,UD_RSC,RUSC
             BUSY_INTR,BUSY_BLKD}, AllocPfRequest) {
   AllocateTBE_PfRequest;
@@ -326,9 +336,18 @@
   ProcessNextState;
 }
 
+transition({I, SC, UC, SD, UD, RU, RSC, RSD, RUSD, RUSC,
+            SC_RSC, SD_RSD, SD_RSC, UC_RSC, UC_RU, UD_RU, UD_RSD, UD_RSC},
+            CleanUnique_Stale, BUSY_BLKD) {
+  Initiate_Request_Stale;
+  Initiate_CleanUnique_Stale;
+  Pop_ReqRdyQueue;
+  ProcessNextState;
+}
+
 // WriteUniquePtl
 
-transition({UD,UD_RU,UD_RSD,UD_RSC,UC,UC_RU,UC_RSC},
+transition({UD,UD_RSD,UD_RSC,UC,UC_RSC},
            {WriteUnique, WriteUniquePtl_PoC, WriteUniqueFull_PoC, WriteUniqueFull_PoC_Alloc},
            BUSY_BLKD) {
   Initiate_Request;
@@ -338,6 +357,16 @@
   ProcessNextState;
 }
 
+transition({UD_RU,UC_RU},
+           {WriteUnique, WriteUniquePtl_PoC, WriteUniqueFull_PoC, WriteUniqueFull_PoC_Alloc},
+           BUSY_BLKD) {
+  Initiate_Request;
+  Initiate_WriteUnique_LocalWrite;
+  Profile_Miss;
+  Pop_ReqRdyQueue;
+  ProcessNextState;
+}
+
 transition({SD, SD_RSD, SD_RSC, SC, SC_RSC},
            {WriteUniquePtl_PoC, WriteUniqueFull_PoC, WriteUniqueFull_PoC_Alloc},
            BUSY_BLKD) {
@@ -498,7 +527,7 @@
   ProcessNextState;
 }
 
-transition({UD_RSC, UC_RSC, SC_RSC, UD, RU, RSD, RUSD, RUSC, UD_RSD, SD_RSD, RSC, UD_RU, UC_RU, SD, UC, SC, I},
+transition({UD_RSC, UC_RSC, SC_RSC, SD_RSC, UD, RU, RSD, RUSD, RUSC, UD_RSD, SD_RSD, RSC, UD_RU, UC_RU, SD, UC, SC, I},
             {WriteBackFull_Stale, WriteEvictFull_Stale, WriteCleanFull_Stale}, BUSY_BLKD) {
   Initiate_Request_Stale;
   Initiate_CopyBack_Stale;
@@ -661,7 +690,7 @@
 
 transition({BUSY_BLKD,BUSY_INTR},
             {ReadShared, ReadNotSharedDirty, ReadUnique, ReadUnique_PoC,
-            ReadOnce, CleanUnique,
+            ReadOnce, CleanUnique, CleanUnique_Stale,
             Load, Store, Prefetch,
             WriteBackFull, WriteBackFull_Stale,
             WriteEvictFull, WriteEvictFull_Stale,
@@ -847,6 +876,12 @@
   ProcessNextState_ClearPending;
 }
 
+transition(BUSY_BLKD, SendCompUCRespStale) {
+  Pop_TriggerQueue;
+  Send_CompUC_Stale;
+  ProcessNextState_ClearPending;
+}
+
 transition(BUSY_BLKD, SendRespSepData) {
   Pop_TriggerQueue;
   Send_RespSepData;
@@ -998,7 +1033,7 @@
 
 transition(BUSY_BLKD, MaintainCoherence) {
   Pop_TriggerQueue;
-  Initiate_MaitainCoherence;
+  Initiate_MaintainCoherence;
   ProcessNextState_ClearPending;
 }
 
@@ -1008,10 +1043,16 @@
   ProcessNextState_ClearPending;
 }
 
+transition(BUSY_BLKD, FinishCopyBack_Stale) {
+  Pop_TriggerQueue;
+  Finish_CopyBack_Stale;
+  ProcessNextState_ClearPending;
+}
+
 transition(BUSY_BLKD, CheckUpgrade_FromStore) {
   Pop_TriggerQueue;
   Callback_Miss; // note: Callback happens only if tbe.dataValid
-  CheckUpgrade_FromStore;
+  CheckUpgrade_FromStoreOrRU;
   ProcessNextState_ClearPending;
 }
 
@@ -1023,7 +1064,7 @@
 
 transition(BUSY_BLKD, CheckUpgrade_FromRU) {
   Pop_TriggerQueue;
-  CheckUpgrade_FromRU;
+  CheckUpgrade_FromStoreOrRU;
   ProcessNextState_ClearPending;
 }
 
@@ -1050,7 +1091,7 @@
 
 transition(BUSY_BLKD,
            {SnpRespData_I_PD,SnpRespData_I,SnpRespData_SC_PD,
-            SnpRespData_SC,SnpRespData_SD,SnpRespData_UD,
+            SnpRespData_SC,SnpRespData_SD,SnpRespData_UC,SnpRespData_UD,
             SnpRespData_SC_Fwded_SC,SnpRespData_SC_Fwded_SD_PD,
             SnpRespData_SC_PD_Fwded_SC,SnpRespData_I_Fwded_SD_PD,
             SnpRespData_I_PD_Fwded_SC,SnpRespData_I_Fwded_SC}) {
@@ -1216,3 +1257,187 @@
   Finalize_UpdateDirectoryFromTBE;
   Finalize_DeallocateRequest;
 }
+
+////////////////////////////////////////////////////////
+// DVM transitions
+
+
+// I, DvmTlbi_Initiate, DvmTlbi_Unconfirmed
+// I, DvmSync_Initiate, DvmSync_Unconfirmed
+  // Sync should expect only DBIDResp,
+  // but Tlbi could expect both DBIDResp and CompDBIDResp.
+  // Other CompDBIDResp handlers call a "Receive" action twice - is that relevant?
+transition(I, DvmTlbi_Initiate, DvmTlbi_Unconfirmed) {
+  Initiate_Request_DVM;
+
+  Send_DvmTlbi;
+  Profile_OutgoingStart_DVM;
+
+  Pop_ReqRdyQueue;
+  ProcessNextState;
+}
+transition(I, DvmSync_Initiate, DvmSync_Unsent) {
+  Initiate_Request_DVM;
+
+  Try_Send_DvmSync;
+  Profile_OutgoingStart_DVM;
+
+  Pop_ReqRdyQueue;
+  ProcessNextState;
+}
+
+transition(DvmSync_Unsent, DvmSync_Send, DvmSync_Unconfirmed) {
+  Pop_TriggerQueue;
+
+  Send_DvmSync;
+
+  ProcessNextState_ClearPending;
+}
+
+// {DvmTlbi_Unconfirmed,DvmSync_Unconfirmed}, RetryAck
+// {DvmTlbi_Unconfirmed,DvmSync_Unconfirmed}, PCrdGrant
+  // See other RetryAck, PCrdGrants
+transition({DvmTlbi_Unconfirmed,DvmSync_Unconfirmed}, RetryAck) {
+  Receive_RetryAck;
+  Pop_RespInQueue;
+  ProcessNextState;
+}
+transition({DvmTlbi_Unconfirmed,DvmSync_Unconfirmed}, PCrdGrant) {
+  Receive_PCrdGrant;
+  Pop_RespInQueue;
+  ProcessNextState;
+}
+
+// Resend the request after RetryAck+PCrdGrant received
+transition({DvmTlbi_Unconfirmed,DvmSync_Unconfirmed}, DoRetry) {
+  Send_Retry_DVM;
+  Pop_RetryTriggerQueue;
+  ProcessNextState_ClearPending;
+}
+
+// DvmTlbi_Unconfirmed, DBIDResp, DvmTlbi_Waiting
+// DvmSync_Unconfirmed, DBIDResp, DvmSync_Waiting
+  // Should both send NCBWrData
+transition(DvmTlbi_Unconfirmed, DBIDResp, DvmTlbi_Waiting) {
+  Receive_ReqResp;
+  Pop_RespInQueue;
+
+  Send_DvmTlbi_NCBWrData;
+  ExpectComp;
+
+  ProcessNextState;
+}
+transition(DvmSync_Unconfirmed, DBIDResp, DvmSync_Waiting) {
+  Receive_ReqResp;
+  Pop_RespInQueue;
+
+  Send_DvmSync_NCBWrData;
+  ExpectComp;
+
+  ProcessNextState;
+}
+
+// DvmTlbi_Unconfirmed, CompDBIDResp, DvmOp_Finished
+  // should call ProcessNextState
+// {DvmTlbi_Waiting,DvmSync_Waiting}, Comp, DvmOp_Finished
+  // should call ProcessNextState
+transition(DvmTlbi_Unconfirmed, CompDBIDResp, DvmOp_Finished) {
+  Receive_ReqResp;
+  Pop_RespInQueue;
+
+  Send_DvmTlbi_NCBWrData;
+
+  // We got the comp as well, so send the callback
+  DvmTlbi_CompCallback;
+  Profile_OutgoingEnd_DVM;
+  Try_Send_Pending_DvmSync;
+  ProcessNextState;
+}
+transition(DvmTlbi_Waiting, Comp, DvmOp_Finished) {
+  Receive_ReqResp;
+  Pop_RespInQueue;
+
+  DvmTlbi_CompCallback;
+  Profile_OutgoingEnd_DVM;
+  Try_Send_Pending_DvmSync;
+  ProcessNextState;
+}
+transition(DvmSync_Waiting, Comp, DvmOp_Finished) {
+  Receive_ReqResp;
+  Pop_RespInQueue;
+
+  DvmSync_CompCallback;
+  Profile_OutgoingEnd_DVM;
+  ProcessNextState;
+}
+
+// DvmOp_Finished, Final, I
+  // Should deallocate DvmOp
+transition(DvmOp_Finished, Final, I) {
+  Pop_TriggerQueue; // "Final" is triggered from Trigger queue, so pop that
+  Finalize_DeallocateDvmRequest;
+}
+
+/////////////////////////////////////////////////
+// DVM snoops
+
+transition(I, {SnpDvmOpNonSync_P1,SnpDvmOpNonSync_P2}, DvmExtTlbi_Partial) {
+  // First message has arrived, could be P1 or P2 because either order is allowed
+  Initiate_DvmSnoop;
+  Pop_SnoopRdyQueue;
+}
+
+transition(I, {SnpDvmOpSync_P1,SnpDvmOpSync_P2}, DvmExtSync_Partial) {
+  // First message has arrived, could be P1 or P2 because either order is allowed
+  Initiate_DvmSnoop;
+  Pop_SnoopRdyQueue;
+}
+
+transition(DvmExtTlbi_Partial, {SnpDvmOpNonSync_P1,SnpDvmOpNonSync_P2}, DvmExtTlbi_Executing) {
+  // TODO - some check that we didn't receive a {P1,P1} or {P2,P2} pair?
+  // We receive this event directly from the SnpInPort, so pop it
+  Pop_SnpInPort;
+
+  // Triggers SnpResp_I from inside Ruby with a delay
+  DvmExtTlbi_EnqueueSnpResp;
+  ProcessNextState;
+}
+
+transition(DvmExtSync_Partial, {SnpDvmOpSync_P1,SnpDvmOpSync_P2}, DvmExtSync_Executing) {
+  // TODO - some check that we didn't receive a {P1,P1} or {P2,P2} pair?
+  // We receive this event directly from the SnpInPort, so pop it
+  Pop_SnpInPort;
+
+  // Tell the CPU model to perform a Sync
+  // e.g. flush load-store-queue
+  DvmExtSync_TriggerCallback;
+
+  // We just wait for the CPU to finish
+}
+
+transition(DvmExtTlbi_Executing, SendSnpIResp, DvmExtOp_Finished) {
+  // TLBI snoop response has been triggered after the delay
+  Pop_TriggerQueue;
+
+  // Send the snoop response to the MN
+  Send_SnpRespI;
+
+  // Should trigger Final state
+  ProcessNextState_ClearPending;
+}
+
+transition(DvmExtSync_Executing, DvmSync_ExternCompleted, DvmExtOp_Finished) {
+  Pop_SeqInPort;
+
+  // The CPU model has declared that the Sync is complete
+  // => send the snoop response to the MN
+  Send_SnpRespI;
+
+  // Should trigger Final state
+  ProcessNextState_ClearPending;
+}
+
+transition(DvmExtOp_Finished, Final, I) {
+  Pop_TriggerQueue;
+  Finalize_DeallocateDvmSnoop;
+}
diff --git a/src/mem/ruby/protocol/chi/CHI-cache.sm b/src/mem/ruby/protocol/chi/CHI-cache.sm
index a0d1888..3770382 100644
--- a/src/mem/ruby/protocol/chi/CHI-cache.sm
+++ b/src/mem/ruby/protocol/chi/CHI-cache.sm
@@ -70,6 +70,7 @@
   Cycles response_latency := 1;
   Cycles snoop_latency := 1;
   Cycles data_latency := 1;
+  Cycles dvm_ext_tlbi_latency := 6;
 
   // When an SC fails, unique lines are locked to this controller for a period
   // proportional to the number of consecutive failed SC requests. See
@@ -87,10 +88,12 @@
   // sequencer is not null and handled LL/SC request types.
   bool send_evictions;
 
-  // Number of entries in the snoop and replacement TBE tables
+  // Number of entries in the snoop, replacement, and DVM TBE tables
   // notice the "number_of_TBEs" parameter is defined by AbstractController
   int number_of_snoop_TBEs;
   int number_of_repl_TBEs;
+  int number_of_DVM_TBEs;
+  int number_of_DVM_snoop_TBEs;
 
   // replacements use the same TBE slot as the request that triggered it
   // in this case the number_of_repl_TBEs parameter is ignored
@@ -232,6 +235,20 @@
     UD_RSD, AccessPermission:Read_Write,   desc="UD + RSD";
     UD_RSC, AccessPermission:Read_Write,   desc="UD + RSC";
 
+    // DVM states - use AccessPermission:Invalid because this prevents "functional reads"
+    DvmTlbi_Unconfirmed, AccessPermission:Invalid,  desc="DVM TLBI waiting for confirmation from MN";
+    DvmSync_Unsent, AccessPermission:Invalid,  desc="DVM Sync waiting for previous TLBIs to complete";
+    DvmSync_Unconfirmed, AccessPermission:Invalid,  desc="DVM Sync waiting for confirmation from MN";
+    DvmTlbi_Waiting, AccessPermission:Invalid,  desc="DVM TLBI confirmed by MN, waiting for completion";
+    DvmSync_Waiting, AccessPermission:Invalid,  desc="DVM Sync confirmed by MN, waiting for completion";
+    DvmOp_Finished, AccessPermission:Invalid,  desc="DVM operation that has completed, about to be deallocated";
+
+    DvmExtTlbi_Partial, AccessPermission:Invalid, desc="External DVM TLBI waiting for second packet from MN";
+    DvmExtTlbi_Executing, AccessPermission:Invalid, desc="External DVM TLBI being executed by this machine";
+    DvmExtSync_Partial, AccessPermission:Invalid, desc="External DVM Sync waiting for second packet from MN";
+    DvmExtSync_Executing, AccessPermission:Invalid, desc="External DVM Sync being executed by this machine";
+    DvmExtOp_Finished, AccessPermission:Invalid, desc="External DVM operation that has completed, about to be deallocated";
+
     // Generic transient state
     // There is only a transient "BUSY" state. The actions taken at this state
     // and the final stable state are defined by information in the TBE.
@@ -256,8 +273,10 @@
     AllocRequest,           desc="Allocates a TBE for a request. Triggers a retry if table is full";
     AllocRequestWithCredit, desc="Allocates a TBE for a request. Always succeeds.";
     AllocSeqRequest,        desc="Allocates a TBE for a sequencer request. Stalls requests if table is full";
+    AllocSeqDvmRequest,     desc="Allocates a TBE for a sequencer DVM request. Stalls requests if table is full";
     AllocPfRequest,         desc="Allocates a TBE for a prefetch request. Stalls requests if table is full";
     AllocSnoop,             desc="Allocates a TBE for a snoop. Stalls snoop if table is full";
+    AllocDvmSnoop,          desc="Allocated a TBE for a DVM snoop. Stalls snoop if table is full";
 
     // Events triggered by sequencer requests or snoops in the rdy queue
     // See CHIRequestType in CHi-msg.sm for descriptions
@@ -288,6 +307,12 @@
     SnpOnceFwd,                  desc="";
     SnpStalled, desc=""; // A snoop stall triggered from the inport
 
+    // DVM sequencer requests
+    DvmTlbi_Initiate, desc=""; // triggered when a CPU core wants to send a TLBI
+    // TLBIs are handled entirely within Ruby, so there's no ExternCompleted message
+    DvmSync_Initiate, desc=""; // triggered when a CPU core wants to send a sync
+    DvmSync_ExternCompleted, desc=""; // triggered when an externally requested Sync is completed
+
     // Events triggered by incoming response messages
     // See CHIResponseType in CHi-msg.sm for descriptions
     CompAck,                 desc="";
@@ -318,6 +343,12 @@
     PCrdGrant_Hazard,        desc="";
     PCrdGrant_PoC_Hazard,    desc="";
 
+    // Events triggered by incoming DVM messages
+    SnpDvmOpSync_P1;
+    SnpDvmOpSync_P2;
+    SnpDvmOpNonSync_P1;
+    SnpDvmOpNonSync_P2;
+
     // Events triggered by incoming data response messages
     // See CHIDataType in CHi-msg.sm for descriptions
     CompData_I,                    desc="";
@@ -356,6 +387,7 @@
     WriteBackFull_Stale,    desc="";
     WriteEvictFull_Stale,   desc="";
     WriteCleanFull_Stale,   desc="";
+    CleanUnique_Stale,   desc="";
 
     // Cache fill handling
     CheckCacheFill,   desc="Check if need to write or update the cache and trigger any necessary allocation and evictions";
@@ -425,6 +457,7 @@
     SendCompIResp,  desc="Ack Evict with Comp_I";
     SendCleanUnique,desc="Send a CleanUnique";
     SendCompUCResp, desc="Ack CleanUnique with Comp_UC";
+    SendCompUCRespStale, desc="Ack stale CleanUnique with Comp_UC";
 
     // Checks if an upgrade using a CleanUnique was sucessfull
     CheckUpgrade_FromStore, desc="Upgrade needed by a Store";
@@ -454,6 +487,9 @@
     SendSnpFwdedData,                 desc="Send SnpResp for a forwarding snoop";
     SendSnpFwdedResp,                 desc="Send SnpRespData for a forwarding snoop";
 
+    // DVM sends
+    DvmSync_Send, desc="Send an unstarted DVM Sync";
+
     // Retry handling
     SendRetryAck,   desc="Send RetryAck";
     SendPCrdGrant,  desc="Send PCrdGrant";
@@ -468,6 +504,7 @@
     TX_Data, desc="Transmit pending data messages";
     MaintainCoherence, desc="Queues a WriteBack or Evict before droping the only valid copy of the block";
     FinishCleanUnique, desc="Sends acks and perform any writeback after a CleanUnique";
+    FinishCopyBack_Stale, desc="Check if a Evict needs to be sent";
     ActionStalledOnHazard, desc="Stall a trigger action because until finish handling snoop hazard";
 
     // This is triggered once a transaction doesn't have
@@ -523,6 +560,7 @@
   // Tracks a pending retry
   structure(RetryQueueEntry) {
     Addr addr,           desc="Line address";
+    bool usesTxnId,      desc="Uses a transaction ID instead of a memory address";
     MachineID retryDest, desc="Retry destination";
   }
 
@@ -541,7 +579,7 @@
     void pushFrontNB(Event);
     void pop();
     // For the retry queue
-    void emplace(Addr,MachineID);
+    void emplace(Addr,bool,MachineID);
     RetryQueueEntry next(); //SLICC won't allow to reuse front()
   }
 
@@ -551,6 +589,8 @@
     bool is_req_tbe, desc="Allocated in the request table";
     bool is_snp_tbe, desc="Allocated in the snoop table";
     bool is_repl_tbe, desc="Allocated in the replacements table";
+    bool is_dvm_tbe, desc="Allocated in the DVM table";
+    bool is_dvm_snp_tbe, desc="Allocated in the DVM snoop table";
 
     int storSlot, desc="Slot in the storage tracker occupied by this entry";
 
@@ -717,6 +757,22 @@
   TBETable snpTBEs,      template="<Cache_TBE>", constructor="m_number_of_snoop_TBEs";
   TBEStorage storSnpTBEs, constructor="this, m_number_of_snoop_TBEs";
 
+  // TBE table for outgoing DVM requests
+  TBETable dvmTBEs,      template="<Cache_TBE>", constructor="m_number_of_DVM_TBEs";
+  TBEStorage storDvmTBEs, constructor="this, m_number_of_DVM_TBEs";
+
+  // TBE table for incoming DVM snoops
+  TBETable dvmSnpTBEs,      template="<Cache_TBE>", constructor="m_number_of_DVM_snoop_TBEs";
+  TBEStorage storDvmSnpTBEs, constructor="this, m_number_of_DVM_snoop_TBEs";
+
+  // DVM data
+  // Queue of non-sync operations that haven't been Comp-d yet.
+  // Before a Sync operation can start, this queue must be emptied
+  TriggerQueue dvmPendingNonSyncsBlockingSync, template="<Cache_Event>";
+  // Used to record if a Sync op is pending
+  bool dvmHasPendingSyncOp, default="false";
+  Addr dvmPendingSyncOp, default="0";
+
   // Retry handling
 
   // Destinations that will be sent PCrdGrant when a TBE becomes available
@@ -728,6 +784,7 @@
     Addr addr;
     Event event;
     MachineID retryDest;
+    bool usesTxnId;
 
     bool functionalRead(Packet *pkt) { return false; }
     bool functionalRead(Packet *pkt, WriteMask &mask) { return false; }
@@ -742,6 +799,7 @@
   // Pending transaction actions (generated by TBE:actions)
   structure(TriggerMsg, interface="Message") {
     Addr addr;
+    bool usesTxnId;
     bool from_hazard; // this actions was generate during a snoop hazard
     bool functionalRead(Packet *pkt) { return false; }
     bool functionalRead(Packet *pkt, WriteMask &mask) { return false; }
diff --git a/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-actions.sm b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-actions.sm
new file mode 100644
index 0000000..a104322
--- /dev/null
+++ b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-actions.sm
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2021-2022 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+////////////////////////////////////////////////////////////////////////////
+// CHI-dvm-misc-node actions definitions
+////////////////////////////////////////////////////////////////////////////
+
+action(AllocateTBE_Request, desc="") {
+  peek(reqInPort, CHIRequestMsg) {
+    assert(in_msg.usesTxnId);
+    assert(in_msg.txnId == address);
+    assert(in_msg.is_local_pf == false);
+    assert(in_msg.allowRetry);
+
+    bool isNonSync := (in_msg.type == CHIRequestType:DvmOpNonSync);
+
+    if (storDvmTBEs.areNSlotsAvailable(1, tbePartition(isNonSync))) {
+      // reserve a slot for this request
+      storDvmTBEs.incrementReserved(tbePartition(isNonSync));
+
+      // Move request to rdy queue
+      peek(reqInPort, CHIRequestMsg) {
+        enqueue(reqRdyOutPort, CHIRequestMsg, allocation_latency) {
+          out_msg := in_msg;
+        }
+      }
+
+    } else {
+      // we don't have resources to track this request; enqueue a retry
+      enqueue(retryTriggerOutPort, RetryTriggerMsg, retry_ack_latency) {
+        out_msg.txnId := in_msg.txnId;
+        out_msg.event := Event:SendRetryAck;
+        out_msg.retryDest := in_msg.requestor;
+
+        RetryQueueEntry en;
+        en.txnId := in_msg.txnId;
+        en.retryDest := in_msg.requestor;
+        en.isNonSync := isNonSync;
+        storDvmTBEs.emplaceRetryEntry(en);
+      }
+    }
+  }
+
+
+  reqInPort.dequeue(clockEdge());
+}
+
+action(AllocateTBE_Request_WithCredit, desc="") {
+  // TBE slot already reserved
+  // Move request to rdy queue
+  peek(reqInPort, CHIRequestMsg) {
+    assert(in_msg.allowRetry == false);
+    assert(in_msg.usesTxnId);
+    enqueue(reqRdyOutPort, CHIRequestMsg, allocation_latency) {
+      assert(in_msg.txnId == address);
+      out_msg := in_msg;
+    }
+  }
+  reqInPort.dequeue(clockEdge());
+}
+
+action(Initiate_Request_DVM, desc="") {
+  bool was_retried := false;
+  peek(reqRdyPort, CHIRequestMsg) {
+    // "address" for DVM = transaction ID
+    TBE tbe := allocateDvmRequestTBE(address, in_msg);
+    set_tbe(tbe);
+    // only a msg that was already retried doesn't allow a retry
+    was_retried := in_msg.allowRetry == false;
+  }
+
+  // Last argument = false, so it isn't treated as a memory request
+  incomingTransactionStart(address, curTransitionEvent(), State:Unallocated, was_retried, false);
+}
+
+action(Pop_ReqRdyQueue, desc="") {
+  reqRdyPort.dequeue(clockEdge());
+}
+
+
+action(Receive_ReqDataResp, desc="") {
+  assert(is_valid(tbe));
+  assert(tbe.expected_req_resp.hasExpected());
+  peek(datInPort, CHIDataMsg) {
+    // Decrement pending
+    if (tbe.expected_req_resp.receiveData(in_msg.type) == false) {
+      error("Received unexpected message");
+    }
+    assert(!tbe.expected_req_resp.hasExpected());
+
+    DPRINTF(RubyProtocol, "Misc Node has receieved NCBWrData\n");
+    // Clear the "expected types" list to prepare for the snooping
+    tbe.expected_snp_resp.clear(1);
+    assert(!tbe.expected_snp_resp.hasExpected());
+
+    // We don't actually use the data contents
+  }
+}
+
+action(Pop_DataInQueue, desc="") {
+  datInPort.dequeue(clockEdge());
+}
+
+action(Receive_SnpResp, desc="") {
+  assert(tbe.expected_snp_resp.hasExpected());
+  peek(rspInPort, CHIResponseMsg) {
+    // Decrement pending
+    if (tbe.expected_snp_resp.receiveResp(in_msg.type) == false) {
+      error("Received unexpected message");
+    }
+    assert(in_msg.stale == false);
+    // assert(in_msg.stale == tbe.is_stale);
+
+    DPRINTF(RubyProtocol, "Misc Node has receieved SnpResp_I\n");
+
+    assert(tbe.pendingTargets.isElement(in_msg.responder));
+    tbe.pendingTargets.remove(in_msg.responder);
+    tbe.receivedTargets.add(in_msg.responder);
+  }
+}
+action(Pop_RespInQueue, desc="") {
+  rspInPort.dequeue(clockEdge());
+}
+
+action(ProcessNextState, desc="") {
+  assert(is_valid(tbe));
+  processNextState(tbe);
+}
+
+// If ProcessNextState invokes a new action, it sets tbe.pendingAction.
+// If you call ProcessNextState again without clearing this variable,
+// nothing will happen. This Action clears the pending state, ensuring
+// a new state is processed.
+action(ProcessNextState_ClearPending, desc="") {
+  assert(is_valid(tbe));
+  clearPendingAction(tbe);
+  processNextState(tbe);
+}
+
+action(Send_Comp, desc="") {
+  assert(is_valid(tbe));
+  Cycles latency := response_latency;
+  CHIResponseType type := CHIResponseType:Comp;
+
+  enqueue(rspOutPort, CHIResponseMsg, latency) {
+    out_msg.addr := address;
+    out_msg.type := type;
+    out_msg.responder := machineID;
+    out_msg.txnId := address;
+    out_msg.usesTxnId := true;
+    out_msg.Destination.add(tbe.requestor);
+  }
+  DPRINTF(RubyProtocol, "Misc Node Sending Comp (for either)\n");
+}
+
+action(Send_Comp_NonSync, desc="") {
+  assert(is_valid(tbe));
+
+  // In the NonSync case, if early_nonsync_comp is set then
+  // we will have already sent a CompDBIDResp.
+  // Thus, only send Comp if !early_nonsync_comp
+
+  if (!early_nonsync_comp) {
+    Cycles latency := response_latency;
+    CHIResponseType type := CHIResponseType:Comp;
+    enqueue(rspOutPort, CHIResponseMsg, latency) {
+        out_msg.addr := address;
+        out_msg.type := type;
+        out_msg.responder := machineID;
+        out_msg.txnId := address;
+        out_msg.usesTxnId := true;
+        out_msg.Destination.add(tbe.requestor);
+    }
+    DPRINTF(RubyProtocol, "Misc Node Sending TLBI Comp\n");
+  }
+}
+
+// NOTICE a trigger event may wakeup another stalled trigger event so
+// this is always called first in the transitions so we don't pop the
+// wrong message
+action(Pop_TriggerQueue, desc="") {
+  triggerInPort.dequeue(clockEdge());
+}
+
+action(Pop_RetryTriggerQueue, desc="") {
+  retryTriggerInPort.dequeue(clockEdge());
+}
+
+action(Finalize_DeallocateRequest, desc="") {
+  assert(is_valid(tbe));
+  assert(tbe.actions.empty());
+  wakeupPendingReqs(tbe);
+  wakeupPendingTgrs(tbe);
+
+  DPRINTF(RubyProtocol, "Deallocating DVM request\n");
+  deallocateDvmTBE(tbe);
+  processRetryQueue();
+  unset_tbe();
+
+  // Last argument = false, so this isn't treated as a memory transaction
+  incomingTransactionEnd(address, curTransitionNextState(), false);
+}
+
+action(Send_DvmNonSyncDBIDResp, desc="") {
+  Cycles latency := response_latency;
+  CHIResponseType type := CHIResponseType:DBIDResp;
+  if (early_nonsync_comp) {
+    type := CHIResponseType:CompDBIDResp;
+  }
+  enqueue(rspOutPort, CHIResponseMsg, latency) {
+    out_msg.addr := address;
+    out_msg.type := type;
+    out_msg.responder := machineID;
+    out_msg.txnId := address;
+    out_msg.usesTxnId := true;
+    out_msg.Destination.add(tbe.requestor);
+  }
+  tbe.expected_req_resp.clear(1);
+  tbe.expected_req_resp.addExpectedDataType(CHIDataType:NCBWrData);
+  tbe.expected_req_resp.setExpectedCount(1);
+  DPRINTF(RubyProtocol, "Misc Node Sending TLBI DBIDResp\n");
+}
+
+action(Send_DvmSyncDBIDResp, desc="") {
+  Cycles latency := response_latency;
+  CHIResponseType type := CHIResponseType:DBIDResp;
+  enqueue(rspOutPort, CHIResponseMsg, latency) {
+    out_msg.addr := address;
+    out_msg.type := type;
+    out_msg.responder := machineID;
+    out_msg.txnId := address;
+    out_msg.usesTxnId := true;
+    out_msg.Destination.add(tbe.requestor);
+  }
+  tbe.expected_req_resp.clear(1);
+  tbe.expected_req_resp.addExpectedDataType(CHIDataType:NCBWrData);
+  tbe.expected_req_resp.setExpectedCount(1);
+  DPRINTF(RubyProtocol, "Misc Node Sending Sync DBIDResp\n");
+}
+
+action(Send_RetryAck, desc="") {
+  peek(retryTriggerInPort, RetryTriggerMsg) {
+    enqueue(rspOutPort, CHIResponseMsg, response_latency) {
+      out_msg.txnId := in_msg.txnId;
+      out_msg.usesTxnId := true;
+      out_msg.type := CHIResponseType:RetryAck;
+      out_msg.responder := machineID;
+      out_msg.Destination.add(in_msg.retryDest);
+    }
+    DPRINTF(RubyProtocol, "Misc Node Sending RetryAck (for either)\n");
+  }
+}
+
+action(Send_PCrdGrant, desc="") {
+  peek(retryTriggerInPort, RetryTriggerMsg) {
+    enqueue(rspOutPort, CHIResponseMsg, response_latency) {
+      out_msg.txnId := in_msg.txnId;
+      out_msg.usesTxnId := true;
+      out_msg.type := CHIResponseType:PCrdGrant;
+      out_msg.responder := machineID;
+      out_msg.Destination.add(in_msg.retryDest);
+    }
+    DPRINTF(RubyProtocol, "Misc Node Sending PCrdGrant (for either)\n");
+  }
+}
+
+action(Send_DvmSnoop_P1, desc="") {
+  Cycles latency := response_latency;
+
+  CHIRequestType type := CHIRequestType:SnpDvmOpSync_P1;
+  if (tbe.isNonSync) {
+    type := CHIRequestType:SnpDvmOpNonSync_P1;
+  }
+
+  assert(tbe.notSentTargets.count() > 0);
+  MachineID target := tbe.notSentTargets.smallestElement();
+
+  enqueue(snpOutPort, CHIRequestMsg, latency) {
+    prepareRequest(tbe, type, out_msg);
+    DPRINTF(RubyProtocol, "Misc Node Sending %d to %d\n", type, target);
+
+    out_msg.usesTxnId := true;
+    out_msg.txnId := tbe.txnId; // for DVM TBEs addr = txnId
+    out_msg.allowRetry := false;
+    out_msg.Destination.clear();
+    out_msg.Destination.add(target);
+
+    out_msg.dataToFwdRequestor := false;
+  }
+
+  tbe.actions.pushNB(Event:DvmSendNextMessage_P2);
+  tbe.delayNextAction := curTick() + cyclesToTicks(intToCycles(1));
+
+  // We are no longer waiting on other transaction activity
+  tbe.waiting_on_other_txns := false;
+}
+
+action(Send_DvmSnoop_P2, desc="") {
+  Cycles latency := response_latency;
+
+  CHIRequestType type := CHIRequestType:SnpDvmOpSync_P2;
+  if (tbe.isNonSync) {
+    type := CHIRequestType:SnpDvmOpNonSync_P2;
+  }
+
+  assert(tbe.notSentTargets.count() > 0);
+  MachineID target := tbe.notSentTargets.smallestElement();
+
+  enqueue(snpOutPort, CHIRequestMsg, latency) {
+    prepareRequest(tbe, type, out_msg);
+    DPRINTF(RubyProtocol, "Misc Node Sending %d to %d\n", type, target);
+
+    out_msg.usesTxnId := true;
+    out_msg.txnId := tbe.txnId; // for DVM TBEs addr = txnId
+    out_msg.allowRetry := false;
+    out_msg.Destination.clear();
+    out_msg.Destination.add(target);
+
+    out_msg.dataToFwdRequestor := false;
+  }
+
+  // Expect a SnpResp_I now we have sent both
+  tbe.expected_snp_resp.addExpectedRespType(CHIResponseType:SnpResp_I);
+  tbe.expected_snp_resp.addExpectedCount(1);
+
+  // Pop the target we just completed off the list
+  tbe.notSentTargets.remove(target);
+  tbe.pendingTargets.add(target);
+  // If we have more targets, enqueue another send
+  if (tbe.notSentTargets.count() > 0) {
+    tbe.actions.pushNB(Event:DvmSendNextMessage_P1);
+  } else {
+    // otherwise enqueue a DvmFinishDistributing, then a blocking DvmFinishWaiting
+    // DvmFinishDistributing will be called immediately,
+    // DvmFinishWaiting will be called once all responses are received
+    tbe.actions.pushNB(Event:DvmFinishDistributing);
+    tbe.actions.push(Event:DvmFinishWaiting);
+  }
+  tbe.delayNextAction := curTick() + cyclesToTicks(intToCycles(1));
+}
+
+action(Enqueue_UpdatePendingOps, desc="") {
+  // The next time updatePendingOps runs
+  // it will check this variable and decide
+  // to actually check for a new sender
+  DPRINTF(RubyProtocol, "Enqueue_UpdatePendingOps from %016x\n", address);
+  needsToCheckPendingOps := true;
+  // Schedule a generic event to make sure we wake up
+  // on the next tick
+  scheduleEvent(intToCycles(1));
+}
+
+action(Profile_OutgoingEnd_DVM, desc="") {
+  assert(is_valid(tbe));
+  // Outgoing transactions = time to send all snoops
+  // Is never rejected by recipient => never received retry ack
+  bool rcvdRetryAck := false;
+  outgoingTransactionEnd(address, rcvdRetryAck, false);
+}
diff --git a/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-funcs.sm b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-funcs.sm
new file mode 100644
index 0000000..ce87d02
--- /dev/null
+++ b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-funcs.sm
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2021-2022 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+////////////////////////////////////////////////////////////////////////////
+// CHI-cache function definitions
+////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////
+// External functions
+
+Tick clockEdge();
+Tick curTick();
+Tick cyclesToTicks(Cycles c);
+Cycles ticksToCycles(Tick t);
+void set_tbe(TBE b);
+void unset_tbe();
+MachineID mapAddressToDownstreamMachine(Addr addr);
+NetDest allUpstreamDest();
+
+void incomingTransactionStart(Addr, Event, State, bool);
+void incomingTransactionEnd(Addr, State);
+void outgoingTransactionStart(Addr, Event);
+void outgoingTransactionEnd(Addr, bool);
+// Overloads for transaction-measuring functions
+// final bool = isMemoryAccess
+// if false, uses a "global access" table
+void incomingTransactionStart(Addr, Event, State, bool, bool);
+void incomingTransactionEnd(Addr, State, bool);
+void outgoingTransactionStart(Addr, Event, bool);
+void outgoingTransactionEnd(Addr, bool, bool);
+Event curTransitionEvent();
+State curTransitionNextState();
+
+void notifyPfHit(RequestPtr req, bool is_read, DataBlock blk) { }
+void notifyPfMiss(RequestPtr req, bool is_read, DataBlock blk) { }
+void notifyPfFill(RequestPtr req, DataBlock blk, bool from_pf) { }
+void notifyPfEvict(Addr blkAddr, bool hwPrefetched) { }
+void notifyPfComplete(Addr addr) { }
+
+void scheduleEvent(Cycles);
+
+////////////////////////////////////////////////////////////////////////////
+// Interface functions required by SLICC
+
+int tbePartition(bool is_non_sync) {
+    if (is_non_sync) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+State getState(TBE tbe, Addr txnId) {
+  if (is_valid(tbe)) {
+      return tbe.state;
+  } else {
+    return State:Unallocated;
+  }
+}
+
+void setState(TBE tbe, Addr txnId, State state) {
+  if (is_valid(tbe)) {
+    tbe.state := state;
+  }
+}
+
+TBE nullTBE(), return_by_pointer="yes" {
+  return OOD;
+}
+
+TBE getCurrentActiveTBE(Addr txnId), return_by_pointer="yes" {
+  // Current Active TBE for an address
+  return dvmTBEs[txnId];
+}
+
+AccessPermission getAccessPermission(Addr txnId) {
+  // MN has no memory
+  return AccessPermission:NotPresent;
+}
+
+void setAccessPermission(Addr txnId, State state) {}
+
+void functionalRead(Addr txnId, Packet *pkt, WriteMask &mask) {
+  // We don't have any memory, so we can't functionalRead
+  // => we don't fill the `mask` argument
+}
+
+int functionalWrite(Addr txnId, Packet *pkt) {
+  // No memory => no functional writes
+  return 0;
+}
+
+Cycles mandatoryQueueLatency(RubyRequestType type) {
+  return intToCycles(1);
+}
+
+Cycles tagLatency(bool from_sequencer) {
+  return intToCycles(0);
+}
+
+Cycles dataLatency() {
+  return intToCycles(0);
+}
+
+bool inCache(Addr txnId) {
+  return false;
+}
+
+bool hasBeenPrefetched(Addr txnId) {
+  return false;
+}
+
+bool inMissQueue(Addr txnId) {
+  return false;
+}
+
+void notifyCoalesced(Addr txnId, RubyRequestType type, RequestPtr req,
+                     DataBlock data_blk, bool was_miss) {
+  DPRINTF(RubySlicc, "Unused notifyCoalesced(txnId=%#x, type=%s, was_miss=%d)\n",
+                      txnId, type, was_miss);
+}
+
+////////////////////////////////////////////////////////////////////////////
+// State->Event converters
+
+Event reqToEvent(CHIRequestType type) {
+  if (type == CHIRequestType:DvmOpNonSync) {
+    return Event:DvmTlbi_Initiate;
+  } else if (type == CHIRequestType:DvmOpSync) {
+    return Event:DvmSync_Initiate;
+  } else {
+    error("Invalid/unexpected CHIRequestType");
+  }
+}
+
+Event respToEvent (CHIResponseType type) {
+  if (type == CHIResponseType:SnpResp_I) {
+    return Event:SnpResp_I;
+  } else {
+    error("Invalid/unexpected CHIResponseType");
+  }
+}
+
+Event dataToEvent (CHIDataType type) {
+  if (type == CHIDataType:NCBWrData) {
+    return Event:NCBWrData;
+  } else {
+    error("Invalid/unexpected CHIDataType");
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Allocation
+
+void clearExpectedReqResp(TBE tbe) {
+  assert(blockSize >= data_channel_size);
+  assert((blockSize % data_channel_size) == 0);
+  tbe.expected_req_resp.clear(blockSize / data_channel_size);
+}
+
+void clearExpectedSnpResp(TBE tbe) {
+  assert(blockSize >= data_channel_size);
+  assert((blockSize % data_channel_size) == 0);
+  tbe.expected_snp_resp.clear(blockSize / data_channel_size);
+}
+
+void initializeTBE(TBE tbe, Addr txnId, int storSlot) {
+  assert(is_valid(tbe));
+
+  tbe.timestamp := curTick();
+
+  tbe.wakeup_pending_req := false;
+  tbe.wakeup_pending_snp := false;
+  tbe.wakeup_pending_tgr := false;
+
+  tbe.txnId := txnId;
+
+  tbe.storSlot := storSlot;
+
+  clearExpectedReqResp(tbe);
+  clearExpectedSnpResp(tbe);
+  // Technically we don't *know* if we're waiting on other transactions,
+  // but we need to stop this transaction from errantly being *finished*.
+  tbe.waiting_on_other_txns := true;
+
+  tbe.sched_responses := 0;
+  tbe.block_on_sched_responses := false;
+
+
+  tbe.pendAction := Event:null;
+  tbe.finalState := State:null;
+  tbe.delayNextAction := intToTick(0);
+
+  // The MN uses the list of "upstream destinations"
+  // as targets for snoops
+  tbe.notSentTargets := allUpstreamDest();
+  tbe.pendingTargets.clear();
+  tbe.receivedTargets.clear();
+}
+
+TBE allocateDvmRequestTBE(Addr txnId, CHIRequestMsg in_msg), return_by_pointer="yes" {
+  DPRINTF(RubySlicc, "allocateDvmRequestTBE %x %016llx\n", in_msg.type, txnId);
+
+  bool isNonSync := in_msg.type == CHIRequestType:DvmOpNonSync;
+
+  int partition := tbePartition(isNonSync);
+  // We must have reserved resources for this allocation
+  storDvmTBEs.decrementReserved(partition);
+  assert(storDvmTBEs.areNSlotsAvailable(1, partition));
+
+  dvmTBEs.allocate(txnId);
+  TBE tbe := dvmTBEs[txnId];
+
+  // Setting .txnId = txnId
+  initializeTBE(tbe, txnId, storDvmTBEs.addEntryToNewSlot(partition));
+
+  tbe.isNonSync := isNonSync;
+
+  tbe.requestor := in_msg.requestor;
+  tbe.reqType := in_msg.type;
+
+  // We don't want to send a snoop request to
+  // the original requestor
+  tbe.notSentTargets.remove(in_msg.requestor);
+
+  return tbe;
+}
+
+void deallocateDvmTBE(TBE tbe) {
+  assert(is_valid(tbe));
+  storDvmTBEs.removeEntryFromSlot(tbe.storSlot, tbePartition(tbe.isNonSync));
+  dvmTBEs.deallocate(tbe.txnId);
+}
+
+void clearPendingAction(TBE tbe) {
+  tbe.pendAction := Event:null;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Retry-related
+
+void processRetryQueue() {
+  // send credit if requestor waiting for it and we have resources
+
+  // Ask the DVM storage if we have space to retry anything.
+  if (storDvmTBEs.hasPossibleRetry()) {
+    RetryQueueEntry toRetry := storDvmTBEs.popNextRetryEntry();
+    storDvmTBEs.incrementReserved(tbePartition(toRetry.isNonSync));
+    enqueue(retryTriggerOutPort, RetryTriggerMsg, crd_grant_latency) {
+      out_msg.txnId := toRetry.txnId;
+      out_msg.retryDest := toRetry.retryDest;
+      out_msg.event := Event:SendPCrdGrant;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Other
+
+void printResources() {
+  DPRINTF(RubySlicc, "Resources(used/rsvd/max): dvmTBEs=%d/%d/%d\n",
+                storDvmTBEs.size(), storDvmTBEs.reserved(), storDvmTBEs.capacity());
+  DPRINTF(RubySlicc, "Resources(in/out size): req=%d/%d rsp=%d/%d dat=%d/%d snp=%d/%d trigger=%d\n",
+                reqIn.getSize(curTick()), reqOut.getSize(curTick()),
+                rspIn.getSize(curTick()), rspOut.getSize(curTick()),
+                datIn.getSize(curTick()), datOut.getSize(curTick()),
+                snpIn.getSize(curTick()), snpOut.getSize(curTick()),
+                triggerQueue.getSize(curTick()));
+}
+
+void printTBEState(TBE tbe) {
+  DPRINTF(RubySlicc, "STATE: txnId=%#x reqType=%d state=%d pendAction=%s\n",
+                      tbe.txnId, tbe.reqType, tbe.state, tbe.pendAction);
+}
+
+void prepareRequest(TBE tbe, CHIRequestType type, CHIRequestMsg & out_msg) {
+  out_msg.addr := tbe.txnId;
+  out_msg.accAddr := tbe.txnId;
+  out_msg.accSize := blockSize;
+  out_msg.requestor := machineID;
+  out_msg.fwdRequestor := tbe.requestor;
+  out_msg.type := type;
+  out_msg.allowRetry := false;
+  out_msg.isSeqReqValid := false;
+  out_msg.is_local_pf := false;
+  out_msg.is_remote_pf := false;
+}
diff --git a/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-ports.sm b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-ports.sm
new file mode 100644
index 0000000..1a6a09c
--- /dev/null
+++ b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-ports.sm
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// ---- Outbound port definitions ----
+// Network interfaces
+out_port(reqOutPort, CHIRequestMsg, reqOut);
+out_port(snpOutPort, CHIRequestMsg, snpOut);
+out_port(rspOutPort, CHIResponseMsg, rspOut);
+out_port(datOutPort, CHIDataMsg, datOut);
+// Internal output ports
+out_port(triggerOutPort, TriggerMsg, triggerQueue);
+out_port(retryTriggerOutPort, RetryTriggerMsg, retryTriggerQueue);
+out_port(schedRspTriggerOutPort, CHIResponseMsg, schedRspTriggerQueue);
+out_port(reqRdyOutPort, CHIRequestMsg, reqRdy);
+out_port(snpRdyOutPort, CHIRequestMsg, snpRdy);
+
+
+// Include helper functions here. Some of them require the outports to be
+// already defined
+// Notice 'processNextState' and 'wakeupPending*' functions are defined after
+// the required input ports. Currently the SLICC compiler does not support
+// separate declaration and definition of functions in the .sm files.
+include "CHI-dvm-misc-node-funcs.sm";
+
+
+// Inbound port definitions and internal triggers queues
+// Notice we never stall input ports connected to the network
+// Incoming data and responses are always consumed.
+// Incoming requests/snoop are moved to the respective internal rdy queue
+// if a TBE can be allocated, or retried otherwise.
+
+// Response
+in_port(rspInPort, CHIResponseMsg, rspIn, rank=11,
+        rsc_stall_handler=rspInPort_rsc_stall_handler) {
+  if (rspInPort.isReady(clockEdge())) {
+    printResources();
+    peek(rspInPort, CHIResponseMsg) {
+      assert(in_msg.usesTxnId);
+      TBE tbe := getCurrentActiveTBE(in_msg.txnId);
+      trigger(respToEvent(in_msg.type), in_msg.txnId, tbe);
+    }
+  }
+}
+bool rspInPort_rsc_stall_handler() {
+  error("rspInPort must never stall\n");
+  return false;
+}
+
+
+// Data
+in_port(datInPort, CHIDataMsg, datIn, rank=10,
+        rsc_stall_handler=datInPort_rsc_stall_handler) {
+  if (datInPort.isReady(clockEdge())) {
+    printResources();
+    peek(datInPort, CHIDataMsg) {
+      assert((in_msg.bitMask.count() <= data_channel_size) && (in_msg.bitMask.count() > 0));
+      assert(in_msg.usesTxnId);
+      trigger(dataToEvent(in_msg.type), in_msg.txnId, getCurrentActiveTBE(in_msg.txnId));
+    }
+  }
+}
+bool datInPort_rsc_stall_handler() {
+  error("datInPort must never stall\n");
+  return false;
+}
+
+// Incoming snoops - should never be used
+in_port(snpInPort, CHIRequestMsg, snpIn, rank=8) {
+  if (snpInPort.isReady(clockEdge())) {
+    printResources();
+    peek(snpInPort, CHIRequestMsg) {
+      error("MN should not receive snoops");
+    }
+  }
+}
+bool snpInPort_rsc_stall_handler() {
+  error("snpInPort must never stall\n");
+  return false;
+}
+
+// Incoming new requests
+in_port(reqInPort, CHIRequestMsg, reqIn, rank=2,
+        rsc_stall_handler=reqInPort_rsc_stall_handler) {
+  if (reqInPort.isReady(clockEdge())) {
+    printResources();
+    peek(reqInPort, CHIRequestMsg) {
+      assert(in_msg.usesTxnId);
+      // Make sure we aren't already processing this
+      assert(!is_valid(getCurrentActiveTBE(in_msg.txnId)));
+      if (in_msg.allowRetry) {
+        trigger(Event:AllocRequest, in_msg.txnId, nullTBE());
+      } else {
+        trigger(Event:AllocRequestWithCredit, in_msg.txnId, nullTBE());
+      }
+    }
+  }
+}
+bool reqInPort_rsc_stall_handler() {
+  error("reqInPort must never stall\n");
+  return false;
+}
+
+
+// Incoming new sequencer requests
+in_port(seqInPort, RubyRequest, mandatoryQueue, rank=1) {
+  if (seqInPort.isReady(clockEdge())) {
+    printResources();
+    peek(seqInPort, RubyRequest) {
+      error("MN should not have sequencer");
+    }
+  }
+}
+
+
+// Action triggers
+in_port(triggerInPort, TriggerMsg, triggerQueue, rank=5,
+        rsc_stall_handler=triggerInPort_rsc_stall_handler) {
+  if (triggerInPort.isReady(clockEdge())) {
+    printResources();
+    peek(triggerInPort, TriggerMsg) {
+      TBE tbe := getCurrentActiveTBE(in_msg.txnId);
+      assert(is_valid(tbe));
+      assert(!in_msg.from_hazard);
+      trigger(tbe.pendAction, in_msg.txnId, tbe);
+    }
+  }
+}
+bool triggerInPort_rsc_stall_handler() {
+  DPRINTF(RubySlicc, "Trigger queue resource stall\n");
+  triggerInPort.recycle(clockEdge(), cyclesToTicks(stall_recycle_lat));
+  return true;
+}
+void wakeupPendingTgrs(TBE tbe) {
+  if (tbe.wakeup_pending_tgr) {
+    Addr txnId := tbe.txnId;
+    wakeup_port(triggerInPort, txnId);
+    tbe.wakeup_pending_tgr := false;
+  }
+}
+
+// Requests with an allocated TBE
+in_port(reqRdyPort, CHIRequestMsg, reqRdy, rank=3,
+        rsc_stall_handler=reqRdyPort_rsc_stall_handler) {
+  if (reqRdyPort.isReady(clockEdge())) {
+    printResources();
+    peek(reqRdyPort, CHIRequestMsg) {
+      assert(in_msg.usesTxnId);
+      TBE tbe := getCurrentActiveTBE(in_msg.txnId);
+      assert(!in_msg.is_local_pf);
+      // Normal request path
+      trigger(reqToEvent(in_msg.type), in_msg.txnId, tbe);
+    }
+  }
+}
+bool reqRdyPort_rsc_stall_handler() {
+  DPRINTF(RubySlicc, "ReqRdy queue resource stall\n");
+  reqRdyPort.recycle(clockEdge(), cyclesToTicks(stall_recycle_lat));
+  return true;
+}
+void wakeupPendingReqs(TBE tbe) {
+  if (tbe.wakeup_pending_req) {
+    Addr txnId := tbe.txnId;
+    wakeup_port(reqRdyPort, txnId);
+    tbe.wakeup_pending_req := false;
+  }
+}
+
+
+// Retry action triggers
+// These are handled separately from other triggers since these events are
+// not tied to a TBE
+in_port(retryTriggerInPort, RetryTriggerMsg, retryTriggerQueue, rank=7) {
+  if (retryTriggerInPort.isReady(clockEdge())) {
+    printResources();
+    peek(retryTriggerInPort, RetryTriggerMsg) {
+      Event ev := in_msg.event;
+      TBE tbe := getCurrentActiveTBE(in_msg.txnId);
+      assert((ev == Event:SendRetryAck) || (ev == Event:SendPCrdGrant));
+      trigger(ev, in_msg.txnId, tbe);
+    }
+  }
+}
+
+// Trigger queue for scheduled responses so transactions don't need to
+// block on a response when the rspOutPort is busy
+in_port(schedRspTriggerInPort, CHIResponseMsg, schedRspTriggerQueue, rank=6) {
+  if (schedRspTriggerInPort.isReady(clockEdge())) {
+    printResources();
+    peek(schedRspTriggerInPort, CHIResponseMsg) {
+      error("Misc Node shouldn't have schedResp");
+    }
+  }
+}
+
+// Enqueues next event depending on the pending actions and the event queue
+void processNextState(TBE tbe) {
+  assert(is_valid(tbe));
+  DPRINTF(RubyProtocol, "GoToNextState state=%d expected_req_resp=%d expected_snp_resp=%d sched_rsp=%d(block=%d) pendAction: %d\n",
+                      tbe.state,
+                      tbe.expected_req_resp.expected(),
+                      tbe.expected_snp_resp.expected(),
+                      tbe.sched_responses, tbe.block_on_sched_responses,
+                      tbe.pendAction);
+
+  // if no pending trigger and not expecting to receive anything, enqueue
+  // next
+  bool has_nb_trigger := (tbe.actions.empty() == false) &&
+                          tbe.actions.frontNB();
+  int expected_msgs := tbe.expected_req_resp.expected() +
+                        tbe.expected_snp_resp.expected();
+  if (tbe.block_on_sched_responses) {
+    expected_msgs := expected_msgs + tbe.sched_responses;
+    tbe.block_on_sched_responses := tbe.sched_responses > 0;
+  }
+
+  // If we are waiting on other transactions to finish, we shouldn't enqueue Final
+  bool would_enqueue_final := tbe.actions.empty();
+  bool allowed_to_enqueue_final := !tbe.waiting_on_other_txns;
+  // if (would_enqueue_final && !allowed) then DON'T enqueue anything
+  // => if (!would_enqueue_final || allowed_to_enqueue_final) then DO
+  bool allowed_to_enqueue_action := !would_enqueue_final || allowed_to_enqueue_final;
+
+  if ((tbe.pendAction == Event:null) &&
+      ((expected_msgs == 0) || has_nb_trigger) &&
+      allowed_to_enqueue_action) {
+    Cycles trigger_latency := intToCycles(0);
+    if (tbe.delayNextAction > curTick()) {
+      trigger_latency := ticksToCycles(tbe.delayNextAction) -
+                          ticksToCycles(curTick());
+      tbe.delayNextAction := intToTick(0);
+    }
+
+    tbe.pendAction := Event:null;
+    if (tbe.actions.empty()) {
+      // time to go to the final state
+      tbe.pendAction := Event:Final;
+    } else {
+      tbe.pendAction := tbe.actions.front();
+      tbe.actions.pop();
+    }
+    assert(tbe.pendAction != Event:null);
+    enqueue(triggerOutPort, TriggerMsg, trigger_latency) {
+      out_msg.txnId := tbe.txnId;
+      out_msg.from_hazard := false;
+    }
+  }
+
+  printTBEState(tbe);
+}
+
+// Runs at the end of every cycle that takes input, checks `needsToCheckPendingOps`.
+// if true, will call updatePendingOps() to check if a new snoop-sender should start.
+// We could return bools if we want to be sure we run on the next cycle,
+// but we have no reason to do that
+void updatePendingOps(), run_on_input_cycle_end="yes" {
+  if (needsToCheckPendingOps) {
+    needsToCheckPendingOps := false;
+    DPRINTF(RubyProtocol, "Misc Node updating pending ops\n");
+    TBE newDistributor := dvmTBEs.chooseNewDistributor();
+    DPRINTF(RubyProtocol, "Misc Node selected %p\n", newDistributor);
+    if (is_valid(newDistributor)) {
+      // can return the current distributor, check for that
+      if (!hasCurrentDistributor || newDistributor.txnId != currentDistributor) {
+        currentDistributor := newDistributor.txnId;
+        hasCurrentDistributor := true;
+
+        // make the new distributor start distributing
+        // by simply telling it to send the next message
+        newDistributor.actions.pushNB(Event:DvmSendNextMessage_P1);
+        processNextState(newDistributor);
+
+        // TODO could move into Profile_OutgoingStart_DVM
+        // Use a useful event name for profiling
+        Event usefulEvent := Event:DvmSync_Initiate;
+        if (newDistributor.isNonSync) {
+          usefulEvent := Event:DvmTlbi_Initiate;
+        }
+        outgoingTransactionStart(newDistributor.txnId, usefulEvent, false);
+      }
+    }
+  }
+}
diff --git a/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-transitions.sm b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-transitions.sm
new file mode 100644
index 0000000..24d524b
--- /dev/null
+++ b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node-transitions.sm
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+////////////////////////////////////////////////////////////////////////////
+// CHI-dvm-misc-node transition definition
+////////////////////////////////////////////////////////////////////////////
+
+// Allocate resources and move to the ready queue
+transition(Unallocated, AllocRequest) {
+  AllocateTBE_Request;
+}
+
+transition(Unallocated, AllocRequestWithCredit) {
+  AllocateTBE_Request_WithCredit;
+}
+
+transition(Unallocated, SendRetryAck) {
+  Send_RetryAck;
+  Pop_RetryTriggerQueue;
+}
+
+transition(Unallocated, SendPCrdGrant) {
+  Send_PCrdGrant;
+  Pop_RetryTriggerQueue;
+}
+
+transition(Unallocated, DvmTlbi_Initiate, DvmNonSync_Partial) {
+  Initiate_Request_DVM;
+  Pop_ReqRdyQueue;
+
+  Send_DvmNonSyncDBIDResp;
+}
+
+transition(Unallocated, DvmSync_Initiate, DvmSync_Partial) {
+  Initiate_Request_DVM;
+  Pop_ReqRdyQueue;
+
+  Send_DvmSyncDBIDResp;
+}
+
+transition(DvmSync_Partial, NCBWrData, DvmSync_ReadyToDist) {
+  Receive_ReqDataResp; // Uses data from top of queue
+  Pop_DataInQueue;     // Pops data from top of queue
+
+  // Update the "Pending Operations" set
+  // This looks at all current DVM operations and updates which operation is distributing.
+  // We may not start snooping immediately.
+  Enqueue_UpdatePendingOps;
+  ProcessNextState;
+}
+
+transition(DvmNonSync_Partial, NCBWrData, DvmNonSync_ReadyToDist) {
+  Receive_ReqDataResp; // Uses data from top of queue
+  Pop_DataInQueue;     // Pops data from top of queue
+
+  // Update the "Pending Operations" set
+  // This looks at all current DVM operations and updates which operation is distributing.
+  // We may not start snooping immediately.
+  Enqueue_UpdatePendingOps;
+  ProcessNextState;
+}
+
+transition({DvmSync_ReadyToDist,DvmSync_Distributing}, DvmSendNextMessage_P1, DvmSync_Distributing) {
+  Pop_TriggerQueue;
+  // Enqueues SendNextMessage_P2
+  Send_DvmSnoop_P1;
+  // Process the enqueued event immediately
+  ProcessNextState_ClearPending;
+}
+transition(DvmSync_Distributing, DvmSendNextMessage_P2) {
+  Pop_TriggerQueue;
+  // This may enqueue a SendNextMessage event, or it could enqueue a FinishSending if there are no elements left.
+  Send_DvmSnoop_P2;
+  // Process the enqueued event immediately
+  ProcessNextState_ClearPending;
+}
+
+transition({DvmNonSync_ReadyToDist,DvmNonSync_Distributing}, DvmSendNextMessage_P1, DvmNonSync_Distributing) {
+  Pop_TriggerQueue;
+  // Enqueues SendNextMessage_P2
+  Send_DvmSnoop_P1;
+  // Process the enqueued event immediately
+  ProcessNextState_ClearPending;
+}
+transition(DvmNonSync_Distributing, DvmSendNextMessage_P2) {
+  Pop_TriggerQueue;
+  // This may enqueue a SendNextMessage event, or it could enqueue a FinishSending if there are no elements left.
+  Send_DvmSnoop_P2;
+  // Process the enqueued event immediately
+  ProcessNextState_ClearPending;
+}
+
+transition(DvmSync_Distributing, DvmFinishDistributing, DvmSync_Waiting) {
+  Pop_TriggerQueue;
+
+  // Now that we're done distributing, pick someone else to start distributing
+  Enqueue_UpdatePendingOps;
+  ProcessNextState_ClearPending;
+}
+
+transition(DvmNonSync_Distributing, DvmFinishDistributing, DvmNonSync_Waiting) {
+  Pop_TriggerQueue;
+
+  // Now that we're done distributing, pick someone else to start distributing
+  Enqueue_UpdatePendingOps;
+  ProcessNextState_ClearPending;
+}
+
+transition(DvmSync_Waiting, DvmFinishWaiting, DvmOp_Complete) {
+//   would enqueue a Comp send
+//   ProcessNextState (which should send a Final event?)
+  Pop_TriggerQueue;
+  Send_Comp;
+  Profile_OutgoingEnd_DVM;
+  // Now that we're done waiting, someone else might be able to start
+  // e.g. because only one Sync can be in progress at once,
+  // our finishing could free up space for the next Sync to start.
+  Enqueue_UpdatePendingOps;
+
+  ProcessNextState_ClearPending;
+}
+
+transition(DvmNonSync_Waiting, DvmFinishWaiting, DvmOp_Complete) {
+//   would enqueue a Comp send
+//   ProcessNextState (which should send a Final event?)
+  Pop_TriggerQueue;
+  // NonSync can Comp early, so this action checks if a Comp would already have been sent
+  Send_Comp_NonSync;
+  Profile_OutgoingEnd_DVM;
+
+  // Now that we're done waiting, someone else might be able to start
+  //(not sure if this applied to NonSyncs, but re-calling this function doesn't hurt)
+  Enqueue_UpdatePendingOps;
+
+  ProcessNextState_ClearPending;
+}
+
+// On receiving a SnpResp
+transition({DvmSync_Distributing,DvmNonSync_Distributing,DvmSync_Waiting,DvmNonSync_Waiting}, SnpResp_I) {
+  Receive_SnpResp; // Uses data from top of resp queue
+  Pop_RespInQueue; // Pops data from top of resp queue
+
+  ProcessNextState;
+}
+
+transition(DvmOp_Complete, Final, Unallocated) {
+  Pop_TriggerQueue; // "Final" event is applied from the trigger queue
+
+  Finalize_DeallocateRequest; // Deallocate the DVM TBE
+}
\ No newline at end of file
diff --git a/src/mem/ruby/protocol/chi/CHI-dvm-misc-node.sm b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node.sm
new file mode 100644
index 0000000..ba38c65
--- /dev/null
+++ b/src/mem/ruby/protocol/chi/CHI-dvm-misc-node.sm
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2021-2022 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+machine(MachineType:MiscNode, "CHI Misc Node for handling and distrbuting DVM operations") :
+
+  // Additional pipeline latency modeling for the different request types
+  // When defined, these are applied after the initial tag array read and
+  // sending necessary snoops.
+  Cycles snp_latency := 0; // Applied before handling any snoop
+  Cycles snp_inv_latency := 0; // Additional latency for invalidating snoops
+
+  // Request TBE allocation latency
+  Cycles allocation_latency := 0;
+
+  // Enqueue latencies for outgoing messages
+  // NOTE: should remove this and only use parameters above?
+  Cycles request_latency := 1;
+  Cycles response_latency := 1;
+  Cycles sched_response_latency := 1;
+  Cycles snoop_latency := 1;
+  Cycles data_latency := 1;
+
+  // Recycle latency on resource stalls
+  Cycles stall_recycle_lat := 1;
+
+  // Number of entries in the TBE tables
+  int number_of_DVM_TBEs;
+  int number_of_non_sync_TBEs;
+
+  // wait for the final tag update to complete before deallocating TBE and
+  // going to final stable state
+  bool dealloc_wait_for_tag := "False";
+
+  // Width of the data channel. Data transfer are split in multiple messages
+  // at the protocol level when this is less than the cache line size.
+  int data_channel_size;
+
+  // Combine Comp+DBIDResp responses for DvmOp(Non-sync)
+  // CHI-D and later only!
+  bool early_nonsync_comp;
+
+  // additional latency for the WU Comp response
+  Cycles comp_wu_latency := 0;
+
+  // Additional latency for sending RetryAck
+  Cycles retry_ack_latency := 0;
+
+  // Additional latency for sending PCrdGrant
+  Cycles crd_grant_latency := 0;
+
+  // Additional latency for retrying a request
+  Cycles retry_req_latency := 0;
+
+  // stall new requests to destinations with a pending retry
+  bool throttle_req_on_retry := "True";
+
+  // Message Queues
+
+  // Interface to the network
+  // Note vnet_type is used by Garnet only. "response" type is assumed to
+  // have data, so use it for data channels and "none" for the rest.
+  // network="To" for outbound queue; network="From" for inbound
+  // virtual networks: 0=request, 1=snoop, 2=response, 3=data
+
+  MessageBuffer * reqOut,   network="To", virtual_network="0", vnet_type="none";
+  MessageBuffer * snpOut,   network="To", virtual_network="1", vnet_type="none";
+  MessageBuffer * rspOut,   network="To", virtual_network="2", vnet_type="none";
+  MessageBuffer * datOut,   network="To", virtual_network="3", vnet_type="response";
+
+  MessageBuffer * reqIn,   network="From", virtual_network="0", vnet_type="none";
+  MessageBuffer * snpIn,   network="From", virtual_network="1", vnet_type="none";
+  MessageBuffer * rspIn,   network="From", virtual_network="2", vnet_type="none";
+  MessageBuffer * datIn,   network="From", virtual_network="3", vnet_type="response";
+
+  // Mandatory queue for receiving requests from the sequencer
+  MessageBuffer * mandatoryQueue;
+
+  // Internal queue for trigger events
+  MessageBuffer * triggerQueue;
+
+  // Internal queue for retry trigger events
+  MessageBuffer * retryTriggerQueue;
+
+  // Internal queue for scheduled response messages
+  MessageBuffer * schedRspTriggerQueue;
+
+  // Internal queue for accepted requests
+  MessageBuffer * reqRdy;
+
+  // Internal queue for accepted snoops
+  MessageBuffer * snpRdy;
+
+{
+
+  ////////////////////////////////////////////////////////////////////////////
+  // States
+  ////////////////////////////////////////////////////////////////////////////
+
+  // Should only involve states relevant to TLBI or Sync operations
+  state_declaration(State, default="MiscNode_State_null") {
+    Unallocated, AccessPermission:Invalid, desc="TBE is not associated with a DVM op";
+
+    DvmSync_Partial, AccessPermission:Invalid, desc="DvmSync which is waiting for extra data";
+    DvmSync_ReadyToDist, AccessPermission:Invalid, desc="DvmSync which has all data, ready to distribute to other cores";
+    DvmSync_Distributing, AccessPermission:Invalid, desc="DvmSync which is distributing snoops to the rest of the cores";
+    DvmSync_Waiting, AccessPermission:Invalid, desc="DvmSync which is waiting for snoop responses to come back";
+
+    DvmNonSync_Partial, AccessPermission:Invalid, desc="DVM non-sync waiting for extra data from initiator";
+    DvmNonSync_ReadyToDist, AccessPermission:Invalid, desc="DVM non-sync with all data, ready to distribute to other cores";
+    DvmNonSync_Distributing, AccessPermission:Invalid, desc="DVM non-sync distributing snoops to the rest of the cores";
+    DvmNonSync_Waiting, AccessPermission:Invalid, desc="DVM non-sync waiting for snoop responses to come back";
+
+    DvmOp_Complete, AccessPermission:Invalid, desc="A completed DVM op";
+
+    // Null state for debugging
+    null, AccessPermission:Invalid, desc="Null state";
+  }
+
+
+  ////////////////////////////////////////////////////////////////////////////
+  // Events
+  ////////////////////////////////////////////////////////////////////////////
+
+  enumeration(Event) {
+    // Events triggered by incoming requests. Allocate TBE and move
+    // request or snoop to the ready queue
+    AllocRequest,           desc="Allocates a TBE for a request. Triggers a retry if table is full";
+    AllocRequestWithCredit, desc="Allocates a TBE for a request. Always succeeds. Used when a client is retrying after being denied.";
+
+    SnpResp_I;
+    NCBWrData;
+
+    // Retry handling
+    SendRetryAck,   desc="Send RetryAck";
+    SendPCrdGrant,  desc="Send PCrdGrant";
+    DoRetry,        desc="Resend the current pending request";
+
+    DvmTlbi_Initiate, desc="Initiate a DVM TLBI on the provided TBE";
+    DvmSync_Initiate, desc="Initiate a DVM Sync on the provided TBE";
+    DvmSendNextMessage_P1, desc="Trigger a SnpDvmOp_P1 message based on the TBE type";
+    DvmSendNextMessage_P2, desc="Trigger a SnpDvmOp_P2 message based on the TBE type";
+    DvmFinishDistributing, desc="Move the TBE out of the Distributing state into Waiting";
+    DvmFinishWaiting, desc="Move the TBE out of the Waiting state and complete it";
+    DvmUpdatePendingOps, desc="Update which operation is currently distributing";
+
+    // This is triggered once a transaction doesn't have
+    // any queued action and is not expecting responses/data. The transaction
+    // is finalized and the next stable state is stored in the cache/directory
+    // See the processNextState and makeFinalState functions
+    Final;
+
+    null;
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+  // Data structures
+  ////////////////////////////////////////////////////////////////////////////
+
+  // Cache block size
+  int blockSize, default="RubySystem::getBlockSizeBytes()";
+
+  // Helper class for tracking expected response and data messages
+  structure(ExpectedMap, external ="yes") {
+    void clear(int dataChunks);
+    void addExpectedRespType(CHIResponseType);
+    void addExpectedDataType(CHIDataType);
+    void setExpectedCount(int val);
+    void addExpectedCount(int val);
+    bool hasExpected();
+    bool hasReceivedResp();
+    bool hasReceivedData();
+    int expected();
+    int received();
+    bool receiveResp(CHIResponseType);
+    bool receiveData(CHIDataType);
+    bool receivedDataType(CHIDataType);
+    bool receivedRespType(CHIResponseType);
+  }
+
+  // Tracks a pending retry
+  structure(RetryQueueEntry) {
+    Addr txnId,           desc="Transaction ID";
+    MachineID retryDest, desc="Retry destination";
+    bool isNonSync, desc="Is a NonSync operation";
+  }
+
+  // Queue for event triggers. Used to specify a list of actions that need
+  // to be performed across multiple transitions.
+  // This class is also used to track pending retries
+  structure(TriggerQueue, external ="yes") {
+    Event front();
+    Event back();
+    bool frontNB();
+    bool backNB();
+    bool empty();
+    void push(Event);
+    void pushNB(Event);
+    void pushFront(Event);
+    void pushFrontNB(Event);
+    void pop();
+  }
+
+  // TBE fields
+  structure(TBE, desc="Transaction buffer entry definition") {
+    Tick timestamp, desc="Time this entry was allocated. Affects order of trigger events";
+
+    int storSlot, desc="Slot in the storage tracker occupied by this entry";
+
+    // Transaction info mostly extracted from the request message
+    Addr txnId, desc="Unique Transaction ID";
+    CHIRequestType reqType, desc="Request type that initiated this transaction";
+    bool isNonSync, desc="Is a non-sync DVM operation";
+    MachineID requestor,    desc="Requestor ID";
+
+    // Transaction state information
+    State state,    desc="SLICC line state";
+
+    NetDest notSentTargets, desc="Set of MachineIDs we haven't snooped yet";
+    NetDest pendingTargets, desc="Set of MachineIDs that were snooped, but haven't responded";
+    NetDest receivedTargets, desc="Set of MachineIDs that have responded to snoops";
+
+    // Helper structures to track expected events and additional transient
+    // state info
+
+    // List of actions to be performed while on a transient state
+    // See the processNextState function for details
+    TriggerQueue actions, template="<MiscNode_Event>", desc="List of actions";
+    Event pendAction,         desc="Current pending action";
+    Tick delayNextAction,     desc="Delay next action until given tick";
+    State finalState,         desc="Final state; set when pendAction==Final";
+
+    // List of expected responses and data. Checks the type of data against the
+    // expected ones for debugging purposes
+    // See the processNextState function for details
+    ExpectedMap expected_req_resp, template="<CHIResponseType,CHIDataType>";
+    ExpectedMap expected_snp_resp, template="<CHIResponseType,CHIDataType>";
+    bool waiting_on_other_txns, desc="Is waiting for other transactions to update before finishing.";
+    CHIResponseType slicchack1; // fix compiler not including headers
+    CHIDataType slicchack2; // fix compiler not including headers
+
+    // Tracks pending scheduled responses
+    int sched_responses;
+    bool block_on_sched_responses;
+
+    // This TBE stalled a message and thus we need to call wakeUpBuffers
+    // at some point
+    bool wakeup_pending_req;
+    bool wakeup_pending_snp;
+    bool wakeup_pending_tgr;
+  }
+
+  // TBE table definition
+  structure(MN_TBETable, external ="yes") {
+    TBE lookup(Addr);
+    void allocate(Addr);
+    void deallocate(Addr);
+    bool isPresent(Addr);
+
+    TBE chooseNewDistributor();
+  }
+
+  structure(TBEStorage, external ="yes") {
+    int size();
+    int capacity();
+    int reserved();
+    int slotsAvailable();
+    bool areNSlotsAvailable(int n);
+    void incrementReserved();
+    void decrementReserved();
+    int addEntryToNewSlot();
+    void removeEntryFromSlot(int slot);
+  }
+
+  structure(MN_TBEStorage, external ="yes") {
+    int size();
+    int capacity();
+    int reserved();
+    int slotsAvailable(int partition);
+    bool areNSlotsAvailable(int n, int partition);
+    void incrementReserved(int partition);
+    void decrementReserved(int partition);
+    int addEntryToNewSlot(int partition);
+    void removeEntryFromSlot(int slot, int partition);
+
+    // Which operation to retry depends on the current available storage.
+    // If there's a NonSync op waiting for PCrdGrant and the Nonsync reserved space is free,
+    // the NonSync takes priority.
+    // => Make the MN_TBEStorage responsible for calculating this.
+    void emplaceRetryEntry(RetryQueueEntry ret);
+    bool hasPossibleRetry();
+    RetryQueueEntry popNextRetryEntry();
+  }
+
+  // Definitions of the TBE tables
+
+  // TBE table for DVM requests
+  MN_TBETable dvmTBEs, constructor="m_number_of_DVM_TBEs";
+  TBEStorage nonSyncTBEs, constructor="this, m_number_of_non_sync_TBEs";
+  TBEStorage genericTBEs, constructor="this, (m_number_of_DVM_TBEs - m_number_of_non_sync_TBEs)";
+  MN_TBEStorage storDvmTBEs, template="<MiscNode_RetryQueueEntry>", constructor="this, {m_genericTBEs_ptr, m_nonSyncTBEs_ptr}";
+
+  // txnId of the current TBE which is distributing snoops
+  // NOTE - this is a safety measure for making sure we don't
+  // tell the same person to start snooping twice.
+  // Don't rely on it, if someone stops distributing and no-one starts
+  // this variable will not be updated.
+  Addr currentDistributor, default="0";
+  bool hasCurrentDistributor, default="false";
+  bool needsToCheckPendingOps, default="false";
+
+  // Pending RetryAck/PCrdGrant
+  structure(RetryTriggerMsg, interface="Message") {
+    Addr txnId;
+    Event event;
+    MachineID retryDest;
+
+    bool functionalRead(Packet *pkt) { return false; }
+    bool functionalRead(Packet *pkt, WriteMask &mask) { return false; }
+    bool functionalWrite(Packet *pkt) { return false; }
+  }
+
+  // Pending transaction actions (generated by TBE:actions)
+  structure(TriggerMsg, interface="Message") {
+    Addr txnId;
+    bool from_hazard; // this actions was generate during a snoop hazard
+    bool functionalRead(Packet *pkt) { return false; }
+    bool functionalRead(Packet *pkt, WriteMask &mask) { return false; }
+    bool functionalWrite(Packet *pkt) { return false; }
+  }
+
+
+  ////////////////////////////////////////////////////////////////////////////
+  // Input/output port definitions
+  ////////////////////////////////////////////////////////////////////////////
+
+  include "CHI-dvm-misc-node-ports.sm";
+  // CHI-dvm-misc-node-ports.sm also includes CHI-dvm-misc-node-funcs.sm
+
+  ////////////////////////////////////////////////////////////////////////////
+  // Actions and transitions
+  ////////////////////////////////////////////////////////////////////////////
+
+  include "CHI-dvm-misc-node-actions.sm";
+  include "CHI-dvm-misc-node-transitions.sm";
+}
diff --git a/src/mem/ruby/protocol/chi/CHI-msg.sm b/src/mem/ruby/protocol/chi/CHI-msg.sm
index 19cf343..0437982 100644
--- a/src/mem/ruby/protocol/chi/CHI-msg.sm
+++ b/src/mem/ruby/protocol/chi/CHI-msg.sm
@@ -46,6 +46,10 @@
   Load;
   Store;
   StoreLine;
+  // Incoming DVM-related requests generated by the sequencer
+  DvmTlbi_Initiate;
+  DvmSync_Initiate;
+  DvmSync_ExternCompleted;
 
   // CHI request types
   ReadShared;
@@ -70,12 +74,19 @@
   SnpShared;
   SnpUnique;
   SnpCleanInvalid;
+  SnpDvmOpSync_P1;
+  SnpDvmOpSync_P2;
+  SnpDvmOpNonSync_P1;
+  SnpDvmOpNonSync_P2;
 
   WriteNoSnpPtl;
   WriteNoSnp;
   ReadNoSnp;
   ReadNoSnpSep;
 
+  DvmOpNonSync;
+  DvmOpSync;
+
   null;
 }
 
@@ -97,6 +108,9 @@
   bool is_local_pf,         desc="Request generated by a local prefetcher";
   bool is_remote_pf,        desc="Request generated a prefetcher in another cache";
 
+  bool usesTxnId,       desc="True if using a Transaction ID", default="false";
+  Addr txnId,           desc="Transaction ID", default="0";
+
   MessageSizeType MessageSize, default="MessageSizeType_Control";
 
   // No data for functional access
@@ -140,6 +154,8 @@
   MachineID responder,  desc="Responder ID";
   NetDest Destination,  desc="Response destination";
   bool stale,           desc="Response to a stale request";
+  bool usesTxnId,       desc="True if using a Transaction ID", default="false";
+  Addr txnId,           desc="Transaction ID", default="0";
   //NOTE: not in CHI and for debuging only
 
   MessageSizeType MessageSize, default="MessageSizeType_Control";
@@ -187,7 +203,8 @@
   NetDest Destination,  desc="Response destination";
   DataBlock dataBlk,    desc="Line data";
   WriteMask bitMask,    desc="Which bytes in the data block are valid";
-
+  bool usesTxnId,       desc="True if using a Transaction ID", default="false";
+  Addr txnId,           desc="Transaction ID", default="0";
 
   MessageSizeType MessageSize, default="MessageSizeType_Data";
 
diff --git a/src/mem/ruby/protocol/chi/CHI.slicc b/src/mem/ruby/protocol/chi/CHI.slicc
index 27724bb..49c9288 100644
--- a/src/mem/ruby/protocol/chi/CHI.slicc
+++ b/src/mem/ruby/protocol/chi/CHI.slicc
@@ -4,3 +4,4 @@
 include "CHI-msg.sm";
 include "CHI-cache.sm";
 include "CHI-mem.sm";
+include "CHI-dvm-misc-node.sm";
\ No newline at end of file
diff --git a/src/mem/ruby/slicc_interface/AbstractController.cc b/src/mem/ruby/slicc_interface/AbstractController.cc
index 396b128..e11d780 100644
--- a/src/mem/ruby/slicc_interface/AbstractController.cc
+++ b/src/mem/ruby/slicc_interface/AbstractController.cc
@@ -108,7 +108,13 @@
         }
         downstreamDestinations.add(mid);
     }
-
+    // Initialize the addr->upstream machine list.
+    // We do not need to map address -> upstream machine,
+    // so we don't examine the address ranges
+    upstreamDestinations.resize();
+    for (auto abs_cntrl : params().upstream_destinations) {
+        upstreamDestinations.add(abs_cntrl->getMachineID());
+    }
 }
 
 void
diff --git a/src/mem/ruby/slicc_interface/AbstractController.hh b/src/mem/ruby/slicc_interface/AbstractController.hh
index 56c164f..19cfe51 100644
--- a/src/mem/ruby/slicc_interface/AbstractController.hh
+++ b/src/mem/ruby/slicc_interface/AbstractController.hh
@@ -214,8 +214,12 @@
     MachineID mapAddressToDownstreamMachine(Addr addr,
                                     MachineType mtype = MachineType_NUM) const;
 
+    /** List of downstream destinations (towards memory) */
     const NetDest& allDownstreamDest() const { return downstreamDestinations; }
 
+    /** List of upstream destinations (towards the CPU) */
+    const NetDest& allUpstreamDest() const { return upstreamDestinations; }
+
   protected:
     //! Profiles original cache requests including PUTs
     void profileRequest(const std::string &request);
@@ -224,23 +228,33 @@
 
     // Tracks outstanding transactions for latency profiling
     struct TransMapPair { unsigned transaction; unsigned state; Tick time; };
-    std::unordered_map<Addr, TransMapPair> m_inTrans;
-    std::unordered_map<Addr, TransMapPair> m_outTrans;
+    std::unordered_map<Addr, TransMapPair> m_inTransAddressed;
+    std::unordered_map<Addr, TransMapPair> m_outTransAddressed;
+
+    std::unordered_map<Addr, TransMapPair> m_inTransUnaddressed;
+    std::unordered_map<Addr, TransMapPair> m_outTransUnaddressed;
 
     /**
      * Profiles an event that initiates a protocol transactions for a specific
      * line (e.g. events triggered by incoming request messages).
      * A histogram with the latency of the transactions is generated for
      * all combinations of trigger event, initial state, and final state.
+     * This function also supports "unaddressed" transactions,
+     * those not associated with an address in memory but
+     * instead associated with a unique ID.
      *
-     * @param addr address of the line
+     * @param addr address of the line, or unique transaction ID
      * @param type event that started the transaction
      * @param initialState state of the line before the transaction
+     * @param isAddressed is addr a line address or a unique ID
      */
     template<typename EventType, typename StateType>
     void incomingTransactionStart(Addr addr,
-        EventType type, StateType initialState, bool retried)
+        EventType type, StateType initialState, bool retried,
+        bool isAddressed=true)
     {
+        auto& m_inTrans =
+          isAddressed ? m_inTransAddressed : m_inTransUnaddressed;
         assert(m_inTrans.find(addr) == m_inTrans.end());
         m_inTrans[addr] = {type, initialState, curTick()};
         if (retried)
@@ -249,13 +263,20 @@
 
     /**
      * Profiles an event that ends a transaction.
+     * This function also supports "unaddressed" transactions,
+     * those not associated with an address in memory but
+     * instead associated with a unique ID.
      *
-     * @param addr address of the line with a outstanding transaction
+     * @param addr address or unique ID with an outstanding transaction
      * @param finalState state of the line after the transaction
+     * @param isAddressed is addr a line address or a unique ID
      */
     template<typename StateType>
-    void incomingTransactionEnd(Addr addr, StateType finalState)
+    void incomingTransactionEnd(Addr addr, StateType finalState,
+        bool isAddressed=true)
     {
+        auto& m_inTrans =
+          isAddressed ? m_inTransAddressed : m_inTransUnaddressed;
         auto iter = m_inTrans.find(addr);
         assert(iter != m_inTrans.end());
         stats.inTransLatHist[iter->second.transaction]
@@ -269,13 +290,20 @@
     /**
      * Profiles an event that initiates a transaction in a peer controller
      * (e.g. an event that sends a request message)
+     * This function also supports "unaddressed" transactions,
+     * those not associated with an address in memory but
+     * instead associated with a unique ID.
      *
-     * @param addr address of the line
+     * @param addr address of the line or a unique transaction ID
      * @param type event that started the transaction
+     * @param isAddressed is addr a line address or a unique ID
      */
     template<typename EventType>
-    void outgoingTransactionStart(Addr addr, EventType type)
+    void outgoingTransactionStart(Addr addr, EventType type,
+        bool isAddressed=true)
     {
+        auto& m_outTrans =
+          isAddressed ? m_outTransAddressed : m_outTransUnaddressed;
         assert(m_outTrans.find(addr) == m_outTrans.end());
         m_outTrans[addr] = {type, 0, curTick()};
     }
@@ -283,11 +311,18 @@
     /**
      * Profiles the end of an outgoing transaction.
      * (e.g. receiving the response for a requests)
+     * This function also supports "unaddressed" transactions,
+     * those not associated with an address in memory but
+     * instead associated with a unique ID.
      *
      * @param addr address of the line with an outstanding transaction
+     * @param isAddressed is addr a line address or a unique ID
      */
-    void outgoingTransactionEnd(Addr addr, bool retried)
+    void outgoingTransactionEnd(Addr addr, bool retried,
+        bool isAddressed=true)
     {
+        auto& m_outTrans =
+          isAddressed ? m_outTransAddressed : m_outTransUnaddressed;
         auto iter = m_outTrans.find(addr);
         assert(iter != m_outTrans.end());
         stats.outTransLatHist[iter->second.transaction]->sample(
@@ -375,6 +410,7 @@
     AddrRangeMap<AddrMapEntry, 3> downstreamAddrMap;
 
     NetDest downstreamDestinations;
+    NetDest upstreamDestinations;
 
   public:
     struct ControllerStats : public statistics::Group
diff --git a/src/mem/ruby/slicc_interface/Controller.py b/src/mem/ruby/slicc_interface/Controller.py
index 15366de2..c73836d 100644
--- a/src/mem/ruby/slicc_interface/Controller.py
+++ b/src/mem/ruby/slicc_interface/Controller.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2017,2019,2020 ARM Limited
+# Copyright (c) 2017,2019-2021 ARM Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -75,5 +75,7 @@
 
     # These can be used by a protocol to enable reuse of the same machine
     # types to model different levels of the cache hierarchy
+    upstream_destinations = VectorParam.RubyController([],
+                    "Possible destinations for requests sent towards the CPU")
     downstream_destinations = VectorParam.RubyController([],
                     "Possible destinations for requests sent towards memory")
diff --git a/src/mem/ruby/slicc_interface/RubyRequest.hh b/src/mem/ruby/slicc_interface/RubyRequest.hh
index 8324f6e..2345c22 100644
--- a/src/mem/ruby/slicc_interface/RubyRequest.hh
+++ b/src/mem/ruby/slicc_interface/RubyRequest.hh
@@ -76,6 +76,9 @@
     uint64_t m_instSeqNum;
     bool m_htmFromTransaction;
     uint64_t m_htmTransactionUid;
+    bool m_isTlbi;
+    // Should be uint64, but SLICC complains about casts
+    Addr m_tlbiTransactionUid;
 
     RubyRequest(Tick curTime, uint64_t _paddr, int _len,
         uint64_t _pc, RubyRequestType _type, RubyAccessMode _access_mode,
@@ -91,11 +94,34 @@
           m_pkt(_pkt),
           m_contextId(_core_id),
           m_htmFromTransaction(false),
-          m_htmTransactionUid(0)
+          m_htmTransactionUid(0),
+          m_isTlbi(false),
+          m_tlbiTransactionUid(0)
     {
         m_LineAddress = makeLineAddress(m_PhysicalAddress);
     }
 
+    /** RubyRequest for memory management commands */
+    RubyRequest(Tick curTime,
+        uint64_t _pc, RubyRequestType _type, RubyAccessMode _access_mode,
+        PacketPtr _pkt, ContextID _proc_id, ContextID _core_id)
+        : Message(curTime),
+          m_PhysicalAddress(0),
+          m_Type(_type),
+          m_ProgramCounter(_pc),
+          m_AccessMode(_access_mode),
+          m_Size(0),
+          m_Prefetch(PrefetchBit_No),
+          m_pkt(_pkt),
+          m_contextId(_core_id),
+          m_htmFromTransaction(false),
+          m_htmTransactionUid(0),
+          m_isTlbi(false),
+          m_tlbiTransactionUid(0)
+    {
+        assert(m_pkt->req->isMemMgmt());
+    }
+
     RubyRequest(Tick curTime, uint64_t _paddr, int _len,
         uint64_t _pc, RubyRequestType _type,
         RubyAccessMode _access_mode, PacketPtr _pkt, PrefetchBit _pb,
@@ -117,7 +143,9 @@
           m_wfid(_proc_id),
           m_instSeqNum(_instSeqNum),
           m_htmFromTransaction(false),
-          m_htmTransactionUid(0)
+          m_htmTransactionUid(0),
+          m_isTlbi(false),
+          m_tlbiTransactionUid(0)
     {
         m_LineAddress = makeLineAddress(m_PhysicalAddress);
     }
@@ -144,7 +172,9 @@
           m_wfid(_proc_id),
           m_instSeqNum(_instSeqNum),
           m_htmFromTransaction(false),
-          m_htmTransactionUid(0)
+          m_htmTransactionUid(0),
+          m_isTlbi(false),
+          m_tlbiTransactionUid(0)
     {
         m_LineAddress = makeLineAddress(m_PhysicalAddress);
     }
diff --git a/src/mem/ruby/slicc_interface/RubySlicc_Util.hh b/src/mem/ruby/slicc_interface/RubySlicc_Util.hh
index d146b4e..edfbe4e 100644
--- a/src/mem/ruby/slicc_interface/RubySlicc_Util.hh
+++ b/src/mem/ruby/slicc_interface/RubySlicc_Util.hh
@@ -49,6 +49,7 @@
 #include <cassert>
 #include <climits>
 
+#include "debug/RubyProtocol.hh"
 #include "debug/RubySlicc.hh"
 #include "mem/packet.hh"
 #include "mem/ruby/common/Address.hh"
@@ -161,6 +162,19 @@
     }
 }
 
+inline bool
+isTlbiCmdRequest(RubyRequestType type)
+{
+    if ((type == RubyRequestType_TLBI)  ||
+        (type == RubyRequestType_TLBI_SYNC) ||
+        (type == RubyRequestType_TLBI_EXT_SYNC) ||
+        (type == RubyRequestType_TLBI_EXT_SYNC_COMP)) {
+            return true;
+    } else {
+            return false;
+    }
+}
+
 inline RubyRequestType
 htmCmdToRubyRequestType(const Packet *pkt)
 {
@@ -178,6 +192,22 @@
     }
 }
 
+inline RubyRequestType
+tlbiCmdToRubyRequestType(const Packet *pkt)
+{
+    if (pkt->req->isTlbi()) {
+        return RubyRequestType_TLBI;
+    } else if (pkt->req->isTlbiSync()) {
+        return RubyRequestType_TLBI_SYNC;
+    } else if (pkt->req->isTlbiExtSync()) {
+        return RubyRequestType_TLBI_EXT_SYNC;
+    } else if (pkt->req->isTlbiExtSyncComp()) {
+        return RubyRequestType_TLBI_EXT_SYNC_COMP;
+    } else {
+        panic("invalid ruby packet type\n");
+    }
+}
+
 inline int
 addressOffset(Addr addr, Addr base)
 {
diff --git a/src/mem/ruby/slicc_interface/SConscript b/src/mem/ruby/slicc_interface/SConscript
index 612af3a..47dd49d 100644
--- a/src/mem/ruby/slicc_interface/SConscript
+++ b/src/mem/ruby/slicc_interface/SConscript
@@ -28,7 +28,7 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('Controller.py', sim_objects=['RubyController'])
diff --git a/src/mem/ruby/structures/MN_TBEStorage.hh b/src/mem/ruby/structures/MN_TBEStorage.hh
new file mode 100644
index 0000000..1adbcca
--- /dev/null
+++ b/src/mem/ruby/structures/MN_TBEStorage.hh
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2021-2022 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEM_RUBY_STRUCTURES_MN_TBESTORAGE_HH__
+#define __MEM_RUBY_STRUCTURES_MN_TBESTORAGE_HH__
+
+#include <cassert>
+#include <unordered_map>
+#include <vector>
+
+#include <base/statistics.hh>
+
+#include "mem/ruby/common/MachineID.hh"
+#include "mem/ruby/structures/TBEStorage.hh"
+
+namespace gem5
+{
+
+namespace ruby
+{
+
+// MN_TBEStorage is composed of multiple TBEStorage
+// partitions that could be used for specific types of TBEs.
+// Partition number 0 is the generic partition and will
+// store any kind of TBEs.
+// Space for specific TBEs will be looked first into the matching
+// partition, and when no space is available the generic one will
+// be used
+template <class RetryEntry>
+class MN_TBEStorage
+{
+  public:
+    MN_TBEStorage(Stats::Group *parent,
+                  std::initializer_list<TBEStorage *> _partitions)
+      : m_stats(parent),
+        partitions(_partitions)
+    {}
+
+    // Returns the current number of slots allocated
+    int
+    size() const
+    {
+        int total = 0;
+        for (auto part : partitions) {
+            total += part->size();
+        }
+        return total;
+    }
+
+    // Returns the total capacity of this TBEStorage table
+    int
+    capacity() const
+    {
+        int total = 0;
+        for (auto part : partitions) {
+            total += part->capacity();
+        }
+        return total;
+    }
+
+    // Returns number of slots currently reserved
+    int
+    reserved() const
+    {
+        int total = 0;
+        for (auto part : partitions) {
+            total += part->reserved();
+        }
+        return total;
+    }
+
+    // Returns the number of slots available for objects of a certain type;
+    int
+    slotsAvailable(int partition) const
+    {
+        auto generic_slots = partitions[0]->slotsAvailable();
+        if (partition) {
+            return partitions[partition]->slotsAvailable() +
+                generic_slots;
+        } else {
+            return generic_slots;
+        }
+    }
+
+    // Returns the TBEStorage utilization
+    float utilization() const { return size() / (float)capacity(); }
+
+    // Returns true if slotsAvailable(partition) >= n;
+    //     current_time is always ignored
+    // This allows this class to be used with check_allocate in SLICC to
+    // trigger resource stalls when there are no slots available
+    bool
+    areNSlotsAvailable(int n, int partition,
+                       Tick current_time = 0) const
+    {
+        return slotsAvailable(partition) >= n;
+    }
+
+    // Increase/decrease the number of reserved slots. Having reserved slots
+    // reduces the number of slots available for allocation
+    void
+    incrementReserved(int partition)
+    {
+        if (partition &&
+            partitions[partition]->areNSlotsAvailable(1)) {
+            partitions[partition]->incrementReserved();
+        } else {
+            partitions[0]->incrementReserved();
+        }
+        m_stats.avg_reserved = reserved();
+    }
+
+    void
+    decrementReserved(int partition)
+    {
+        if (partition && (partitions[partition]->reserved() > 0)) {
+            partitions[partition]->decrementReserved();
+        } else {
+            partitions[0]->decrementReserved();
+        }
+        m_stats.avg_reserved = reserved();
+    }
+
+    // Assign a TBETable entry to a free slot and returns the slot number.
+    // Notice we don't need any info from TBETable and just track the number
+    // of entries assigned to each slot.
+    // This funcion requires slotsAvailable() > 0
+    int
+    addEntryToNewSlot(int partition)
+    {
+        if (partition && partitions[partition]->areNSlotsAvailable(1)) {
+            int part_slot = partitions[partition]->addEntryToNewSlot();
+
+            m_stats.avg_size = size();
+            m_stats.avg_util = utilization();
+
+            return part_slot;
+        } else {
+            int generic_slot = partitions[0]->addEntryToNewSlot();
+
+            m_stats.avg_size = size();
+            m_stats.avg_util = utilization();
+
+            return partitions[partition]->capacity() + generic_slot;
+        }
+    }
+
+    // addEntryToSlot(int) is not supported.
+
+    // Remove an entry from an existing non-empty slot. The slot becomes
+    // available again when the number of assigned entries == 0
+    void
+    removeEntryFromSlot(int slot, int partition)
+    {
+        auto part_capacity = partitions[partition]->capacity();
+        if (slot < part_capacity) {
+            partitions[partition]->removeEntryFromSlot(slot);
+        } else {
+            partitions[0]->removeEntryFromSlot(
+                slot - part_capacity);
+        }
+
+        m_stats.avg_size = size();
+        m_stats.avg_util = utilization();
+    }
+
+    // Insert a "retry entry" into the queue
+    void
+    emplaceRetryEntry(RetryEntry entry)
+    {
+        m_retryEntries.push_back(entry);
+    }
+
+    // Check if a retry is possible
+    bool
+    hasPossibleRetry()
+    {
+        auto retry_iter = getNextRetryEntryIter();
+        return retry_iter != m_retryEntries.end();
+    }
+
+    // Peek what the next thing to retry should be
+    // Should only be called if hasPossibleRetry() returns true
+    RetryEntry
+    popNextRetryEntry()
+    {
+        auto retry_iter = getNextRetryEntryIter();
+        assert(retry_iter != m_retryEntries.end());
+
+        auto entry = *retry_iter;
+
+        m_retryEntries.erase(retry_iter);
+
+        return entry;
+    }
+
+  private:
+    struct MN_TBEStorageStats : public statistics::Group
+    {
+        MN_TBEStorageStats(statistics::Group *parent)
+          : statistics::Group(parent),
+            ADD_STAT(avg_size, "Avg. number of slots allocated"),
+            ADD_STAT(avg_util, "Avg. utilization"),
+            ADD_STAT(avg_reserved, "Avg. number of slots reserved")
+        {}
+
+        // Statistical variables
+        statistics::Average avg_size;
+        statistics::Average avg_util;
+        statistics::Average avg_reserved;
+    } m_stats;
+
+    std::vector<TBEStorage *> partitions;
+
+    std::list<RetryEntry> m_retryEntries;
+
+    typename std::list<RetryEntry>::iterator
+    getNextRetryEntryIter()
+    {
+        auto begin_it = m_retryEntries.begin();
+        auto end_it = m_retryEntries.end();
+
+        for (auto it = begin_it; it != end_it; it++) {
+            if (areNSlotsAvailable(1, it->getisNonSync()))
+                return it;
+        }
+
+        return end_it;
+    }
+};
+
+} // namespace ruby
+
+} // namespace gem5
+
+#endif
diff --git a/src/mem/ruby/structures/MN_TBETable.cc b/src/mem/ruby/structures/MN_TBETable.cc
new file mode 100644
index 0000000..43a2dab
--- /dev/null
+++ b/src/mem/ruby/structures/MN_TBETable.cc
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <mem/ruby/structures/MN_TBETable.hh>
+
+namespace gem5
+{
+
+namespace ruby
+{
+
+// Based on the current set of TBEs, choose a new "distributor"
+// Can return null -> no distributor
+MiscNode_TBE*
+MN_TBETable::chooseNewDistributor()
+{
+    // Run over the current TBEs, gather information
+    std::vector<MiscNode_TBE*> ready_sync_tbes;
+    std::vector<MiscNode_TBE*> ready_nonsync_tbes;
+    std::vector<MiscNode_TBE*> potential_sync_dependency_tbes;
+    bool has_waiting_sync = false;
+    int waiting_count = 0;
+    for (auto& keyValuePair : m_map) {
+        MiscNode_TBE& tbe = keyValuePair.second;
+
+        switch (tbe.getstate()) {
+            case MiscNode_State_DvmSync_Distributing:
+            case MiscNode_State_DvmNonSync_Distributing:
+                // If something is still distributing, just return it
+                return &tbe;
+            case MiscNode_State_DvmSync_ReadyToDist:
+                ready_sync_tbes.push_back(&tbe);
+                break;
+            case MiscNode_State_DvmNonSync_ReadyToDist:
+                ready_nonsync_tbes.push_back(&tbe);
+                // Sync ops can potentially depend on not-executed NonSync ops
+                potential_sync_dependency_tbes.push_back(&tbe);
+                break;
+            case MiscNode_State_DvmSync_Waiting:
+                has_waiting_sync = true;
+                waiting_count++;
+                break;
+            case MiscNode_State_DvmNonSync_Waiting:
+                waiting_count++;
+                // Sync ops can potentially depend on not-finished NonSync ops
+                potential_sync_dependency_tbes.push_back(&tbe);
+                break;
+            default:
+                break;
+        }
+    }
+
+    // At most ~4 pending snoops at the RN-F
+    // => for safety we only allow 4 ops waiting + distributing at a time
+    // => if 4 are waiting currently, don't start distributing another one
+    assert(waiting_count <= 4);
+    if (waiting_count == 4) {
+        return nullptr;
+    }
+
+    // If there's a waiting Sync op, don't allow other Sync ops to start.
+    if (has_waiting_sync) {
+        ready_sync_tbes.clear();
+    }
+
+    // We need to handle NonSync -> Sync dependencies
+    // If we send CompDBIDResp for a Non-Sync that hasn't started,
+    // the RN-F can send a dependent Sync immediately afterwards.
+    // The Non-Sync must receive all responses before the Sync starts.
+    // => ignore Syncs which arrive after unfinished NonSyncs
+    auto hasNonSyncDependency = [&](const MiscNode_TBE* sync_tbe) {
+        for (const auto* potential_dep : potential_sync_dependency_tbes) {
+            if (sync_tbe->gettimestamp() > potential_dep->gettimestamp() &&
+                sync_tbe->getrequestor() == potential_dep->getrequestor()) {
+                // A NonSync from the same machine arrived before us
+                // => we have a dependency
+                return true;
+            }
+        }
+        return false;
+    };
+    // Erase-remove idiom to remove elements at arbitrary indices
+    // https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
+    // This calls an O(n) function n times = O(n^2) worst case.
+    // TODO this should be improved if n grows > 16
+    ready_sync_tbes.erase(
+        std::remove_if(ready_sync_tbes.begin(), ready_sync_tbes.end(),
+                       hasNonSyncDependency),
+        ready_sync_tbes.end()
+    );
+
+    // TODO shouldn't use age?
+
+    // Extend ready_nonsync_tbes with the contents of ready_sync_tbes
+    ready_nonsync_tbes.insert(ready_nonsync_tbes.end(),
+                              ready_sync_tbes.begin(), ready_sync_tbes.end());
+
+    // Check if no candidates
+    if (ready_nonsync_tbes.empty())
+        return nullptr;
+
+    // Otherwise select the minimum timestamp = oldest element
+    auto it = std::min_element(
+        ready_nonsync_tbes.begin(), ready_nonsync_tbes.end(),
+        [](const MiscNode_TBE* a, const MiscNode_TBE* b) {
+            return a->gettimestamp() - b->gettimestamp();
+        }
+    );
+    assert(it != ready_nonsync_tbes.end());
+    return *it;
+}
+
+} // namespace ruby
+
+} // namespace gem5
diff --git a/src/mem/ruby/structures/MN_TBETable.hh b/src/mem/ruby/structures/MN_TBETable.hh
new file mode 100644
index 0000000..7cb92ab
--- /dev/null
+++ b/src/mem/ruby/structures/MN_TBETable.hh
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEM_RUBY_STRUCTURES_MN_TBETABLE_HH__
+#define __MEM_RUBY_STRUCTURES_MN_TBETABLE_HH__
+
+#include <algorithm>
+#include <vector>
+
+#include "mem/ruby/protocol/MiscNode_TBE.hh"
+#include "mem/ruby/structures/TBETable.hh"
+
+namespace gem5
+{
+
+namespace ruby
+{
+
+// Custom class only used for the CHI protocol Misc Node
+// Includes the definition of the MiscNode_TBE, because it
+// includes functions that rely on fields in the structure
+class MN_TBETable : public TBETable<MiscNode_TBE>
+{
+  public:
+    MN_TBETable(int number_of_TBEs)
+      : TBETable(number_of_TBEs)
+    {}
+
+    MiscNode_TBE* chooseNewDistributor();
+};
+
+} // namespace ruby
+
+} // namespace gem5
+
+#endif
diff --git a/src/mem/ruby/structures/SConscript b/src/mem/ruby/structures/SConscript
index 086406d..cae0390 100644
--- a/src/mem/ruby/structures/SConscript
+++ b/src/mem/ruby/structures/SConscript
@@ -1,5 +1,17 @@
 # -*- mode:python -*-
 
+# Copyright (c) 2021 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
 # Copyright (c) 2012 Mark D. Hill and David A. Wood
 # All rights reserved.
 #
@@ -28,7 +40,7 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
 SimObject('RubyCache.py', sim_objects=['RubyCache'])
@@ -44,3 +56,5 @@
 Source('TimerTable.cc')
 Source('BankedArray.cc')
 Source('TBEStorage.cc')
+if env['PROTOCOL'] == 'CHI':
+    Source('MN_TBETable.cc')
diff --git a/src/mem/ruby/structures/TBETable.hh b/src/mem/ruby/structures/TBETable.hh
index 5bbf16d..9030d52 100644
--- a/src/mem/ruby/structures/TBETable.hh
+++ b/src/mem/ruby/structures/TBETable.hh
@@ -76,8 +76,8 @@
     // Print cache contents
     void print(std::ostream& out) const;
 
-  private:
-    // Private copy constructor and assignment operator
+  protected:
+    // Protected copy constructor and assignment operator
     TBETable(const TBETable& obj);
     TBETable& operator=(const TBETable& obj);
 
diff --git a/src/mem/ruby/system/RubyPort.cc b/src/mem/ruby/system/RubyPort.cc
index 37c34c5..48f655d 100644
--- a/src/mem/ruby/system/RubyPort.cc
+++ b/src/mem/ruby/system/RubyPort.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013,2020 ARM Limited
+ * Copyright (c) 2012-2013,2020-2021 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -268,8 +268,7 @@
     // Check for pio requests and directly send them to the dedicated
     // pio port.
     if (pkt->cmd != MemCmd::MemSyncReq) {
-        if (!isPhysMemAddress(pkt)) {
-            assert(!pkt->req->isHTMCmd());
+        if (!pkt->req->isMemMgmt() && !isPhysMemAddress(pkt)) {
             assert(ruby_port->memRequestPort.isConnected());
             DPRINTF(RubyPort, "Request address %#x assumed to be a "
                     "pio address\n", pkt->getAddr());
@@ -475,6 +474,54 @@
 }
 
 void
+RubyPort::ruby_unaddressed_callback(PacketPtr pkt)
+{
+    DPRINTF(RubyPort, "Unaddressed callback for %s\n", pkt->cmdString());
+
+    assert(pkt->isRequest());
+
+    // First we must retrieve the request port from the sender State
+    RubyPort::SenderState *senderState =
+        safe_cast<RubyPort::SenderState *>(pkt->popSenderState());
+    MemResponsePort *port = senderState->port;
+    assert(port != NULL);
+    delete senderState;
+
+    port->hitCallback(pkt);
+
+    trySendRetries();
+}
+
+void
+RubyPort::ruby_stale_translation_callback(Addr txnId)
+{
+    DPRINTF(RubyPort, "Stale Translation Callback\n");
+
+    // Allocate the invalidate request and packet on the stack, as it is
+    // assumed they will not be modified or deleted by receivers.
+    // TODO: should this really be using funcRequestorId?
+    auto request = std::make_shared<Request>(
+        0, RubySystem::getBlockSizeBytes(), Request::TLBI_EXT_SYNC,
+        Request::funcRequestorId);
+    // Store the txnId in extraData instead of the address
+    request->setExtraData(txnId);
+
+    // Use a single packet to signal all snooping ports of the external sync.
+    // This assumes that snooping ports do NOT modify the packet/request
+    // TODO rename TlbiExtSync to StaleTranslation
+    Packet pkt(request, MemCmd::TlbiExtSync);
+    // TODO - see where response_ports is filled, might be we only want to send
+    // to specific places
+    for (auto &port : response_ports) {
+        // check if the connected request port is snooping
+        if (port->isSnooping()) {
+            // send as a snoop request
+            port->sendTimingSnoopReq(&pkt);
+        }
+    }
+}
+
+void
 RubyPort::trySendRetries()
 {
     //
diff --git a/src/mem/ruby/system/RubyPort.hh b/src/mem/ruby/system/RubyPort.hh
index 12a88d4..e9d073e 100644
--- a/src/mem/ruby/system/RubyPort.hh
+++ b/src/mem/ruby/system/RubyPort.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013,2019 ARM Limited
+ * Copyright (c) 2012-2013,2019,2021 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -179,6 +179,8 @@
   protected:
     void trySendRetries();
     void ruby_hit_callback(PacketPtr pkt);
+    void ruby_unaddressed_callback(PacketPtr pkt);
+    void ruby_stale_translation_callback(Addr txnId);
     void testDrainComplete();
     void ruby_eviction_callback(Addr address);
 
diff --git a/src/mem/ruby/system/RubySystem.cc b/src/mem/ruby/system/RubySystem.cc
index 76d3f25..91c4bc3 100644
--- a/src/mem/ruby/system/RubySystem.cc
+++ b/src/mem/ruby/system/RubySystem.cc
@@ -477,6 +477,7 @@
     for (auto& network : m_networks) {
         network->resetStats();
     }
+    ClockedObject::resetStats();
 }
 
 #ifndef PARTIAL_FUNC_READS
diff --git a/src/mem/ruby/system/SConscript b/src/mem/ruby/system/SConscript
index 2b4a804..c0b85bb 100644
--- a/src/mem/ruby/system/SConscript
+++ b/src/mem/ruby/system/SConscript
@@ -40,31 +40,31 @@
 
 Import('*')
 
-if env['PROTOCOL'] == 'None':
+if env['CONF']['PROTOCOL'] == 'None':
     Return()
 
-env.Append(CPPDEFINES=['PROTOCOL_' + env['PROTOCOL']])
+env.Append(CPPDEFINES=['PROTOCOL_' + env['CONF']['PROTOCOL']])
 
-if env['PROTOCOL'] in env['NEED_PARTIAL_FUNC_READS']:
+if env['CONF']['PROTOCOL'] in env['NEED_PARTIAL_FUNC_READS']:
     env.Append(CPPDEFINES=['PARTIAL_FUNC_READS'])
 
-if env['BUILD_GPU']:
+if env['CONF']['BUILD_GPU']:
     SimObject('GPUCoalescer.py', sim_objects=['RubyGPUCoalescer'])
 SimObject('RubySystem.py', sim_objects=['RubySystem'])
 SimObject('Sequencer.py', sim_objects=[
     'RubyPort', 'RubyPortProxy', 'RubySequencer', 'RubyHTMSequencer',
     'DMASequencer'])
-if env['BUILD_GPU']:
+if env['CONF']['BUILD_GPU']:
     SimObject('VIPERCoalescer.py', sim_objects=['VIPERCoalescer'])
 
 Source('CacheRecorder.cc')
 Source('DMASequencer.cc')
-if env['BUILD_GPU']:
+if env['CONF']['BUILD_GPU']:
     Source('GPUCoalescer.cc')
 Source('HTMSequencer.cc')
 Source('RubyPort.cc')
 Source('RubyPortProxy.cc')
 Source('RubySystem.cc')
 Source('Sequencer.cc')
-if env['BUILD_GPU']:
+if env['CONF']['BUILD_GPU']:
     Source('VIPERCoalescer.cc')
diff --git a/src/mem/ruby/system/Sequencer.cc b/src/mem/ruby/system/Sequencer.cc
index ac52c5f..601e23a 100644
--- a/src/mem/ruby/system/Sequencer.cc
+++ b/src/mem/ruby/system/Sequencer.cc
@@ -79,6 +79,8 @@
     assert(m_max_outstanding_requests > 0);
     assert(m_deadlock_threshold > 0);
 
+    m_unaddressedTransactionCnt = 0;
+
     m_runningGarnetStandalone = p.garnet_standalone;
 
 
@@ -308,6 +310,42 @@
         schedule(deadlockCheckEvent, clockEdge(m_deadlock_threshold));
     }
 
+    if (isTlbiCmdRequest(primary_type)) {
+        assert(primary_type == secondary_type);
+
+        switch (primary_type) {
+        case RubyRequestType_TLBI_EXT_SYNC_COMP:
+            // Don't have to store any data on this
+            break;
+        case RubyRequestType_TLBI:
+        case RubyRequestType_TLBI_SYNC:
+            {
+                incrementUnaddressedTransactionCnt();
+
+                // returns pair<inserted element, was inserted>
+                [[maybe_unused]] auto insert_data = \
+                    m_UnaddressedRequestTable.emplace(
+                        getCurrentUnaddressedTransactionID(),
+                        SequencerRequest(
+                            pkt, primary_type, secondary_type, curCycle()));
+
+                // if insert_data.second is false, wasn't inserted
+                assert(insert_data.second &&
+                       "Another TLBI request with the same ID exists");
+
+                DPRINTF(RubySequencer, "Inserting TLBI request %016x\n",
+                        getCurrentUnaddressedTransactionID());
+
+                break;
+            }
+
+        default:
+            panic("Unexpected TLBI RubyRequestType");
+        }
+
+        return RequestStatus_Ready;
+    }
+
     Addr line_addr = makeLineAddress(pkt->getAddr());
     // Check if there is any outstanding request for the same cache line.
     auto &seq_req_list = m_RequestTable[line_addr];
@@ -656,10 +694,61 @@
     }
 }
 
+void
+Sequencer::unaddressedCallback(Addr unaddressedReqId,
+                               RubyRequestType reqType,
+                               const MachineType mach,
+                               const Cycles initialRequestTime,
+                               const Cycles forwardRequestTime,
+                               const Cycles firstResponseTime)
+{
+    DPRINTF(RubySequencer, "unaddressedCallback ID:%08x type:%d\n",
+            unaddressedReqId, reqType);
+
+    switch (reqType) {
+      case RubyRequestType_TLBI_EXT_SYNC:
+      {
+        // This should trigger the CPU to wait for stale translations
+        // and send an EXT_SYNC_COMP once complete.
+
+        // Don't look for the ID in our requestTable.
+        // It won't be there because we didn't request this Sync
+        ruby_stale_translation_callback(unaddressedReqId);
+        break;
+      }
+      case RubyRequestType_TLBI:
+      case RubyRequestType_TLBI_SYNC:
+      {
+        // These signal that a TLBI operation that this core initiated
+        // of the respective type (TLBI or Sync) has finished.
+
+        assert(m_UnaddressedRequestTable.find(unaddressedReqId)
+               != m_UnaddressedRequestTable.end());
+
+        {
+            SequencerRequest &seq_req =
+                m_UnaddressedRequestTable.at(unaddressedReqId);
+            assert(seq_req.m_type == reqType);
+
+            PacketPtr pkt = seq_req.pkt;
+
+            ruby_unaddressed_callback(pkt);
+            testDrainComplete();
+        }
+
+        m_UnaddressedRequestTable.erase(unaddressedReqId);
+        break;
+      }
+      default:
+        panic("Unexpected TLBI RubyRequestType");
+    }
+}
+
 bool
 Sequencer::empty() const
 {
-    return m_RequestTable.empty();
+    return m_RequestTable.empty() &&
+           m_UnaddressedRequestTable.empty();
 }
 
 RequestStatus
@@ -716,6 +805,9 @@
             primary_type = RubyRequestType_Locked_RMW_Read;
         }
         secondary_type = RubyRequestType_ST;
+    } else if (pkt->req->isTlbiCmd()) {
+        primary_type = secondary_type = tlbiCmdToRubyRequestType(pkt);
+        DPRINTF(RubySequencer, "Issuing TLBI\n");
     } else {
         //
         // To support SwapReq, we need to check isWrite() first: a SwapReq
@@ -749,7 +841,8 @@
     }
 
     // Check if the line is blocked for a Locked_RMW
-    if (m_controller->isBlocked(makeLineAddress(pkt->getAddr())) &&
+    if (!pkt->req->isMemMgmt() &&
+        m_controller->isBlocked(makeLineAddress(pkt->getAddr())) &&
         (primary_type != RubyRequestType_Locked_RMW_Write)) {
         // Return that this request's cache line address aliases with
         // a prior request that locked the cache line. The request cannot
@@ -788,16 +881,45 @@
 
     // check if the packet has data as for example prefetch and flush
     // requests do not
-    std::shared_ptr<RubyRequest> msg =
-        std::make_shared<RubyRequest>(clockEdge(), pkt->getAddr(),
-                                      pkt->getSize(), pc, secondary_type,
-                                      RubyAccessMode_Supervisor, pkt,
-                                      PrefetchBit_No, proc_id, core_id);
+    std::shared_ptr<RubyRequest> msg;
+    if (pkt->req->isMemMgmt()) {
+        msg = std::make_shared<RubyRequest>(clockEdge(),
+                                            pc, secondary_type,
+                                            RubyAccessMode_Supervisor, pkt,
+                                            proc_id, core_id);
 
-    DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\n",
-            curTick(), m_version, "Seq", "Begin", "", "",
-            printAddress(msg->getPhysicalAddress()),
-            RubyRequestType_to_string(secondary_type));
+        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s\n",
+                curTick(), m_version, "Seq", "Begin", "", "",
+                RubyRequestType_to_string(secondary_type));
+
+        if (pkt->req->isTlbiCmd()) {
+            msg->m_isTlbi = true;
+            switch (secondary_type) {
+              case RubyRequestType_TLBI_EXT_SYNC_COMP:
+                msg->m_tlbiTransactionUid = pkt->req->getExtraData();
+                break;
+              case RubyRequestType_TLBI:
+              case RubyRequestType_TLBI_SYNC:
+                msg->m_tlbiTransactionUid = \
+                    getCurrentUnaddressedTransactionID();
+                break;
+              default:
+                panic("Unexpected TLBI RubyRequestType");
+            }
+            DPRINTF(RubySequencer, "Issuing TLBI %016x\n",
+                    msg->m_tlbiTransactionUid);
+        }
+    } else {
+        msg = std::make_shared<RubyRequest>(clockEdge(), pkt->getAddr(),
+                                            pkt->getSize(), pc, secondary_type,
+                                            RubyAccessMode_Supervisor, pkt,
+                                            PrefetchBit_No, proc_id, core_id);
+
+        DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\n",
+                curTick(), m_version, "Seq", "Begin", "", "",
+                printAddress(msg->getPhysicalAddress()),
+                RubyRequestType_to_string(secondary_type));
+    }
 
     // hardware transactional memory
     // If the request originates in a transaction,
@@ -852,5 +974,28 @@
     ruby_eviction_callback(address);
 }
 
+void
+Sequencer::incrementUnaddressedTransactionCnt()
+{
+    m_unaddressedTransactionCnt++;
+    // Limit m_unaddressedTransactionCnt to 32 bits,
+    // top 32 bits should always be zeroed out
+    uint64_t aligned_txid = \
+        m_unaddressedTransactionCnt << RubySystem::getBlockSizeBits();
+
+    if (aligned_txid > 0xFFFFFFFFull) {
+        m_unaddressedTransactionCnt = 0;
+    }
+}
+
+uint64_t
+Sequencer::getCurrentUnaddressedTransactionID() const
+{
+    return (
+        uint64_t(m_version & 0xFFFFFFFF) << 32) |
+        (m_unaddressedTransactionCnt << RubySystem::getBlockSizeBits()
+    );
+}
+
 } // namespace ruby
 } // namespace gem5
diff --git a/src/mem/ruby/system/Sequencer.hh b/src/mem/ruby/system/Sequencer.hh
index 9812f0c..020a7d8 100644
--- a/src/mem/ruby/system/Sequencer.hh
+++ b/src/mem/ruby/system/Sequencer.hh
@@ -126,6 +126,13 @@
                       const Cycles forwardRequestTime = Cycles(0),
                       const Cycles firstResponseTime = Cycles(0));
 
+    void unaddressedCallback(Addr unaddressedReqId,
+                             RubyRequestType requestType,
+                             const MachineType mach = MachineType_NUM,
+                             const Cycles initialRequestTime = Cycles(0),
+                             const Cycles forwardRequestTime = Cycles(0),
+                             const Cycles firstResponseTime = Cycles(0));
+
     RequestStatus makeRequest(PacketPtr pkt) override;
     virtual bool empty() const;
     int outstandingCount() const override { return m_outstanding_count; }
@@ -215,6 +222,9 @@
   protected:
     // RequestTable contains both read and write requests, handles aliasing
     std::unordered_map<Addr, std::list<SequencerRequest>> m_RequestTable;
+    // UnadressedRequestTable contains "unaddressed" requests,
+    // guaranteed not to alias each other
+    std::unordered_map<uint64_t, SequencerRequest> m_UnaddressedRequestTable;
 
     Cycles m_deadlock_threshold;
 
@@ -240,6 +250,8 @@
 
     int m_coreId;
 
+    uint64_t m_unaddressedTransactionCnt;
+
     bool m_runningGarnetStandalone;
 
     //! Histogram for number of outstanding requests per cycle.
@@ -303,6 +315,18 @@
      */
     bool llscStoreConditional(const Addr);
 
+
+    /**
+     * Increment the unaddressed transaction counter
+     */
+    void incrementUnaddressedTransactionCnt();
+
+    /**
+     * Generate the current unaddressed transaction ID based on the counter
+     * and the Sequencer object's version id.
+     */
+    uint64_t getCurrentUnaddressedTransactionID() const;
+
   public:
     /**
      * Searches for cache line address in the global monitor
diff --git a/src/mem/shared_memory_server.cc b/src/mem/shared_memory_server.cc
new file mode 100644
index 0000000..24dd9f6
--- /dev/null
+++ b/src/mem/shared_memory_server.cc
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mem/shared_memory_server.hh"
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cerrno>
+#include <cstring>
+
+#include "base/logging.hh"
+#include "base/output.hh"
+#include "base/pollevent.hh"
+#include "base/socket.hh"
+
+namespace gem5
+{
+namespace memory
+{
+
+SharedMemoryServer::SharedMemoryServer(const SharedMemoryServerParams& params)
+    : SimObject(params), unixSocketPath(simout.resolve(params.server_path)),
+      system(params.system), serverFd(-1)
+{
+    fatal_if(system == nullptr, "Requires a system to share memory from!");
+    // Ensure the unix socket path to use is not occupied. Also, if there's
+    // actually anything to be removed, warn the user something might be off.
+    if (unlink(unixSocketPath.c_str()) == 0) {
+        warn(
+            "The server path %s was occupied and will be replaced. Please "
+            "make sure there is no other server using the same path.",
+            unixSocketPath.c_str());
+    }
+    // Create a new unix socket.
+    serverFd = ListenSocket::socketCloexec(AF_UNIX, SOCK_STREAM, 0);
+    panic_if(serverFd < 0, "%s: cannot create unix socket: %s", name().c_str(),
+             strerror(errno));
+    // Bind to the specified path.
+    sockaddr_un serv_addr = {};
+    serv_addr.sun_family = AF_UNIX;
+    strncpy(serv_addr.sun_path, unixSocketPath.c_str(),
+            sizeof(serv_addr.sun_path) - 1);
+    warn_if(strlen(serv_addr.sun_path) != unixSocketPath.size(),
+            "%s: unix socket path truncated, expect '%s' but get '%s'",
+            name().c_str(), unixSocketPath.c_str(), serv_addr.sun_path);
+    int bind_retv = bind(serverFd, reinterpret_cast<sockaddr*>(&serv_addr),
+                         sizeof(serv_addr));
+    fatal_if(bind_retv != 0, "%s: cannot bind unix socket: %s", name().c_str(),
+             strerror(errno));
+    // Start listening.
+    int listen_retv = listen(serverFd, 1);
+    fatal_if(listen_retv != 0, "%s: listen failed: %s", name().c_str(),
+             strerror(errno));
+    listenSocketEvent.reset(new ListenSocketEvent(serverFd, this));
+    pollQueue.schedule(listenSocketEvent.get());
+    inform("%s: listening at %s", name().c_str(), unixSocketPath.c_str());
+}
+
+SharedMemoryServer::~SharedMemoryServer()
+{
+    int unlink_retv = unlink(unixSocketPath.c_str());
+    warn_if(unlink_retv != 0, "%s: cannot unlink unix socket: %s",
+            name().c_str(), strerror(errno));
+    int close_retv = close(serverFd);
+    warn_if(close_retv != 0, "%s: cannot close unix socket: %s",
+            name().c_str(), strerror(errno));
+}
+
+SharedMemoryServer::BaseShmPollEvent::BaseShmPollEvent(
+    int fd, SharedMemoryServer* shm_server)
+    : PollEvent(fd, POLLIN), shmServer(shm_server),
+      eventName(shmServer->name() + ".fd" + std::to_string(fd))
+{
+}
+
+const std::string&
+SharedMemoryServer::BaseShmPollEvent::name() const
+{
+    return eventName;
+}
+
+bool
+SharedMemoryServer::BaseShmPollEvent::tryReadAll(void* buffer, size_t size)
+{
+    char* char_buffer = reinterpret_cast<char*>(buffer);
+    for (size_t offset = 0; offset < size;) {
+        ssize_t retv = recv(pfd.fd, char_buffer + offset, size - offset, 0);
+        if (retv >= 0) {
+            offset += retv;
+        } else if (errno != EINTR) {
+            warn("%s: recv failed: %s", name().c_str(), strerror(errno));
+            return false;
+        }
+    }
+    return true;
+}
+
+void
+SharedMemoryServer::ListenSocketEvent::process(int revents)
+{
+    panic_if(revents & (POLLERR | POLLNVAL), "%s: listen socket is broken",
+             name().c_str());
+    int cli_fd = ListenSocket::acceptCloexec(pfd.fd, nullptr, nullptr);
+    panic_if(cli_fd < 0, "%s: accept failed: %s", name().c_str(),
+             strerror(errno));
+    panic_if(shmServer->clientSocketEvent.get(),
+             "%s: cannot serve two clients at once", name().c_str());
+    inform("%s: accept new connection %d", name().c_str(), cli_fd);
+    shmServer->clientSocketEvent.reset(
+        new ClientSocketEvent(cli_fd, shmServer));
+    pollQueue.schedule(shmServer->clientSocketEvent.get());
+}
+
+void
+SharedMemoryServer::ClientSocketEvent::process(int revents)
+{
+    do {
+        // Ensure the connection is not closed nor broken.
+        if (revents & (POLLHUP | POLLERR | POLLNVAL)) {
+            break;
+        }
+
+        // Receive a request packet. We ignore the endianness as unix socket
+        // only allows communication on the same system anyway.
+        RequestType req_type;
+        struct
+        {
+            uint64_t start;
+            uint64_t end;
+        } request;
+        if (!tryReadAll(&req_type, sizeof(req_type))) {
+            break;
+        }
+        if (req_type != RequestType::kGetPhysRange) {
+            warn("%s: receive unknown request: %d", name().c_str(),
+                 static_cast<int>(req_type));
+            break;
+        }
+        if (!tryReadAll(&request, sizeof(request))) {
+            break;
+        }
+        AddrRange range(request.start, request.end);
+        inform("%s: receive request: %s", name().c_str(),
+               range.to_string().c_str());
+
+        // Identify the backing store.
+        const auto& stores = shmServer->system->getPhysMem().getBackingStore();
+        auto it = std::find_if(
+            stores.begin(), stores.end(), [&](const BackingStoreEntry& entry) {
+                return entry.shmFd >= 0 && range.isSubset(entry.range);
+            });
+        if (it == stores.end()) {
+            warn("%s: cannot find backing store for %s", name().c_str(),
+                 range.to_string().c_str());
+            break;
+        }
+        inform("%s: find shared backing store for %s at %s, shm=%d:%lld",
+               name().c_str(), range.to_string().c_str(),
+               it->range.to_string().c_str(), it->shmFd,
+               (unsigned long long)it->shmOffset);
+
+        // Populate response message.
+        // mmap fd @ offset <===> [start, end] in simulated phys mem.
+        msghdr msg = {};
+        // Setup iovec for fields other than fd. We ignore the endianness as
+        // unix socket only allows communication on the same system anyway.
+        struct
+        {
+            off_t offset;
+        } response;
+        // (offset of the request range in shared memory) =
+        //     (offset of the full range in shared memory) +
+        //     (offset of the request range in the full range)
+        response.offset = it->shmOffset + (range.start() - it->range.start());
+        iovec ios = {.iov_base = &response, .iov_len = sizeof(response)};
+        msg.msg_iov = &ios;
+        msg.msg_iovlen = 1;
+        // Setup fd as an ancillary data.
+        union
+        {
+            char buf[CMSG_SPACE(sizeof(it->shmFd))];
+            struct cmsghdr align;
+        } cmsgs;
+        msg.msg_control = cmsgs.buf;
+        msg.msg_controllen = sizeof(cmsgs.buf);
+        cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(it->shmFd));
+        memcpy(CMSG_DATA(cmsg), &it->shmFd, sizeof(it->shmFd));
+        // Send the response.
+        int retv = sendmsg(pfd.fd, &msg, 0);
+        if (retv < 0) {
+            warn("%s: sendmsg failed: %s", name().c_str(), strerror(errno));
+            break;
+        }
+        if (retv != sizeof(response)) {
+            warn("%s: failed to send all response at once", name().c_str());
+            break;
+        }
+
+        // Request done.
+        inform("%s: request done", name().c_str());
+        return;
+    } while (false);
+
+    // If we ever reach here, our client either close the connection or is
+    // somehow broken. We'll just close the connection and move on.
+    inform("%s: closing connection", name().c_str());
+    close(pfd.fd);
+    shmServer->clientSocketEvent.reset();
+}
+
+} // namespace memory
+} // namespace gem5
diff --git a/src/mem/shared_memory_server.hh b/src/mem/shared_memory_server.hh
new file mode 100644
index 0000000..9102d74
--- /dev/null
+++ b/src/mem/shared_memory_server.hh
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEM_SHARED_MEMORY_SERVER_HH__
+#define __MEM_SHARED_MEMORY_SERVER_HH__
+
+#include <memory>
+#include <string>
+
+#include "base/pollevent.hh"
+#include "params/SharedMemoryServer.hh"
+#include "sim/sim_object.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+namespace memory
+{
+
+class SharedMemoryServer : public SimObject
+{
+  public:
+    enum class RequestType : int
+    {
+        kGetPhysRange = 0,
+    };
+
+    explicit SharedMemoryServer(const SharedMemoryServerParams& params);
+    ~SharedMemoryServer();
+
+  private:
+    class BaseShmPollEvent : public PollEvent
+    {
+      public:
+        BaseShmPollEvent(int fd, SharedMemoryServer* shm_server);
+
+        const std::string& name() const;
+
+      protected:
+        bool tryReadAll(void* buffer, size_t size);
+
+        SharedMemoryServer* shmServer;
+        std::string eventName;
+    };
+
+    class ListenSocketEvent : public BaseShmPollEvent
+    {
+      public:
+        using BaseShmPollEvent::BaseShmPollEvent;
+        void process(int revent) override;
+    };
+
+    class ClientSocketEvent : public BaseShmPollEvent
+    {
+      public:
+        using BaseShmPollEvent::BaseShmPollEvent;
+        void process(int revent) override;
+    };
+
+    std::string unixSocketPath;
+    System* system;
+
+    int serverFd;
+    std::unique_ptr<ListenSocketEvent> listenSocketEvent;
+    std::unique_ptr<ClientSocketEvent> clientSocketEvent;
+};
+
+} // namespace memory
+} // namespace gem5
+
+#endif  // __MEM_SHARED_MEMORY_SERVER_HH__
diff --git a/src/mem/slicc/parser.py b/src/mem/slicc/parser.py
index cc45f95..36df4b6 100644
--- a/src/mem/slicc/parser.py
+++ b/src/mem/slicc/parser.py
@@ -752,11 +752,11 @@
         p[0] = p[2]
 
     def p_expr__is_valid_ptr(self, p):
-        "aexpr : IS_VALID '(' var ')'"
+        "aexpr : IS_VALID '(' expr ')'"
         p[0] = ast.IsValidPtrExprAST(self, p[3], True)
 
     def p_expr__is_invalid_ptr(self, p):
-        "aexpr : IS_INVALID '(' var ')'"
+        "aexpr : IS_INVALID '(' expr ')'"
         p[0] = ast.IsValidPtrExprAST(self, p[3], False)
 
     def p_literal__string(self, p):
diff --git a/src/mem/slicc/symbols/StateMachine.py b/src/mem/slicc/symbols/StateMachine.py
index 5d315e9..a9f7373 100644
--- a/src/mem/slicc/symbols/StateMachine.py
+++ b/src/mem/slicc/symbols/StateMachine.py
@@ -105,6 +105,8 @@
         self.objects = []
         self.TBEType   = None
         self.EntryType = None
+        # Python's sets are not sorted so we have to be careful when using
+        # this to generate deterministic output.
         self.debug_flags = set()
         self.debug_flags.add('RubyGenerated')
         self.debug_flags.add('RubySlicc')
@@ -516,8 +518,9 @@
 
         code(boolvec_include)
         code(base_include)
-
-        for f in self.debug_flags:
+        # We have to sort self.debug_flags in order to produce deterministic
+        # output and avoid unnecessary rebuilds of the generated files.
+        for f in sorted(self.debug_flags):
             code('#include "debug/${{f}}.hh"')
         code('''
 #include "mem/ruby/network/Network.hh"
@@ -1246,7 +1249,9 @@
 #include "base/logging.hh"
 
 ''')
-        for f in self.debug_flags:
+        # We have to sort self.debug_flags in order to produce deterministic
+        # output and avoid unnecessary rebuilds of the generated files.
+        for f in sorted(self.debug_flags):
             code('#include "debug/${{f}}.hh"')
         code('''
 #include "mem/ruby/protocol/${ident}_Controller.hh"
diff --git a/src/mem/sys_bridge.cc b/src/mem/sys_bridge.cc
new file mode 100644
index 0000000..3037a1d
--- /dev/null
+++ b/src/mem/sys_bridge.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2021 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mem/sys_bridge.hh"
+
+#include "sim/system.hh"
+
+namespace gem5
+{
+
+SysBridge::PacketData
+SysBridge::BridgingPort::replaceReqID(PacketPtr pkt)
+{
+    RequestPtr old_req = pkt->req;
+    RequestPtr new_req = std::make_shared<Request>(
+            old_req->getPaddr(), old_req->getSize(), old_req->getFlags(), id);
+    pkt->req = new_req;
+    return {old_req};
+}
+
+SysBridge::SysBridge(const SysBridgeParams &p) : SimObject(p),
+    sourcePort(p.name + ".source_port", this, &targetPort,
+            p.target->getRequestorId(this)),
+    targetPort(p.name + ".target_port", this, &sourcePort,
+            p.source->getRequestorId(this))
+{}
+
+Port &
+SysBridge::getPort(const std::string &if_name, PortID idx)
+{
+    if (if_name == "source_port")
+        return sourcePort;
+    else if (if_name == "target_port")
+        return targetPort;
+    else
+        return SimObject::getPort(if_name, idx);
+}
+
+} // namespace gem5
diff --git a/src/mem/sys_bridge.hh b/src/mem/sys_bridge.hh
new file mode 100644
index 0000000..8fa3131
--- /dev/null
+++ b/src/mem/sys_bridge.hh
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2021 Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEM_SYS_BRIDGE_HH__
+#define __MEM_SYS_BRIDGE_HH__
+
+#include "base/trace.hh"
+#include "base/types.hh"
+#include "debug/SysBridge.hh"
+#include "mem/port.hh"
+#include "params/SysBridge.hh"
+#include "sim/sim_object.hh"
+
+namespace gem5
+{
+
+/**
+ * Each System object in gem5 is responsible for a set of RequestorIDs which
+ * identify different sources for memory requests within that System. Each
+ * object within the memory system is responsible for requesting an ID if it
+ * needs one, and then using that ID in the requests it sends out.
+ *
+ * When a simulation has multiple System objects within it, components
+ * registered with different Systems may be able to interact through the memory
+ * system. If an object uses a RequestorID it got from its parent System, and
+ * that ends up being handled by an object which is using a different parent,
+ * the target object may misinterpret or misattribute that ID, or if the number
+ * of IDs in the two systems are different, it might even index an array based
+ * on the requestor out of bounds.
+ *
+ * This SysBridge object helps handle that situation by translating requests
+ * going across it in either direction, upstream or downstream, so that they
+ * always have a RequestorID which is valid in the current System. They
+ * register themselves as a Requestor in each System, and use that ID in the
+ * foreign System, restoring the original ID as the request makes its way back
+ * to its source.
+ *
+ * Example:
+ * # One System with a CPU in it.
+ * sys1 = System(...)
+ * sys1.cpu = CPU(...)
+ *
+ * # One System with memory in it.
+ * sys2 = System(...)
+ * sys2.memory = Memory(...)
+ *
+ * # A SysBridge for crossing from sys1 to sys2.
+ * sys2.sys_bridge = SysBridge(
+ *         source=sys1,
+ *         target=sys2,
+ *         target_port=sys2.memory.port,
+ *         source_port=sys1.cpu.port)
+ */
+
+class SysBridge : public SimObject
+{
+  private:
+    class SysBridgeTargetPort;
+    class SysBridgeSourcePort;
+
+    // A structure for whatever we need to keep when bridging a packet.
+    struct PacketData
+    {
+        RequestPtr req;
+    };
+
+    class SysBridgeSenderState : public Packet::SenderState
+    {
+      private:
+        PacketData pData;
+
+      public:
+        SysBridgeSenderState(const PacketData &data) : pData(data) {}
+
+        const PacketData &data() const { return pData; }
+    };
+
+    class BridgingPort
+    {
+      protected:
+        RequestorID id;
+
+        // Replace the requestor ID in pkt, and return any scratch data we'll
+        // need going back the other way.
+        PacketData replaceReqID(PacketPtr pkt);
+        // Restore pkt to use its original requestor ID.
+        static void
+        restoreReqID(PacketPtr pkt, const PacketData &data)
+        {
+            pkt->req = data.req;
+        }
+
+        static void
+        restoreReqID(PacketPtr pkt, const PacketData &data, PacketData &backup)
+        {
+            backup.req = pkt->req;
+            restoreReqID(pkt, data);
+        }
+
+        BridgingPort(RequestorID _id) : id(_id) {}
+    };
+
+    class SysBridgeTargetPort : public RequestPort, public BridgingPort
+    {
+      private:
+        SysBridgeSourcePort *sourcePort;
+
+      public:
+        SysBridgeTargetPort(const std::string &_name, SimObject *owner,
+                SysBridgeSourcePort *source_port, RequestorID _id) :
+            RequestPort(_name, owner), BridgingPort(_id),
+            sourcePort(source_port)
+        {
+            DPRINTF(SysBridge, "Target side requestor ID = %s.\n", _id);
+        }
+
+      private:
+        void
+        recvRangeChange() override
+        {
+            sourcePort->sendRangeChange();
+        }
+
+        Tick
+        recvAtomicSnoop(PacketPtr pkt) override
+        {
+            DPRINTF(SysBridge, "recvAtomicSnoop incoming ID %d.\n",
+                    pkt->requestorId());
+            auto data = replaceReqID(pkt);
+            DPRINTF(SysBridge, "recvAtomicSnoop outgoing ID %d.\n",
+                    pkt->requestorId());
+            Tick tick = sourcePort->sendAtomicSnoop(pkt);
+            restoreReqID(pkt, data);
+            DPRINTF(SysBridge, "recvAtomicSnoop restored ID %d.\n",
+                    pkt->requestorId());
+            return tick;
+        }
+
+        bool
+        recvTimingResp(PacketPtr pkt) override
+        {
+            auto *state = dynamic_cast<SysBridgeSenderState *>(
+                    pkt->popSenderState());
+            PacketData backup;
+            DPRINTF(SysBridge, "recvTimingResp incoming ID %d.\n",
+                    pkt->requestorId());
+            restoreReqID(pkt, state->data(), backup);
+            DPRINTF(SysBridge, "recvTimingResp restored ID %d.\n",
+                    pkt->requestorId());
+            if (!sourcePort->sendTimingResp(pkt)) {
+                restoreReqID(pkt, backup);
+                DPRINTF(SysBridge, "recvTimingResp un-restored ID %d.\n",
+                        pkt->requestorId());
+                pkt->pushSenderState(state);
+                return false;
+            } else {
+                delete state;
+                return true;
+            }
+        }
+
+        void
+        recvTimingSnoopReq(PacketPtr pkt) override
+        {
+            DPRINTF(SysBridge, "recvTimingSnoopReq incoming ID %d.\n",
+                    pkt->requestorId());
+            auto *state = new SysBridgeSenderState(replaceReqID(pkt));
+            pkt->pushSenderState(state);
+            DPRINTF(SysBridge, "recvTimingSnoopReq outgoing ID %d.\n",
+                    pkt->requestorId());
+            sourcePort->sendTimingSnoopReq(pkt);
+        }
+
+        void recvReqRetry() override { sourcePort->sendRetryReq(); }
+        void
+        recvRetrySnoopResp() override
+        {
+            sourcePort->sendRetrySnoopResp();
+        }
+
+        void
+        recvFunctionalSnoop(PacketPtr pkt) override
+        {
+            DPRINTF(SysBridge, "recvFunctionalSnoop incoming ID %d.\n",
+                    pkt->requestorId());
+            auto data = replaceReqID(pkt);
+            DPRINTF(SysBridge, "recvFunctionalSnoop outgoing ID %d.\n",
+                    pkt->requestorId());
+            sourcePort->sendFunctionalSnoop(pkt);
+            restoreReqID(pkt, data);
+            DPRINTF(SysBridge, "recvFunctionalSnoop restored ID %d.\n",
+                    pkt->requestorId());
+        }
+    };
+
+    class SysBridgeSourcePort : public ResponsePort, public BridgingPort
+    {
+      private:
+        SysBridgeTargetPort *targetPort;
+
+      public:
+        SysBridgeSourcePort(const std::string &_name, SimObject *owner,
+                SysBridgeTargetPort *target_port, RequestorID _id) :
+            ResponsePort(_name, owner), BridgingPort(_id),
+            targetPort(target_port)
+        {
+            DPRINTF(SysBridge, "Source side requestor ID = %s.\n", _id);
+        }
+
+      private:
+        bool
+        recvTimingReq(PacketPtr pkt) override
+        {
+            DPRINTF(SysBridge, "recvTimingReq incoming ID %d.\n",
+                    pkt->requestorId());
+            auto *state = new SysBridgeSenderState(replaceReqID(pkt));
+            pkt->pushSenderState(state);
+            DPRINTF(SysBridge, "recvTimingReq outgoing ID %d.\n",
+                    pkt->requestorId());
+            if (!targetPort->sendTimingReq(pkt)) {
+                restoreReqID(pkt, state->data());
+                DPRINTF(SysBridge, "recvTimingReq restored ID %d.\n",
+                        pkt->requestorId());
+                pkt->popSenderState();
+                delete state;
+                return false;
+            } else {
+                return true;
+            }
+        }
+
+        bool
+        tryTiming(PacketPtr pkt) override
+        {
+            // Since tryTiming shouldn't actually send the packet, we should
+            // be able to clean up inline like we would for atomic methods.
+            // This may not actually be necessary at all, but it's a little
+            // safer.
+            DPRINTF(SysBridge, "tryTiming incoming ID %d.\n",
+                    pkt->requestorId());
+            auto data = replaceReqID(pkt);
+            DPRINTF(SysBridge, "tryTiming outgoing ID %d.\n",
+                    pkt->requestorId());
+            bool ret = targetPort->tryTiming(pkt);
+            restoreReqID(pkt, data);
+            DPRINTF(SysBridge, "tryTiming restored ID %d.\n",
+                    pkt->requestorId());
+            return ret;
+        }
+
+        bool
+        recvTimingSnoopResp(PacketPtr pkt) override
+        {
+            auto *state = dynamic_cast<SysBridgeSenderState *>(
+                    pkt->popSenderState());
+            DPRINTF(SysBridge, "recvTimingSnoopResp incoming ID %d.\n",
+                    pkt->requestorId());
+            restoreReqID(pkt, state->data());
+            DPRINTF(SysBridge, "recvTimingSnoopResp restored ID %d.\n",
+                    pkt->requestorId());
+            return targetPort->sendTimingSnoopResp(pkt);
+        }
+
+        void recvRespRetry() override { targetPort->sendRetryResp(); }
+
+        Tick
+        recvAtomic(PacketPtr pkt) override
+        {
+            DPRINTF(SysBridge, "recvAtomic incoming ID %d.\n",
+                    pkt->requestorId());
+            auto data = replaceReqID(pkt);
+            DPRINTF(SysBridge, "recvAtomic outgoing ID %d.\n",
+                    pkt->requestorId());
+            Tick tick = targetPort->sendAtomic(pkt);
+            restoreReqID(pkt, data);
+            DPRINTF(SysBridge, "recvAtomic restored ID %d.\n",
+                    pkt->requestorId());
+            return tick;
+        }
+
+        Tick
+        recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor) override
+        {
+            DPRINTF(SysBridge, "recvAtomicBackdoor incoming ID %d.\n",
+                    pkt->requestorId());
+            auto data = replaceReqID(pkt);
+            DPRINTF(SysBridge, "recvAtomicBackdoor outgoing ID %d.\n",
+                    pkt->requestorId());
+            Tick tick = targetPort->sendAtomicBackdoor(pkt, backdoor);
+            restoreReqID(pkt, data);
+            DPRINTF(SysBridge, "recvAtomicBackdoor restored ID %d.\n",
+                    pkt->requestorId());
+            return tick;
+        }
+
+        void
+        recvFunctional(PacketPtr pkt) override
+        {
+            DPRINTF(SysBridge, "recvFunctional incoming ID %d.\n",
+                    pkt->requestorId());
+            auto data = replaceReqID(pkt);
+            DPRINTF(SysBridge, "recvFunctional outgoing ID %d.\n",
+                    pkt->requestorId());
+            targetPort->sendFunctional(pkt);
+            restoreReqID(pkt, data);
+            DPRINTF(SysBridge, "recvFunctional restored ID %d.\n",
+                    pkt->requestorId());
+        }
+
+        AddrRangeList
+        getAddrRanges() const override
+        {
+            return targetPort->getAddrRanges();
+        }
+    };
+
+    SysBridgeSourcePort sourcePort;
+    SysBridgeTargetPort targetPort;
+
+  public:
+    Port &getPort(const std::string &if_name,
+            PortID idx=InvalidPortID) override;
+
+    SysBridge(const SysBridgeParams &p);
+};
+
+} // namespace gem5
+
+#endif //__MEM_SYS_BRIDGE_HH__
diff --git a/src/mem/translation_gen.test.cc b/src/mem/translation_gen.test.cc
index 2bda1ea..276e240 100644
--- a/src/mem/translation_gen.test.cc
+++ b/src/mem/translation_gen.test.cc
@@ -31,6 +31,7 @@
 #include <initializer_list>
 #include <list>
 #include <memory>
+#include <ostream>
 #include <vector>
 
 #include "base/cprintf.hh"
@@ -47,8 +48,6 @@
 // A dummy fault class so we have something to return from failed translations.
 class FaultBase {};
 
-} // namespace gem5
-
 Fault dummyFault1 = std::make_shared<gem5::FaultBase>();
 Fault dummyFault2 = std::make_shared<gem5::FaultBase>();
 
@@ -64,6 +63,8 @@
     return os;
 }
 
+} // namespace gem5
+
 using RangeList = std::list<TranslationGen::Range>;
 
 // Compare ranges which are returned by the generator.
diff --git a/src/proto/SConscript b/src/proto/SConscript
index 09589a9..42a1a4c 100644
--- a/src/proto/SConscript
+++ b/src/proto/SConscript
@@ -38,14 +38,8 @@
 Import('*')
 
 # Only build if we have protobuf support
-if env['HAVE_PROTOBUF']:
-    ProtoBuf('inst_dep_record.proto')
-    ProtoBuf('packet.proto')
-    ProtoBuf('inst.proto')
-    Source('protobuf.cc')
-    Source('protoio.cc')
-
-    # protoc relies on the fact that undefined preprocessor symbols are
-    # explanded to 0 but since we use -Wundef they end up generating
-    # warnings.
-    env.Append(CCFLAGS='-DPROTOBUF_INLINE_NOT_IN_HEADERS=0')
+ProtoBuf('inst_dep_record.proto', tags='protobuf')
+ProtoBuf('packet.proto', tags='protobuf')
+ProtoBuf('inst.proto', tags='protobuf')
+Source('protobuf.cc', tags='protobuf')
+Source('protoio.cc', tags='protobuf')
diff --git a/src/proto/SConsopts b/src/proto/SConsopts
index 946d361..6b5b25d 100644
--- a/src/proto/SConsopts
+++ b/src/proto/SConsopts
@@ -65,13 +65,18 @@
     # automatically added to the LIBS environment variable. After
     # this, we can use the HAVE_PROTOBUF flag to determine if we have
     # got both protoc and libprotobuf available.
-    conf.env['HAVE_PROTOBUF'] = conf.env['HAVE_PROTOC'] and \
+    conf.env['CONF']['HAVE_PROTOBUF'] = conf.env['HAVE_PROTOC'] and \
         conf.CheckLibWithHeader('protobuf', 'google/protobuf/message.h',
                                 'C++', 'GOOGLE_PROTOBUF_VERIFY_VERSION;')
 
 # If we have the compiler but not the library, print another warning.
-if main['HAVE_PROTOC'] and not main['HAVE_PROTOBUF']:
+if main['HAVE_PROTOC'] and not main['CONF']['HAVE_PROTOBUF']:
     warning('Did not find protocol buffer library and/or headers.\n'
             'Please install libprotobuf-dev for tracing support.')
 
-export_vars.append('HAVE_PROTOBUF')
+if main['CONF']['HAVE_PROTOBUF']:
+    main.TagImplies('protobuf', 'gem5 lib')
+    # protoc relies on the fact that undefined preprocessor symbols are
+    # explanded to 0 but since we use -Wundef they end up generating
+    # warnings.
+    main.Append(CCFLAGS='-DPROTOBUF_INLINE_NOT_IN_HEADERS=0')
diff --git a/src/proto/protoio.cc b/src/proto/protoio.cc
index 72e463b..acf04de 100644
--- a/src/proto/protoio.cc
+++ b/src/proto/protoio.cc
@@ -37,6 +37,8 @@
 
 #include "proto/protoio.hh"
 
+#include <string>
+
 #include "base/logging.hh"
 
 using namespace google::protobuf;
@@ -53,7 +55,7 @@
     // wrapped in a gzip stream if the filename ends with .gz. The
     // latter stream is in turn wrapped in a coded stream
     wrappedFileStream = new io::OstreamOutputStream(&fileStream);
-    if (filename.find_last_of('.') != string::npos &&
+    if (filename.find_last_of('.') != std::string::npos &&
         filename.substr(filename.find_last_of('.') + 1) == "gz") {
         gzipStream = new io::GzipOutputStream(wrappedFileStream);
         zeroCopyStream = gzipStream;
diff --git a/src/python/SConscript b/src/python/SConscript
index 984ae82..b595ba9 100644
--- a/src/python/SConscript
+++ b/src/python/SConscript
@@ -39,6 +39,8 @@
 PySource('gem5.components', 'gem5/components/__init__.py')
 PySource('gem5.components.boards', 'gem5/components/boards/__init__.py')
 PySource('gem5.components.boards', 'gem5/components/boards/abstract_board.py')
+PySource('gem5.components.boards',
+    'gem5/components/boards/abstract_system_board.py')
 PySource('gem5.components.boards', 'gem5/components/boards/mem_mode.py')
 PySource('gem5.components.boards', 'gem5/components/boards/riscv_board.py')
 PySource('gem5.components.boards.experimental',
@@ -48,6 +50,7 @@
 PySource('gem5.components.boards', 'gem5/components/boards/simple_board.py')
 PySource('gem5.components.boards', 'gem5/components/boards/test_board.py')
 PySource('gem5.components.boards', 'gem5/components/boards/x86_board.py')
+PySource('gem5.components.boards', 'gem5/components/boards/arm_board.py')
 PySource('gem5.components.boards',
     "gem5/components/boards/kernel_disk_workload.py")
 PySource('gem5.components.boards',
@@ -208,6 +211,7 @@
 PySource('gem5.prebuilt.demo', 'gem5/prebuilt/demo/x86_demo_board.py')
 PySource('gem5.resources', 'gem5/resources/__init__.py')
 PySource('gem5.resources', 'gem5/resources/downloader.py')
+PySource('gem5.resources', 'gem5/resources/md5_utils.py')
 PySource('gem5.resources', 'gem5/resources/resource.py')
 PySource('gem5.utils', 'gem5/utils/__init__.py')
 PySource('gem5.utils', 'gem5/utils/filelock.py')
@@ -217,7 +221,6 @@
 PySource('', 'importer.py')
 PySource('m5', 'm5/__init__.py')
 PySource('m5', 'm5/SimObject.py')
-PySource('m5', 'm5/config.py')
 PySource('m5', 'm5/core.py')
 PySource('m5', 'm5/debug.py')
 PySource('m5', 'm5/event.py')
diff --git a/src/python/gem5/components/boards/abstract_board.py b/src/python/gem5/components/boards/abstract_board.py
index 30fbfca..3067f7d 100644
--- a/src/python/gem5/components/boards/abstract_board.py
+++ b/src/python/gem5/components/boards/abstract_board.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2021 The Regents of the University of California
+# Copyright (c) 2022 The Regents of the University of California
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,7 @@
 from typing import List
 
 
-class AbstractBoard(System):
+class AbstractBoard:
     """The abstract board interface.
 
     Boards are used as the object which can connect together all other
@@ -65,10 +65,9 @@
         self,
         clk_freq: str,
         processor: "AbstractProcessor",
-        memory: "AbstractMemory",
+        memory: "AbstractMemorySystem",
         cache_hierarchy: "AbstractCacheHierarchy",
     ) -> None:
-        super().__init__()
         """
         :param clk_freq: The clock frequency for this board.
         :param processor: The processor for this board.
@@ -76,6 +75,9 @@
         :param cache_hierarchy: The Cachie Hierarchy for this board.
         """
 
+        if not isinstance(self, System):
+            raise Exception("A gem5 stdlib board must inherit from System.")
+
         # Set up the clock domain and the voltage domain.
         self.clk_domain = SrcClockDomain()
         self.clk_domain.clock = clk_freq
@@ -86,6 +88,12 @@
         self.memory = memory
         self.cache_hierarchy = cache_hierarchy
 
+        # This variable determines whether the board is to be executed in
+        # full-system or syscall-emulation mode. This is set when the workload
+        # is defined. Whether or not the board is to be run in FS mode is
+        # determined by which kind of workload is set.
+        self._is_fs = None
+
         # Setup the board and memory system's memory ranges.
         self._setup_memory_ranges()
 
@@ -140,6 +148,33 @@
         """
         return self.clk_domain
 
+    def _set_fullsystem(self, is_fs: bool) -> None:
+        """
+        Sets whether this board is to be run in FS or SE mode. This is set
+        via the workload (the workload specified determines whether this will
+        be run in FS mode or not). This is not intended to be set in a
+        configuration script ergo, it's private.
+
+        :param is_fs: Set whether the board is to be run in FS mode or SE mode.
+        """
+        self._is_fs = is_fs
+
+    def is_fullsystem(self) -> bool:
+        """
+        Returns True if the board is to be run in FS mode. Otherwise the board
+        is to be run in Se mode. An exception will be thrown if this has not
+        been set.
+
+        This function is used by the Simulator module to setup the simulation
+        correctly.
+        """
+        if self._is_fs  == None:
+            raise Exception("The workload for this board not yet to be set. "
+                            "Whether the board is to be executed in FS or SE "
+                            "mode is determined by which 'set workload' "
+                            "function is run.")
+        return self._is_fs
+
     @abstractmethod
     def _setup_board(self) -> None:
         """
diff --git a/src/python/m5/config.py b/src/python/gem5/components/boards/abstract_system_board.py
similarity index 66%
rename from src/python/m5/config.py
rename to src/python/gem5/components/boards/abstract_system_board.py
index 926ea14..463a5b6 100644
--- a/src/python/m5/config.py
+++ b/src/python/gem5/components/boards/abstract_system_board.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2008 The Hewlett-Packard Development Company
+# Copyright (c) 2021 The Regents of the University of California
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -24,25 +24,31 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-import os
-from os.path import isdir, isfile, join as joinpath
+from abc import ABCMeta
 
+from .abstract_board import AbstractBoard
 
-confdir = os.environ.get('M5_CONFIG')
+from m5.objects import System
 
-if not confdir:
-    # HOME is not set when running regressions, due to use of scons
-    # Execute() function.
-    homedir = os.environ.get('HOME')
-    if homedir and isdir(joinpath(homedir, '.m5')):
-        confdir = joinpath(homedir, '.m5')
+class AbstractSystemBoard(System, AbstractBoard):
 
-def get(name):
-    if not confdir:
-        return None
-    conffile = joinpath(confdir, name)
-    if not isfile(conffile):
-        return None
+    """
+    An abstract board for cases where boards should inherit from System.
+    """
 
-    return conffile
-
+    __metaclass__ = ABCMeta
+    def __init__(
+        self,
+        clk_freq: str,
+        processor: "AbstractProcessor",
+        memory: "AbstractMemorySystem",
+        cache_hierarchy: "AbstractCacheHierarchy",
+    ):
+        System.__init__(self)
+        AbstractBoard.__init__(
+            self,
+            clk_freq=clk_freq,
+            processor=processor,
+            memory=memory,
+            cache_hierarchy=cache_hierarchy,
+        )
\ No newline at end of file
diff --git a/src/python/gem5/components/boards/arm_board.py b/src/python/gem5/components/boards/arm_board.py
new file mode 100644
index 0000000..8434658
--- /dev/null
+++ b/src/python/gem5/components/boards/arm_board.py
@@ -0,0 +1,378 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from m5.objects import (
+    Port,
+    IOXBar,
+    Bridge,
+    BadAddr,
+    Terminal,
+    PciVirtIO,
+    VncServer,
+    AddrRange,
+    ArmSystem,
+    ArmRelease,
+    ArmFsLinux,
+    VirtIOBlock,
+    CowDiskImage,
+    RawDiskImage,
+    VoltageDomain,
+    SrcClockDomain,
+    ArmDefaultRelease,
+    VExpress_GEM5_Base,
+    VExpress_GEM5_Foundation,
+)
+
+import os
+import m5
+from abc import ABCMeta
+from ...isas import ISA
+from typing import List
+from m5.util import fatal
+from ...utils.requires import requires
+from ...utils.override import overrides
+from .abstract_board import AbstractBoard
+from ...resources.resource import AbstractResource
+from .kernel_disk_workload import KernelDiskWorkload
+from ..cachehierarchies.classic.no_cache import NoCache
+from ..processors.abstract_processor import AbstractProcessor
+from ..memory.abstract_memory_system import AbstractMemorySystem
+from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy
+
+class ArmBoard(ArmSystem, AbstractBoard, KernelDiskWorkload):
+    """
+    A board capable of full system simulation for ARM instructions. It is based
+    ARMv8.
+
+    The board is based on Arm Motherboard Express uATX (V2M-P1), Arm
+    CoreTile Express A15x2 (V2P-CA15) and on Armv8-A FVP Foundation platform
+    v11.8, depending on the simulated platform. These boards are parts of ARM's
+    Versatile(TM) Express family of boards.
+
+    **Limitations**
+    * The board currently does not support ruby caches.
+    * stage2 walker ports are ignored.
+    * This version does not support SECURITY extension.
+    """
+    __metaclass__ = ABCMeta
+
+    def __init__(
+        self,
+        clk_freq: str,
+        processor: AbstractProcessor,
+        memory: AbstractMemorySystem,
+        cache_hierarchy: AbstractCacheHierarchy,
+        platform: VExpress_GEM5_Base = VExpress_GEM5_Foundation(),
+        release: ArmRelease = ArmDefaultRelease()
+    ) -> None:
+        super().__init__()
+        AbstractBoard.__init__(
+            self,
+            clk_freq = clk_freq,
+            processor = processor,
+            memory = memory,
+            cache_hierarchy = cache_hierarchy,
+        )
+
+        # This board requires ARM ISA to work.
+
+        requires(isa_required = ISA.ARM)
+
+        # Setting the voltage domain here.
+
+        self.voltage_domain = self.clk_domain.voltage_domain
+
+        # Setting up ARM release here. We use the ARM default release, which
+        # corresponds to an ARMv8 system.
+
+        self.release = release
+
+        # RealView sets up most of the on-chip and off-chip devices and GIC
+        # for the ARM board. These devices' iformation is also used to
+        # generate the dtb file.
+
+        self._setup_realview(platform)
+
+        # ArmBoard's memory can only be setup once realview is initialized.
+
+        self._setup_arm_memory_ranges()
+
+        # Setting multi_proc of ArmSystem by counting the number of processors.
+
+        if processor.get_num_cores() != 1:
+            self.multi_proc = False
+        else:
+            self.multi_proc = True
+
+    @overrides(AbstractBoard)
+    def _setup_board(self) -> None:
+
+        # This board is expected to run full-system simulation.
+        # Loading ArmFsLinux() from `src/arch/arm/ArmFsWorkload.py`
+
+        self.workload = ArmFsLinux()
+
+        # We are fixing the following variable for the ArmSystem to work. The
+        # security extension is checked while generating the dtb file in
+        # realview. This board does not have security extention enabled.
+
+        self._have_psci = False
+
+        # highest_el_is_64 is set to True. True if the register width of the
+        # highest implemented exception level is 64 bits.
+
+        self.highest_el_is_64 = True
+
+        # Setting up the voltage and the clock domain here for the ARM board.
+        # The ArmSystem/RealView expects voltage_domain to be a parameter.
+        # The voltage and the clock frequency are taken from the devices.py
+        # file from configs/example/arm
+
+        self.voltage_domain = VoltageDomain(voltage="1.0V")
+        self.clk_domain = SrcClockDomain(
+            clock="1GHz", voltage_domain=self.voltage_domain
+        )
+
+        # The ARM board supports both Terminal and VncServer.
+
+        self.terminal = Terminal()
+        self.vncserver = VncServer()
+
+        # Incoherent I/O Bus
+
+        self.iobus = IOXBar()
+        self.iobus.badaddr_responder = BadAddr()
+        self.iobus.default = self.iobus.badaddr_responder.pio
+
+    def _setup_io_devices(self) -> None:
+        """
+        This method connects the I/O devices to the I/O bus.
+        """
+
+        # We setup the iobridge for the ARM Board. The default
+        # cache_hierarchy's NoCache class has an iobridge has a latency of
+        # 10. We are using an iobridge with latency = 50ns, taken from the
+        # configs/example/arm/devices.py
+
+        self.iobridge = Bridge(delay="50ns")
+        self.iobridge.mem_side_port = self.iobus.cpu_side_ports
+        self.iobridge.cpu_side_port = (
+            self.cache_hierarchy.get_mem_side_port()
+        )
+
+        # We either have iocache or dmabridge depending upon the
+        # cache_hierarchy. If we have "NoCache", then we use the dmabridge.
+        # Otherwise, we use the iocache on the board.
+
+        if isinstance(self.cache_hierarchy, NoCache) is False:
+
+            # The ArmBoard does not support ruby caches.
+
+            if self.get_cache_hierarchy().is_ruby():
+                fatal("Ruby caches are not supported by the ArmBoard.")
+
+            # The classic caches are setup in the  _setup_io_cache() method,
+            # defined under the cachehierarchy class. Verified it with both
+            # PrivateL1PrivateL2CacheHierarchy and PrivateL1CacheHierarchy
+            # classes.
+
+        else:
+
+            # This corresponds to a machine without caches. We have a DMA
+            # beidge in this case. Parameters of this bridge are also taken
+            # from the common/example/arm/devices.py file.
+
+            self.dmabridge = Bridge(
+                delay="50ns", ranges=self.mem_ranges
+            )
+
+            self.dmabridge.mem_side_port = self.get_dma_ports()[0]
+            self.dmabridge.cpu_side_port = self.get_dma_ports()[1]
+
+        self.realview.attachOnChipIO(
+            self.cache_hierarchy.membus, self.iobridge
+        )
+        self.realview.attachIO(self.iobus)
+
+    def _setup_realview(self, platform) -> None:
+        """
+        Notes:
+        The ARM Board has realview platform. Most of the on-chip and
+        off-chip devices are setup by the RealView platform. Currently, there
+        are 5 different types of realview platforms supported by the ArmBoard.
+
+        :param platform: the user can specify the platform while instantiating
+        an ArmBoard object.
+        """
+
+        # Currently, the ArmBoard supports VExpress_GEM5_V1,
+        # VExpress_GEM5_V1_HDLcd and VExpress_GEM5_Foundation.
+        # VExpress_GEM5_V2 and VExpress_GEM5_V2_HDLcd are not supported by the
+        # ArmBoard.
+
+        self.realview = platform
+
+        # We need to setup the global interrupt controller (GIC) addr for the
+        # realview system.
+
+        if hasattr(self.realview.gic, "cpu_addr"):
+            self.gic_cpu_addr = self.realview.gic.cpu_addr
+
+    def _setup_io_cache(self):
+        pass
+
+    @overrides(AbstractBoard)
+    def has_io_bus(self) -> bool:
+        return True
+
+    @overrides(AbstractBoard)
+    def get_io_bus(self) -> IOXBar:
+        return [self.iobus.cpu_side_ports, self.iobus.mem_side_ports]
+
+    @overrides(AbstractBoard)
+    def has_coherent_io(self) -> bool:
+        return True
+
+    @overrides(AbstractBoard)
+    def get_mem_side_coherent_io_port(self) -> Port:
+        return self.iobus.mem_side_ports
+
+    @overrides(AbstractBoard)
+    def has_dma_ports(self) -> bool:
+        return True
+
+    def _setup_coherent_io_bridge(self, board: AbstractBoard) -> None:
+        pass
+
+    @overrides(AbstractBoard)
+    def get_dma_ports(self) -> List[Port]:
+        return [
+            self.cache_hierarchy.get_cpu_side_port(),
+            self.iobus.mem_side_ports
+        ]
+
+    @overrides(AbstractBoard)
+    def connect_system_port(self, port: Port) -> None:
+        self.system_port = port
+
+    @overrides(KernelDiskWorkload)
+    def get_disk_device(self):
+        return "/dev/vda"
+
+    @overrides(KernelDiskWorkload)
+    def _add_disk_to_board(self, disk_image: AbstractResource):
+
+        # We define the image.
+
+        image = CowDiskImage(
+            child=RawDiskImage(read_only=True), read_only=False
+        )
+
+        self.pci_devices = [PciVirtIO(vio=VirtIOBlock(image=image))]
+        self.realview.attachPciDevice(
+                self.pci_devices[0], self.iobus
+        )
+
+        # Now that the disk and workload are set, we can generate the device
+        # tree file. We will generate the dtb file everytime the board is
+        # boot-up.
+
+        image.child.image_file = disk_image.get_local_path()
+
+        # _setup_io_devices needs to be implemented.
+
+        self._setup_io_devices()
+
+        # Specifying the dtb file location to the workload.
+
+        self.workload.dtb_filename = os.path.join(
+                m5.options.outdir, "device.dtb"
+        )
+
+        # Calling generateDtb from class ArmSystem to add memory information to
+        # the dtb file.
+
+        self.generateDtb(self.workload.dtb_filename)
+
+        # Finally we need to setup the bootloader for the ArmBoard. An ARM
+        # system requires three inputs to simulate a full system: a disk image,
+        # the kernel file and the bootloader file(s).
+
+        self.realview.setupBootLoader(
+                self, self.workload.dtb_filename, self._bootloader)
+
+    def _get_memory_ranges(self, mem_size) -> list:
+        """
+        This method is taken from configs/example/arm/devices.py. It sets up
+        all the memory ranges for the board.
+        """
+        mem_ranges = []
+
+        for mem_range in self.realview._mem_regions:
+            size_in_range = min(mem_size, mem_range.size())
+            mem_ranges.append(
+                AddrRange(start = mem_range.start, size = size_in_range)
+            )
+
+            mem_size -= size_in_range
+            if mem_size == 0:
+                return mem_ranges
+
+        raise ValueError("Memory size too big for platform capabilities")
+
+    @overrides(AbstractBoard)
+    def _setup_memory_ranges(self) -> None:
+        """
+        The ArmBoard's memory can only be setup after realview is setup. Once
+        realview is initialized, we call _setup_arm_memory_ranges() to
+        correctly setup the memory ranges.
+        """
+        pass
+
+    def _setup_arm_memory_ranges(self) -> None:
+
+        # We setup the memory here. The memory size is specified in the run
+        # script that the user uses.
+
+        memory = self.get_memory()
+        mem_size = memory.get_size()
+
+        self.mem_ranges = self._get_memory_ranges(mem_size)
+        memory.set_memory_range(self.mem_ranges)
+
+    @overrides(KernelDiskWorkload)
+    def get_default_kernel_args(self) -> List[str]:
+
+        # The default kernel string is taken from the devices.py file.
+
+        return [
+            "console=ttyAMA0",
+            "lpj=19988480",
+            "norandmaps",
+            "root={root_value}",
+            "rw",
+            "mem=%s" % self.get_memory().get_size(),
+        ]
diff --git a/src/python/gem5/components/boards/experimental/lupv_board.py b/src/python/gem5/components/boards/experimental/lupv_board.py
index d6b1dc5..59eedd8 100644
--- a/src/python/gem5/components/boards/experimental/lupv_board.py
+++ b/src/python/gem5/components/boards/experimental/lupv_board.py
@@ -28,14 +28,13 @@
 from typing import List
 
 from ....utils.override import overrides
-from ..abstract_board import AbstractBoard
+from ..abstract_system_board import AbstractSystemBoard
 from ...processors.abstract_processor import AbstractProcessor
 from ...memory.abstract_memory_system import AbstractMemorySystem
 from ...cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy
 from ..kernel_disk_workload import KernelDiskWorkload
 from ....resources.resource import AbstractResource
 from ....isas import ISA
-from ....utils.requires import requires
 
 import m5
 from m5.objects import (
@@ -73,7 +72,7 @@
     FdtState,
 )
 
-class LupvBoard(AbstractBoard, KernelDiskWorkload):
+class LupvBoard(AbstractSystemBoard, KernelDiskWorkload):
     """
     A board capable of full system simulation for RISC-V.
     This board uses a set of LupIO education-friendly devices.
@@ -91,13 +90,17 @@
         cache_hierarchy: AbstractCacheHierarchy,
     ) -> None:
 
-        requires(isa_required=ISA.RISCV)
         if cache_hierarchy.is_ruby():
             raise EnvironmentError("RiscvBoard is not compatible with Ruby")
 
+        if processor.get_isa() != ISA.RISCV:
+            raise Exception("The LupvBoard requires a processor using the "
+                "RISCV ISA. Current processor "
+                f"ISA: '{processor.get_isa().name}'.")
+
         super().__init__(clk_freq, processor, memory, cache_hierarchy)
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def _setup_board(self) -> None:
 
         self.workload = RiscvLinux()
@@ -242,22 +245,22 @@
                 uncacheable=uncacheable_range
             )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_dma_ports(self) -> bool:
         return False
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_dma_ports(self) -> List[Port]:
         raise NotImplementedError(
             "The LupvBoard does not have DMA Ports. "
             "Use `has_dma_ports()` to check this."
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_io_bus(self) -> bool:
         return True
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_io_bus(self) -> IOXBar:
         return self.iobus
 
@@ -267,7 +270,7 @@
     def get_mem_side_coherent_io_port(self) -> Port:
         return self.iobus.mem_side_ports
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def _setup_memory_ranges(self):
         memory = self.get_memory()
         mem_size = memory.get_size()
diff --git a/src/python/gem5/components/boards/kernel_disk_workload.py b/src/python/gem5/components/boards/kernel_disk_workload.py
index 031fc60..23824d1 100644
--- a/src/python/gem5/components/boards/kernel_disk_workload.py
+++ b/src/python/gem5/components/boards/kernel_disk_workload.py
@@ -26,6 +26,7 @@
 
 from abc import abstractmethod
 
+from .abstract_board import AbstractBoard
 from ...resources.resource import AbstractResource
 
 from typing import List, Optional
@@ -133,6 +134,7 @@
         self,
         kernel: AbstractResource,
         disk_image: AbstractResource,
+        bootloader: Optional[AbstractResource] = None,
         readfile: Optional[str] = None,
         readfile_contents: Optional[str] = None,
         kernel_args: Optional[List[str]] = None,
@@ -144,6 +146,8 @@
 
         :param kernel: The kernel to boot.
         :param disk_image: The disk image to mount.
+        :param bootloader: The current implementation of the ARM board requires
+        three resources to operate -- kernel, disk image, and, a bootloader.
         :param readfile: An optional parameter stating the file to be read by
         by `m5 readfile`.
         :param readfile_contents: An optional parameter stating the contents of
@@ -156,6 +160,14 @@
         items. True by default.
         """
 
+        # We assume this this is in a multiple-inheritance setup with an
+        # Abstract board. This function will not work otherwise.
+        assert(isinstance(self,AbstractBoard))
+
+        # If we are setting a workload of this type, we need to run as a
+        # full-system simulation.
+        self._set_fullsystem(True)
+
         # Set the kernel to use.
         self.workload.object_file = kernel.get_local_path()
 
@@ -166,6 +178,13 @@
             root_value=self.get_default_kernel_root_val(disk_image=disk_image)
         )
 
+        # Setting the bootloader information for ARM board. The current
+        # implementation of the ArmBoard class expects a boot loader file to be
+        # provided along with the kernel and the disk image.
+
+        if bootloader is not None:
+            self._bootloader = [bootloader.get_local_path()]
+
         # Set the readfile.
         if readfile:
             self.readfile = readfile
@@ -181,4 +200,4 @@
         self._add_disk_to_board(disk_image=disk_image)
 
         # Set whether to exit on work items.
-        self.exit_on_work_items = exit_on_work_items
\ No newline at end of file
+        self.exit_on_work_items = exit_on_work_items
diff --git a/src/python/gem5/components/boards/riscv_board.py b/src/python/gem5/components/boards/riscv_board.py
index 7dfa9db..9f8b1c0 100644
--- a/src/python/gem5/components/boards/riscv_board.py
+++ b/src/python/gem5/components/boards/riscv_board.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2021 The Regents of the University of California
+# Copyright (c) 2022 EXAscale Performance SYStems (EXAPSYS)
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -29,7 +30,7 @@
 from typing import List
 
 from ...utils.override import overrides
-from .abstract_board import AbstractBoard
+from .abstract_system_board import AbstractSystemBoard
 from .kernel_disk_workload import KernelDiskWorkload
 from ..processors.abstract_processor import AbstractProcessor
 from ..memory.abstract_memory_system import AbstractMemorySystem
@@ -37,7 +38,6 @@
 from ...resources.resource import AbstractResource
 
 from ...isas import ISA
-from ...utils.requires import requires
 
 import m5
 
@@ -50,6 +50,8 @@
     IOXBar,
     RiscvRTC,
     HiFive,
+    GenericRiscvPciHost,
+    IGbE_e1000,
     CowDiskImage,
     RawDiskImage,
     RiscvMmioVirtIO,
@@ -69,7 +71,7 @@
 )
 
 
-class RiscvBoard(AbstractBoard, KernelDiskWorkload):
+class RiscvBoard(AbstractSystemBoard, KernelDiskWorkload):
     """
     A board capable of full system simulation for RISC-V
 
@@ -89,9 +91,13 @@
         cache_hierarchy: AbstractCacheHierarchy,
     ) -> None:
         super().__init__(clk_freq, processor, memory, cache_hierarchy)
-        requires(isa_required=ISA.RISCV)
 
-    @overrides(AbstractBoard)
+        if processor.get_isa() != ISA.RISCV:
+            raise Exception("The RISCVBoard requires a processor using the"
+                "RISCV ISA. Current processor ISA: "
+                f"'{processor.get_isa().name}'.")
+
+    @overrides(AbstractSystemBoard)
     def _setup_board(self) -> None:
         self.workload = RiscvLinux()
 
@@ -135,6 +141,16 @@
 
     def _setup_io_devices(self) -> None:
         """Connect the I/O devices to the I/O bus"""
+        #Add PCI
+        self.platform.pci_host.pio = self.iobus.mem_side_ports
+
+        #Add Ethernet card
+        self.ethernet = IGbE_e1000(pci_bus=0, pci_dev=0, pci_func=0,
+                                   InterruptLine=1, InterruptPin=1)
+
+        self.ethernet.host = self.platform.pci_host
+        self.ethernet.pio  = self.iobus.mem_side_ports
+        self.ethernet.dma  = self.iobus.cpu_side_ports
 
         if self.get_cache_hierarchy().is_ruby():
             for device in self._off_chip_devices + self._on_chip_devices:
@@ -156,6 +172,11 @@
                 for dev in self._off_chip_devices
             ]
 
+            #PCI
+            self.bridge.ranges.append(AddrRange(0x2F000000, size='16MB'))
+            self.bridge.ranges.append(AddrRange(0x30000000, size='256MB'))
+            self.bridge.ranges.append(AddrRange(0x40000000, size='512MB'))
+
     def _setup_pma(self) -> None:
         """Set the PMA devices on each core"""
 
@@ -164,40 +185,45 @@
             for dev in self._on_chip_devices + self._off_chip_devices
         ]
 
+        #PCI
+        uncacheable_range.append(AddrRange(0x2F000000, size='16MB'))
+        uncacheable_range.append(AddrRange(0x30000000, size='256MB'))
+        uncacheable_range.append(AddrRange(0x40000000, size='512MB'))
+
         # TODO: Not sure if this should be done per-core like in the example
         for cpu in self.get_processor().get_cores():
             cpu.get_mmu().pma_checker = PMAChecker(
                 uncacheable=uncacheable_range
             )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_dma_ports(self) -> bool:
         return False
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_dma_ports(self) -> List[Port]:
         raise NotImplementedError(
             "RISCVBoard does not have DMA Ports. "
             "Use `has_dma_ports()` to check this."
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_io_bus(self) -> bool:
         return True
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_io_bus(self) -> IOXBar:
         return self.iobus
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_coherent_io(self) -> bool:
         return True
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_mem_side_coherent_io_port(self) -> Port:
         return self.iobus.mem_side_ports
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def _setup_memory_ranges(self):
         memory = self.get_memory()
         mem_size = memory.get_size()
@@ -319,6 +345,71 @@
 
         soc_node.append(plic_node)
 
+        # PCI
+        pci_state = FdtState(addr_cells=3, size_cells=2,
+                             cpu_cells=1, interrupt_cells=1)
+        pci_node = FdtNode("pci")
+
+        if int(self.platform.pci_host.conf_device_bits) == 8:
+            pci_node.appendCompatible("pci-host-cam-generic")
+        elif int(self.platform.pci_host.conf_device_bits) == 12:
+            pci_node.appendCompatible("pci-host-ecam-generic")
+        else:
+            m5.fatal("No compatibility string for the set conf_device_width")
+
+        pci_node.append(FdtPropertyStrings("device_type", ["pci"]))
+
+        # Cell sizes of child nodes/peripherals
+        pci_node.append(pci_state.addrCellsProperty())
+        pci_node.append(pci_state.sizeCellsProperty())
+        pci_node.append(pci_state.interruptCellsProperty())
+        # PCI address for CPU
+        pci_node.append(FdtPropertyWords("reg",
+            soc_state.addrCells(self.platform.pci_host.conf_base) +
+            soc_state.sizeCells(self.platform.pci_host.conf_size) ))
+
+        # Ranges mapping
+        # For now some of this is hard coded, because the PCI module does not
+        # have a proper full understanding of the memory map, but adapting the
+        # PCI module is beyond the scope of what I'm trying to do here.
+        # Values are taken from the ARM VExpress_GEM5_V1 platform.
+        ranges = []
+        # Pio address range
+        ranges += self.platform.pci_host.pciFdtAddr(space=1, addr=0)
+        ranges += soc_state.addrCells(self.platform.pci_host.pci_pio_base)
+        ranges += pci_state.sizeCells(0x10000)  # Fixed size
+
+        # AXI memory address range
+        ranges += self.platform.pci_host.pciFdtAddr(space=2, addr=0)
+        ranges += soc_state.addrCells(self.platform.pci_host.pci_mem_base)
+        ranges += pci_state.sizeCells(0x40000000) # Fixed size
+        pci_node.append(FdtPropertyWords("ranges", ranges))
+
+        # Interrupt mapping
+        plic_handle = int_state.phandle(plic)
+        int_base    = self.platform.pci_host.int_base
+
+        interrupts = []
+
+        for i in range(int(self.platform.pci_host.int_count)):
+            interrupts += self.platform.pci_host.pciFdtAddr(device=i,
+                addr=0) + [int(i) + 1, plic_handle, int(int_base) + i]
+
+        pci_node.append(FdtPropertyWords("interrupt-map", interrupts))
+
+        int_count = int(self.platform.pci_host.int_count)
+        if int_count & (int_count - 1):
+            fatal("PCI interrupt count should be power of 2")
+
+        intmask = self.platform.pci_host.pciFdtAddr(device=int_count - 1,
+                                                    addr=0) + [0x0]
+        pci_node.append(FdtPropertyWords("interrupt-map-mask", intmask))
+
+        if self.platform.pci_host._dma_coherent:
+            pci_node.append(FdtProperty("dma-coherent"))
+
+        soc_node.append(pci_node)
+
         # UART node
         uart = self.platform.uart
         uart_node = uart.generateBasicPioDeviceNode(
diff --git a/src/python/gem5/components/boards/se_binary_workload.py b/src/python/gem5/components/boards/se_binary_workload.py
index 485c1a3..c166add 100644
--- a/src/python/gem5/components/boards/se_binary_workload.py
+++ b/src/python/gem5/components/boards/se_binary_workload.py
@@ -24,6 +24,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from .abstract_board import AbstractBoard
 from ...resources.resource import AbstractResource
 
 from m5.objects import SEWorkload, Process
@@ -49,6 +50,14 @@
         :param binary: The resource encapsulating the binary to be run.
         """
 
+        # We assume this this is in a multiple-inheritance setup with an
+        # Abstract board. This function will not work otherwise.
+        assert(isinstance(self,AbstractBoard))
+
+        # If we are setting a workload of this type, we need to run as a
+        # SE-mode simulation.
+        self._set_fullsystem(False)
+
         self.workload = SEWorkload.init_compatible(binary.get_local_path())
 
         process = Process()
diff --git a/src/python/gem5/components/boards/simple_board.py b/src/python/gem5/components/boards/simple_board.py
index d0f4f2a..532475d 100644
--- a/src/python/gem5/components/boards/simple_board.py
+++ b/src/python/gem5/components/boards/simple_board.py
@@ -30,7 +30,7 @@
     Port,
 )
 
-from .abstract_board import AbstractBoard
+from .abstract_system_board import AbstractSystemBoard
 from .se_binary_workload import SEBinaryWorkload
 from ..processors.abstract_processor import AbstractProcessor
 from ..memory.abstract_memory_system import AbstractMemorySystem
@@ -40,7 +40,7 @@
 from typing import List
 
 
-class SimpleBoard(AbstractBoard, SEBinaryWorkload):
+class SimpleBoard(AbstractSystemBoard, SEBinaryWorkload):
     """
     This is an incredibly simple system. It contains no I/O, and will work only
     with a classic cache hierarchy setup.
@@ -65,44 +65,44 @@
             cache_hierarchy=cache_hierarchy,
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def _setup_board(self) -> None:
         pass
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_io_bus(self) -> bool:
         return False
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_io_bus(self) -> IOXBar:
         raise NotImplementedError(
             "SimpleBoard does not have an IO Bus. "
             "Use `has_io_bus()` to check this."
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_dma_ports(self) -> bool:
         return False
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_dma_ports(self) -> List[Port]:
         raise NotImplementedError(
             "SimpleBoard does not have DMA Ports. "
             "Use `has_dma_ports()` to check this."
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_coherent_io(self) -> bool:
         return False
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_mem_side_coherent_io_port(self) -> Port:
         raise NotImplementedError(
             "SimpleBoard does not have any I/O ports. Use has_coherent_io to "
             "check this."
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def _setup_memory_ranges(self) -> None:
         memory = self.get_memory()
 
diff --git a/src/python/gem5/components/boards/test_board.py b/src/python/gem5/components/boards/test_board.py
index d39e25d..7031e0e 100644
--- a/src/python/gem5/components/boards/test_board.py
+++ b/src/python/gem5/components/boards/test_board.py
@@ -32,7 +32,7 @@
 
 from .mem_mode import MemMode, mem_mode_to_string
 from ...utils.override import overrides
-from .abstract_board import AbstractBoard
+from .abstract_system_board import AbstractSystemBoard
 from ..processors.abstract_processor import AbstractProcessor
 from ..memory.abstract_memory_system import AbstractMemorySystem
 from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy
@@ -41,7 +41,7 @@
 from typing import List
 
 
-class TestBoard(AbstractBoard):
+class TestBoard(AbstractSystemBoard):
 
     """This is a Testing Board used to run traffic generators on a simple
     architecture.
@@ -63,44 +63,44 @@
             cache_hierarchy=cache_hierarchy,
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def _setup_board(self) -> None:
         pass
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_io_bus(self) -> bool:
         return False
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_io_bus(self) -> IOXBar:
         raise NotImplementedError(
             "The TestBoard does not have an IO Bus. "
             "Use `has_io_bus()` to check this."
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_dma_ports(self) -> List[Port]:
         return False
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_dma_ports(self) -> List[Port]:
         raise NotImplementedError(
             "The TestBoard does not have DMA Ports. "
             "Use `has_dma_ports()` to check this."
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_coherent_io(self) -> bool:
         return False
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_mem_side_coherent_io_port(self):
         raise NotImplementedError(
             "SimpleBoard does not have any I/O ports. Use has_coherent_io to "
             "check this."
         )
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def _setup_memory_ranges(self) -> None:
         memory = self.get_memory()
 
@@ -109,6 +109,6 @@
         self.mem_ranges = [AddrRange(memory.get_size())]
         memory.set_memory_range(self.mem_ranges)
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_dma_ports(self) -> bool:
         return False
diff --git a/src/python/gem5/components/boards/x86_board.py b/src/python/gem5/components/boards/x86_board.py
index 6f53001..6761bdb 100644
--- a/src/python/gem5/components/boards/x86_board.py
+++ b/src/python/gem5/components/boards/x86_board.py
@@ -28,7 +28,7 @@
 from .kernel_disk_workload import KernelDiskWorkload
 from ...resources.resource import AbstractResource
 from ...utils.override import overrides
-from .abstract_board import AbstractBoard
+from .abstract_system_board import AbstractSystemBoard
 from ...isas import ISA
 
 from m5.objects import (
@@ -57,12 +57,11 @@
 from ..processors.abstract_processor import AbstractProcessor
 from ..memory.abstract_memory_system import AbstractMemorySystem
 from ..cachehierarchies.abstract_cache_hierarchy import AbstractCacheHierarchy
-from ...utils.requires import requires
 
 from typing import List, Sequence
 
 
-class X86Board(AbstractBoard, KernelDiskWorkload):
+class X86Board(AbstractSystemBoard, KernelDiskWorkload):
     """
     A board capable of full system simulation for X86.
 
@@ -85,9 +84,11 @@
             cache_hierarchy=cache_hierarchy,
         )
 
-        requires(isa_required=ISA.X86)
+        if self.get_processor().get_isa() != ISA.X86:
+            raise Exception("The X86Board requires a processor using the X86 "
+                f"ISA. Current processor ISA: '{processor.get_isa().name}'.")
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def _setup_board(self) -> None:
         self.pc = Pc()
 
@@ -99,6 +100,8 @@
         # Set up all of the I/O.
         self._setup_io_devices()
 
+        self.m5ops_base = 0xffff0000
+
     def _setup_io_devices(self):
         """ Sets up the x86 IO devices.
 
@@ -248,31 +251,31 @@
 
         self.workload.e820_table.entries = entries
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_io_bus(self) -> bool:
         return True
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_io_bus(self) -> BaseXBar:
         return self.iobus
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_dma_ports(self) -> bool:
         return True
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_dma_ports(self) -> Sequence[Port]:
         return [self.pc.south_bridge.ide.dma, self.iobus.mem_side_ports]
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def has_coherent_io(self) -> bool:
         return True
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def get_mem_side_coherent_io_port(self) -> Port:
         return self.iobus.mem_side_ports
 
-    @overrides(AbstractBoard)
+    @overrides(AbstractSystemBoard)
     def _setup_memory_ranges(self):
         memory = self.get_memory()
 
diff --git a/src/python/gem5/components/cachehierarchies/chi/private_l1_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/chi/private_l1_cache_hierarchy.py
index d692710..58dc780 100644
--- a/src/python/gem5/components/cachehierarchies/chi/private_l1_cache_hierarchy.py
+++ b/src/python/gem5/components/cachehierarchies/chi/private_l1_cache_hierarchy.py
@@ -35,7 +35,6 @@
 )
 from gem5.coherence_protocol import CoherenceProtocol
 from gem5.isas import ISA
-from gem5.runtime import get_runtime_isa
 from gem5.utils.requires import requires
 from gem5.utils.override import overrides
 from gem5.components.boards.abstract_board import AbstractBoard
@@ -155,7 +154,7 @@
             network=self.ruby_system.network,
             core=core,
             cache_line_size=board.get_cache_line_size(),
-            target_isa=get_runtime_isa(),
+            target_isa=board.get_processor().get_isa(),
             clk_domain=board.get_clock_domain(),
         )
         cluster.icache = PrivateL1MOESICache(
@@ -164,7 +163,7 @@
             network=self.ruby_system.network,
             core=core,
             cache_line_size=board.get_cache_line_size(),
-            target_isa=get_runtime_isa(),
+            target_isa=board.get_processor().get_isa(),
             clk_domain=board.get_clock_domain(),
         )
 
@@ -194,7 +193,7 @@
         )
 
         # Connect the interrupt ports
-        if get_runtime_isa() == ISA.X86:
+        if board.get_processor().get_isa() == ISA.X86:
             int_req_port = cluster.dcache.sequencer.interrupt_out_port
             int_resp_port = cluster.dcache.sequencer.in_ports
             core.connect_interrupt(int_req_port, int_resp_port)
diff --git a/src/python/gem5/components/cachehierarchies/classic/no_cache.py b/src/python/gem5/components/cachehierarchies/classic/no_cache.py
index e57e713..7e8d314 100644
--- a/src/python/gem5/components/cachehierarchies/classic/no_cache.py
+++ b/src/python/gem5/components/cachehierarchies/classic/no_cache.py
@@ -28,7 +28,6 @@
 from ..abstract_cache_hierarchy import AbstractCacheHierarchy
 from ...boards.abstract_board import AbstractBoard
 from ....isas import ISA
-from ....runtime import get_runtime_isa
 
 from m5.objects import Bridge, BaseXBar, SystemXBar, BadAddr, Port
 
@@ -107,7 +106,7 @@
                 self.membus.cpu_side_ports, self.membus.cpu_side_ports
             )
 
-            if get_runtime_isa() == ISA.X86:
+            if board.get_processor().get_isa() == ISA.X86:
                 int_req_port = self.membus.mem_side_ports
                 int_resp_port = self.membus.cpu_side_ports
                 core.connect_interrupt(int_req_port, int_resp_port)
diff --git a/src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py
index f1fe5df..c2755fe 100644
--- a/src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py
+++ b/src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py
@@ -31,7 +31,6 @@
 from .caches.mmu_cache import MMUCache
 from ...boards.abstract_board import AbstractBoard
 from ....isas import ISA
-from ....runtime import get_runtime_isa
 
 from m5.objects import Cache, BaseXBar, SystemXBar, BadAddr, Port
 
@@ -129,7 +128,7 @@
                 self.iptw_caches[i].cpu_side, self.dptw_caches[i].cpu_side
             )
 
-            if get_runtime_isa() == ISA.X86:
+            if board.get_processor().get_isa() == ISA.X86:
                 int_req_port = self.membus.mem_side_ports
                 int_resp_port = self.membus.cpu_side_ports
                 cpu.connect_interrupt(int_req_port, int_resp_port)
diff --git a/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py
index 7c4e829..ff66b30 100644
--- a/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py
+++ b/src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py
@@ -33,8 +33,6 @@
 from .caches.mmu_cache import MMUCache
 from ...boards.abstract_board import AbstractBoard
 from ....isas import ISA
-from ....runtime import get_runtime_isa
-
 from m5.objects import Cache, L2XBar, BaseXBar, SystemXBar, BadAddr, Port
 
 from ....utils.override import *
@@ -166,7 +164,7 @@
                 self.iptw_caches[i].cpu_side, self.dptw_caches[i].cpu_side
             )
 
-            if get_runtime_isa() == ISA.X86:
+            if board.get_processor().get_isa() == ISA.X86:
                 int_req_port = self.membus.mem_side_ports
                 int_resp_port = self.membus.cpu_side_ports
                 cpu.connect_interrupt(int_req_port, int_resp_port)
diff --git a/src/python/gem5/components/cachehierarchies/ruby/mesi_two_level_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/ruby/mesi_two_level_cache_hierarchy.py
index 9d423659..cd3ea7f 100644
--- a/src/python/gem5/components/cachehierarchies/ruby/mesi_two_level_cache_hierarchy.py
+++ b/src/python/gem5/components/cachehierarchies/ruby/mesi_two_level_cache_hierarchy.py
@@ -30,7 +30,6 @@
 from ....coherence_protocol import CoherenceProtocol
 from ....isas import ISA
 from ...boards.abstract_board import AbstractBoard
-from ....runtime import get_runtime_isa
 from ....utils.requires import requires
 
 from .topologies.simple_pt2pt import SimplePt2Pt
@@ -106,7 +105,7 @@
                 core,
                 self._num_l2_banks,
                 cache_line_size,
-                get_runtime_isa(),
+                board.processor.get_isa(),
                 board.get_clock_domain(),
             )
 
@@ -129,7 +128,7 @@
             )
 
             # Connect the interrupt ports
-            if get_runtime_isa() == ISA.X86:
+            if board.get_processor().get_isa() == ISA.X86:
                 int_req_port = cache.sequencer.interrupt_out_port
                 int_resp_port = cache.sequencer.in_ports
                 core.connect_interrupt(int_req_port, int_resp_port)
diff --git a/src/python/gem5/components/cachehierarchies/ruby/mi_example_cache_hierarchy.py b/src/python/gem5/components/cachehierarchies/ruby/mi_example_cache_hierarchy.py
index 523ba49..352bf00 100644
--- a/src/python/gem5/components/cachehierarchies/ruby/mi_example_cache_hierarchy.py
+++ b/src/python/gem5/components/cachehierarchies/ruby/mi_example_cache_hierarchy.py
@@ -34,7 +34,6 @@
 from ....coherence_protocol import CoherenceProtocol
 from ....isas import ISA
 from ....utils.override import overrides
-from ....runtime import get_runtime_isa
 from ....utils.requires import requires
 
 
@@ -93,7 +92,7 @@
                 network=self.ruby_system.network,
                 core=core,
                 cache_line_size=board.get_cache_line_size(),
-                target_isa=get_runtime_isa(),
+                target_isa=board.get_processor().get_isa(),
                 clk_domain=board.get_clock_domain(),
             )
 
@@ -116,7 +115,7 @@
             )
 
             # Connect the interrupt ports
-            if get_runtime_isa() == ISA.X86:
+            if board.get_processor().get_isa() == ISA.X86:
                 int_req_port = cache.sequencer.interrupt_out_port
                 int_resp_port = cache.sequencer.in_ports
                 core.connect_interrupt(int_req_port, int_resp_port)
diff --git a/src/python/gem5/components/processors/abstract_core.py b/src/python/gem5/components/processors/abstract_core.py
index a16196f..32f597d 100644
--- a/src/python/gem5/components/processors/abstract_core.py
+++ b/src/python/gem5/components/processors/abstract_core.py
@@ -26,12 +26,15 @@
 
 from abc import ABCMeta, abstractmethod
 from typing import Optional
+import importlib
+import platform
+
 from .cpu_types import CPUTypes
+from ...isas import ISA
 from ...utils.requires import requires
 
 from m5.objects import BaseMMU, Port, SubSystem
 
-
 class AbstractCore(SubSystem):
     __metaclass__ = ABCMeta
 
@@ -45,6 +48,10 @@
         return self._cpu_type
 
     @abstractmethod
+    def get_isa(self) -> ISA:
+        raise NotImplementedError
+
+    @abstractmethod
     def connect_icache(self, port: Port) -> None:
         """
         This function should connect the response port from the instruction
@@ -102,3 +109,77 @@
         This is used in the board to setup system-specific MMU settings.
         """
         raise NotImplementedError
+
+    @classmethod
+    def cpu_simobject_factory(cls, cpu_type: CPUTypes, isa: ISA, core_id: int):
+        """
+        A factory used to return the SimObject core object given the cpu type,
+        and ISA target. An exception will be thrown if there is an
+        incompatibility.
+
+        :param cpu_type: The target CPU type.
+        :param isa: The target ISA.
+        :param core_id: The id of the core to be returned.
+        """
+        requires(isa_required=isa)
+
+        _isa_string_map = {
+            ISA.X86 : "X86",
+            ISA.ARM : "Arm",
+            ISA.RISCV : "Riscv",
+            ISA.SPARC : "Sparc",
+            ISA.POWER : "Power",
+            ISA.MIPS : "Mips",
+        }
+
+        _cpu_types_string_map = {
+            CPUTypes.ATOMIC : "AtomicSimpleCPU",
+            CPUTypes.O3 : "O3CPU",
+            CPUTypes.TIMING : "TimingSimpleCPU",
+            CPUTypes.KVM : "KvmCPU",
+            CPUTypes.MINOR : "MinorCPU",
+        }
+
+        if isa not in _isa_string_map:
+            raise NotImplementedError(f"ISA '{isa.name}' does not have an"
+                "entry in `AbstractCore.cpu_simobject_factory._isa_string_map`"
+            )
+
+        if cpu_type not in _cpu_types_string_map:
+            raise NotImplementedError(f"CPUType '{cpu_type.name}' "
+                "does not have an entry in "
+                "`AbstractCore.cpu_simobject_factory._cpu_types_string_map`"
+            )
+
+        if cpu_type == CPUTypes.KVM:
+            # For some reason, the KVM CPU is under "m5.objects" not the
+            # "m5.objects.{ISA}CPU".
+            module_str = f"m5.objects"
+        else:
+            module_str = f"m5.objects.{_isa_string_map[isa]}CPU"
+
+        # GEM5 compiles two versions of KVM for ARM depending upon the host CPU
+        # : ArmKvmCPU and ArmV8KvmCPU for 32 bit (Armv7l) and 64 bit (Armv8)
+        # respectively.
+
+        if isa.name == "ARM" and \
+                cpu_type == CPUTypes.KVM and \
+                platform.architecture()[0] == "64bit":
+            cpu_class_str = f"{_isa_string_map[isa]}V8"\
+                            f"{_cpu_types_string_map[cpu_type]}"
+        else:
+            cpu_class_str = f"{_isa_string_map[isa]}"\
+                            f"{_cpu_types_string_map[cpu_type]}"
+
+        try:
+            to_return_cls = getattr(importlib.import_module(module_str),
+                                    cpu_class_str
+                                   )
+        except ImportError:
+            raise Exception(
+                f"Cannot find CPU type '{cpu_type.name}' for '{isa.name}' "
+                "ISA. Please ensure you have compiled the correct version of "
+                "gem5."
+            )
+
+        return to_return_cls(cpu_id=core_id)
diff --git a/src/python/gem5/components/processors/abstract_generator_core.py b/src/python/gem5/components/processors/abstract_generator_core.py
index 5caed38..111a229 100644
--- a/src/python/gem5/components/processors/abstract_generator_core.py
+++ b/src/python/gem5/components/processors/abstract_generator_core.py
@@ -30,6 +30,8 @@
 
 from .cpu_types import CPUTypes
 from .abstract_core import AbstractCore
+from ...isas import ISA
+from ...utils.requires import requires
 
 from typing import Optional
 
@@ -52,9 +54,14 @@
         # TODO: Remove the CPU Type parameter. This not needed.
         # Jira issue here: https://gem5.atlassian.net/browse/GEM5-1031
         super().__init__(CPUTypes.TIMING)
+        requires(isa_required=ISA.NULL)
         self.port_end = PortTerminator()
 
     @overrides(AbstractCore)
+    def get_isa(self) -> ISA:
+        return ISA.NULL
+
+    @overrides(AbstractCore)
     def connect_icache(self, port: Port) -> None:
         """
         Generator cores only have one request port which we will connect to
diff --git a/src/python/gem5/components/processors/abstract_processor.py b/src/python/gem5/components/processors/abstract_processor.py
index 07ddcdb..74e7825 100644
--- a/src/python/gem5/components/processors/abstract_processor.py
+++ b/src/python/gem5/components/processors/abstract_processor.py
@@ -25,11 +25,14 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from abc import ABCMeta, abstractmethod
+
+from ...utils.requires import requires
 from .abstract_core import AbstractCore
 
 from m5.objects import SubSystem
 
 from ..boards.abstract_board import AbstractBoard
+from ...isas import ISA
 
 from typing import List
 
@@ -41,6 +44,13 @@
         super().__init__()
         assert len(cores) > 0
 
+        # In the stdlib we assume the system processor conforms to a single
+        # ISA target.
+        assert len(set(core.get_isa() for core in cores)) == 1
+        self._isa = cores[0].get_isa()
+
+        requires(isa_required=self._isa)
+
         self.cores = cores
 
     def get_num_cores(self) -> int:
@@ -49,6 +59,9 @@
     def get_cores(self) -> List[AbstractCore]:
         return self.cores
 
+    def get_isa(self) -> ISA:
+        return self._isa
+
     @abstractmethod
     def incorporate_processor(self, board: AbstractBoard) -> None:
         raise NotImplementedError
diff --git a/src/python/gem5/components/processors/cpu_types.py b/src/python/gem5/components/processors/cpu_types.py
index 6e71dcc..969b7a1 100644
--- a/src/python/gem5/components/processors/cpu_types.py
+++ b/src/python/gem5/components/processors/cpu_types.py
@@ -25,10 +25,43 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from enum import Enum
-
+from typing import Set
+import os
 
 class CPUTypes(Enum):
-    ATOMIC = 1
-    KVM = 2
-    O3 = 3
-    TIMING = 4
+    ATOMIC = "atomic"
+    KVM = "kvm"
+    O3 = "o3"
+    TIMING = "timing"
+    MINOR = "minor"
+
+def get_cpu_types_str_set() -> Set[CPUTypes]:
+    """
+    Returns a set of all the CPU types as strings.
+    """
+    return {cpu_type.value for cpu_type in CPUTypes}
+
+def get_cpu_type_from_str(input: str) -> CPUTypes:
+    """
+    Will return the correct enum given the input string. This is matched on
+    the enum's value. E.g., "kvm" will return ISA.KVM. Throws an exception if
+    the input string is invalid.
+
+    `get_cpu_types_str_set()` can be used to determine the valid strings.
+
+    This is for parsing text inputs that specify CPU Type targets.
+
+    :param input: The CPU Type to return, as a string. Case-insensitive.
+    """
+    for cpu_type in CPUTypes:
+        if input.lower() == cpu_type.value:
+            return cpu_type
+
+    valid_cpu_types_list_str =str()
+    for cpu_type_str in get_cpu_types_str_set():
+        valid_cpu_types_list_str += f"{os.linesep}{cpu_type_str}"
+
+    raise Exception(
+        f"CPU type '{input}' does not correspond to a known CPU type. "
+        f"Known CPU Types:{valid_cpu_types_list_str}"
+    )
diff --git a/src/python/gem5/components/processors/simple_core.py b/src/python/gem5/components/processors/simple_core.py
index 89c78d0..92290a3 100644
--- a/src/python/gem5/components/processors/simple_core.py
+++ b/src/python/gem5/components/processors/simple_core.py
@@ -25,46 +25,51 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from typing import Optional
-from ...runtime import get_runtime_isa
+from python.gem5.utils.requires import requires
 from ..processors.abstract_core import AbstractCore
 
 from .cpu_types import CPUTypes
 from ...isas import ISA
+from ...runtime import get_runtime_isa
 from ...utils.override import overrides
 
 from m5.objects import (
     BaseMMU,
     Port,
-    AtomicSimpleCPU,
-    DerivO3CPU,
-    TimingSimpleCPU,
     BaseCPU,
     Process,
 )
 
 
 class SimpleCore(AbstractCore):
-    def __init__(self, cpu_type: CPUTypes, core_id: int):
+
+    def __init__(
+        self,
+        cpu_type: CPUTypes,
+        core_id: int,
+        isa: Optional[ISA]= None
+    ):
         super().__init__(cpu_type=cpu_type)
-
-        if cpu_type == CPUTypes.ATOMIC:
-            self.core = AtomicSimpleCPU(cpu_id=core_id)
-        elif cpu_type == CPUTypes.O3:
-            self.core = DerivO3CPU(cpu_id=core_id)
-        elif cpu_type == CPUTypes.TIMING:
-            self.core = TimingSimpleCPU(cpu_id=core_id)
-        elif cpu_type == CPUTypes.KVM:
-            from m5.objects import X86KvmCPU
-            self.core = X86KvmCPU(cpu_id=core_id)
+        if isa:
+            requires(isa_required=isa)
+            self._isa = isa
         else:
-            raise NotImplementedError
-
+            self._isa = get_runtime_isa()
+        self.core = AbstractCore.cpu_simobject_factory(
+            isa=self._isa,
+            cpu_type=cpu_type,
+            core_id=core_id
+        )
         self.core.createThreads()
 
     def get_simobject(self) -> BaseCPU:
         return self.core
 
     @overrides(AbstractCore)
+    def get_isa(self) -> ISA:
+        return self._isa
+
+    @overrides(AbstractCore)
     def connect_icache(self, port: Port) -> None:
         self.core.icache_port = port
 
@@ -74,7 +79,18 @@
 
     @overrides(AbstractCore)
     def connect_walker_ports(self, port1: Port, port2: Port) -> None:
-        self.core.mmu.connectWalkerPorts(port1, port2)
+        if self.get_isa() == ISA.ARM:
+
+            # Unlike X86 and RISCV MMU, the ARM MMU has two L1 TLB walker ports
+            # named `walker` and `stage2_walker` for both data and instruction.
+            # The gem5 standard library currently supports one TLB walker port
+            # per cache level. Therefore, we are explicitly setting the walker
+            # ports and not setting the stage2_walker ports for ARM systems.
+
+            self.core.mmu.itb_walker.port = port1
+            self.core.mmu.dtb_walker.port = port2
+        else:
+            self.core.mmu.connectWalkerPorts(port1, port2)
 
     @overrides(AbstractCore)
     def set_workload(self, process: Process) -> None:
@@ -94,7 +110,7 @@
         # controller as we require it. Not sure how true this is in all cases.
         self.core.createInterruptController()
 
-        if get_runtime_isa() == ISA.X86:
+        if self.get_isa() == ISA.X86:
             if interrupt_requestor != None:
                 self.core.interrupts[0].pio = interrupt_requestor
                 self.core.interrupts[0].int_responder = interrupt_requestor
diff --git a/src/python/gem5/components/processors/simple_processor.py b/src/python/gem5/components/processors/simple_processor.py
index 8f32cf1..3c9c5c8 100644
--- a/src/python/gem5/components/processors/simple_processor.py
+++ b/src/python/gem5/components/processors/simple_processor.py
@@ -33,19 +33,39 @@
 
 from .abstract_processor import AbstractProcessor
 from .cpu_types import CPUTypes
+from ...isas import ISA
 from ..boards.abstract_board import AbstractBoard
 
+from typing import Optional
+
 
 class SimpleProcessor(AbstractProcessor):
     """
     A SimpeProcessor contains a number of cores of a a single CPUType.
     """
 
-    def __init__(self, cpu_type: CPUTypes, num_cores: int) -> None:
+    def __init__(
+        self,
+        cpu_type: CPUTypes,
+        num_cores: int,
+        isa: Optional[ISA] = None,
+    ) -> None:
+        """
+        param cpu_type: The CPU type for each type in the processor.
+:
+        :param num_cores: The number of CPU cores in the processor.
+
+        :param isa: The ISA of the processor. This argument is optional. If not
+        set the `runtime.get_runtime_isa` is used to determine the ISA at
+        runtime. **WARNING**: This functionality is deprecated. It is
+        recommended you explicitly set your ISA via SimpleProcessor
+        construction.
+        """
         super().__init__(
             cores=self._create_cores(
                 cpu_type=cpu_type,
                 num_cores=num_cores,
+                isa = isa,
             )
         )
 
@@ -55,9 +75,15 @@
 
             self.kvm_vm = KvmVM()
 
-    def _create_cores(self, cpu_type: CPUTypes, num_cores: int):
+    def _create_cores(
+        self,
+        cpu_type: CPUTypes,
+        num_cores: int,
+        isa: Optional[ISA]
+    ):
         return [
-            SimpleCore(cpu_type=cpu_type, core_id=i) for i in range(num_cores)
+            SimpleCore(cpu_type=cpu_type, core_id=i, isa=isa,) \
+                for i in range(num_cores)
         ]
 
     @overrides(AbstractProcessor)
@@ -66,7 +92,7 @@
             board.kvm_vm = self.kvm_vm
 
         # Set the memory mode.
-        if self._cpu_type == CPUTypes.TIMING or self._cpu_type == CPUTypes.O3:
+        if self._cpu_type in (CPUTypes.TIMING, CPUTypes.O3, CPUTypes.MINOR):
             board.set_mem_mode(MemMode.TIMING)
         elif self._cpu_type == CPUTypes.KVM:
             board.set_mem_mode(MemMode.ATOMIC_NONCACHING)
diff --git a/src/python/gem5/components/processors/simple_switchable_processor.py b/src/python/gem5/components/processors/simple_switchable_processor.py
index f13bb22..52ba013 100644
--- a/src/python/gem5/components/processors/simple_switchable_processor.py
+++ b/src/python/gem5/components/processors/simple_switchable_processor.py
@@ -29,9 +29,12 @@
 from ..processors.simple_core import SimpleCore
 from ..processors.cpu_types import CPUTypes
 from .switchable_processor import SwitchableProcessor
+from ...isas import ISA
 
 from ...utils.override import *
 
+from typing import Optional
+
 
 class SimpleSwitchableProcessor(SwitchableProcessor):
     """
@@ -46,7 +49,21 @@
         starting_core_type: CPUTypes,
         switch_core_type: CPUTypes,
         num_cores: int,
+        isa: Optional[ISA] = None,
     ) -> None:
+        """
+        param starting_core_type: The CPU type for each type in the processor
+        to start with (i.e., when the simulation has just started).
+:
+        :param switch_core_types: The CPU type for each core, to be switched
+        to..
+
+        :param isa: The ISA of the processor. This argument is optional. If not
+        set the `runtime.get_runtime_isa` is used to determine the ISA at
+        runtime. **WARNING**: This functionality is deprecated. It is
+        recommended you explicitly set your ISA via SimpleSwitchableProcessor
+        construction.
+        """
 
         if num_cores <= 0:
             raise AssertionError("Number of cores must be a positive integer!")
@@ -66,11 +83,11 @@
 
         switchable_cores = {
             self._start_key: [
-                SimpleCore(cpu_type=starting_core_type, core_id=i)
+                SimpleCore(cpu_type=starting_core_type, core_id=i, isa=isa)
                 for i in range(num_cores)
             ],
             self._switch_key: [
-                SimpleCore(cpu_type=switch_core_type, core_id=i)
+                SimpleCore(cpu_type=switch_core_type, core_id=i, isa=isa)
                 for i in range(num_cores)
             ],
         }
diff --git a/src/python/gem5/isas.py b/src/python/gem5/isas.py
index 9fb7e96..c8b2a38 100644
--- a/src/python/gem5/isas.py
+++ b/src/python/gem5/isas.py
@@ -28,14 +28,59 @@
 Specifies the ISA enum
 """
 
+import os
 from enum import Enum
-
+from typing import Set
 
 class ISA(Enum):
-    X86 = 1
-    RISCV = 2
-    ARM = 3
-    MIPS = 4
-    POWER = 5
-    SPARC = 6
-    NULL = 7
+    """
+    The ISA Enums which may be used in the gem5 stdlib to specify ISAs.
+
+    Their value may be used to translate the ISA to strings and compare against
+    inputs and environment variables.
+
+    E.g., to check if the X86 ISA is compiled:
+
+    ```
+    if buildEnv[f"USE_{ISA.X86.value}_ISA"]:
+        ...
+    ```
+    """
+    X86 = "x86"
+    RISCV = "riscv"
+    ARM = "arm"
+    MIPS = "mips"
+    POWER = "power"
+    SPARC = "sparc"
+    NULL = "null"
+
+def get_isas_str_set() -> Set[ISA]:
+    """
+    Returns a set of all the ISA as strings.
+    """
+    return {isa.value for isa in ISA}
+
+def get_isa_from_str(input: str) -> ISA:
+    """
+    Will return the correct enum given the input string. This is matched on
+    the enum's value. E.g., "x86" will return ISA.X86. Throws an exception if
+    the input string is invalid.
+
+    `get_isas_str_set()` can be used to determine the valid strings.
+
+    This is for parsing text inputs that specify ISA targets.
+
+    :param input: The ISA to return, as a string. Case-insensitive.
+    """
+    for isa in ISA:
+        if input.lower() == isa.value:
+            return isa
+
+    valid_isas_str_list =str()
+    for isa_str in get_isa_from_str():
+        valid_isas_str_list += f"{os.linesep}{isa_str}"
+
+    raise Exception(
+        f"Value '{input}' does not correspond to a known ISA. Known ISAs:"
+        f"{valid_isas_str_list}"
+    )
diff --git a/src/python/gem5/prebuilt/demo/x86_demo_board.py b/src/python/gem5/prebuilt/demo/x86_demo_board.py
index 7583849..e83fe3a 100644
--- a/src/python/gem5/prebuilt/demo/x86_demo_board.py
+++ b/src/python/gem5/prebuilt/demo/x86_demo_board.py
@@ -75,7 +75,11 @@
              "real-world system. Use with caution.")
 
         memory = SingleChannelDDR3_1600(size="2GB")
-        processor = SimpleProcessor(cpu_type=CPUTypes.TIMING, num_cores=4)
+        processor = SimpleProcessor(
+            cpu_type=CPUTypes.TIMING,
+            isa=ISA.X86,
+            num_cores=4
+        )
         cache_hierarchy = MESITwoLevelCacheHierarchy(
             l1d_size="32kB",
             l1d_assoc=8,
diff --git a/src/python/gem5/resources/downloader.py b/src/python/gem5/resources/downloader.py
index 2a857e5..3481c9d 100644
--- a/src/python/gem5/resources/downloader.py
+++ b/src/python/gem5/resources/downloader.py
@@ -26,14 +26,23 @@
 
 import json
 import urllib.request
+import urllib.parse
 import hashlib
 import os
 import shutil
 import gzip
 import hashlib
 import base64
+import time
+import random
+from pathlib import Path
+import tarfile
+from tempfile import gettempdir
+from urllib.error import HTTPError
 from typing import List, Dict
 
+from .md5_utils import md5_file, md5_dir
+
 from ..utils.filelock import FileLock
 
 """
@@ -45,11 +54,86 @@
     """
     Specifies the version of resources.json to obtain.
     """
-    return "21.2"
+    return "22.0"
 
 def _get_resources_json_uri() -> str:
     return "https://resources.gem5.org/resources.json"
 
+def _url_validator(url):
+    try:
+        result = urllib.parse.urlparse(url)
+        return all([result.scheme, result.netloc, result.path])
+    except:
+        return False
+
+def _get_resources_json_at_path(path: str, use_caching: bool = True) -> Dict:
+    '''
+    Returns a resource JSON, in the form of a Python Dict. The location
+    of the JSON must be specified.
+
+    If `use_caching` is True, and a URL is passed, a copy of the JSON will be
+    cached locally, and used for up to an hour after retrieval.
+
+    :param path: The URL or local path of the JSON file.
+    :param use_caching: True if a cached file is to be used (up to an hour),
+    otherwise the file will be retrieved from the URL regardless. True by
+    default. Only valid in cases where a URL is passed.
+    '''
+
+    # If a local valid path is passed, just load it.
+    if Path(path).is_file():
+        return json.load(open(path))
+
+    # If it's not a local path, it should be a URL. We check this here and
+    # raise an Exception if it's not.
+    if not _url_validator(path):
+        raise Exception(
+            f"Resources location '{path}' is not a valid path or URL."
+        )
+
+    download_path = os.path.join(
+        gettempdir(),
+        f"gem5-resources-{hashlib.md5(path.encode()).hexdigest()}"
+        f"-{str(os.getuid())}.json",
+    )
+
+    # We apply a lock on the resources file for when it's downloaded, or
+    # re-downloaded, and read. This stops a corner-case from occuring where
+    # the file is re-downloaded while being read by another gem5 thread.
+    # Note the timeout is 120 so the `_download` function is given time to run
+    # its Truncated Exponential Backoff algorithm
+    # (maximum of roughly 1 minute). Typically this code will run quickly.
+    with FileLock("{}.lock".format(download_path), timeout=120):
+
+        # The resources.json file can change at any time, but to avoid
+        # excessive retrieval we cache a version locally and use it for up to
+        # an hour before obtaining a fresh copy.
+        #
+        # `time.time()` and `os.path.getmtime(..)` both return an unix epoch
+        # time in seconds. Therefore, the value of "3600" here represents an
+        # hour difference between the two values. `time.time()` gets the
+        # current time, and `os.path.getmtime(<file>)` gets the modification
+        # time of the file. This is the most portable solution as other ideas,
+        # like "file creation time", are  not always the same concept between
+        # operating systems.
+        if not use_caching or not os.path.exists(download_path) or \
+            (time.time() - os.path.getmtime(download_path)) > 3600:
+                    _download(path, download_path)
+
+    with open(download_path) as f:
+        file_contents = f.read()
+
+    try:
+        to_return = json.loads(file_contents)
+    except json.JSONDecodeError:
+        # This is a bit of a hack. If the URL specified exists in a Google
+        # Source repo (which is the case when on the gem5 develop branch) we
+        # retrieve the JSON in base64 format. This cannot be loaded directly as
+        # text. Conversion is therefore needed.
+        to_return = json.loads(base64.b64decode(file_contents).decode("utf-8"))
+
+    return to_return
+
 def _get_resources_json() -> Dict:
     """
     Gets the Resources JSON.
@@ -57,33 +141,17 @@
     :returns: The Resources JSON (as a Python Dictionary).
     """
 
-
-    with urllib.request.urlopen(_get_resources_json_uri()) as url:
-        try:
-            to_return = json.loads(url.read())
-        except json.JSONDecodeError:
-            # This is a bit of a hack. If the URL specified exists in a Google
-            # Source repo (which is the case when on the gem5 develop branch)
-            # we retrieve the JSON in base64 format. This cannot be loaded
-            # directly as text. Conversion is therefore needed.
-            to_return = json.loads(
-                base64.b64decode(url.read()).decode("utf-8")
-            )
+    path = os.getenv("GEM5_RESOURCE_JSON", _get_resources_json_uri())
+    to_return = _get_resources_json_at_path(path = path)
 
     # If the current version pulled is not correct, look up the
     # "previous-versions" field to find the correct one.
     version = _resources_json_version_required()
     if to_return["version"] != version:
         if version in to_return["previous-versions"].keys():
-            with urllib.request.urlopen(
-                    to_return["previous-versions"][version]
-                ) as url:
-                try:
-                    to_return = json.loads(url.read())
-                except json.JSONDecodeError:
-                    to_return = json.loads(
-                        base64.b64decode(url.read()).decode("utf-8")
-                    )
+            to_return = _get_resources_json_at_path(
+                path = to_return["previous-versions"][version]
+            )
         else:
             # This should never happen, but we thrown an exception to explain
             # that we can't find the version.
@@ -149,43 +217,56 @@
 
     return to_return
 
-
-def _get_md5(file: str) -> str:
-    """
-    Gets the md5 of a file.
-
-    :param file: The file needing an md5 value.
-
-    :returns: The md5 of the input file.
-    """
-
-    # Note: This code is slightly more complex than you might expect as
-    # `hashlib.md5(<file>)` returns malloc errors for large files (such as
-    # disk images).
-    md5_object = hashlib.md5()
-    block_size = 128 * md5_object.block_size
-    a_file = open(file, "rb")
-    chunk = a_file.read(block_size)
-
-    while chunk:
-        md5_object.update(chunk)
-        chunk = a_file.read(block_size)
-
-    return md5_object.hexdigest()
-
-
-def _download(url: str, download_to: str) -> None:
+def _download(
+    url: str,
+    download_to: str,
+    max_attempts: int = 6,
+) -> None:
     """
     Downloads a file.
 
+    The function will run a Truncated Exponential Backoff algorithm to retry
+    the download if the HTTP Status Code returned is deemed retryable.
+
     :param url: The URL of the file to download.
 
     :param download_to: The location the downloaded file is to be stored.
+
+    :param max_attempts: The max number of download attempts before stopping.
+    The default is 6. This translates to roughly 1 minute of retrying before
+    stopping.
     """
 
     # TODO: This whole setup will only work for single files we can get via
     # wget. We also need to support git clones going forward.
-    urllib.request.urlretrieve(url, download_to)
+
+
+    attempt = 0
+    while True:
+        # The loop will be broken on a successful download, via a `return`, or
+        # if an exception is raised. An exception will be raised if the maximum
+        # number of download attempts has been reached or if a HTTP status code
+        # other than 408, 429, or 5xx is received.
+        try:
+            urllib.request.urlretrieve(url, download_to)
+            return
+        except HTTPError as e:
+            # If the error code retrieved is retryable, we retry using a
+            # Truncated Exponential backoff algorithm, truncating after
+            # "max_attempts". We consider HTTP status codes 408, 429, and 5xx
+            # as retryable. If any other is retrieved we raise the error.
+            if e.code in (408, 429) or 500 <= e.code < 600:
+                attempt += 1
+                if attempt >= max_attempts:
+                    raise Exception(
+                        f"After {attempt} attempts, the resource json could "
+                        "not be retrieved. HTTP Status Code retrieved: "
+                        f"{e.code}"
+                    )
+                time.sleep((2 ** attempt) + random.uniform(0, 1))
+            else:
+                raise e
+
 
 
 def list_resources() -> List[str]:
@@ -224,6 +305,7 @@
     resource_name: str,
     to_path: str,
     unzip: bool = True,
+    untar: bool = True,
     download_md5_mismatch: bool = True,
 ) -> None:
     """
@@ -238,6 +320,9 @@
     :param unzip: If true, gzipped resources will be unzipped prior to saving
     to `to_path`. True by default.
 
+    :param untar: If true, tar achieve resource will be unpacked prior to
+    saving to `to_path`. True by default.
+
     :param download_md5_mismatch: If a resource is present with an incorrect
     hash (e.g., an outdated version of the resource is present), `get_resource`
     will delete this local resource and re-download it if this parameter is
@@ -259,17 +344,20 @@
 
         if os.path.exists(to_path):
 
-            if not os.path.isfile(to_path):
-                raise Exception(
-                    "There is a directory at '{}'.".format(to_path)
-                )
+            if os.path.isfile(to_path):
+                md5 = md5_file(Path(to_path))
+            else:
+                md5 = md5_dir(Path(to_path))
 
-            if _get_md5(to_path) == resource_json["md5sum"]:
+            if md5 == resource_json["md5sum"]:
                 # In this case, the file has already been download, no need to
                 # do so again.
                 return
             elif download_md5_mismatch:
-                os.remove(to_path)
+                if os.path.isfile(to_path):
+                    os.remove(to_path)
+                else:
+                    shutil.rmtree(to_path)
             else:
                 raise Exception(
                     "There already a file present at '{}' but "
@@ -295,8 +383,16 @@
                 )
             )
 
+        run_tar_extract = untar and "is_tar_archive" in resource_json and \
+                          resource_json["is_tar_archive"]
+
+        tar_extension = ".tar"
+        if run_tar_extract:
+            download_dest += tar_extension
+
+        zip_extension = ".gz"
         if run_unzip:
-            download_dest += ".gz"
+            download_dest += zip_extension
 
         # TODO: Might be nice to have some kind of download status bar here.
         # TODO: There might be a case where this should be silenced.
@@ -320,10 +416,22 @@
                     resource_name, download_dest
                 )
             )
+            unzip_to = download_dest[:-len(zip_extension)]
             with gzip.open(download_dest, "rb") as f:
-                with open(to_path, "wb") as o:
+                with open(unzip_to, "wb") as o:
                     shutil.copyfileobj(f, o)
             os.remove(download_dest)
+            download_dest = unzip_to
             print(
                 "Finished decompressing resource '{}'.".format(resource_name)
             )
+
+        if run_tar_extract:
+            print(
+                f"Unpacking the the resource '{resource_name}' "
+                f"('{download_dest}')"
+            )
+            unpack_to = download_dest[:-len(tar_extension)]
+            with tarfile.open(download_dest) as f:
+                f.extractall(unpack_to)
+            os.remove(download_dest)
diff --git a/src/python/gem5/resources/md5_utils.py b/src/python/gem5/resources/md5_utils.py
new file mode 100644
index 0000000..b98a81e
--- /dev/null
+++ b/src/python/gem5/resources/md5_utils.py
@@ -0,0 +1,80 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from pathlib import Path
+import hashlib
+from _hashlib import HASH as Hash
+
+def _md5_update_from_file(filename:  Path, hash: Hash) -> Hash:
+    assert filename.is_file()
+    with open(str(filename), "rb") as f:
+        for chunk in iter(lambda: f.read(4096), b""):
+            hash.update(chunk)
+    return hash
+
+def _md5_update_from_dir(directory:  Path, hash: Hash) -> Hash:
+    assert directory.is_dir()
+    for path in sorted(directory.iterdir(), key=lambda p: str(p).lower()):
+        hash.update(path.name.encode())
+        if path.is_file():
+            hash = _md5_update_from_file(path, hash)
+        elif path.is_dir():
+            hash = _md5_update_from_dir(path, hash)
+    return hash
+
+def md5(path: Path) -> str:
+    """
+    Gets the md5 value of a file or directory. `md5_file` is used if the path
+    is a file and `md5_dir` is used if the path is a directory. An exception
+    is returned if the path is not a valid file or directory.
+
+    :param path: The path to get the md5 of.
+    """
+    if path.is_file():
+        return md5_file(Path(path))
+    elif path.is_dir():
+        return md5_dir(Path(path))
+    else:
+        raise Exception(f"Path '{path}' is not a valid file or directory.")
+
+def md5_file(filename:  Path) -> str:
+    """
+    Gives the md5 hash of a file
+
+    :filename: The file in which the md5 is to be calculated.
+    """
+    return str(_md5_update_from_file(filename, hashlib.md5()).hexdigest())
+
+def md5_dir(directory: Path) -> str:
+    """
+    Gives the md5 value of a directory.
+
+    This is achieved by getting the md5 hash of all files in the directory.
+
+    Note: The path of files are also hashed so the md5 of the directory changes
+    if empty files are included or filenames are changed.
+    """
+    return str(_md5_update_from_dir(directory, hashlib.md5()).hexdigest())
diff --git a/src/python/gem5/resources/resource.py b/src/python/gem5/resources/resource.py
index f03490b..f72215a 100644
--- a/src/python/gem5/resources/resource.py
+++ b/src/python/gem5/resources/resource.py
@@ -153,7 +153,12 @@
                     )
                 )
         else:
-            os.makedirs(resource_directory)
+            # `exist_ok=True` here as, occasionally, if multiple instance of
+            # gem5 are started simultaneously, a race condition can exist to
+            # create the resource directory. Without `exit_ok=True`, threads
+            # which lose this race will thrown a `FileExistsError` exception.
+            # `exit_ok=True` ensures no exception is thrown.
+            os.makedirs(resource_directory, exist_ok=True)
 
         to_path = os.path.join(resource_directory, resource_name)
 
diff --git a/src/python/gem5/runtime.py b/src/python/gem5/runtime.py
index 9e1f607..623228c 100644
--- a/src/python/gem5/runtime.py
+++ b/src/python/gem5/runtime.py
@@ -29,35 +29,59 @@
 """
 
 from m5.defines import buildEnv
+from m5.util import warn
 
-from .isas import ISA
+from .isas import ISA, get_isa_from_str, get_isas_str_set
 from .coherence_protocol import CoherenceProtocol
+from typing import Set
+
+def get_supported_isas() -> Set[ISA]:
+    """
+    Returns the set of all the ISAs compiled into the current binary.
+    """
+    supported_isas = set()
+
+    if "TARGET_ISA" in buildEnv.keys():
+        supported_isas.add(get_isa_from_str(buildEnv["TARGET_ISA"]))
+
+    for key in get_isas_str_set():
+        if f"USE_{key.upper()}_ISA" in buildEnv:
+            supported_isas.add(get_isa_from_str(key))
+
+    return supported_isas
+
 
 
 def get_runtime_isa() -> ISA:
-    """Gets the target ISA.
-    This can be inferred at runtime.
+    """
+    Returns a single target ISA at runtime.
+
+    This determined via the "TARGET_ISA" parameter, which is set at
+    compilation. If not set, but only one ISA is compiled, we assume it's the
+    one ISA. If neither the "TARGET_ISA" parameter is set and there are
+    multiple ISA targets, an exception is thrown.
+
+    **WARNING**: This function is deprecated and may be removed in future
+    versions of gem5. This function should not be relied upon to run gem5
+    simulations.
 
     :returns: The target ISA.
     """
-    isa_map = {
-        "sparc": ISA.SPARC,
-        "mips": ISA.MIPS,
-        "null": ISA.NULL,
-        "arm": ISA.ARM,
-        "x86": ISA.X86,
-        "power": ISA.POWER,
-        "riscv": ISA.RISCV,
-    }
 
-    isa_str = str(buildEnv["TARGET_ISA"]).lower()
-    if isa_str not in isa_map.keys():
-        raise NotImplementedError(
-            "ISA '" + buildEnv["TARGET_ISA"] + "' not recognized."
-        )
+    warn("The `get_runtime_isa` function is deprecated. Please migrate away "
+         "from using this function.")
 
-    return isa_map[isa_str]
+    if "TARGET_ISA" in buildEnv.keys():
+        return get_isa_from_str(buildEnv["TARGET_ISA"])
 
+    supported_isas = get_supported_isas()
+
+    if len(supported_isas) == 1:
+        return next(iter(supported_isas))
+
+    raise Exception("Cannot determine the the runtime ISA. Either the "
+                    "'TARGET_ISA' parameter must be set or the binary only "
+                    "compiled to one ISA.")
 
 def get_runtime_coherence_protocol() -> CoherenceProtocol:
     """Gets the cache coherence protocol.
diff --git a/src/python/gem5/simulate/simulator.py b/src/python/gem5/simulate/simulator.py
index 1645dfc..9c03589 100644
--- a/src/python/gem5/simulate/simulator.py
+++ b/src/python/gem5/simulate/simulator.py
@@ -32,6 +32,7 @@
 from m5.util import warn
 
 import os
+from pathlib import Path
 from typing import Optional, List, Tuple, Dict, Generator, Union
 
 from .exit_event_generators import (
@@ -71,17 +72,19 @@
     def __init__(
         self,
         board: AbstractBoard,
-        full_system: bool = True,
+        full_system: Optional[bool] = None,
         on_exit_event: Optional[
             Dict[Union[str, ExitEvent], Generator[Optional[bool], None, None]]
         ] = None,
         expected_execution_order: Optional[List[ExitEvent]] = None,
+        checkpoint_path: Optional[Path] = None,
     ) -> None:
         """
         :param board: The board to be simulated.
-        :param full_system: Whether to run in full-system simulation or not. If
-        False, the simulation will run in Syscall-Execution mode. True by
-        default.
+        :param full_system: Whether to run as a full-system simulation or not.
+        This is optional and used to override default behavior. If not set,
+        whether or not to run in FS mode will be determined via the board's
+        `is_fullsystem()` function.
         :param on_exit_event: An optional map to specify the generator to
         execute on each exit event. The generator may yield a boolean which,
         if True, will have the Simulator exit the run loop.
@@ -90,6 +93,9 @@
         encountered (e.g., 'Workbegin', 'Workend', then 'Exit'), an Exception
         is thrown. If this parameter is not specified, any ordering of exit
         events is valid.
+        :param checkpoint_path: An optional parameter specifying the directory
+        of the checkpoint to instantiate from. When the path is None, no
+        checkpoint will be loaded. By default, the path is None.
 
         `on_exit_event` usage notes
         ---------------------------
@@ -177,6 +183,8 @@
         self._last_exit_event = None
         self._exit_event_count = 0
 
+        self._checkpoint_path = checkpoint_path
+
     def get_stats(self) -> Dict:
         """
         Obtain the current simulation statistics as a Dictionary, conforming
@@ -264,7 +272,12 @@
         """
 
         if not self._instantiated:
-            root = Root(full_system=self._full_system, board=self._board)
+            root = Root(
+                full_system=self._full_system
+                if self._full_system is not None
+                else self._board.is_fullsystem(),
+                board=self._board,
+            )
 
             # We take a copy of the Root in case it's required elsewhere
             # (for example, in `get_stats()`).
@@ -277,7 +290,10 @@
                 m5.ticks.fixGlobalFrequency()
                 root.sim_quantum = m5.ticks.fromSeconds(0.001)
 
-            m5.instantiate()
+            # m5.instantiate() takes a parameter specifying the path to the
+            # checkpoint directory. If the parameter is None, no checkpoint
+            # will be restored.
+            m5.instantiate(self._checkpoint_path)
             self._instantiated = True
 
     def run(self, max_ticks: int = m5.MaxTick) -> None:
@@ -347,3 +363,13 @@
             # run loop.
             if exit_on_completion:
                 return
+
+    def save_checkpoint(self, checkpoint_dir: Path) -> None:
+        """
+        This function will save the checkpoint to the specified directory.
+
+        :param checkpoint_dir: The path to the directory where the checkpoint
+        will be saved.
+        """
+        m5.checkpoint(str(checkpoint_dir))
+
diff --git a/src/python/gem5/utils/requires.py b/src/python/gem5/utils/requires.py
index 7789254..f4322ca 100644
--- a/src/python/gem5/utils/requires.py
+++ b/src/python/gem5/utils/requires.py
@@ -24,7 +24,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-from ..runtime import get_runtime_coherence_protocol, get_runtime_isa
+from ..runtime import get_runtime_coherence_protocol, get_supported_isas
 from ..isas import ISA
 from ..coherence_protocol import CoherenceProtocol
 from typing import Optional
@@ -59,7 +59,7 @@
     Ensures the ISA/Coherence protocol/KVM requirements are met. An exception
     will be raise if they are not.
 
-    :param isa_required: The ISA gem5 must be compiled to.
+    :param isa_required: The ISA(s) gem5 must be compiled to.
     :param coherence_protocol_required: The coherence protocol gem5 must be
         compiled to.
     :param kvm_required: The host system must have the Kernel-based Virtual
@@ -68,18 +68,37 @@
         protocol do not match that of the current gem5 binary.
     """
 
-    runtime_isa = get_runtime_isa()
+    supported_isas = get_supported_isas()
     runtime_coherence_protocol = get_runtime_coherence_protocol()
     kvm_available = os.access("/dev/kvm", mode=os.R_OK | os.W_OK)
 
-    if isa_required != None and isa_required.value != runtime_isa.value:
-        raise Exception(
-            _get_exception_str(
-                msg="The current ISA is '{}'. Required: '{}'".format(
-                    runtime_isa.name, isa_required.name
-                )
-            )
-        )
+    # Note, previously I had the following code here:
+    #
+    # `if isa_required != None and isa_required not in supported_isas:`
+    #
+    # However, for reasons I do not currently understand, I frequently
+    # encountered errors such as the following:
+    #
+    # ```
+    # Exception: The required ISA is 'RISCV'. Supported ISAs:
+    # SPARC
+    # RISCV
+    # ARM
+    # X86
+    # POWER
+    # MIPS
+    # ```
+    #
+    # I do not know why this happens and my various attempts at tracking down
+    # why the enum did not compare correctly yielded no results. The following
+    # code works, even though it is verbose and appears functionally equivalent
+    # to the original code.
+    if isa_required != None and isa_required.value not in \
+        (isa.value for isa in supported_isas):
+        msg=f"The required ISA is '{isa_required.name}'. Supported ISAs: "
+        for isa in supported_isas:
+            msg += f"{os.linesep}{isa.name}"
+        raise Exception(_get_exception_str(msg=msg))
 
     if (
         coherence_protocol_required != None
diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py
index b07d90f..26147a1 100644
--- a/src/python/m5/SimObject.py
+++ b/src/python/m5/SimObject.py
@@ -116,281 +116,6 @@
                isinstance(value, (FunctionType, MethodType, ModuleType,
                                   classmethod, type))
 
-def createCxxConfigDirectoryEntryFile(code, name, simobj, is_header):
-    entry_class = 'CxxConfigDirectoryEntry_%s' % name
-    param_class = '%sCxxConfigParams' % name
-
-    code('#include "params/%s.hh"' % name)
-
-    if not is_header:
-        for param in simobj._params.values():
-            if isSimObjectClass(param.ptype):
-                code('#include "%s"' % param.ptype._value_dict['cxx_header'])
-                code('#include "params/%s.hh"' % param.ptype.__name__)
-            else:
-                param.ptype.cxx_ini_predecls(code)
-
-    if is_header:
-        member_prefix = ''
-        end_of_decl = ';'
-        code('#include "sim/cxx_config.hh"')
-        code()
-        code('namespace gem5')
-        code('{')
-        code()
-        code('class ${param_class} : public CxxConfigParams,'
-            ' public ${name}Params')
-        code('{')
-        code('  private:')
-        code.indent()
-        code('class DirectoryEntry : public CxxConfigDirectoryEntry')
-        code('{')
-        code('  public:')
-        code.indent()
-        code('DirectoryEntry();');
-        code()
-        code('CxxConfigParams *makeParamsObject() const')
-        code('{ return new ${param_class}; }')
-        code.dedent()
-        code('};')
-        code()
-        code.dedent()
-        code('  public:')
-        code.indent()
-    else:
-        member_prefix = '%s::' % param_class
-        end_of_decl = ''
-        code('#include "%s"' % simobj._value_dict['cxx_header'])
-        code('#include "base/str.hh"')
-        code('#include "cxx_config/${name}.hh"')
-
-        code('namespace gem5')
-        code('{')
-        code()
-        code('${member_prefix}DirectoryEntry::DirectoryEntry()');
-        code('{')
-
-        def cxx_bool(b):
-            return 'true' if b else 'false'
-
-        code.indent()
-        for param in simobj._params.values():
-            is_vector = isinstance(param, m5.params.VectorParamDesc)
-            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
-
-            code('parameters["%s"] = new ParamDesc("%s", %s, %s);' %
-                (param.name, param.name, cxx_bool(is_vector),
-                cxx_bool(is_simobj)));
-
-        for port in simobj._ports.values():
-            is_vector = isinstance(port, m5.params.VectorPort)
-            is_requestor = port.role == 'GEM5 REQUESTOR'
-
-            code('ports["%s"] = new PortDesc("%s", %s, %s);' %
-                (port.name, port.name, cxx_bool(is_vector),
-                cxx_bool(is_requestor)))
-
-        code.dedent()
-        code('}')
-        code()
-
-    code('bool ${member_prefix}setSimObject(const std::string &name,')
-    code('    SimObject *simObject)${end_of_decl}')
-
-    if not is_header:
-        code('{')
-        code.indent()
-        code('bool ret = true;')
-        code()
-        code('if (false) {')
-        for param in simobj._params.values():
-            is_vector = isinstance(param, m5.params.VectorParamDesc)
-            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
-
-            if is_simobj and not is_vector:
-                code('} else if (name == "${{param.name}}") {')
-                code.indent()
-                code('this->${{param.name}} = '
-                    'dynamic_cast<${{param.ptype.cxx_type}}>(simObject);')
-                code('if (simObject && !this->${{param.name}})')
-                code('   ret = false;')
-                code.dedent()
-        code('} else {')
-        code('    ret = false;')
-        code('}')
-        code()
-        code('return ret;')
-        code.dedent()
-        code('}')
-
-    code()
-    code('bool ${member_prefix}setSimObjectVector('
-        'const std::string &name,')
-    code('    const std::vector<SimObject *> &simObjects)${end_of_decl}')
-
-    if not is_header:
-        code('{')
-        code.indent()
-        code('bool ret = true;')
-        code()
-        code('if (false) {')
-        for param in simobj._params.values():
-            is_vector = isinstance(param, m5.params.VectorParamDesc)
-            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
-
-            if is_simobj and is_vector:
-                code('} else if (name == "${{param.name}}") {')
-                code.indent()
-                code('this->${{param.name}}.clear();')
-                code('for (auto i = simObjects.begin(); '
-                    'ret && i != simObjects.end(); i ++)')
-                code('{')
-                code.indent()
-                code('${{param.ptype.cxx_type}} object = '
-                    'dynamic_cast<${{param.ptype.cxx_type}}>(*i);')
-                code('if (*i && !object)')
-                code('    ret = false;')
-                code('else')
-                code('    this->${{param.name}}.push_back(object);')
-                code.dedent()
-                code('}')
-                code.dedent()
-        code('} else {')
-        code('    ret = false;')
-        code('}')
-        code()
-        code('return ret;')
-        code.dedent()
-        code('}')
-
-    code()
-    code('void ${member_prefix}setName(const std::string &name_)'
-        '${end_of_decl}')
-
-    if not is_header:
-        code('{')
-        code.indent()
-        code('this->name = name_;')
-        code.dedent()
-        code('}')
-
-    if is_header:
-        code('const std::string &${member_prefix}getName()')
-        code('{ return this->name; }')
-
-    code()
-    code('bool ${member_prefix}setParam(const std::string &name,')
-    code('    const std::string &value, const Flags flags)${end_of_decl}')
-
-    if not is_header:
-        code('{')
-        code.indent()
-        code('bool ret = true;')
-        code()
-        code('if (false) {')
-        for param in simobj._params.values():
-            is_vector = isinstance(param, m5.params.VectorParamDesc)
-            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
-
-            if not is_simobj and not is_vector:
-                code('} else if (name == "${{param.name}}") {')
-                code.indent()
-                param.ptype.cxx_ini_parse(code,
-                    'value', 'this->%s' % param.name, 'ret =')
-                code.dedent()
-        code('} else {')
-        code('    ret = false;')
-        code('}')
-        code()
-        code('return ret;')
-        code.dedent()
-        code('}')
-
-    code()
-    code('bool ${member_prefix}setParamVector('
-        'const std::string &name,')
-    code('    const std::vector<std::string> &values,')
-    code('    const Flags flags)${end_of_decl}')
-
-    if not is_header:
-        code('{')
-        code.indent()
-        code('bool ret = true;')
-        code()
-        code('if (false) {')
-        for param in simobj._params.values():
-            is_vector = isinstance(param, m5.params.VectorParamDesc)
-            is_simobj = issubclass(param.ptype, m5.SimObject.SimObject)
-
-            if not is_simobj and is_vector:
-                code('} else if (name == "${{param.name}}") {')
-                code.indent()
-                code('${{param.name}}.clear();')
-                code('for (auto i = values.begin(); '
-                    'ret && i != values.end(); i ++)')
-                code('{')
-                code.indent()
-                code('${{param.ptype.cxx_type}} elem;')
-                param.ptype.cxx_ini_parse(code,
-                    '*i', 'elem', 'ret =')
-                code('if (ret)')
-                code('    this->${{param.name}}.push_back(elem);')
-                code.dedent()
-                code('}')
-                code.dedent()
-        code('} else {')
-        code('    ret = false;')
-        code('}')
-        code()
-        code('return ret;')
-        code.dedent()
-        code('}')
-
-    code()
-    code('bool ${member_prefix}setPortConnectionCount('
-        'const std::string &name,')
-    code('    unsigned int count)${end_of_decl}')
-
-    if not is_header:
-        code('{')
-        code.indent()
-        code('bool ret = true;')
-        code()
-        code('if (false)')
-        code('    ;')
-        for port in simobj._ports.values():
-            code('else if (name == "${{port.name}}")')
-            code('    this->port_${{port.name}}_connection_count = count;')
-        code('else')
-        code('    ret = false;')
-        code()
-        code('return ret;')
-        code.dedent()
-        code('}')
-
-    code()
-    code('SimObject *${member_prefix}simObjectCreate()${end_of_decl}')
-
-    if not is_header:
-        code('{')
-        if hasattr(simobj, 'abstract') and simobj.abstract:
-            code('    return NULL;')
-        else:
-            code('    return this->create();')
-        code('}')
-
-    if is_header:
-        code()
-        code('static CxxConfigDirectoryEntry'
-            ' *${member_prefix}makeDirectoryEntry()')
-        code('{ return new DirectoryEntry; }')
-
-    if is_header:
-        code.dedent()
-        code('};')
-
-    code('} // namespace gem5')
-
 # The metaclass for SimObject.  This class controls how new classes
 # that derive from SimObject are instantiated, and provides inherited
 # class behavior (just like a class controls how instances of that
@@ -708,366 +433,6 @@
     def pybind_predecls(cls, code):
         code('#include "${{cls.cxx_header}}"')
 
-    def params_create_decl(cls, code, python_enabled):
-        py_class_name = cls.pybind_class
-
-        # The 'local' attribute restricts us to the params declared in
-        # the object itself, not including inherited params (which
-        # will also be inherited from the base class's param struct
-        # here). Sort the params based on their key
-        params = list(map(lambda k_v: k_v[1],
-                          sorted(cls._params.local.items())))
-        ports = cls._ports.local
-
-        # only include pybind if python is enabled in the build
-        if python_enabled:
-
-            code('''#include "pybind11/pybind11.h"
-#include "pybind11/stl.h"
-
-#include <type_traits>
-
-#include "base/compiler.hh"
-#include "params/$cls.hh"
-#include "sim/init.hh"
-#include "sim/sim_object.hh"
-
-#include "${{cls.cxx_header}}"
-
-''')
-        else:
-            code('''
-#include <type_traits>
-
-#include "base/compiler.hh"
-#include "params/$cls.hh"
-
-#include "${{cls.cxx_header}}"
-
-''')
-        # only include the python params code if python is enabled.
-        if python_enabled:
-            for param in params:
-                param.pybind_predecls(code)
-
-            code('''namespace py = pybind11;
-
-namespace gem5
-{
-
-static void
-module_init(py::module_ &m_internal)
-{
-    py::module_ m = m_internal.def_submodule("param_${cls}");
-''')
-            code.indent()
-            if cls._base:
-                code('py::class_<${cls}Params, ${{cls._base.type}}Params, ' \
-                    'std::unique_ptr<${{cls}}Params, py::nodelete>>(' \
-                    'm, "${cls}Params")')
-            else:
-                code('py::class_<${cls}Params, ' \
-                    'std::unique_ptr<${cls}Params, py::nodelete>>(' \
-                    'm, "${cls}Params")')
-
-            code.indent()
-            if not hasattr(cls, 'abstract') or not cls.abstract:
-                code('.def(py::init<>())')
-                code('.def("create", &${cls}Params::create)')
-
-            param_exports = cls.cxx_param_exports + [
-                PyBindProperty(k)
-                for k, v in sorted(cls._params.local.items())
-            ] + [
-                PyBindProperty("port_%s_connection_count" % port.name)
-                for port in ports.values()
-            ]
-            for exp in param_exports:
-                exp.export(code, "%sParams" % cls)
-
-            code(';')
-            code()
-            code.dedent()
-
-            bases = []
-            if 'cxx_base' in cls._value_dict:
-                # If the c++ base class implied by python inheritance was
-                # overridden, use that value.
-                if cls.cxx_base:
-                    bases.append(cls.cxx_base)
-            elif cls._base:
-                # If not and if there was a SimObject base, use its c++ class
-                # as this class' base.
-                bases.append(cls._base.cxx_class)
-            # Add in any extra bases that were requested.
-            bases.extend(cls.cxx_extra_bases)
-
-            if bases:
-                base_str = ", ".join(bases)
-                code('py::class_<${{cls.cxx_class}}, ${base_str}, ' \
-                    'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
-                    'm, "${py_class_name}")')
-            else:
-                code('py::class_<${{cls.cxx_class}}, ' \
-                    'std::unique_ptr<${{cls.cxx_class}}, py::nodelete>>(' \
-                    'm, "${py_class_name}")')
-            code.indent()
-            for exp in cls.cxx_exports:
-                exp.export(code, cls.cxx_class)
-            code(';')
-            code.dedent()
-            code()
-            code.dedent()
-            code('}')
-            code()
-            code('static EmbeddedPyBind '
-                 'embed_obj("${0}", module_init, "${1}");',
-                cls, cls._base.type if cls._base else "")
-            code()
-            code('} // namespace gem5')
-
-        # include the create() methods whether or not python is enabled.
-        if not hasattr(cls, 'abstract') or not cls.abstract:
-            if 'type' in cls.__dict__:
-                code()
-                code('namespace gem5')
-                code('{')
-                code()
-                code('namespace')
-                code('{')
-                code()
-                # If we can't define a default create() method for this params
-                # struct because the SimObject doesn't have the right
-                # constructor, use template magic to make it so we're actually
-                # defining a create method for this class instead.
-                code('class Dummy${cls}ParamsClass')
-                code('{')
-                code('  public:')
-                code('    ${{cls.cxx_class}} *create() const;')
-                code('};')
-                code()
-                code('template <class CxxClass, class Enable=void>')
-                code('class Dummy${cls}Shunt;')
-                code()
-                # This version directs to the real Params struct and the
-                # default behavior of create if there's an appropriate
-                # constructor.
-                code('template <class CxxClass>')
-                code('class Dummy${cls}Shunt<CxxClass, std::enable_if_t<')
-                code('    std::is_constructible_v<CxxClass,')
-                code('        const ${cls}Params &>>>')
-                code('{')
-                code('  public:')
-                code('    using Params = ${cls}Params;')
-                code('    static ${{cls.cxx_class}} *')
-                code('    create(const Params &p)')
-                code('    {')
-                code('        return new CxxClass(p);')
-                code('    }')
-                code('};')
-                code()
-                # This version diverts to the DummyParamsClass and a dummy
-                # implementation of create if the appropriate constructor does
-                # not exist.
-                code('template <class CxxClass>')
-                code('class Dummy${cls}Shunt<CxxClass, std::enable_if_t<')
-                code('    !std::is_constructible_v<CxxClass,')
-                code('        const ${cls}Params &>>>')
-                code('{')
-                code('  public:')
-                code('    using Params = Dummy${cls}ParamsClass;')
-                code('    static ${{cls.cxx_class}} *')
-                code('    create(const Params &p)')
-                code('    {')
-                code('        return nullptr;')
-                code('    }')
-                code('};')
-                code()
-                code('} // anonymous namespace')
-                code()
-                # An implementation of either the real Params struct's create
-                # method, or the Dummy one. Either an implementation is
-                # mandantory since this was shunted off to the dummy class, or
-                # one is optional which will override this weak version.
-                code('[[maybe_unused]] ${{cls.cxx_class}} *')
-                code('Dummy${cls}Shunt<${{cls.cxx_class}}>::Params::create() '
-                     'const')
-                code('{')
-                code('    return Dummy${cls}Shunt<${{cls.cxx_class}}>::')
-                code('        create(*this);')
-                code('}')
-                code()
-                code('} // namespace gem5')
-
-    _warned_about_nested_templates = False
-
-    # Generate the C++ declaration (.hh file) for this SimObject's
-    # param struct.  Called from src/SConscript.
-    def cxx_param_decl(cls, code):
-        # The 'local' attribute restricts us to the params declared in
-        # the object itself, not including inherited params (which
-        # will also be inherited from the base class's param struct
-        # here). Sort the params based on their key
-        params = list(map(lambda k_v: k_v[1], sorted(cls._params.local.items())))
-        ports = cls._ports.local
-        try:
-            ptypes = [p.ptype for p in params]
-        except:
-            print(cls, p, p.ptype_str)
-            print(params)
-            raise
-
-        class CxxClass(object):
-            def __init__(self, sig, template_params=[]):
-                # Split the signature into its constituent parts. This could
-                # potentially be done with regular expressions, but
-                # it's simple enough to pick appart a class signature
-                # manually.
-                parts = sig.split('<', 1)
-                base = parts[0]
-                t_args = []
-                if len(parts) > 1:
-                    # The signature had template arguments.
-                    text = parts[1].rstrip(' \t\n>')
-                    arg = ''
-                    # Keep track of nesting to avoid splitting on ","s embedded
-                    # in the arguments themselves.
-                    depth = 0
-                    for c in text:
-                        if c == '<':
-                            depth = depth + 1
-                            if depth > 0 and not \
-                                    self._warned_about_nested_templates:
-                                self._warned_about_nested_templates = True
-                                print('Nested template argument in cxx_class.'
-                                      ' This feature is largely untested and '
-                                      ' may not work.')
-                        elif c == '>':
-                            depth = depth - 1
-                        elif c == ',' and depth == 0:
-                            t_args.append(arg.strip())
-                            arg = ''
-                        else:
-                            arg = arg + c
-                    if arg:
-                        t_args.append(arg.strip())
-                # Split the non-template part on :: boundaries.
-                class_path = base.split('::')
-
-                # The namespaces are everything except the last part of the
-                # class path.
-                self.namespaces = class_path[:-1]
-                # And the class name is the last part.
-                self.name = class_path[-1]
-
-                self.template_params = template_params
-                self.template_arguments = []
-                # Iterate through the template arguments and their values. This
-                # will likely break if parameter packs are used.
-                for arg, param in zip(t_args, template_params):
-                    type_keys = ('class', 'typename')
-                    # If a parameter is a type, parse it recursively. Otherwise
-                    # assume it's a constant, and store it verbatim.
-                    if any(param.strip().startswith(kw) for kw in type_keys):
-                        self.template_arguments.append(CxxClass(arg))
-                    else:
-                        self.template_arguments.append(arg)
-
-            def declare(self, code):
-                # First declare any template argument types.
-                for arg in self.template_arguments:
-                    if isinstance(arg, CxxClass):
-                        arg.declare(code)
-                # Re-open the target namespace.
-                for ns in self.namespaces:
-                    code('namespace $ns {')
-                # If this is a class template...
-                if self.template_params:
-                    code('template <${{", ".join(self.template_params)}}>')
-                # The actual class declaration.
-                code('class ${{self.name}};')
-                # Close the target namespaces.
-                for ns in reversed(self.namespaces):
-                    code('} // namespace $ns')
-
-        code('''\
-#ifndef __PARAMS__${cls}__
-#define __PARAMS__${cls}__
-
-''')
-
-
-        # The base SimObject has a couple of params that get
-        # automatically set from Python without being declared through
-        # the normal Param mechanism; we slip them in here (needed
-        # predecls now, actual declarations below)
-        if cls == SimObject:
-            code('''#include <string>''')
-
-        cxx_class = CxxClass(cls._value_dict['cxx_class'],
-                             cls._value_dict['cxx_template_params'])
-
-        # A forward class declaration is sufficient since we are just
-        # declaring a pointer.
-        cxx_class.declare(code)
-
-        for param in params:
-            param.cxx_predecls(code)
-        for port in ports.values():
-            port.cxx_predecls(code)
-        code()
-
-        if cls._base:
-            code('#include "params/${{cls._base.type}}.hh"')
-            code()
-
-        for ptype in ptypes:
-            if issubclass(ptype, Enum):
-                code('#include "enums/${{ptype.__name__}}.hh"')
-                code()
-
-        code('namespace gem5')
-        code('{')
-        code('')
-
-        # now generate the actual param struct
-        code("struct ${cls}Params")
-        if cls._base:
-            code("    : public ${{cls._base.type}}Params")
-        code("{")
-        if not hasattr(cls, 'abstract') or not cls.abstract:
-            if 'type' in cls.__dict__:
-                code("    ${{cls.cxx_type}} create() const;")
-
-        code.indent()
-        if cls == SimObject:
-            code('''
-    SimObjectParams() {}
-    virtual ~SimObjectParams() {}
-
-    std::string name;
-            ''')
-
-        for param in params:
-            param.cxx_decl(code)
-        for port in ports.values():
-            port.cxx_decl(code)
-
-        code.dedent()
-        code('};')
-        code()
-        code('} // namespace gem5')
-
-        code()
-        code('#endif // __PARAMS__${cls}__')
-        return code
-
-    # Generate the C++ declaration/definition files for this SimObject's
-    # param struct to allow C++ initialisation
-    def cxx_config_param_file(cls, code, is_header):
-        createCxxConfigDirectoryEntryFile(code, cls.__name__, cls, is_header)
-        return code
-
 # This *temporary* definition is required to support calls from the
 # SimObject class definition to the MetaSimObject methods (in
 # particular _set_param, which gets called for parameters with default
@@ -1810,6 +1175,8 @@
 
     # Call C++ to create C++ object corresponding to this object
     def createCCObject(self):
+        if self.abstract:
+            fatal(f"Cannot instantiate an abstract SimObject ({self.path()})")
         self.getCCParams()
         self.getCCObject() # force creation
 
diff --git a/src/python/m5/main.py b/src/python/m5/main.py
index f649e77..b216840 100644
--- a/src/python/m5/main.py
+++ b/src/python/m5/main.py
@@ -61,7 +61,6 @@
 
 
 def parse_options():
-    from . import config
     from .options import OptionParser
 
     options = OptionParser(usage=usage, description=brief_copyright)
@@ -155,13 +154,6 @@
     option("--list-sim-objects", action='store_true', default=False,
         help="List all built-in SimObjects, their params and default values")
 
-    # load the options.py config file to allow people to set their own
-    # default options
-    options_file = config.get('options.py')
-    if options_file:
-        scope = { 'options' : options }
-        exec(compile(open(options_file).read(), options_file, 'exec'), scope)
-
     arguments = options.parse_args()
     return options,arguments
 
@@ -318,7 +310,7 @@
 
     verbose = options.verbose - options.quiet
     if verbose >= 0:
-        print("gem5 Simulator System.  http://gem5.org")
+        print("gem5 Simulator System.  https://www.gem5.org")
         print(brief_copyright)
         print()
 
diff --git a/src/python/m5/params.py b/src/python/m5/params.py
index 39137c5..57a3d3e 100644
--- a/src/python/m5/params.py
+++ b/src/python/m5/params.py
@@ -1318,146 +1318,6 @@
 
         super().__init__(name, bases, init_dict)
 
-    # Generate C++ class declaration for this enum type.
-    # Note that we wrap the enum in a class/struct to act as a namespace,
-    # so that the enum strings can be brief w/o worrying about collisions.
-    def cxx_decl(cls, code):
-        wrapper_name = cls.wrapper_name
-        wrapper = 'struct' if cls.wrapper_is_struct else 'namespace'
-        name = cls.__name__ if cls.enum_name is None else cls.enum_name
-        idem_macro = '__ENUM__%s__%s__' % (wrapper_name, name)
-
-        code('''\
-#ifndef $idem_macro
-#define $idem_macro
-
-namespace gem5
-{
-''')
-        if cls.is_class:
-            code('''\
-enum class $name
-{
-''')
-        else:
-            code('''\
-$wrapper $wrapper_name {
-    enum $name
-    {
-''')
-            code.indent(1)
-        code.indent(1)
-        for val in cls.vals:
-            code('$val = ${{cls.map[val]}},')
-        code('Num_$name = ${{len(cls.vals)}}')
-        code.dedent(1)
-        code('};')
-
-        if cls.is_class:
-            code('''\
-extern const char *${name}Strings[static_cast<int>(${name}::Num_${name})];
-''')
-        elif cls.wrapper_is_struct:
-            code('static const char *${name}Strings[Num_${name}];')
-        else:
-            code('extern const char *${name}Strings[Num_${name}];')
-
-        if not cls.is_class:
-            code.dedent(1)
-            code('}; // $wrapper_name')
-
-        code()
-        code('} // namespace gem5')
-
-        code()
-        code('#endif // $idem_macro')
-
-    def cxx_def(cls, code):
-        wrapper_name = cls.wrapper_name
-        file_name = cls.__name__
-        name = cls.__name__ if cls.enum_name is None else cls.enum_name
-
-        code('#include "base/compiler.hh"')
-        code('#include "enums/$file_name.hh"')
-
-        code()
-        code('namespace gem5')
-        code('{')
-        code()
-
-        if cls.wrapper_is_struct:
-            code('const char *${wrapper_name}::${name}Strings'
-                '[Num_${name}] =')
-        else:
-            if cls.is_class:
-                code('''\
-const char *${name}Strings[static_cast<int>(${name}::Num_${name})] =
-''')
-            else:
-                code('''GEM5_DEPRECATED_NAMESPACE(Enums, enums);
-namespace enums
-{''')
-                code.indent(1)
-                code('const char *${name}Strings[Num_${name}] =')
-
-        code('{')
-        code.indent(1)
-        for val in cls.vals:
-            code('"$val",')
-        code.dedent(1)
-        code('};')
-
-        if not cls.wrapper_is_struct and not cls.is_class:
-            code.dedent(1)
-            code('} // namespace enums')
-
-        code('} // namespace gem5')
-
-
-    def pybind_def(cls, code):
-        name = cls.__name__
-        enum_name = cls.__name__ if cls.enum_name is None else cls.enum_name
-        wrapper_name = enum_name if cls.is_class else cls.wrapper_name
-
-        code('''#include "pybind11/pybind11.h"
-#include "pybind11/stl.h"
-
-#include <sim/init.hh>
-
-namespace py = pybind11;
-
-namespace gem5
-{
-
-static void
-module_init(py::module_ &m_internal)
-{
-    py::module_ m = m_internal.def_submodule("enum_${name}");
-
-''')
-        if cls.is_class:
-            code('py::enum_<${enum_name}>(m, "enum_${name}")')
-        else:
-            code('py::enum_<${wrapper_name}::${enum_name}>(m, "enum_${name}")')
-
-        code.indent()
-        code.indent()
-        for val in cls.vals:
-            code('.value("${val}", ${wrapper_name}::${val})')
-        code('.value("Num_${name}", ${wrapper_name}::Num_${enum_name})')
-        if not cls.is_class:
-            code('.export_values()')
-        code(';')
-        code.dedent()
-
-        code('}')
-        code.dedent()
-        code()
-        code('static EmbeddedPyBind embed_enum("enum_${name}", module_init);')
-        code()
-        code('} // namespace gem5')
-
-
 # Base class for enum types.
 class Enum(ParamValue, metaclass=MetaEnum):
     vals = []
diff --git a/src/python/m5/stats/gem5stats.py b/src/python/m5/stats/gem5stats.py
index 9a2259a..3b4bc7e 100644
--- a/src/python/m5/stats/gem5stats.py
+++ b/src/python/m5/stats/gem5stats.py
@@ -234,17 +234,21 @@
         _prepare_stats(child)
 
 
-def get_simstat(root: Union[Root, List[SimObject]],
+def get_simstat(root: Union[SimObject, List[SimObject]],
                 prepare_stats: bool = True) -> SimStat:
     """
-    This function will return the SimStat object for a simulation. From the
-    SimStat object all stats within the current gem5 simulation are present.
+    This function will return the SimStat object for a simulation given a
+    SimObject (typically a Root SimObject), or list of SimObjects. The returned
+    SimStat object will contain all the stats for all the SimObjects contained
+    within the "root", inclusive of the "root" SimObject/SimObjects.
 
     Parameters
     ----------
-    root: Union[Root, List[Root]]
-        The root, or a list of Simobjects, of the simulation for translation to
-        a SimStat object.
+    root: Union[SimObject, List[SimObject]]
+        A SimObject, or list of SimObjects, of the simulation for translation
+        into a SimStat object. Typically this is the simulation's Root
+        SimObject as this will obtain the entirety of a run's statistics in a
+        single SimStat object.
 
     prepare_stats: bool
         Dictates whether the stats are to be prepared prior to creating the
@@ -269,6 +273,8 @@
 
     for r in root:
         if isinstance(r, Root):
+            # The Root is a special case, we jump directly into adding its
+            # constituent Groups.
             if prepare_stats:
                 _prepare_stats(r)
             for key in r.getStatGroups():
@@ -276,12 +282,11 @@
         elif isinstance(r, SimObject):
             if prepare_stats:
                 _prepare_stats(r)
-            stats_map[r.name] = get_stats_group(r)
+            stats_map[r.get_name()] = get_stats_group(r)
         else:
-            raise TypeError("Object (" + str(r) + ") passed is neither Root "
-                            "nor SimObject. " + __name__ + " only processes "
-                            "Roots, SimObjects, or a list of Roots and/or "
-                            "SimObjects.")
+            raise TypeError("Object (" + str(r) + ") passed is not a "
+                            "SimObject. " + __name__ + " only processes "
+                            "SimObjects, or a list of  SimObjects.")
 
 
 
diff --git a/src/python/m5/util/dot_writer_ruby.py b/src/python/m5/util/dot_writer_ruby.py
index 4123cac..1794cef 100644
--- a/src/python/m5/util/dot_writer_ruby.py
+++ b/src/python/m5/util/dot_writer_ruby.py
@@ -133,17 +133,17 @@
 
 
 def do_ruby_dot(root, outdir, dotFilename):
-    if not pydot:
+    RubyNetwork = getattr(m5.objects, 'RubyNetwork', None)
+
+    if not pydot or not RubyNetwork:
         return
 
-    # Generate a graph for all ruby systems
-    networks = []
-    for obj in root.descendants():
-        if isinstance(obj, m5.objects.RubyNetwork):
-            networks.append(obj)
+    # Generate a graph for all ruby networks.
+    def is_ruby_network(obj):
+        return isinstance(obj, RubyNetwork)
 
-    for network in networks:
-        # We assume each ruby system has a single network
+    for network in filter(is_ruby_network, root.descendants()):
+        # We assume each ruby system has a single network.
         rubydotFilename = dotFilename.replace(".dot",
                                 "." + network.get_parent().path() + ".dot")
         _do_dot(network, outdir, rubydotFilename)
diff --git a/src/sim/SConscript b/src/sim/SConscript
index 8bf5f5d..969a3e4 100644
--- a/src/sim/SConscript
+++ b/src/sim/SConscript
@@ -45,6 +45,7 @@
 
 Source('async.cc')
 Source('backtrace_%s.cc' % env['BACKTRACE_IMPL'], add_tags='gem5 trace')
+Source('bufval.cc')
 Source('core.cc')
 Source('cur_tick.cc', add_tags='gem5 trace')
 Source('tags.cc')
@@ -52,10 +53,11 @@
 Source('cxx_manager.cc')
 Source('cxx_config_ini.cc')
 Source('debug.cc')
+Source('drain.cc', add_tags='gem5 drain')
 Source('py_interact.cc', add_tags='python')
-Source('eventq.cc')
+Source('eventq.cc', add_tags='gem5 events')
 Source('futex_map.cc')
-Source('global_event.cc')
+Source('global_event.cc', add_tags='gem5 drain')
 Source('globals.cc')
 Source('init.cc', add_tags='python')
 Source('init_signals.cc')
@@ -66,9 +68,8 @@
 Source('redirect_path.cc')
 Source('root.cc')
 Source('serialize.cc', add_tags='gem5 serialize')
-Source('drain.cc')
 Source('se_workload.cc')
-Source('sim_events.cc')
+Source('sim_events.cc', add_tags='gem5 drain')
 Source('sim_object.cc')
 Source('sub_system.cc')
 Source('ticked_object.cc')
@@ -89,16 +90,21 @@
 Source('workload.cc')
 Source('mem_pool.cc')
 
+env.TagImplies('gem5 drain', ['gem5 events', 'gem5 trace'])
+env.TagImplies('gem5 events', ['gem5 serialize', 'gem5 trace'])
 env.TagImplies('gem5 serialize', 'gem5 trace')
 
+GTest('bufval.test', 'bufval.test.cc', 'bufval.cc')
 GTest('byteswap.test', 'byteswap.test.cc', '../base/types.cc')
+GTest('globals.test', 'globals.test.cc', 'globals.cc',
+    with_tag('gem5 serialize'))
 GTest('guest_abi.test', 'guest_abi.test.cc')
 GTest('port.test', 'port.test.cc', 'port.cc')
 GTest('proxy_ptr.test', 'proxy_ptr.test.cc')
 GTest('serialize.test', 'serialize.test.cc', with_tag('gem5 serialize'))
 GTest('serialize_handlers.test', 'serialize_handlers.test.cc')
 
-if env['TARGET_ISA'] != 'null':
+if env['CONF']['TARGET_ISA'] != 'null':
     SimObject('InstTracer.py', sim_objects=['InstTracer'])
     SimObject('Process.py', sim_objects=['Process', 'EmulatedDriver'])
     Source('faults.cc')
@@ -116,7 +122,6 @@
 DebugFlag('CxxConfig')
 DebugFlag('Drain')
 DebugFlag('Event')
-DebugFlag('Fault')
 DebugFlag('Flow')
 DebugFlag('IPI')
 DebugFlag('IPR')
diff --git a/src/sim/System.py b/src/sim/System.py
index 596e25c..b5bd5df 100644
--- a/src/sim/System.py
+++ b/src/sim/System.py
@@ -38,7 +38,6 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from m5.SimObject import *
-from m5.defines import buildEnv
 from m5.params import *
 from m5.proxy import *
 
@@ -88,6 +87,9 @@
     shared_backstore = Param.String("", "backstore's shmem segment filename, "
         "use to directly address the backstore from another host-OS process. "
         "Leave this empty to unset the MAP_SHARED flag.")
+    auto_unlink_shared_backstore = Param.Bool(False, "Automatically remove the "
+        "shmem segment file upon destruction. This is used only if "
+        "shared_backstore is non-empty.")
 
     cache_line_size = Param.Unsigned(64, "Cache line size in bytes")
 
@@ -124,10 +126,5 @@
 
     # SE mode doesn't use the ISA System subclasses, and so we need to set an
     # ISA specific value in this class directly.
-    m5ops_base = Param.Addr(
-        0xffff0000 if buildEnv['TARGET_ISA'] == 'x86' else 0,
-        "Base of the 64KiB PA range used for memory-mapped m5ops. Set to 0 "
-        "to disable.")
-
-    if buildEnv['USE_KVM']:
-        kvm_vm = Param.KvmVM(NULL, 'KVM VM (i.e., shared memory domain)')
+    m5ops_base = Param.Addr(0, "Base of the 64KiB PA range used for "
+       "memory-mapped m5ops. Set to 0 to disable.")
diff --git a/src/sim/bufval.cc b/src/sim/bufval.cc
new file mode 100644
index 0000000..48248d1
--- /dev/null
+++ b/src/sim/bufval.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2022 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "sim/bufval.hh"
+
+#include <cassert>
+#include <sstream>
+
+#include "base/intmath.hh"
+#include "base/logging.hh"
+#include "sim/byteswap.hh"
+
+namespace gem5
+{
+
+std::pair<std::uint64_t, bool>
+getUintX(const void *buf, std::size_t bytes, ByteOrder endian)
+{
+    assert(buf);
+    switch (bytes) {
+      case sizeof(std::uint64_t):
+        return {gtoh(*(const std::uint64_t *)buf, endian), true};
+      case sizeof(std::uint32_t):
+        return {gtoh(*(const std::uint32_t *)buf, endian), true};
+      case sizeof(std::uint16_t):
+        return {gtoh(*(const std::uint16_t *)buf, endian), true};
+      case sizeof(std::uint8_t):
+        return {gtoh(*(const std::uint8_t *)buf, endian), true};
+      default:
+        return {0, false};
+    }
+}
+
+bool
+setUintX(std::uint64_t val, void *buf, std::size_t bytes, ByteOrder endian)
+{
+    assert(buf);
+
+    switch (bytes) {
+      case sizeof(std::uint64_t):
+        *(std::uint64_t *)buf = htog<std::uint64_t>(val, endian);
+        return true;
+      case sizeof(std::uint32_t):
+        *(std::uint32_t *)buf = htog<std::uint32_t>(val, endian);
+        return true;
+      case sizeof(std::uint16_t):
+        *(std::uint16_t *)buf = htog<std::uint16_t>(val, endian);
+        return true;
+      case sizeof(std::uint8_t):
+        *(std::uint8_t *)buf = htog<std::uint8_t>(val, endian);
+        return true;
+      default:
+        return false;
+    }
+}
+
+std::pair<std::string, bool>
+printUintX(const void *buf, std::size_t bytes, ByteOrder endian)
+{
+    auto [val, success] = getUintX(buf, bytes, endian);
+    if (!success)
+        return {"", false};
+
+    std::ostringstream out;
+    out << "0x";
+    out.width(2 * bytes);
+    out.fill('0');
+    out.setf(std::ios::hex, std::ios::basefield);
+    out << val;
+
+    return {out.str(), true};
+}
+
+std::string
+printByteBuf(const void *buf, std::size_t bytes, ByteOrder endian,
+        std::size_t chunk_size)
+{
+    assert(buf);
+
+    std::ostringstream out;
+    out << "[";
+
+    out.width(2);
+    out.fill('0');
+    out.setf(std::ios::hex, std::ios::basefield);
+
+    // Bytes that fall outside of a complete chunk. Will always be MSBs.
+    size_t extra = bytes % chunk_size;
+
+    const uint8_t *ptr = (const uint8_t *)buf;
+    int step = 1;
+
+    if (endian == ByteOrder::big) {
+        step = -1;
+        ptr = ptr + bytes - 1;
+
+        // If there's an incomplete chunk, start with that.
+        if (extra) {
+            bytes -= extra;
+            while (extra--) {
+                out.width(2);
+                out << (unsigned)*ptr;
+                ptr += step;
+            }
+            if (bytes)
+                out << " ";
+        }
+    }
+
+    // Print all the complete chunks.
+    while (bytes >= chunk_size) {
+        for (int i = 0; i < chunk_size; i++) {
+            out.width(2);
+            out << (unsigned)*ptr;
+            ptr += step;
+        }
+        bytes -= chunk_size;
+        if (bytes)
+            out << " ";
+    }
+
+    // Print any trailing leftovers. Only happens for little endian.
+    while (bytes--) {
+        out.width(2);
+        out << (unsigned)*ptr;
+        ptr += step;
+    }
+
+    out.width(0);
+    out << "]";
+
+    return out.str();
+}
+
+} // namespace gem5
diff --git a/src/sim/bufval.hh b/src/sim/bufval.hh
new file mode 100644
index 0000000..cf8ead4
--- /dev/null
+++ b/src/sim/bufval.hh
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SIM_BUFVAL_HH__
+#define __SIM_BUFVAL_HH__
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#include "enums/ByteOrder.hh"
+
+namespace gem5
+{
+
+// Extract an integer with a given endianness from a variably sized buffer.
+// Returns the value extraced (if any) and a bool indicating success.
+std::pair<std::uint64_t, bool> getUintX(const void *buf, std::size_t bytes,
+        ByteOrder endian);
+
+// Set a variably sized buffer to an integer value with a given endianness.
+// Returns whether the assignment was successful.
+bool setUintX(std::uint64_t val, void *buf, std::size_t bytes,
+        ByteOrder endian);
+
+// Print an integer with a given endianness into a string from a variably
+// sized buffer. Returns the string (if any) and a bool indicating success.
+std::pair<std::string, bool> printUintX(const void *buf, std::size_t bytes,
+        ByteOrder endian);
+
+// Print a buffer as "chunk_size" sized groups of bytes. The endianness
+// determines if the bytes are output in memory order (little) or inverse of
+// memory order (big). The default is in memory order so that this acts like
+// a hexdump type utility. The return value is a string holding the printed
+// bytes.
+std::string printByteBuf(const void *buf, std::size_t bytes,
+        ByteOrder endian=ByteOrder::little, std::size_t chunk_size=4);
+
+} // namespace gem5
+
+#endif // __SIM_BUFVAL_HH__
diff --git a/src/sim/bufval.test.cc b/src/sim/bufval.test.cc
new file mode 100644
index 0000000..e5a0635
--- /dev/null
+++ b/src/sim/bufval.test.cc
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2022 Google Inc
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <array>
+
+#include "sim/bufval.hh"
+
+using namespace gem5;
+
+using testing::ElementsAre;
+
+template <class First>
+bool
+pairFail(const std::pair<First, bool> &p)
+{
+    return !p.second;
+}
+
+bool
+pairFailU64(const std::pair<std::uint64_t, bool> &p)
+{
+    return pairFail(p);
+}
+bool
+pairFailStr(const std::pair<std::string, bool> &p)
+{
+    return pairFail(p);
+}
+
+template <class First>
+bool
+pairVal(const std::pair<First, bool> &p, const First &expected)
+{
+    auto &[first, success] = p;
+    return success && (first == expected);
+}
+
+bool
+pairValU64(const std::pair<std::uint64_t, bool> &p, std::uint64_t expected)
+{
+    return pairVal(p, expected);
+}
+
+bool
+pairValStr(const std::pair<std::string, bool> &p, const std::string &expected)
+{
+    return pairVal(p, expected);
+}
+
+TEST(GetUintX, BadSize)
+{
+    uint8_t dummy{};
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 0, ByteOrder::little));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 0, ByteOrder::big));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 3, ByteOrder::little));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 3, ByteOrder::big));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 5, ByteOrder::little));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 5, ByteOrder::big));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 6, ByteOrder::little));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 6, ByteOrder::big));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 7, ByteOrder::little));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 7, ByteOrder::big));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 9, ByteOrder::little));
+    EXPECT_PRED1(pairFailU64, getUintX(&dummy, 9, ByteOrder::big));
+}
+
+TEST(GetUintX, LittleEndian)
+{
+    const std::array<uint8_t, 9> buf =
+        {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9};
+    EXPECT_PRED2(pairValU64, getUintX(buf.data(), 1, ByteOrder::little),
+            0x01);
+    EXPECT_PRED2(pairValU64, getUintX(buf.data(), 2, ByteOrder::little),
+            0x0201);
+    EXPECT_PRED2(pairValU64, getUintX(buf.data(), 4, ByteOrder::little),
+            0x04030201);
+    EXPECT_PRED2(pairValU64, getUintX(buf.data(), 8, ByteOrder::little),
+            0x0807060504030201);
+}
+
+TEST(GetUintX, BigEndian)
+{
+    const std::array<uint8_t, 9> buf =
+        {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9};
+    EXPECT_PRED2(pairValU64, getUintX(buf.data(), 1, ByteOrder::big),
+            0x01);
+    EXPECT_PRED2(pairValU64, getUintX(buf.data(), 2, ByteOrder::big),
+            0x0102);
+    EXPECT_PRED2(pairValU64, getUintX(buf.data(), 4, ByteOrder::big),
+            0x01020304);
+    EXPECT_PRED2(pairValU64, getUintX(buf.data(), 8, ByteOrder::big),
+            0x0102030405060708);
+}
+
+TEST(SetUintX, BadSize)
+{
+    uint8_t dummy{};
+    EXPECT_FALSE(setUintX(0, &dummy, 0, ByteOrder::little));
+    EXPECT_FALSE(setUintX(0, &dummy, 0, ByteOrder::big));
+    EXPECT_FALSE(setUintX(0, &dummy, 3, ByteOrder::little));
+    EXPECT_FALSE(setUintX(0, &dummy, 3, ByteOrder::big));
+    EXPECT_FALSE(setUintX(0, &dummy, 5, ByteOrder::little));
+    EXPECT_FALSE(setUintX(0, &dummy, 5, ByteOrder::big));
+    EXPECT_FALSE(setUintX(0, &dummy, 6, ByteOrder::little));
+    EXPECT_FALSE(setUintX(0, &dummy, 6, ByteOrder::big));
+    EXPECT_FALSE(setUintX(0, &dummy, 7, ByteOrder::little));
+    EXPECT_FALSE(setUintX(0, &dummy, 7, ByteOrder::big));
+    EXPECT_FALSE(setUintX(0, &dummy, 9, ByteOrder::little));
+    EXPECT_FALSE(setUintX(0, &dummy, 9, ByteOrder::big));
+}
+
+TEST(SetUintX, LittleEndian)
+{
+    const std::array<uint8_t, 9> orig_buf =
+        {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9};
+    auto buf = orig_buf;
+
+    EXPECT_TRUE(setUintX(0xf1, buf.data(), 1, ByteOrder::little));
+    EXPECT_THAT(buf,
+            ElementsAre(0xf1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9));
+    EXPECT_TRUE(setUintX(0xe2e1, buf.data(), 2, ByteOrder::little));
+    EXPECT_THAT(buf,
+            ElementsAre(0xe1, 0xe2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9));
+    EXPECT_TRUE(setUintX(0xd4d3d2d1, buf.data(), 4, ByteOrder::little));
+    EXPECT_THAT(buf,
+            ElementsAre(0xd1, 0xd2, 0xd3, 0xd4, 0x5, 0x6, 0x7, 0x8, 0x9));
+    EXPECT_TRUE(setUintX(0xc8c7c6c5c4c3c2c1, buf.data(), 8,
+                ByteOrder::little));
+    EXPECT_THAT(buf,
+            ElementsAre(0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0x9));
+}
+
+TEST(SetUintX, BigEndian)
+{
+    const std::array<uint8_t, 9> orig_buf =
+        {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9};
+    auto buf = orig_buf;
+
+    EXPECT_TRUE(setUintX(0xf1, buf.data(), 1, ByteOrder::big));
+    EXPECT_THAT(buf,
+            ElementsAre(0xf1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9));
+    EXPECT_TRUE(setUintX(0xe1e2, buf.data(), 2, ByteOrder::big));
+    EXPECT_THAT(buf,
+            ElementsAre(0xe1, 0xe2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9));
+    EXPECT_TRUE(setUintX(0xd1d2d3d4, buf.data(), 4, ByteOrder::big));
+    EXPECT_THAT(buf,
+            ElementsAre(0xd1, 0xd2, 0xd3, 0xd4, 0x5, 0x6, 0x7, 0x8, 0x9));
+    EXPECT_TRUE(setUintX(0xc1c2c3c4c5c6c7c8, buf.data(), 8, ByteOrder::big));
+    EXPECT_THAT(buf,
+            ElementsAre(0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0x9));
+}
+
+TEST(PrintUintX, BadSize)
+{
+    uint8_t dummy{};
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 0, ByteOrder::little));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 0, ByteOrder::big));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 3, ByteOrder::little));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 3, ByteOrder::big));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 5, ByteOrder::little));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 5, ByteOrder::big));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 6, ByteOrder::little));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 6, ByteOrder::big));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 7, ByteOrder::little));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 7, ByteOrder::big));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 9, ByteOrder::little));
+    EXPECT_PRED1(pairFailStr, printUintX(&dummy, 9, ByteOrder::big));
+}
+
+TEST(PrintUintX, LittleEndian)
+{
+    const std::array<uint8_t, 9> buf =
+        {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9};
+    EXPECT_PRED2(pairValStr, printUintX(buf.data(), 1, ByteOrder::little),
+            "0x01");
+    EXPECT_PRED2(pairValStr, printUintX(buf.data(), 2, ByteOrder::little),
+            "0x0201");
+    EXPECT_PRED2(pairValStr, printUintX(buf.data(), 4, ByteOrder::little),
+            "0x04030201");
+    EXPECT_PRED2(pairValStr, printUintX(buf.data(), 8, ByteOrder::little),
+            "0x0807060504030201");
+}
+
+TEST(PrintUintX, BigEndian)
+{
+    const std::array<uint8_t, 9> buf =
+        {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9};
+    EXPECT_PRED2(pairValStr, printUintX(buf.data(), 1, ByteOrder::big),
+            "0x01");
+    EXPECT_PRED2(pairValStr, printUintX(buf.data(), 2, ByteOrder::big),
+            "0x0102");
+    EXPECT_PRED2(pairValStr, printUintX(buf.data(), 4, ByteOrder::big),
+            "0x01020304");
+    EXPECT_PRED2(pairValStr, printUintX(buf.data(), 8, ByteOrder::big),
+            "0x0102030405060708");
+}
+
+TEST(PrintByteBuf, LittleEndian)
+{
+    const std::array<uint8_t, 9> buf =
+        {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa};
+
+    EXPECT_EQ(printByteBuf(buf.data(), 0, ByteOrder::little, 3), "[]");
+    EXPECT_EQ(printByteBuf(buf.data(), 1, ByteOrder::little, 3), "[01]");
+    EXPECT_EQ(printByteBuf(buf.data(), 2, ByteOrder::little, 3), "[0102]");
+    EXPECT_EQ(printByteBuf(buf.data(), 3, ByteOrder::little, 3),
+            "[010203]");
+    EXPECT_EQ(printByteBuf(buf.data(), 4, ByteOrder::little, 3),
+            "[010203 04]");
+    EXPECT_EQ(printByteBuf(buf.data(), 5, ByteOrder::little, 3),
+            "[010203 0405]");
+    EXPECT_EQ(printByteBuf(buf.data(), 6, ByteOrder::little, 3),
+            "[010203 040506]");
+    EXPECT_EQ(printByteBuf(buf.data(), 7, ByteOrder::little, 3),
+            "[010203 040506 07]");
+    EXPECT_EQ(printByteBuf(buf.data(), 8, ByteOrder::little, 3),
+            "[010203 040506 0708]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 3),
+            "[010203 040506 07080a]");
+}
+
+TEST(PrintByteBuf, BigEndian)
+{
+    const std::array<uint8_t, 9> buf =
+        {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa};
+
+    EXPECT_EQ(printByteBuf(buf.data(), 0, ByteOrder::big, 3), "[]");
+    EXPECT_EQ(printByteBuf(buf.data(), 1, ByteOrder::big, 3), "[01]");
+    EXPECT_EQ(printByteBuf(buf.data(), 2, ByteOrder::big, 3), "[0201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 3, ByteOrder::big, 3), "[030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 4, ByteOrder::big, 3),
+            "[04 030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 5, ByteOrder::big, 3),
+            "[0504 030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 6, ByteOrder::big, 3),
+            "[060504 030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 7, ByteOrder::big, 3),
+            "[07 060504 030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 8, ByteOrder::big, 3),
+            "[0807 060504 030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 3),
+            "[0a0807 060504 030201]");
+}
+
+TEST(PrintByteBuf, ChunkSize)
+{
+    const std::array<uint8_t, 9> buf =
+        {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa};
+
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 1),
+            "[01 02 03 04 05 06 07 08 0a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 2),
+            "[0102 0304 0506 0708 0a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 3),
+            "[010203 040506 07080a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 4),
+            "[01020304 05060708 0a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 5),
+            "[0102030405 0607080a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 6),
+            "[010203040506 07080a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 7),
+            "[01020304050607 080a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 8),
+            "[0102030405060708 0a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 9),
+            "[01020304050607080a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 10),
+            "[01020304050607080a]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::little, 100),
+            "[01020304050607080a]");
+
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 1),
+            "[0a 08 07 06 05 04 03 02 01]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 2),
+            "[0a 0807 0605 0403 0201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 3),
+            "[0a0807 060504 030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 4),
+            "[0a 08070605 04030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 5),
+            "[0a080706 0504030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 6),
+            "[0a0807 060504030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 7),
+            "[0a08 07060504030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 8),
+            "[0a 0807060504030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 9),
+            "[0a0807060504030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 10),
+            "[0a0807060504030201]");
+    EXPECT_EQ(printByteBuf(buf.data(), 9, ByteOrder::big, 100),
+            "[0a0807060504030201]");
+}
diff --git a/src/sim/cxx_config.cc b/src/sim/cxx_config.cc
index dc25402..2f14ae9 100644
--- a/src/sim/cxx_config.cc
+++ b/src/sim/cxx_config.cc
@@ -43,6 +43,11 @@
 const std::string CxxConfigParams::invalidName = "<invalid>";
 
 /** Directory of all SimObject classes config details */
-std::map<std::string, CxxConfigDirectoryEntry *> cxx_config_directory;
+std::map<std::string, CxxConfigDirectoryEntry *> &
+cxxConfigDirectory()
+{
+    static std::map<std::string, CxxConfigDirectoryEntry *> dir;
+    return dir;
+}
 
 } // namespace gem5
diff --git a/src/sim/cxx_config.hh b/src/sim/cxx_config.hh
index 081860a..ba34919 100644
--- a/src/sim/cxx_config.hh
+++ b/src/sim/cxx_config.hh
@@ -119,6 +119,9 @@
     virtual ~CxxConfigDirectoryEntry() { }
 };
 
+/** Directory of all SimObject classes config details */
+std::map<std::string, CxxConfigDirectoryEntry *> &cxxConfigDirectory();
+
 /** Base for peer classes of SimObjectParams derived classes with parameter
  *  modifying member functions. C++ configuration will offer objects of
  *  these classes to SimObjects as params rather than SimObjectParams
@@ -128,6 +131,18 @@
   private:
     static const std::string invalidName;
 
+  protected:
+    struct AddToConfigDir
+    {
+        AddToConfigDir(const std::string &name, CxxConfigDirectoryEntry *entry)
+        {
+            auto it_success = cxxConfigDirectory().insert({name, entry});
+            panic_if(!it_success.second,
+                    "Failed to insert config directory entry %s (duplicate?).",
+                    name);
+        }
+    };
+
   public:
     /** Flags passable to setParam... to smooth over any parsing difference
      *  between different config files */
@@ -229,14 +244,6 @@
     virtual CxxConfigParams::Flags getFlags() const { return 0; }
 };
 
-/** Directory of all SimObject classes config details */
-extern std::map<std::string, CxxConfigDirectoryEntry *>
-    cxx_config_directory;
-
-/** Initialise cxx_config_directory.  This is defined in the
- *  auto-generated .../cxx_config/init.cc */
-void cxxConfigInit();
-
 } // namespace gem5
 
 #endif // __SIM_CXX_CONFIG_HH__
diff --git a/src/sim/cxx_manager.cc b/src/sim/cxx_manager.cc
index 9c25091..609b939 100644
--- a/src/sim/cxx_manager.cc
+++ b/src/sim/cxx_manager.cc
@@ -65,14 +65,14 @@
     if (!configFile.getParam(object_name, "type", object_type))
         throw Exception(object_name, "Sim object has no 'type' field");
 
-    if (cxx_config_directory.find(object_type) ==
-        cxx_config_directory.end())
+    if (cxxConfigDirectory().find(object_type) ==
+        cxxConfigDirectory().end())
     {
         throw Exception(object_name, csprintf(
             "No sim object type %s is available", object_type));
     }
 
-    const CxxConfigDirectoryEntry *entry = cxx_config_directory[object_type];
+    const CxxConfigDirectoryEntry *entry = cxxConfigDirectory()[object_type];
 
     return *entry;
 }
diff --git a/src/sim/faults.cc b/src/sim/faults.cc
index 115c0ed..c0a7d76 100644
--- a/src/sim/faults.cc
+++ b/src/sim/faults.cc
@@ -46,7 +46,7 @@
 #include "base/logging.hh"
 #include "cpu/base.hh"
 #include "cpu/thread_context.hh"
-#include "debug/Fault.hh"
+#include "debug/Faults.hh"
 #include "mem/page_table.hh"
 #include "sim/full_system.hh"
 #include "sim/process.hh"
@@ -59,7 +59,7 @@
 {
     panic_if(!FullSystem, "fault (%s) detected @ PC %s",
              name(), tc->pcState());
-    DPRINTF(Fault, "Fault %s at PC: %s\n", name(), tc->pcState());
+    DPRINTF(Faults, "Fault %s at PC: %s\n", name(), tc->pcState());
 }
 
 void
diff --git a/src/sim/futex_map.cc b/src/sim/futex_map.cc
index 8f622e2..b28e255 100644
--- a/src/sim/futex_map.cc
+++ b/src/sim/futex_map.cc
@@ -123,8 +123,8 @@
 
         if (waiter.checkMask(bitmask)) {
             waiter.tc->activate();
-            iter = waiterList.erase(iter);
             waitingTcs.erase(waiter.tc);
+            iter = waiterList.erase(iter);
             woken_up++;
         } else {
             ++iter;
diff --git a/src/sim/globals.test.cc b/src/sim/globals.test.cc
new file mode 100644
index 0000000..8900c19
--- /dev/null
+++ b/src/sim/globals.test.cc
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2022 Daniel R. Carvalho
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest-spi.h>
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include "base/gtest/cur_tick_fake.hh"
+#include "base/gtest/logging.hh"
+#include "base/gtest/serialization_fixture.hh"
+#include "sim/globals.hh"
+
+// The version tags are declared as extern
+namespace gem5
+{
+std::set<std::string> version_tags;
+} // namespace gem5
+
+using namespace gem5;
+
+// Use the tick handled to manipulate the current tick
+GTestTickHandler tickHandler;
+
+using GlobalsSerializationFixture = SerializationFixture;
+using GlobalsSerializationFixtureDeathTest = GlobalsSerializationFixture;
+
+/** Test serialization. */
+TEST_F(GlobalsSerializationFixture, Serialization)
+{
+    Globals globals;
+    tickHandler.setCurTick(1234);
+    version_tags = { "first-tag", "second-tag", "third-tag", "fourth-tag" };
+
+    // Serialization
+    std::ofstream cp(getCptPath());
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+    globals.serialize(cp);
+
+    // The checkpoint must be flushed, otherwise the file may not be up-
+    // to-date and the assertions below will fail
+    cp.close();
+
+    // Verify the output
+    std::ifstream is(getCptPath());
+    assert(is.good());
+    std::string str = std::string(std::istreambuf_iterator<char>(is),
+        std::istreambuf_iterator<char>());
+    ASSERT_THAT(str, ::testing::StrEq("\n[Section1]\ncurTick=1234\n"
+        "version_tags=first-tag fourth-tag second-tag third-tag\n"));
+}
+
+/** Test unserialization. */
+TEST_F(GlobalsSerializationFixture, Unserialization)
+{
+    version_tags = { "first-tag-un", "second-tag-un", "third-tag-un",
+        "fourth-tag-un" };
+    simulateSerialization("\n[Section1]\ncurTick=1111\nversion_tags="
+        "first-tag-un second-tag-un third-tag-un fourth-tag-un\n");
+
+    Globals globals;
+    CheckpointIn cp(getDirName());
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+
+    gtestLogOutput.str("");
+    globals.unserialize(cp);
+    ASSERT_THAT(gtestLogOutput.str(), ::testing::StrEq(""));
+    ASSERT_EQ(globals.unserializedCurTick, 1111);
+}
+
+/**
+ * Test that unserialization fails when there are no version tags in the
+ * checkpoint.
+ */
+TEST_F(GlobalsSerializationFixture, UnserializationCptNoVersionTags)
+{
+    version_tags = {};
+    simulateSerialization("\n[Section1]\ncurTick=2222\n");
+
+    // Unserialization
+    Globals globals;
+    CheckpointIn cp(getDirName());
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+
+    gtestLogOutput.str("");
+    globals.unserialize(cp);
+    ASSERT_THAT(gtestLogOutput.str(),
+        ::testing::HasSubstr("Checkpoint uses an old versioning scheme."));
+    ASSERT_EQ(globals.unserializedCurTick, 2222);
+}
+
+/** Test that a warning is thrown when the cpt misses any of gem5's tags. */
+TEST_F(GlobalsSerializationFixture, UnserializationCptMissingVersionTags)
+{
+    version_tags = { "first-tag-un", "second-tag-un", "third-tag-un",
+        "fourth-tag-un" };
+    simulateSerialization("\n[Section1]\ncurTick=3333\n"
+        "version_tags=second-tag-un fourth-tag-un\n");
+
+    Globals globals;
+    CheckpointIn cp(getDirName());
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+
+    gtestLogOutput.str("");
+    globals.unserialize(cp);
+    ASSERT_THAT(gtestLogOutput.str(), ::testing::HasSubstr(
+        "warn:   first-tag-un\nwarn:   third-tag-un\n"));
+    ASSERT_EQ(globals.unserializedCurTick, 3333);
+}
+
+/** Test that a warning is thrown when gem5 misses any of the cpt's tags. */
+TEST_F(GlobalsSerializationFixture, UnserializationGem5MissingVersionTags)
+{
+    version_tags = { "first-tag-un", "second-tag-un", "third-tag-un" };
+    simulateSerialization("\n[Section1]\ncurTick=4444\nversion_tags="
+        "first-tag-un second-tag-un third-tag-un fourth-tag-un\n");
+
+    Globals globals;
+    CheckpointIn cp(getDirName());
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+
+    gtestLogOutput.str("");
+    globals.unserialize(cp);
+    ASSERT_THAT(gtestLogOutput.str(),
+        ::testing::HasSubstr("warn:   fourth-tag-un\n"));
+    ASSERT_EQ(globals.unserializedCurTick, 4444);
+}
+
+/**
+ * Test that unserialization fails when there are is no cur tick in the
+ * checkpoint.
+ */
+TEST_F(GlobalsSerializationFixtureDeathTest, UnserializationCptNoCurTick)
+{
+    simulateSerialization("\n[Section1]\n");
+
+    Globals globals;
+    CheckpointIn cp(getDirName());
+    Serializable::ScopedCheckpointSection scs(cp, "Section1");
+    ASSERT_ANY_THROW(globals.unserialize(cp));
+}
diff --git a/src/sim/insttracer.hh b/src/sim/insttracer.hh
index b99af06..6389d0f 100644
--- a/src/sim/insttracer.hh
+++ b/src/sim/insttracer.hh
@@ -44,9 +44,9 @@
 #include <memory>
 
 #include "arch/generic/pcstate.hh"
-#include "arch/generic/vec_pred_reg.hh"
-#include "arch/generic/vec_reg.hh"
+#include "arch/vecregs.hh"
 #include "base/types.hh"
+#include "config/the_isa.hh"
 #include "cpu/inst_seq.hh"
 #include "cpu/static_inst.hh"
 #include "sim/sim_object.hh"
@@ -83,9 +83,9 @@
      * Memory request information in the instruction accessed memory.
      * @see mem_valid
      */
-    Addr addr; ///< The address that was accessed
-    Addr size; ///< The size of the memory request
-    unsigned flags; ///< The flags that were assigned to the request.
+    Addr addr = 0; ///< The address that was accessed
+    Addr size = 0; ///< The size of the memory request
+    unsigned flags = 0; ///< The flags that were assigned to the request.
 
     /** @} */
 
@@ -103,19 +103,19 @@
         double as_double;
         TheISA::VecRegContainer* as_vec;
         TheISA::VecPredRegContainer* as_pred;
-    } data;
+    } data = {0};
 
     /** @defgroup fetch_seq
      * This records the serial number that the instruction was fetched in.
      * @see fetch_seq_valid
      */
-    InstSeqNum fetch_seq;
+    InstSeqNum fetch_seq = 0;
 
     /** @defgroup commit_seq
      * This records the instruction number that was committed in the pipeline
      * @see cp_seq_valid
      */
-    InstSeqNum cp_seq;
+    InstSeqNum cp_seq = 0;
 
     /** @ingroup data
      * What size of data was written?
@@ -130,42 +130,39 @@
         DataDouble = 3,
         DataVec = 5,
         DataVecPred = 6
-    } data_status;
+    } data_status = DataInvalid;
 
     /** @ingroup memory
      * Are the memory fields in the record valid?
      */
-    bool mem_valid;
+    bool mem_valid = false;
 
     /** @ingroup fetch_seq
      * Are the fetch sequence number fields valid?
      */
-    bool fetch_seq_valid;
+    bool fetch_seq_valid = false;
     /** @ingroup commit_seq
      * Are the commit sequence number fields valid?
      */
-    bool cp_seq_valid;
+    bool cp_seq_valid = false;
 
     /** is the predicate for execution this inst true or false (not execed)?
      */
-    bool predicate;
+    bool predicate = true;
 
     /**
      * Did the execution of this instruction fault? (requires ExecFaulting
      * to be enabled)
      */
-    bool faulting;
+    bool faulting = false;
 
   public:
     InstRecord(Tick _when, ThreadContext *_thread,
                const StaticInstPtr _staticInst, const PCStateBase &_pc,
                const StaticInstPtr _macroStaticInst=nullptr)
         : when(_when), thread(_thread), staticInst(_staticInst),
-        pc(_pc.clone()), macroStaticInst(_macroStaticInst), addr(0), size(0),
-        flags(0), fetch_seq(0), cp_seq(0), data_status(DataInvalid),
-        mem_valid(false), fetch_seq_valid(false), cp_seq_valid(false),
-        predicate(true), faulting(false)
-    { }
+        pc(_pc.clone()), macroStaticInst(_macroStaticInst)
+    {}
 
     virtual ~InstRecord()
     {
@@ -179,9 +176,13 @@
     }
 
     void setWhen(Tick new_when) { when = new_when; }
-    void setMem(Addr a, Addr s, unsigned f)
+    void
+    setMem(Addr a, Addr s, unsigned f)
     {
-        addr = a; size = s; flags = f; mem_valid = true;
+        addr = a;
+        size = s;
+        flags = f;
+        mem_valid = true;
     }
 
     template <typename T, size_t N>
@@ -195,17 +196,42 @@
                       "Type T has an unrecognized size.");
     }
 
-    void setData(uint64_t d) { data.as_int = d; data_status = DataInt64; }
-    void setData(uint32_t d) { data.as_int = d; data_status = DataInt32; }
-    void setData(uint16_t d) { data.as_int = d; data_status = DataInt16; }
-    void setData(uint8_t d) { data.as_int = d; data_status = DataInt8; }
+    void
+    setData(uint64_t d)
+    {
+        data.as_int = d;
+        data_status = DataInt64;
+    }
+    void
+    setData(uint32_t d)
+    {
+        data.as_int = d;
+        data_status = DataInt32;
+    }
+    void
+    setData(uint16_t d)
+    {
+        data.as_int = d;
+        data_status = DataInt16;
+    }
+    void
+    setData(uint8_t d)
+    {
+        data.as_int = d;
+        data_status = DataInt8;
+    }
 
     void setData(int64_t d) { setData((uint64_t)d); }
     void setData(int32_t d) { setData((uint32_t)d); }
     void setData(int16_t d) { setData((uint16_t)d); }
     void setData(int8_t d)  { setData((uint8_t)d); }
 
-    void setData(double d) { data.as_double = d; data_status = DataDouble; }
+    void
+    setData(double d)
+    {
+        data.as_double = d;
+        data_status = DataDouble;
+    }
 
     void
     setData(TheISA::VecRegContainer& d)
@@ -221,11 +247,19 @@
         data_status = DataVecPred;
     }
 
-    void setFetchSeq(InstSeqNum seq)
-    { fetch_seq = seq; fetch_seq_valid = true; }
+    void
+    setFetchSeq(InstSeqNum seq)
+    {
+        fetch_seq = seq;
+        fetch_seq_valid = true;
+    }
 
-    void setCPSeq(InstSeqNum seq)
-    { cp_seq = seq; cp_seq_valid = true; }
+    void
+    setCPSeq(InstSeqNum seq)
+    {
+        cp_seq = seq;
+        cp_seq_valid = true;
+    }
 
     void setPredicate(bool val) { predicate = val; }
 
@@ -261,11 +295,9 @@
 class InstTracer : public SimObject
 {
   public:
-    InstTracer(const Params &p) : SimObject(p)
-    {}
+    InstTracer(const Params &p) : SimObject(p) {}
 
-    virtual ~InstTracer()
-    {};
+    virtual ~InstTracer() {}
 
     virtual InstRecord *
         getInstRecord(Tick when, ThreadContext *tc,
diff --git a/src/sim/mem_pool.cc b/src/sim/mem_pool.cc
index 20b6eda..d58399d 100644
--- a/src/sim/mem_pool.cc
+++ b/src/sim/mem_pool.cc
@@ -169,6 +169,7 @@
 void
 MemPools::serialize(CheckpointOut &cp) const
 {
+    ScopedCheckpointSection sec(cp, "mempools");
     int num_pools = pools.size();
     SERIALIZE_SCALAR(num_pools);
 
@@ -179,6 +180,10 @@
 void
 MemPools::unserialize(CheckpointIn &cp)
 {
+    // Delete previous mem_pools
+    pools.clear();
+
+    ScopedCheckpointSection sec(cp, "mempools");
     int num_pools = 0;
     UNSERIALIZE_SCALAR(num_pools);
 
diff --git a/src/sim/process.cc b/src/sim/process.cc
index 3a631a5..97130bd 100644
--- a/src/sim/process.cc
+++ b/src/sim/process.cc
@@ -388,6 +388,7 @@
     memState->unserialize(cp);
     pTable->unserialize(cp);
     fds->unserialize(cp);
+
     /**
      * Checkpoints for pipes, device drivers or sockets currently
      * do not work. Need to come back and fix them at a later date.
diff --git a/src/sim/se_workload.cc b/src/sim/se_workload.cc
index d3c8570..4d2bd54 100644
--- a/src/sim/se_workload.cc
+++ b/src/sim/se_workload.cc
@@ -54,6 +54,18 @@
 }
 
 void
+SEWorkload::serialize(CheckpointOut &cp) const
+{
+    memPools.serialize(cp);
+}
+
+void
+SEWorkload::unserialize(CheckpointIn &cp)
+{
+    memPools.unserialize(cp);
+}
+
+void
 SEWorkload::syscall(ThreadContext *tc)
 {
     tc->getProcessPtr()->syscall(tc);
diff --git a/src/sim/se_workload.hh b/src/sim/se_workload.hh
index 5bc597f..e212ad6 100644
--- a/src/sim/se_workload.hh
+++ b/src/sim/se_workload.hh
@@ -81,6 +81,9 @@
         panic("No workload symbol table for syscall emulation mode.");
     }
 
+    void serialize(CheckpointOut &cp) const override;
+    void unserialize(CheckpointIn &cp) override;
+
     void syscall(ThreadContext *tc) override;
 
     // For now, assume the only type of events are system calls.
diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh
index 546ae75..59a97d9 100644
--- a/src/sim/syscall_emul.hh
+++ b/src/sim/syscall_emul.hh
@@ -90,6 +90,7 @@
 #include "base/intmath.hh"
 #include "base/loader/object_file.hh"
 #include "base/logging.hh"
+#include "base/random.hh"
 #include "base/trace.hh"
 #include "base/types.hh"
 #include "config/the_isa.hh"
@@ -1672,6 +1673,7 @@
     }
 
     if (flags & OS::TGT_CLONE_THREAD) {
+        cp->pTable->initState();
         cp->pTable->shared = true;
         cp->useForClone = true;
     }
@@ -3039,6 +3041,23 @@
     return (result == -1) ? -errno : result;
 }
 
+template <typename OS>
+SyscallReturn
+getrandomFunc(SyscallDesc *desc, ThreadContext *tc,
+              VPtr<> buf_ptr, typename OS::size_t count,
+              unsigned int flags)
+{
+    SETranslatingPortProxy proxy(tc);
+
+    TypedBufferArg<uint8_t> buf(buf_ptr, count);
+    for (int i = 0; i < count; ++i) {
+        buf[i] = gem5::random_mt.random<uint8_t>();
+    }
+    buf.copyOut(proxy);
+
+    return count;
+}
+
 } // namespace gem5
 
 #endif // __SIM_SYSCALL_EMUL_HH__
diff --git a/src/sim/system.cc b/src/sim/system.cc
index 86ba3be..b7fba8a 100644
--- a/src/sim/system.cc
+++ b/src/sim/system.cc
@@ -50,11 +50,6 @@
 #include "base/str.hh"
 #include "base/trace.hh"
 #include "config/the_isa.hh"
-#include "config/use_kvm.hh"
-#if USE_KVM
-#include "cpu/kvm/base.hh"
-#include "cpu/kvm/vm.hh"
-#endif
 #if !IS_NULL_ISA
 #include "cpu/base.hh"
 #endif
@@ -100,31 +95,18 @@
 }
 
 void
-System::Threads::insert(ThreadContext *tc, ContextID id)
+System::Threads::insert(ThreadContext *tc)
 {
-    if (id == InvalidContextID) {
-        for (id = 0; id < size(); id++) {
-            if (!threads[id].context)
-                break;
-        }
-    }
-
+    ContextID id = size();
     tc->setContextId(id);
 
-    if (id >= size())
-        threads.resize(id + 1);
-
-    fatal_if(threads[id].context,
-            "Cannot have two thread contexts with the same id (%d).", id);
-
-    auto *sys = tc->getSystemPtr();
-
-    auto &t = thread(id);
+    auto &t = threads.emplace_back();
     t.context = tc;
     // Look up this thread again on resume, in case the threads vector has
     // been reallocated.
     t.resumeEvent = new EventFunctionWrapper(
-            [this, id](){ thread(id).resume(); }, sys->name());
+            [this, id](){ thread(id).resume(); },
+            tc->getSystemPtr()->name());
 }
 
 void
@@ -199,11 +181,8 @@
       init_param(p.init_param),
       physProxy(_systemPort, p.cache_line_size),
       workload(p.workload),
-#if USE_KVM
-      kvmVM(p.kvm_vm),
-#endif
       physmem(name() + ".physmem", p.memories, p.mmap_using_noreserve,
-              p.shared_backstore),
+              p.shared_backstore, p.auto_unlink_shared_backstore),
       ShadowRomRanges(p.shadow_rom_ranges.begin(),
                       p.shadow_rom_ranges.end()),
       memoryMode(p.mem_mode),
@@ -222,12 +201,6 @@
     // add self to global system list
     systemList.push_back(this);
 
-#if USE_KVM
-    if (kvmVM) {
-        kvmVM->setSystem(this);
-    }
-#endif
-
     // check if the cache line size is a value known to work
     if (_cacheLineSize != 16 && _cacheLineSize != 32 &&
         _cacheLineSize != 64 && _cacheLineSize != 128) {
@@ -272,9 +245,9 @@
 }
 
 void
-System::registerThreadContext(ThreadContext *tc, ContextID assigned)
+System::registerThreadContext(ThreadContext *tc)
 {
-    threads.insert(tc, assigned);
+    threads.insert(tc);
 
     workload->registerThreadContext(tc);
 
@@ -316,24 +289,6 @@
     }
 }
 
-bool
-System::validKvmEnvironment() const
-{
-#if USE_KVM
-    if (threads.empty())
-        return false;
-
-    for (auto *tc: threads) {
-        if (!dynamic_cast<BaseKvmCPU *>(tc->getCpuPtr()))
-            return false;
-    }
-
-    return true;
-#else
-    return false;
-#endif
-}
-
 Addr
 System::memSize() const
 {
diff --git a/src/sim/system.hh b/src/sim/system.hh
index 8f09b96..bb855e0 100644
--- a/src/sim/system.hh
+++ b/src/sim/system.hh
@@ -144,7 +144,7 @@
             return threads[id];
         }
 
-        void insert(ThreadContext *tc, ContextID id=InvalidContextID);
+        void insert(ThreadContext *tc);
         void replace(ThreadContext *tc, ContextID id);
 
         friend class System;
@@ -334,10 +334,13 @@
      * Get a pointer to the Kernel Virtual Machine (KVM) SimObject,
      * if present.
      */
-    KvmVM *getKvmVM() { return kvmVM; }
+    KvmVM *getKvmVM() const { return kvmVM; }
 
-    /** Verify gem5 configuration will support KVM emulation */
-    bool validKvmEnvironment() const;
+    /**
+     * Set the pointer to the Kernel Virtual Machine (KVM) SimObject. For use
+     * by that object to declare itself to the system.
+     */
+    void setKvmVM(KvmVM *const vm) { kvmVM = vm; }
 
     /** Get a pointer to access the physical memory of the system */
     memory::PhysicalMemory& getPhysMem() { return physmem; }
@@ -398,7 +401,7 @@
 
   protected:
 
-    KvmVM *const kvmVM = nullptr;
+    KvmVM *kvmVM = nullptr;
 
     memory::PhysicalMemory physmem;
 
@@ -575,8 +578,7 @@
 
   public:
 
-    void registerThreadContext(
-            ThreadContext *tc, ContextID assigned=InvalidContextID);
+    void registerThreadContext(ThreadContext *tc);
     void replaceThreadContext(ThreadContext *tc, ContextID context_id);
 
     void serialize(CheckpointOut &cp) const override;
diff --git a/src/systemc/SConscript b/src/systemc/SConscript
index 57cb1d9..06bdb50 100644
--- a/src/systemc/SConscript
+++ b/src/systemc/SConscript
@@ -25,7 +25,7 @@
 
 Import('*')
 
-if not env['USE_SYSTEMC']:
+if not env['CONF']['USE_SYSTEMC']:
     Return()
 
 env.UseSystemcCheck(warn=True)
diff --git a/src/systemc/SConsopts b/src/systemc/SConsopts
index 891431c..e870288 100644
--- a/src/systemc/SConsopts
+++ b/src/systemc/SConsopts
@@ -44,7 +44,7 @@
 
 main.AddMethod(use_systemc_check, 'UseSystemcCheck')
 
+main['CONF']['USE_SYSTEMC'] = main.UseSystemcCheck()
+
 sticky_vars.Add(BoolVariable('USE_SYSTEMC', 'Enable SystemC API support',
                              main.UseSystemcCheck()))
-
-export_vars.append('USE_SYSTEMC')
diff --git a/src/systemc/channel/SConscript b/src/systemc/channel/SConscript
index 97e9d29..b9de66c 100644
--- a/src/systemc/channel/SConscript
+++ b/src/systemc/channel/SConscript
@@ -25,7 +25,7 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     Source('messages.cc')
     Source('sc_clock.cc')
     Source('sc_event_queue.cc')
diff --git a/src/systemc/core/SConscript b/src/systemc/core/SConscript
index 8805e9b..2b88111 100644
--- a/src/systemc/core/SConscript
+++ b/src/systemc/core/SConscript
@@ -27,7 +27,7 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     SimObject('SystemC.py', sim_objects=[
         'SystemC_Kernel', 'SystemC_ScObject', 'SystemC_ScModule'])
 
@@ -39,7 +39,6 @@
     Source('object.cc')
     Source('port.cc')
     Source('process.cc')
-    Source('scheduler.cc')
     Source('sched_event.cc')
     Source('sensitivity.cc')
     Source('time.cc')
@@ -66,9 +65,17 @@
         Source('python.cc')
         Source('sc_main_python.cc')
         append = {}
-        with gem5_scons.Configure(main) as conf:
+        with gem5_scons.Configure(env) as conf:
             for flag in ('-Wno-self-assign-overloaded', '-Wno-self-assign'):
                 if conf.CheckCxxFlag(flag, autoadd=False):
                     append['CCFLAGS'] = [flag]
                     break
         Source('sc_time_python.cc', append=append)
+
+    # Disable the false positive warning for the event members of the scheduler.
+    with gem5_scons.Configure(env) as conf:
+        flag = '-Wno-free-nonheap-object'
+        append = {}
+        if conf.CheckCxxFlag(flag, autoadd=False):
+            append['CCFLAGS'] = [flag]
+        Source('scheduler.cc', append=append)
diff --git a/src/systemc/core/kernel.cc b/src/systemc/core/kernel.cc
index a9ff4bb..ae67e46 100644
--- a/src/systemc/core/kernel.cc
+++ b/src/systemc/core/kernel.cc
@@ -85,6 +85,8 @@
 void
 Kernel::regStats()
 {
+    gem5::SimObject::regStats();
+
     if (scMainFiber.finished() || stopAfterCallbacks)
         return;
 
diff --git a/src/systemc/core/sensitivity.cc b/src/systemc/core/sensitivity.cc
index 8c4aa23..1367b29 100644
--- a/src/systemc/core/sensitivity.cc
+++ b/src/systemc/core/sensitivity.cc
@@ -214,9 +214,21 @@
 
 DynamicSensitivityEventOrList::DynamicSensitivityEventOrList(
         Process *p, const sc_core::sc_event_or_list *eol) :
-    Sensitivity(p), DynamicSensitivity(p), SensitivityEvents(p, eol->events)
+    Sensitivity(p),
+    DynamicSensitivity(p),
+    SensitivityEvents(p, eol->events),
+    list(eol)
 {}
 
+DynamicSensitivityEventOrList::~DynamicSensitivityEventOrList()
+{
+    if (list->autoDelete) {
+        panic_if(list->busy,
+                 "sc_event_or_list can never be busy in gem5 implementation");
+        delete list;
+    }
+}
+
 bool
 DynamicSensitivityEventOrList::notifyWork(Event *e)
 {
@@ -233,9 +245,21 @@
 
 DynamicSensitivityEventAndList::DynamicSensitivityEventAndList(
         Process *p, const sc_core::sc_event_and_list *eal) :
-    Sensitivity(p), DynamicSensitivity(p), SensitivityEvents(p, eal->events)
+    Sensitivity(p),
+    DynamicSensitivity(p),
+    SensitivityEvents(p, eal->events),
+    list(eal)
 {}
 
+DynamicSensitivityEventAndList::~DynamicSensitivityEventAndList()
+{
+    if (list->autoDelete) {
+        panic_if(list->busy,
+                 "sc_event_and_list can never be busy in gem5 implementation");
+        delete list;
+    }
+}
+
 bool
 DynamicSensitivityEventAndList::notifyWork(Event *e)
 {
diff --git a/src/systemc/core/sensitivity.hh b/src/systemc/core/sensitivity.hh
index 9e76969..af4ce2d 100644
--- a/src/systemc/core/sensitivity.hh
+++ b/src/systemc/core/sensitivity.hh
@@ -274,8 +274,11 @@
 
     DynamicSensitivityEventOrList(
             Process *p, const sc_core::sc_event_or_list *eol);
+    ~DynamicSensitivityEventOrList();
 
     bool notifyWork(Event *e) override;
+
+    const sc_core::sc_event_or_list *list;
 };
 
 //XXX This sensitivity can't be reused. To reset it, it has to be deleted and
@@ -290,8 +293,11 @@
 
     DynamicSensitivityEventAndList(
             Process *p, const sc_core::sc_event_and_list *eal);
+    ~DynamicSensitivityEventAndList();
 
     bool notifyWork(Event *e) override;
+
+    const sc_core::sc_event_and_list *list;
 };
 
 } // namespace sc_gem5
diff --git a/src/systemc/dt/SConscript b/src/systemc/dt/SConscript
index cccacbe..012551c 100644
--- a/src/systemc/dt/SConscript
+++ b/src/systemc/dt/SConscript
@@ -25,5 +25,5 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     Source('sc_mempool.cc')
diff --git a/src/systemc/dt/bit/SConscript b/src/systemc/dt/bit/SConscript
index 3c00dcf..a21e9e6 100644
--- a/src/systemc/dt/bit/SConscript
+++ b/src/systemc/dt/bit/SConscript
@@ -25,7 +25,7 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     Source('messages.cc')
     Source('sc_bit.cc')
     Source('sc_bv_base.cc')
diff --git a/src/systemc/dt/fx/SConscript b/src/systemc/dt/fx/SConscript
index f849138..2cf4134 100644
--- a/src/systemc/dt/fx/SConscript
+++ b/src/systemc/dt/fx/SConscript
@@ -25,7 +25,7 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     Source('messages.cc')
     Source('sc_fxcast_switch.cc')
     Source('sc_fxdefs.cc')
diff --git a/src/systemc/dt/int/SConscript b/src/systemc/dt/int/SConscript
index 159eb6f..c76c90d 100644
--- a/src/systemc/dt/int/SConscript
+++ b/src/systemc/dt/int/SConscript
@@ -27,8 +27,8 @@
 
 from gem5_scons.util import compareVersions
 
-if env['USE_SYSTEMC']:
-    if main['GCC'] and compareVersions(main['CXXVERSION'], '10.0') >= 0:
+if env['CONF']['USE_SYSTEMC']:
+    if env['GCC'] and compareVersions(env['CXXVERSION'], '10.0') >= 0:
         disable_false_positives = {
             "CCFLAGS": [ "-Wno-array-bounds",
                          "-Wno-stringop-overflow" ]
diff --git a/src/systemc/dt/misc/SConscript b/src/systemc/dt/misc/SConscript
index 6f3fe46..aef013d 100644
--- a/src/systemc/dt/misc/SConscript
+++ b/src/systemc/dt/misc/SConscript
@@ -25,6 +25,6 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     Source('sc_concatref.cc')
     Source('sc_value_base.cc')
diff --git a/src/systemc/ext/channel/sc_fifo.hh b/src/systemc/ext/channel/sc_fifo.hh
index 5d3dab4..2f0a4f4 100644
--- a/src/systemc/ext/channel/sc_fifo.hh
+++ b/src/systemc/ext/channel/sc_fifo.hh
@@ -53,15 +53,16 @@
     explicit sc_fifo(int size=16) :
             sc_fifo_in_if<T>(), sc_fifo_out_if<T>(),
             sc_prim_channel(sc_gen_unique_name("fifo")),
+            _reader(NULL), _writer(NULL),
             _size(size), _num_free(size), _num_available(0),
-            _readsHappened(false), _writesHappened(false),
-            _reader(NULL), _writer(NULL)
+            _readsHappened(false), _writesHappened(false)
     {}
     explicit sc_fifo(const char *name, int size=16) :
             sc_fifo_in_if<T>(), sc_fifo_out_if<T>(),
-            sc_prim_channel(name), _size(size), _num_free(size),
-            _num_available(0), _readsHappened(false), _writesHappened(false),
-            _reader(NULL), _writer(NULL)
+            sc_prim_channel(name),
+            _reader(NULL), _writer(NULL),
+            _size(size), _num_free(size), _num_available(0),
+            _readsHappened(false), _writesHappened(false)
     {}
     virtual ~sc_fifo() {}
 
diff --git a/src/systemc/python/SConscript b/src/systemc/python/SConscript
index 26e72e4..1995862 100644
--- a/src/systemc/python/SConscript
+++ b/src/systemc/python/SConscript
@@ -25,7 +25,7 @@
 
 Import('*')
 
-if not env['USE_SYSTEMC'] or not env['USE_PYTHON']:
+if not env['CONF']['USE_SYSTEMC'] or not env['USE_PYTHON']:
     Return()
 
 PySource('m5', 'systemc.py')
diff --git a/src/systemc/tests/SConscript b/src/systemc/tests/SConscript
index fb916d2..63114f8 100644
--- a/src/systemc/tests/SConscript
+++ b/src/systemc/tests/SConscript
@@ -25,7 +25,7 @@
 
 Import('*')
 
-if env['USE_SYSTEMC'] and GetOption('with_systemc_tests'):
+if env['CONF']['USE_SYSTEMC'] and GetOption('with_systemc_tests'):
 
     from gem5_scons import Transform
 
diff --git a/src/systemc/tlm_bridge/SConscript b/src/systemc/tlm_bridge/SConscript
index 7aba370..87616bb 100644
--- a/src/systemc/tlm_bridge/SConscript
+++ b/src/systemc/tlm_bridge/SConscript
@@ -25,7 +25,7 @@
 
 Import('*')
 
-if not env['USE_SYSTEMC']:
+if not env['CONF']['USE_SYSTEMC']:
     Return()
 
 SimObject('TlmBridge.py', sim_objects=[
diff --git a/src/systemc/tlm_core/2/generic_payload/SConscript b/src/systemc/tlm_core/2/generic_payload/SConscript
index 5c00fa6..0620074 100644
--- a/src/systemc/tlm_core/2/generic_payload/SConscript
+++ b/src/systemc/tlm_core/2/generic_payload/SConscript
@@ -25,6 +25,6 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     Source('gp.cc')
     Source('phase.cc')
diff --git a/src/systemc/tlm_core/2/quantum/SConscript b/src/systemc/tlm_core/2/quantum/SConscript
index 7a34326..0ed4588 100644
--- a/src/systemc/tlm_core/2/quantum/SConscript
+++ b/src/systemc/tlm_core/2/quantum/SConscript
@@ -25,7 +25,7 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     Source('global_quantum.cc')
     if env['USE_PYTHON']:
         Source('global_quantum_python.cc')
diff --git a/src/systemc/tlm_utils/SConscript b/src/systemc/tlm_utils/SConscript
index f2e9715..64ca99a 100644
--- a/src/systemc/tlm_utils/SConscript
+++ b/src/systemc/tlm_utils/SConscript
@@ -25,6 +25,6 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     Source('convenience_socket_bases.cc')
     Source('instance_specific_extensions.cc')
diff --git a/src/systemc/utils/SConscript b/src/systemc/utils/SConscript
index 0ac56d3..9c54af0 100644
--- a/src/systemc/utils/SConscript
+++ b/src/systemc/utils/SConscript
@@ -25,7 +25,7 @@
 
 Import('*')
 
-if env['USE_SYSTEMC']:
+if env['CONF']['USE_SYSTEMC']:
     Source('functions.cc')
     Source('messages.cc')
     Source('report.cc')
diff --git a/system/arm/bootloader/arm64/boot.S b/system/arm/bootloader/arm64/boot.S
index 8af177f..3809f72 100644
--- a/system/arm/bootloader/arm64/boot.S
+++ b/system/arm/bootloader/arm64/boot.S
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 ARM Limited
+ * Copyright (c) 2012, 2020, 2022 Arm Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -61,7 +61,8 @@
         orr	x0, x0, #(1 << 10)		// 64-bit EL2
         msr	scr_el3, x0
 
-        msr	cptr_el3, xzr			// Disable copro. traps to EL3
+        mov	x0, #(1 << 8)			// Disable SVE trap to EL3
+        msr	cptr_el3, x0			// Disable copro. traps to EL3
 
         /*
          * Check for the primary CPU to avoid a race on the distributor
@@ -73,30 +74,46 @@
         // extract the primary CPU.
         ldr x1, =0xff00ffffff
 #ifdef GICV3
-        and	x2, x0, #0xff // use Aff0 as cpuid for now...
         tst	x0, x1 // check for cpuid==zero
-        b.ne	1f // secondary CPU
+        b.ne	3f // secondary CPU
 
         ldr	x1, =GIC_DIST_BASE // GICD_CTLR
         mov	w0, #7 // EnableGrp0 | EnableGrp1NS | EnableGrp1S
         str	w0, [x1]
+        mov	x2, #0xffe8
+        ldr	w2, [x1, x2] // GICD_PIDR2
+        and	w3, w2, #0xf0 // GICD_PIDR2.Read ArchRev
+        cmp	w3, #0x30 // Check if GICv3
+        mov	w4, #0x20000 // 128KiB redistributor stride
+        mov	w5, #0x40000 // 256KiB redistributor stride
+        csel	w2, w4, w5, eq // Select the correct redistributor stride
 
-
-1:      ldr	x1, =GIC_REDIST_BASE + 0x10000 + 0x80 // GICR_IGROUPR0
-        // 128K for each redistributor, 256K strided...
-        mov	x3, #1 << 18 // GICv4
-        mul	x3, x3, x2
-        add	x1, x1, x3
+        ldr	x1, =GIC_REDIST_BASE
         mov	w0, #~0 // Grp1 interrupts
-        str	w0, [x1], #4
-        b.ne	2f // Only local interrupts for secondary CPUs
+1:  ldr	w3, [x1, #0x14] // GICR_WAKER
+        and	w3, w3, #~0x2 // Setting GICR_WAKER.ProcessorSleep to 0
+        str	w3, [x1, #0x14]
+        dsb	sy
+2:  ldr	w3, [x1, #0x14] // Re-reading GICR_WAKER
+        ands	w3, w3, #0x4 // Checking GICR_WAKER.ChildrenAsleep
+        b.ne	2b // Keep reading GICR_WAKER.ChildrenAsleep until awake
+
+        add	x5, x1, #0x10000 // SGI base
+        str	w0, [x5, #0x80] // GICR_IGROUPR0
+        ldr	w4, [x1, #0x8] // GICR_TYPER
+        add	x1, x1, x2 // Point to next redistributor
+        // Check GICR_TYPER.Last, which is set to 1
+        // if this is the last redistributor
+        ands	w4, w4, #0x10
+        b.eq	1b // Branch back if not last redistributor
+
         ldr	x1, =GIC_DIST_BASE + 0x84 // GICD_IGROUPR
         str	w0, [x1], #4
         str	w0, [x1], #4
         str	w0, [x1], #4
 
         /* SRE & Disable IRQ/FIQ Bypass & Allow EL2 access to ICC_SRE_EL2 */
-2:      mrs	x10, S3_6_C12_C12_5 // read ICC_SRE_EL3
+3:  mrs	x10, S3_6_C12_C12_5 // read ICC_SRE_EL3
         orr	x10, x10, #0xf      // enable 0xf
         msr	S3_6_C12_C12_5, x10 // write ICC_SRE_EL3
         isb
diff --git a/system/arm/bootloader/arm64/makefile b/system/arm/bootloader/arm64/makefile
index dbf7128..f434f46 100644
--- a/system/arm/bootloader/arm64/makefile
+++ b/system/arm/bootloader/arm64/makefile
@@ -41,7 +41,8 @@
 
 all: mkdir $(BUILDDIR)/boot_emm.arm64 \
 	$(BUILDDIR)/boot.arm64  \
-	$(BUILDDIR)/boot_v2.arm64
+	$(BUILDDIR)/boot_v2.arm64 \
+	$(BUILDDIR)/boot_foundation.arm64
 
 #v1 has a GIC V2
 $(BUILDDIR)/boot_emm.o: CPPFLAGS += -UGICV3 -DGIC_CPU_BASE=0x2c002000 \
@@ -53,6 +54,10 @@
 $(BUILDDIR)/boot_v2.o: CPPFLAGS += -DGICV3 -DGIC_REDIST_BASE=0x2c010000 \
 	-DGIC_DIST_BASE=0x2c000000
 
+#Foundation has a GIC V3
+$(BUILDDIR)/boot_foundation.o: CPPFLAGS += -DGICV3 -DGIC_REDIST_BASE=0x2f100000 \
+	-DGIC_DIST_BASE=0x2f000000
+
 $(BUILDDIR)/%.arm64: $(BUILDDIR)/%.o
 	$(LD) -o $@ $< $(LDFLAGS)
 
@@ -64,6 +69,7 @@
 	install -m 644 $(BUILDDIR)/boot_emm.arm64 \
 		$(BUILDDIR)/boot.arm64 \
 		$(BUILDDIR)/boot_v2.arm64 \
+		$(BUILDDIR)/boot_foundation.arm64 \
 		$(DESTDIR)/.
 
 mkdir:
@@ -71,4 +77,5 @@
 
 clean:
 	rm -f $(BUILDDIR)/*.o
-	rm -f $(BUILDDIR)/boot_emm.arm64 $(BUILDDIR)/boot.arm64 $(BUILDDIR)/boot_v2.arm64
+	rm -f $(BUILDDIR)/boot_emm.arm64 $(BUILDDIR)/boot.arm64 \
+        $(BUILDDIR)/boot_v2.arm64 $(BUILDDIR)/boot_foundation.arm64
diff --git a/tests/compiler-tests.sh b/tests/compiler-tests.sh
index 43092da..354444c 100755
--- a/tests/compiler-tests.sh
+++ b/tests/compiler-tests.sh
@@ -9,12 +9,16 @@
 gem5_root="${dir}/.."
 build_dir="${gem5_root}/build"
 
+# The per-container Docker memory limit.
+docker_mem_limit="18g"
+
 # All Docker images in the gem5 testing GCR which we want to compile with.
 images=("gcc-version-11"
         "gcc-version-10"
         "gcc-version-9"
         "gcc-version-8"
         "gcc-version-7"
+        "clang-version-12"
         "clang-version-11"
         "clang-version-10"
         "clang-version-9"
@@ -31,7 +35,7 @@
 # A subset of the above list: these images will build against every target,
 # ignoring builds_per_compiler.
 comprehensive=("gcc-version-11"
-               "clang-version-11")
+               "clang-version-12")
 
 # All build targets in build_opt/ which we want to build using each image.
 builds=("ARM"
@@ -99,7 +103,7 @@
     # targets for this test
     build_indices=(${build_permutation[@]:0:$builds_count})
 
-    repo_name="${base_url}/${compiler}:v21-2"
+    repo_name="${base_url}/${compiler}:v22-0"
 
     # Grab compiler image
     docker pull $repo_name >/dev/null
@@ -124,7 +128,8 @@
             # Build with container
             {
                 docker run --rm -v "${gem5_root}":"/gem5" -u $UID:$GID \
-                    -w /gem5 $repo_name /usr/bin/env python3 /usr/bin/scons \
+                    -w /gem5 --memory="${docker_mem_limit}" $repo_name \
+                    /usr/bin/env python3 /usr/bin/scons \
                     "${build_out}" "${build_args}"
             }>"${build_stdout}" 2>"${build_stderr}"
             result=$?
diff --git a/tests/gem5/asmtest/tests.py b/tests/gem5/asmtest/tests.py
index 6f2c7117..cd473b0 100755
--- a/tests/gem5/asmtest/tests.py
+++ b/tests/gem5/asmtest/tests.py
@@ -40,7 +40,7 @@
 def asm_test(test, #The full path of the test
              cpu_type,
              num_cpus=4,
-             max_tick=10000000000,
+             max_tick=None,
              ruby=False,
              debug_flags=None, # Debug flags passed to gem5
              full_system = False
@@ -58,10 +58,10 @@
     if not debug_flags is None:
         gem5_args += ['--debug-flags', str(debug_flags)]
 
-    config_args = [
-        '-m', str(max_tick),
-        '--cpu-type', cpu_type,
-    ]
+    config_args = ['--cpu-type', cpu_type]
+
+    if max_tick:
+        config_args += ['-m', str(max_tick) ]
 
     if full_system:
         config_args += [
@@ -214,6 +214,17 @@
     'rv64um-ps-remu',
     'rv64um-ps-remuw',
     'rv64um-ps-remw',
+    'rv64uzfh-ps-fadd',
+    'rv64uzfh-ps-fclass',
+    'rv64uzfh-ps-fcmp',
+    'rv64uzfh-ps-fcvt',
+    'rv64uzfh-ps-fcvt_w',
+    'rv64uzfh-ps-fdiv',
+    'rv64uzfh-ps-fmadd',
+    'rv64uzfh-ps-fmin',
+    'rv64uzfh-ps-ldst',
+    'rv64uzfh-ps-move',
+    'rv64uzfh-ps-recoding',
 )
 
 
diff --git a/tests/gem5/configs/arm_generic.py b/tests/gem5/configs/arm_generic.py
index 007b89b..5ea5e64 100644
--- a/tests/gem5/configs/arm_generic.py
+++ b/tests/gem5/configs/arm_generic.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2012, 2017, 2019 ARM Limited
+# Copyright (c) 2012, 2017, 2019, 2022 Arm Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -71,7 +71,7 @@
     ARM-specific create_system method to a class deriving from one of
     the generic base systems.
     """
-    def __init__(self, machine_type, aarch64_kernel, **kwargs):
+    def __init__(self, machine_type, aarch64_kernel, enable_dvm, **kwargs):
         """
         Arguments:
           machine_type -- String describing the platform to simulate
@@ -79,10 +79,11 @@
           use_ruby -- True if ruby is used instead of the classic memory system
         """
         self.machine_type = machine_type
+        self.aarch64_kernel = aarch64_kernel
+        self.enable_dvm = enable_dvm
         self.num_cpus = kwargs.get('num_cpus', 1)
         self.mem_size = kwargs.get('mem_size', '256MB')
         self.use_ruby = kwargs.get('use_ruby', False)
-        self.aarch64_kernel = aarch64_kernel
 
     def init_kvm(self, system):
         """Do KVM-specific system initialization.
@@ -123,6 +124,12 @@
                                         self.machine_type, self.num_cpus,
                                         sc, ruby=self.use_ruby)
 
+        # TODO: This is removing SECURITY and VIRTUALIZATION extensions
+        # from AArch32 runs to fix long regressions. Find a fix or
+        # remove EL3/EL2 support at AArch32
+        if not self.aarch64_kernel:
+            system.release = ArmRelease(extensions=["LPAE"])
+
         # We typically want the simulator to panic if the kernel
         # panics or oopses. This prevents the simulator from running
         # an obviously failed test case until the end of time.
@@ -133,6 +140,10 @@
                     default_kernels[self.machine_type])
 
         self.init_system(system)
+        if self.enable_dvm:
+            for cpu in system.cpu:
+                for decoder in cpu.decoder:
+                    decoder.dvm_enabled = True
 
         system.workload.dtb_filename = \
             os.path.join(m5.options.outdir, 'system.dtb')
@@ -146,6 +157,7 @@
     def __init__(self,
                  machine_type='VExpress_GEM5_Foundation',
                  aarch64_kernel=True,
+                 enable_dvm=False,
                  **kwargs):
         """Initialize an ARM system that supports full system simulation.
 
@@ -157,7 +169,7 @@
         """
         BaseFSSystem.__init__(self, **kwargs)
         LinuxArmSystemBuilder.__init__(
-            self, machine_type, aarch64_kernel, **kwargs)
+            self, machine_type, aarch64_kernel, enable_dvm, **kwargs)
 
     def create_caches_private(self, cpu):
         # Use the more representative cache configuration
@@ -180,7 +192,7 @@
                  **kwargs):
         BaseFSSystemUniprocessor.__init__(self, **kwargs)
         LinuxArmSystemBuilder.__init__(
-            self, machine_type, aarch64_kernel, **kwargs)
+            self, machine_type, aarch64_kernel, False, **kwargs)
 
 class LinuxArmFSSwitcheroo(LinuxArmSystemBuilder, BaseFSSwitcheroo):
     """Uniprocessor ARM system prepared for CPU switching"""
@@ -191,4 +203,4 @@
                  **kwargs):
         BaseFSSwitcheroo.__init__(self, **kwargs)
         LinuxArmSystemBuilder.__init__(
-            self, machine_type, aarch64_kernel, **kwargs)
+            self, machine_type, aarch64_kernel, False, **kwargs)
diff --git a/tests/gem5/configs/boot_kvm_fork_run.py b/tests/gem5/configs/boot_kvm_fork_run.py
index 662ef23..c4160fd 100644
--- a/tests/gem5/configs/boot_kvm_fork_run.py
+++ b/tests/gem5/configs/boot_kvm_fork_run.py
@@ -47,14 +47,16 @@
 from gem5.coherence_protocol import CoherenceProtocol
 from gem5.isas import ISA
 from gem5.components.memory import SingleChannelDDR3_1600
-from gem5.components.processors.cpu_types import CPUTypes
+from gem5.components.processors.cpu_types import(
+    CPUTypes,
+    get_cpu_types_str_set,
+    get_cpu_type_from_str,
+)
 from gem5.components.processors.simple_switchable_processor import (
     SimpleSwitchableProcessor,
 )
 from gem5.resources.resource import Resource
-from gem5.runtime import (
-    get_runtime_coherence_protocol, get_runtime_isa
-)
+from gem5.runtime import get_runtime_coherence_protocol
 from gem5.utils.requires import requires
 
 parser = argparse.ArgumentParser(
@@ -80,7 +82,7 @@
     "-c",
     "--cpu",
     type=str,
-    choices=("kvm", "atomic", "timing", "o3"),
+    choices=get_cpu_types_str_set(),
     required=True,
     help="The CPU type.",
 )
@@ -117,7 +119,7 @@
 requires(
     isa_required=ISA.X86,
     coherence_protocol_required=coherence_protocol_required,
-    kvm_required=(args.cpu == "kvm"),
+    kvm_required=True,
 )
 
 cache_hierarchy = None
@@ -163,26 +165,10 @@
 
 memory = SingleChannelDDR3_1600(size="3GB")
 
-# Setup a Processor.
-cpu_type = None
-if args.cpu == "kvm":
-    cpu_type = CPUTypes.KVM
-elif args.cpu == "atomic":
-    cpu_type = CPUTypes.ATOMIC
-elif args.cpu == "timing":
-    cpu_type = CPUTypes.TIMING
-elif args.cpu == "o3":
-    cpu_type = CPUTypes.O3
-else:
-    raise NotImplementedError(
-        "CPU type '{}' is not supported in the boot tests.".format(args.cpu)
-    )
-
-assert cpu_type != None
-
 processor = SimpleSwitchableProcessor(
     starting_core_type=CPUTypes.KVM,
-    switch_core_type=cpu_type,
+    switch_core_type=get_cpu_type_from_str(args.cpu),
+    isa=ISA.X86,
     num_cores=args.num_cpus,
 )
 
@@ -218,7 +204,7 @@
 
 # Begin running of the simulation. This will exit once the Linux system boot
 # is complete.
-print("Running with ISA: " + get_runtime_isa().name)
+print("Running with ISA: " + processor.get_isa().name)
 print("Running with protocol: " + get_runtime_coherence_protocol().name)
 print()
 
diff --git a/tests/gem5/configs/boot_kvm_switch_exit.py b/tests/gem5/configs/boot_kvm_switch_exit.py
index 5cfee40..a807f84 100644
--- a/tests/gem5/configs/boot_kvm_switch_exit.py
+++ b/tests/gem5/configs/boot_kvm_switch_exit.py
@@ -37,14 +37,16 @@
 from gem5.components.boards.x86_board import X86Board
 from gem5.coherence_protocol import CoherenceProtocol
 from gem5.components.memory import SingleChannelDDR3_1600
-from gem5.components.processors.cpu_types import CPUTypes
+from gem5.components.processors.cpu_types import(
+    CPUTypes,
+    get_cpu_types_str_set,
+    get_cpu_type_from_str,
+)
 from gem5.components.processors.simple_switchable_processor import (
     SimpleSwitchableProcessor,
 )
 from gem5.resources.resource import Resource
-from gem5.runtime import (
-    get_runtime_coherence_protocol, get_runtime_isa
-)
+from gem5.runtime import get_runtime_coherence_protocol
 from gem5.simulate.simulator import Simulator
 from gem5.simulate.exit_event import ExitEvent
 from gem5.utils.requires import requires
@@ -73,7 +75,7 @@
     "-c",
     "--cpu",
     type=str,
-    choices=("kvm", "atomic", "timing", "o3"),
+    choices=get_cpu_types_str_set(),
     required=True,
     help="The CPU type.",
 )
@@ -149,25 +151,10 @@
 memory = SingleChannelDDR3_1600(size="3GB")
 
 # Setup a Processor.
-cpu_type = None
-if args.cpu == "kvm":
-    cpu_type = CPUTypes.KVM
-elif args.cpu == "atomic":
-    cpu_type = CPUTypes.ATOMIC
-elif args.cpu == "timing":
-    cpu_type = CPUTypes.TIMING
-elif args.cpu == "o3":
-    cpu_type = CPUTypes.O3
-else:
-    raise NotImplementedError(
-        "CPU type '{}' is not supported in the boot tests.".format(args.cpu)
-    )
-
-assert cpu_type != None
-
 processor = SimpleSwitchableProcessor(
     starting_core_type=CPUTypes.KVM,
-    switch_core_type=cpu_type,
+    switch_core_type=get_cpu_type_from_str(args.cpu),
+    isa=ISA.X86,
     num_cores=args.num_cpus,
 )
 
@@ -199,7 +186,7 @@
 
 # Begin running of the simulation. This will exit once the Linux system boot
 # is complete.
-print("Running with ISA: " + get_runtime_isa().name)
+print("Running with ISA: " + processor.get_isa().name)
 print("Running with protocol: " + get_runtime_coherence_protocol().name)
 print()
 
diff --git a/tests/gem5/configs/download_check.py b/tests/gem5/configs/download_check.py
new file mode 100644
index 0000000..613a1c4
--- /dev/null
+++ b/tests/gem5/configs/download_check.py
@@ -0,0 +1,105 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from gem5.resources.downloader import (
+    list_resources,
+    get_resources_json_obj,
+    get_resource,
+)
+
+from gem5.resources.md5_utils import md5
+
+import os
+import shutil
+import argparse
+from pathlib import Path
+
+parser = argparse.ArgumentParser(
+    description="A script that will checks that input resource IDs will "
+    "download a resource and that resources md5 value is correct. "
+    "If no resource IDs are specified, all will be checked."
+)
+
+parser.add_argument(
+    "ids",
+    nargs="*",  # Accepts 0 or more arguments.
+    type=str,
+    help="The resource IDs to check. If not set, all resources will be "
+    "checked",
+)
+
+parser.add_argument(
+    "--download-directory",
+    type=str,
+    required=True,
+    help="The directory to download the resources as part of these test. The "
+    "contents of this directory will be wiped after running the tests.",
+)
+
+args = parser.parse_args()
+
+# If the directory doesn't exist, create it.
+if not Path(args.download_directory).exists():
+    os.makedirs(args.download_directory)
+
+
+ids = args.ids
+if len(ids) == 0:
+    ids = list_resources()
+
+# We log all the errors as they occur then dump them at the end. This means we
+# can be aware of all download errors in a single failure.
+errors = str()
+
+for id in ids:
+    if id not in list_resources():
+        errors += (
+            f"Resource with ID '{id}' not found in "
+            + f"`list_resources()`.{os.linesep}"
+        )
+        continue
+
+    resource_json = get_resources_json_obj(id)
+    download_path = os.path.join(args.download_directory, id)
+    try:
+        get_resource(resource_name=id, to_path=download_path)
+    except Exception:
+        errors += f"Failure to download resource '{id}'.{os.linesep}"
+        continue
+
+    if md5(Path(download_path)) != resource_json["md5sum"]:
+        errors += (
+            f"Downloaded resource '{id}' md5 "
+            + f"({md5(Path(download_path))}) differs to that in the "
+            + f"JSON ({resource_json['md5sum']}).{os.linesep}"
+        )
+
+    # Remove the downloaded resource.
+    shutil.rmtree(download_path, ignore_errors=True)
+
+# If errors exist, raise an exception highlighting them.
+if errors:
+    raise Exception(errors)
diff --git a/tests/gem5/configs/parsec_disk_run.py b/tests/gem5/configs/parsec_disk_run.py
index a5cf41d..456fce0 100644
--- a/tests/gem5/configs/parsec_disk_run.py
+++ b/tests/gem5/configs/parsec_disk_run.py
@@ -43,7 +43,10 @@
 from gem5.components.processors.simple_switchable_processor import (
     SimpleSwitchableProcessor,
 )
-from gem5.components.processors.cpu_types import CPUTypes
+from gem5.components.processors.cpu_types import(
+    get_cpu_types_str_set,
+    get_cpu_type_from_str,
+)
 from gem5.isas import ISA
 from gem5.runtime import get_runtime_isa, get_runtime_coherence_protocol
 from gem5.simulate.simulator import Simulator
@@ -75,7 +78,7 @@
     "-b",
     "--boot-cpu",
     type=str,
-    choices=("kvm", "timing", "atomic", "o3"),
+    choices=get_cpu_types_str_set(),
     required=False,
     help="The CPU type to run before and after the ROI. If not specified will "
     "be equal to that of the CPU type used in the ROI.",
@@ -85,7 +88,7 @@
     "-c",
     "--cpu",
     type=str,
-    choices=("kvm", "timing", "atomic", "o3"),
+    choices=get_cpu_types_str_set(),
     required=True,
     help="The CPU type used in the ROI.",
 )
@@ -174,23 +177,9 @@
 # Setup the memory system.
 memory = SingleChannelDDR3_1600(size="3GB")
 
-
-def input_to_cputype(input: str) -> CPUTypes:
-    if input == "kvm":
-        return CPUTypes.KVM
-    elif input == "timing":
-        return CPUTypes.TIMING
-    elif input == "atomic":
-        return CPUTypes.ATOMIC
-    elif input == "o3":
-        return CPUTypes.O3
-    else:
-        raise NotADirectoryError("Unknown CPU type '{}'.".format(input))
-
-
-roi_type = input_to_cputype(args.cpu)
+roi_type = get_cpu_type_from_str(args.cpu)
 if args.boot_cpu != None:
-    boot_type = input_to_cputype(args.boot_cpu)
+    boot_type = get_cpu_type_from_str(args.boot_cpu)
 else:
     boot_type = roi_type
 
@@ -198,6 +187,7 @@
 processor = SimpleSwitchableProcessor(
     starting_core_type=boot_type,
     switch_core_type=roi_type,
+    isa=ISA.X86,
     num_cores=args.num_cpus,
 )
 
diff --git a/src/cpu/minor/SConsopts b/tests/gem5/configs/realview64-o3-dual-ruby.py
similarity index 84%
rename from src/cpu/minor/SConsopts
rename to tests/gem5/configs/realview64-o3-dual-ruby.py
index 16ff599..83c38e2 100644
--- a/src/cpu/minor/SConsopts
+++ b/tests/gem5/configs/realview64-o3-dual-ruby.py
@@ -1,6 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2012-2014 ARM Limited
+# Copyright (c) 2017, 2019, 2022 Arm Limited
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -35,6 +33,12 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+from m5.objects import *
+from arm_generic import *
 
-main.Append(ALL_CPU_MODELS=['MinorCPU'])
+root = LinuxArmFSSystem(mem_mode='timing',
+                        mem_class=DDR3_1600_8x8,
+                        cpu_class=O3CPU,
+                        num_cpus=2,
+                        enable_dvm=True,
+                        use_ruby=True).create_root()
diff --git a/tests/gem5/configs/realview64-simple-atomic-checkpoint.py b/tests/gem5/configs/realview64-simple-atomic-checkpoint.py
index 2a3ee7a..37dcbc6 100644
--- a/tests/gem5/configs/realview64-simple-atomic-checkpoint.py
+++ b/tests/gem5/configs/realview64-simple-atomic-checkpoint.py
@@ -43,5 +43,5 @@
                                     mem_class=SimpleMemory,
                                     cpu_class=AtomicSimpleCPU).create_root()
 
-run_test = functools.partial(checkpoint.run_test, interval=0.2)
-
+run_test = functools.partial(checkpoint.run_test, interval=0.2,
+                             max_checkpoints=3)
diff --git a/src/cpu/simple/SConsopts b/tests/gem5/configs/requires_check.py
similarity index 65%
copy from src/cpu/simple/SConsopts
copy to tests/gem5/configs/requires_check.py
index f12fee2..8ec566a 100644
--- a/src/cpu/simple/SConsopts
+++ b/tests/gem5/configs/requires_check.py
@@ -1,6 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
+# Copyright (c) 2022 The Regents of the University of California
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -26,6 +24,32 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+"""
+This is a very simple script to test the behavior of 'gem5.utils.requires'`
+"""
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+from gem5.utils.requires import requires
+from gem5.isas import ISA, get_isas_str_set, get_isa_from_str
+
+
+import argparse
+
+parser = argparse.ArgumentParser(
+    description="A simple script used to check the behavior of "
+                "`gem5.utils.requires`."
+)
+
+parser.add_argument(
+    "-i",
+    "--required-isa",
+    type=str,
+    choices=get_isas_str_set(),
+    required=True,
+    help="The required ISA. A non-zero exit code is returned if the "
+         "requirements are not met." ,
+)
+
+args = parser.parse_args()
+required_isa = get_isa_from_str(args.required_isa)
+
+requires(isa_required=required_isa)
diff --git a/tests/gem5/configs/riscv_boot_exit_run.py b/tests/gem5/configs/riscv_boot_exit_run.py
index 6420542..fd57e45 100644
--- a/tests/gem5/configs/riscv_boot_exit_run.py
+++ b/tests/gem5/configs/riscv_boot_exit_run.py
@@ -60,7 +60,7 @@
     "-c",
     "--cpu",
     type=str,
-    choices=("kvm", "atomic", "timing", "o3"),
+    choices=("kvm", "atomic", "timing", "o3", "minor"),
     required=True,
     help="The CPU type.",
 )
@@ -139,12 +139,18 @@
     cpu_type = CPUTypes.TIMING
 elif args.cpu == "o3":
     cpu_type = CPUTypes.O3
+elif args.cpu == "minor":
+    cpu_type = CPUTypes.MINOR
 else:
     raise NotImplementedError(
         "CPU type '{}' is not supported in the boot tests.".format(args.cpu)
     )
 
-processor = SimpleProcessor(cpu_type=cpu_type, num_cores=args.num_cpus)
+processor = SimpleProcessor(
+    cpu_type=cpu_type,
+    isa=ISA.RISCV,
+    num_cores=args.num_cpus,
+)
 
 # Setup the board.
 board = RiscvBoard(
diff --git a/src/cpu/simple/SConsopts b/tests/gem5/configs/runtime_isa_check.py
similarity index 60%
copy from src/cpu/simple/SConsopts
copy to tests/gem5/configs/runtime_isa_check.py
index f12fee2..8f17693 100644
--- a/src/cpu/simple/SConsopts
+++ b/tests/gem5/configs/runtime_isa_check.py
@@ -1,6 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
+# Copyright (c) 2022 The Regents of the University of California
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -26,6 +24,39 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+"""
+This is a very simple script to test the output given by
+`gem5.runtime.get_runtime_isa`
+"""
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+from gem5.runtime import get_runtime_isa
+from gem5.isas import ISA, get_isas_str_set, get_isa_from_str
+
+import argparse
+
+parser = argparse.ArgumentParser(
+    description="A simple script used to check the output of "
+                "`gem5.runtime.get_runtime_isa`"
+)
+
+parser.add_argument(
+    "-e",
+    "--expected-isa",
+    type=str,
+    choices=get_isas_str_set(),
+    required=True,
+    help="The expected ISA. If not returned by `get_runtime_isa`, a "
+         "non-zero exit code will be returned by the script" ,
+)
+
+args = parser.parse_args()
+runtime_isa = get_runtime_isa()
+expected_isa = get_isa_from_str(args.expected_isa)
+
+if runtime_isa == expected_isa:
+    exit(0)
+
+print(f"ISA expected: {args.expected_isa}")
+print(f"get_runtime_isa() returned: {runtime_isa.value}")
+
+exit(1)
diff --git a/tests/gem5/configs/simple_binary_run.py b/tests/gem5/configs/simple_binary_run.py
index 3a44602..ebe833a 100644
--- a/tests/gem5/configs/simple_binary_run.py
+++ b/tests/gem5/configs/simple_binary_run.py
@@ -31,12 +31,16 @@
 """
 
 from gem5.resources.resource import Resource
-from gem5.components.processors.cpu_types import CPUTypes
+from gem5.components.processors.cpu_types import(
+    get_cpu_types_str_set,
+    get_cpu_type_from_str,
+)
 from gem5.components.memory import SingleChannelDDR3_1600
 from gem5.components.boards.simple_board import SimpleBoard
 from gem5.components.cachehierarchies.classic.no_cache import NoCache
 from gem5.components.processors.simple_processor import SimpleProcessor
 from gem5.simulate.simulator import Simulator
+from gem5.isas import get_isa_from_str, get_isas_str_set
 
 import argparse
 
@@ -53,11 +57,18 @@
 parser.add_argument(
     "cpu",
     type=str,
-    choices=("kvm", "timing", "atomic", "o3"),
+    choices=get_cpu_types_str_set(),
     help="The CPU type used.",
 )
 
 parser.add_argument(
+    "isa",
+    type=str,
+    choices=get_isas_str_set(),
+    help="The ISA used",
+)
+
+parser.add_argument(
     "-r",
     "--resource-directory",
     type=str,
@@ -67,22 +78,14 @@
 
 args = parser.parse_args()
 
-def input_to_cputype(input: str) -> CPUTypes:
-    if input == "kvm":
-        return CPUTypes.KVM
-    elif input == "timing":
-        return CPUTypes.TIMING
-    elif input == "atomic":
-        return CPUTypes.ATOMIC
-    elif input == "o3":
-        return CPUTypes.O3
-    else:
-        raise NotADirectoryError("Unknown CPU type '{}'.".format(input))
-
 # Setup the system.
 cache_hierarchy = NoCache()
 memory = SingleChannelDDR3_1600()
-processor = SimpleProcessor(cpu_type=input_to_cputype(args.cpu), num_cores=1)
+processor = SimpleProcessor(
+    cpu_type=get_cpu_type_from_str(args.cpu),
+    isa=get_isa_from_str(args.isa),
+    num_cores=1,
+)
 
 motherboard = SimpleBoard(
     clk_freq="3GHz",
@@ -97,7 +100,7 @@
 motherboard.set_se_binary_workload(binary)
 
 # Run the simulation
-simulator = Simulator(board=motherboard, full_system=False)
+simulator = Simulator(board=motherboard)
 simulator.run()
 
 print(
diff --git a/tests/gem5/configs/supported_isa_check.py b/tests/gem5/configs/supported_isa_check.py
new file mode 100644
index 0000000..5f535e7
--- /dev/null
+++ b/tests/gem5/configs/supported_isa_check.py
@@ -0,0 +1,68 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+This is a very simple script to test the output given by
+`gem5.runtime.get_supported_isas`
+"""
+
+from gem5.runtime import get_supported_isas
+from gem5.isas import get_isas_str_set, get_isa_from_str
+
+import os
+import argparse
+
+parser = argparse.ArgumentParser(
+    description="A simple script used to check the output of "
+    "`gem5.runtime.get_supported_isas`"
+)
+
+parser.add_argument(
+    "-e",
+    "--expected-isa",
+    type=str,
+    choices=get_isas_str_set(),
+    required=True,
+    help="An ISA expected to be included in the binary. If not returned by "
+    "`get_supported_isas`, a non-zero exit code will be returned by the "
+    "script",
+)
+
+args = parser.parse_args()
+supported_isas = get_supported_isas()
+expected_isa = get_isa_from_str(args.expected_isa)
+
+if expected_isa in supported_isas:
+    exit(0)
+
+print(f"ISA expected: {args.expected_isa}")
+
+supported_isas_str = ""
+for isa in supported_isas:
+    supported_isas += f"{os.linesep}{isa.value}"
+print(f"get_supported_isas() returned:{supported_isas}")
+
+exit(1)
diff --git a/tests/gem5/configs/x86_boot_exit_run.py b/tests/gem5/configs/x86_boot_exit_run.py
index 93c9028..358276c 100644
--- a/tests/gem5/configs/x86_boot_exit_run.py
+++ b/tests/gem5/configs/x86_boot_exit_run.py
@@ -30,16 +30,16 @@
 
 import m5
 
-from gem5.runtime import (
-    get_runtime_coherence_protocol,
-    get_runtime_isa,
-)
+from gem5.runtime import get_runtime_coherence_protocol
 from gem5.isas import ISA
 from gem5.utils.requires import requires
 from gem5.resources.resource import Resource
 from gem5.coherence_protocol import CoherenceProtocol
 from gem5.components.boards.x86_board import X86Board
-from gem5.components.processors.cpu_types import CPUTypes
+from gem5.components.processors.cpu_types import(
+    get_cpu_types_str_set,
+    get_cpu_type_from_str,
+)
 from gem5.components.processors.simple_processor import SimpleProcessor
 from gem5.simulate.simulator import Simulator
 
@@ -70,7 +70,7 @@
     "-c",
     "--cpu",
     type=str,
-    choices=("kvm", "atomic", "timing", "o3"),
+    choices=get_cpu_types_str_set(),
     required=True,
     help="The CPU type.",
 )
@@ -169,24 +169,11 @@
 memory = memory_class(size="3GiB")
 
 # Setup a Processor.
-
-cpu_type = None
-if args.cpu == "kvm":
-    cpu_type = CPUTypes.KVM
-elif args.cpu == "atomic":
-    cpu_type = CPUTypes.ATOMIC
-elif args.cpu == "timing":
-    cpu_type = CPUTypes.TIMING
-elif args.cpu == "o3":
-    cpu_type = CPUTypes.O3
-else:
-    raise NotImplementedError(
-        "CPU type '{}' is not supported in the boot tests.".format(args.cpu)
-    )
-
-assert cpu_type != None
-
-processor = SimpleProcessor(cpu_type=cpu_type, num_cores=args.num_cpus)
+processor = SimpleProcessor(
+    cpu_type=get_cpu_type_from_str(args.cpu),
+    isa=ISA.X86,
+    num_cores=args.num_cpus,
+)
 
 # Setup the motherboard.
 motherboard = X86Board(
@@ -216,7 +203,7 @@
 
 # Begin running of the simulation. This will exit once the Linux system boot
 # is complete.
-print("Running with ISA: " + get_runtime_isa().name)
+print("Running with ISA: " + processor.get_isa().name)
 print("Running with protocol: " + get_runtime_coherence_protocol().name)
 print()
 
diff --git a/tests/gem5/cpu_tests/test.py b/tests/gem5/cpu_tests/test.py
index a962337..c0322b2 100644
--- a/tests/gem5/cpu_tests/test.py
+++ b/tests/gem5/cpu_tests/test.py
@@ -46,7 +46,7 @@
 workloads = ('Bubblesort','FloatMM')
 
 valid_isas = {
-    constants.gcn3_x86_tag :
+    constants.vega_x86_tag :
         ('AtomicSimpleCPU', 'TimingSimpleCPU', 'DerivO3CPU'),
     constants.arm_tag:
         ('AtomicSimpleCPU', 'TimingSimpleCPU', 'MinorCPU', 'DerivO3CPU'),
@@ -60,7 +60,7 @@
 base_url = config.resource_url + '/test-progs/cpu-tests/bin/'
 
 isa_url = {
-    constants.gcn3_x86_tag : base_url + "x86",
+    constants.vega_x86_tag : base_url + "x86",
     constants.arm_tag : base_url + "arm",
     constants.riscv_tag : base_url + "riscv",
 }
diff --git a/tests/gem5/fs/linux/arm/test.py b/tests/gem5/fs/linux/arm/test.py
index 5b64c80..facff57 100644
--- a/tests/gem5/fs/linux/arm/test.py
+++ b/tests/gem5/fs/linux/arm/test.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2019-2021 Arm Limited
+# Copyright (c) 2019-2022 Arm Limited
 # All rights reserved
 #
 # The license below extends only to copyright in the software and shall
@@ -79,6 +79,7 @@
     'realview-simple-timing-ruby',
     'realview64-simple-timing-ruby',
     'realview64-simple-timing-dual-ruby',
+    'realview64-o3-dual-ruby',
 
 
     # The following tests fail. These are recorded in the GEM5-640
@@ -92,7 +93,7 @@
     #'realview-simple-timing-dual-ruby',
 ]
 
-tarball = 'aarch-system-20210904.tar.bz2'
+tarball = 'aarch-system-20220505.tar.bz2'
 url = config.resource_url + "/arm/" + tarball
 filepath = os.path.dirname(os.path.abspath(__file__))
 path = joinpath(config.bin_path, 'arm')
@@ -135,7 +136,8 @@
         valid_isas=(constants.arm_tag,),
         length=constants.quick_tag,
         valid_hosts=valid_hosts,
-        fixtures=(arm_fs_binaries,)
+        fixtures=(arm_fs_binaries,),
+        uses_kvm= name in arm_fs_kvm_tests,
     )
 
 for name in arm_fs_long_tests:
@@ -151,5 +153,6 @@
         config_args=args,
         valid_isas=(constants.arm_tag,),
         length=constants.long_tag,
-        fixtures=(arm_fs_binaries,)
+        fixtures=(arm_fs_binaries,),
+        uses_kvm= name in arm_fs_kvm_tests,
     )
diff --git a/src/cpu/simple/SConsopts b/tests/gem5/gem5-resources/test_download_resources.py
similarity index 69%
copy from src/cpu/simple/SConsopts
copy to tests/gem5/gem5-resources/test_download_resources.py
index f12fee2..acc72d7 100644
--- a/src/cpu/simple/SConsopts
+++ b/tests/gem5/gem5-resources/test_download_resources.py
@@ -1,6 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
+# Copyright (c) 2022 The Regents of the University of California
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -26,6 +24,23 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+from testlib import *
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+if config.bin_path:
+    resource_path = joinpath(config.bin_path, "resource-downloading-test")
+else:
+    resource_path = joinpath(
+        absdirpath(__file__), "..", "resources", "resource_downloading-test"
+    )
+
+gem5_verify_config(
+    name="test-resource-downloading",
+    fixtures=(),
+    verifiers=(),
+    config=joinpath(
+        config.base_dir, "tests", "gem5", "configs", "download_check.py"
+    ),
+    config_args=["--resource-directory", resource_path],
+    valid_isas=(constants.null_tag,),
+    length=constants.very_long_tag,
+)
diff --git a/tests/gem5/gem5_library_example_tests/test_gem5_library_examples.py b/tests/gem5/gem5_library_example_tests/test_gem5_library_examples.py
index 6f3300d..805f942 100644
--- a/tests/gem5/gem5_library_example_tests/test_gem5_library_examples.py
+++ b/tests/gem5/gem5_library_example_tests/test_gem5_library_examples.py
@@ -33,6 +33,8 @@
 import os
 
 hello_verifier = verifier.MatchRegex(re.compile(r"Hello world!"))
+save_checkpoint_verifier = verifier.MatchRegex(
+    re.compile(r"Done taking a checkpoint"))
 
 gem5_verify_config(
     name="test-gem5-library-example-arm-hello",
@@ -51,6 +53,41 @@
     length=constants.quick_tag,
 )
 
+gem5_verify_config(
+    name="test-gem5-library-riscv-hello-save-checkpoint",
+    fixtures=(),
+    verifiers=(save_checkpoint_verifier,),
+    config=joinpath(
+        config.base_dir,
+        "configs",
+        "example",
+        "gem5_library",
+        "checkpoints",
+        "riscv-hello-save-checkpoint.py"
+    ),
+    config_args=[],
+    valid_isas=(constants.riscv_tag,),
+    valid_hosts=constants.supported_hosts,
+    length=constants.quick_tag,
+)
+
+gem5_verify_config(
+    name="test-gem5-library-riscv-hello-restore-checkpoint",
+    fixtures=(),
+    verifiers=(hello_verifier,),
+    config=joinpath(
+        config.base_dir,
+        "configs",
+        "example",
+        "gem5_library",
+        "checkpoints",
+        "riscv-hello-restore-checkpoint.py"
+    ),
+    config_args=[],
+    valid_isas=(constants.riscv_tag,),
+    valid_hosts=constants.supported_hosts,
+    length=constants.quick_tag,
+)
 
 if os.access("/dev/kvm", mode=os.R_OK | os.W_OK):
     # The x86-ubuntu-run uses KVM cores, this test will therefore only be run
@@ -68,8 +105,9 @@
         ),
         config_args=[],
         valid_isas=(constants.x86_tag,),
-        valid_hosts=constants.supported_hosts,
+        valid_hosts=(constants.host_x86_64_tag,),
         length=constants.long_tag,
+        uses_kvm=True,
     )
 
 gem5_verify_config(
@@ -106,8 +144,9 @@
         config_args=["--benchmark","blackscholes","--size","simsmall"],
         valid_isas=(constants.x86_tag,),
         protocol="MESI_Two_Level",
-        valid_hosts=constants.supported_hosts,
+        valid_hosts=(constants.host_x86_64_tag,),
         length=constants.long_tag,
+        uses_kvm=True,
     )
 
 if os.access("/dev/kvm", mode=os.R_OK | os.W_OK):
@@ -133,8 +172,9 @@
         ],
         valid_isas=(constants.x86_tag,),
         protocol="MESI_Two_Level",
-        valid_hosts=constants.supported_hosts,
+        valid_hosts=(constants.host_x86_64_tag,),
         length=constants.long_tag,
+        uses_kvm=True,
     )
 
 if os.access("/dev/kvm", mode=os.R_OK | os.W_OK):
@@ -154,8 +194,9 @@
         config_args=["--benchmark","bfs","--synthetic","1","--size","1"],
         valid_isas=(constants.x86_tag,),
         protocol="MESI_Two_Level",
-        valid_hosts=constants.supported_hosts,
+        valid_hosts=(constants.host_x86_64_tag,),
         length=constants.long_tag,
+        uses_kvm=True,
     )
 
 gem5_verify_config(
@@ -191,3 +232,20 @@
     valid_hosts=constants.supported_hosts,
     length=constants.long_tag,
 )
+
+gem5_verify_config(
+    name="test-gem5-library-example-arm-ubuntu-boot-test",
+    fixtures=(),
+    verifiers=(),
+    config=joinpath(
+        config.base_dir,
+        "configs",
+        "example",
+        "gem5_library",
+        "arm-ubuntu-boot-exit.py",
+    ),
+    config_args=[],
+    valid_isas=(constants.arm_tag,),
+    valid_hosts=constants.supported_hosts,
+    length=constants.long_tag,
+)
diff --git a/tests/gem5/gpu/test_gpu_ruby_random.py b/tests/gem5/gpu/test_gpu_ruby_random.py
new file mode 100644
index 0000000..83f0e9f
--- /dev/null
+++ b/tests/gem5/gpu/test_gpu_ruby_random.py
@@ -0,0 +1,100 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from testlib import *
+
+"""
+This file contains random tests for the Ruby GPU protocols.
+"""
+
+# This test will first run the GPU protocol random tester -- it should take
+# about 30 seconds to run and provides good coverage for the coherence
+# protocol.
+#
+# Input choices (some are default and thus implicit):
+# - use small cache size to encourage races
+# - use small system size to encourage races since more requests per CU (and
+#   faster sim)
+# - use small address range to encourage more races
+# - use small episode length to encourage more races
+# - 50K tests runs in ~30 seconds with reasonably good coverage
+# - num-dmas = 0 because VIPER doesn't support partial cache line writes, which
+#   DMAs need
+gem5_verify_config(
+    name="ruby-gpu-random-test-perCheckin",
+    fixtures=(),
+    verifiers=(),
+    config=joinpath(
+        config.base_dir,
+        "configs",
+        "example",
+        "ruby_gpu_random_test.py",
+    ),
+    config_args=[
+        "--test-length",
+        "50000",
+        "--num-dmas",
+        "0",
+    ],
+    valid_isas=(constants.vega_x86_tag,),
+    valid_hosts=constants.supported_hosts,
+    length=constants.quick_tag,
+)
+
+
+# This test will run the GPU protocol random tester in nightly -- it should
+# take about 30 minutes to run and provides good coverage for the coherence
+# protocol.
+#
+# Input choices (some are default and thus implicit):
+#  - use small cache size to encourage races
+#  - use small system size to encourage races since more requests per CU (and
+#    faster sim)
+#  - use small address range to encourage more races
+#  - use small episode length to encourage more races
+#  - 5M tests runs in ~30 minutes with reasonably good coverage
+#  - num-dmas = 0 because VIPER doesn't support partial cache line writes,
+#    which DMAs need
+gem5_verify_config(
+    name="ruby-gpu-random-test-nightly",
+    fixtures=(),
+    verifiers=(),
+    config=joinpath(
+        config.base_dir,
+        "configs",
+        "example",
+        "ruby_gpu_random_test.py",
+    ),
+    config_args=[
+        "--test-length",
+        "5000000",
+        "--num-dmas",
+        "0",
+    ],
+    valid_isas=(constants.vega_x86_tag,),
+    valid_hosts=constants.supported_hosts,
+    length=constants.long_tag,
+)
diff --git a/tests/gem5/hello_se/test_hello_se.py b/tests/gem5/hello_se/test_hello_se.py
index ea1f987..9cbfa52 100644
--- a/tests/gem5/hello_se/test_hello_se.py
+++ b/tests/gem5/hello_se/test_hello_se.py
@@ -46,8 +46,16 @@
 
 import re
 
+isa_str_map = {
+    constants.gcn3_x86_tag: "x86",
+    constants.arm_tag: "arm",
+    constants.mips_tag: "mips",
+    constants.riscv_tag: "riscv",
+    constants.sparc_tag: "sparc",
+}
+
 static_progs = {
-    constants.gcn3_x86_tag: (
+    constants.vega_x86_tag: (
         "x86-hello64-static",
         "x86-hello32-static",
     ),
@@ -60,20 +68,20 @@
     constants.sparc_tag: ("sparc-hello",),
 }
 
-dynamic_progs = {constants.gcn3_x86_tag: ("x86-hello64-dynamic",)}
+dynamic_progs = {constants.vega_x86_tag: ("x86-hello64-dynamic",)}
 
 cpu_types = {
-    constants.gcn3_x86_tag: ("timing", "atomic", "o3"),
-    constants.arm_tag: ("timing", "atomic", "o3"),
+    constants.vega_x86_tag: ("timing", "atomic", "o3"),
+    constants.arm_tag: ("timing", "atomic", "o3", "minor"),
     constants.mips_tag: ("timing", "atomic", "o3"),
-    constants.riscv_tag: ("timing", "atomic", "o3"),
+    constants.riscv_tag: ("timing", "atomic", "o3", "minor"),
     constants.sparc_tag: ("timing", "atomic"),
 }
 
 # We only want to test x86, arm, and riscv on quick. Mips and sparc will be
 # left for long.
 os_length = {
-    constants.gcn3_x86_tag: constants.quick_tag,
+    constants.vega_x86_tag: constants.quick_tag,
     constants.arm_tag: constants.quick_tag,
     constants.mips_tag: constants.long_tag,
     constants.riscv_tag: constants.quick_tag,
@@ -109,6 +117,7 @@
             cpu,
             "--resource-directory",
             resource_path,
+            isa_str_map[isa],
         ],
         valid_isas=(isa,),
         valid_hosts=hosts,
diff --git a/tests/gem5/insttest_se/test.py b/tests/gem5/insttest_se/test.py
index 25c8823..dfae7ce 100644
--- a/tests/gem5/insttest_se/test.py
+++ b/tests/gem5/insttest_se/test.py
@@ -62,6 +62,7 @@
                     cpu,
                     "--resource-directory",
                     resource_path,
+                    "sparc",
                 ],
                 valid_isas=(isa,),
                 length=constants.long_tag,
diff --git a/tests/gem5/kvm-fork-tests/test_kvm_fork_run.py b/tests/gem5/kvm-fork-tests/test_kvm_fork_run.py
index e3a6e64..b7986c7 100644
--- a/tests/gem5/kvm-fork-tests/test_kvm_fork_run.py
+++ b/tests/gem5/kvm-fork-tests/test_kvm_fork_run.py
@@ -59,7 +59,7 @@
         isa_to_use = constants.x86_tag
     else:
         protocol_to_use = None
-        isa_to_use = constants.gcn3_x86_tag
+        isa_to_use = constants.vega_x86_tag
 
     gem5_verify_config(
         name=name,
@@ -86,9 +86,10 @@
             "--kernel-args=''",
         ],
         valid_isas=(isa_to_use,),
-        valid_hosts=constants.supported_hosts,
+        valid_hosts=(constants.host_x86_64_tag,),
         protocol=protocol_to_use,
         length=length,
+        uses_kvm=True,
     )
 
 
diff --git a/tests/gem5/kvm-switch-tests/test_kvm_cpu_switch.py b/tests/gem5/kvm-switch-tests/test_kvm_cpu_switch.py
index 62ae30c..ede90db25 100644
--- a/tests/gem5/kvm-switch-tests/test_kvm_cpu_switch.py
+++ b/tests/gem5/kvm-switch-tests/test_kvm_cpu_switch.py
@@ -59,7 +59,7 @@
         isa_to_use = constants.x86_tag
     else:
         protocol_to_use = None
-        isa_to_use = constants.gcn3_x86_tag
+        isa_to_use = constants.vega_x86_tag
 
     gem5_verify_config(
         name=name,
@@ -84,9 +84,10 @@
             "--kernel-args=''",
         ],
         valid_isas=(isa_to_use,),
-        valid_hosts=constants.supported_hosts,
+        valid_hosts=(constants.host_x86_64_tag,),
         protocol=protocol_to_use,
         length=length,
+        uses_kvm=True,
     )
 
 
diff --git a/tests/gem5/learning_gem5/part1_test.py b/tests/gem5/learning_gem5/part1_test.py
index 4153165..32dc666 100644
--- a/tests/gem5/learning_gem5/part1_test.py
+++ b/tests/gem5/learning_gem5/part1_test.py
@@ -36,7 +36,7 @@
     config_args = [],
     length = constants.quick_tag,
     valid_isas=(
-        constants.gcn3_x86_tag,
+        constants.vega_x86_tag,
         constants.riscv_tag,
         constants.arm_tag,
     ),
@@ -50,7 +50,7 @@
     config_args = [],
     length = constants.quick_tag,
     valid_isas=(
-        constants.gcn3_x86_tag,
+        constants.vega_x86_tag,
         constants.riscv_tag,
         constants.arm_tag
     ),
diff --git a/tests/gem5/learning_gem5/part2_test.py b/tests/gem5/learning_gem5/part2_test.py
index 24d623c..f3658b7 100644
--- a/tests/gem5/learning_gem5/part2_test.py
+++ b/tests/gem5/learning_gem5/part2_test.py
@@ -52,7 +52,7 @@
     config=joinpath(config_path, 'simple_memobj.py'),
     config_args = [],
     # note: by default the above script uses x86
-    valid_isas=(constants.gcn3_x86_tag,),
+    valid_isas=(constants.vega_x86_tag,),
 )
 
 gem5_verify_config(
@@ -61,7 +61,7 @@
     config=joinpath(config_path, 'simple_cache.py'),
     config_args = [],
     # note: by default the above script uses x86
-    valid_isas=(constants.gcn3_x86_tag,),
+    valid_isas=(constants.vega_x86_tag,),
 )
 
 # Note: for simple memobj and simple cache I want to use the traffic generator
diff --git a/tests/gem5/learning_gem5/part3_test.py b/tests/gem5/learning_gem5/part3_test.py
index 249951b..ad9ea8d 100644
--- a/tests/gem5/learning_gem5/part3_test.py
+++ b/tests/gem5/learning_gem5/part3_test.py
@@ -40,9 +40,9 @@
     config_args = [],
     protocol = 'MSI',
     # Currently only x86 has the threads test
-    valid_isas=(constants.gcn3_x86_tag,),
+    valid_isas=(constants.x86_tag,),
     # dynamically linked
-    valid_hosts=constants.target_host[constants.gcn3_x86_tag],
+    valid_hosts=constants.target_host[constants.x86_tag],
     length=constants.long_tag,
 )
 
@@ -53,6 +53,6 @@
     config_args = [],
     protocol = 'MSI',
     # Currently only x86 has the threads test
-    valid_isas=(constants.gcn3_x86_tag,),
+    valid_isas=(constants.x86_tag,),
     length=constants.long_tag,
 )
diff --git a/tests/gem5/m5_util/test_exit.py b/tests/gem5/m5_util/test_exit.py
index 48d9645..d06e703 100644
--- a/tests/gem5/m5_util/test_exit.py
+++ b/tests/gem5/m5_util/test_exit.py
@@ -68,6 +68,7 @@
         "atomic",
         "--resource-directory",
         resource_path,
+        "x86",
     ],
-    valid_isas=(constants.gcn3_x86_tag,),
+    valid_isas=(constants.vega_x86_tag,),
 )
diff --git a/tests/gem5/memory/test.py b/tests/gem5/memory/test.py
index db20ab5..01bd68b 100644
--- a/tests/gem5/memory/test.py
+++ b/tests/gem5/memory/test.py
@@ -68,17 +68,30 @@
 )
 
 null_tests = [
-    ('garnet_synth_traffic', ['--sim-cycles', '5000000']),
-    ('memcheck', ['--maxtick', '2000000000', '--prefetchers']),
-    ('ruby_mem_test', ['--abs-max-tick', '20000000',
-        '--functional', '10']),
-    ('ruby_random_test', ['--maxloads', '5000']),
-    ('ruby_direct_test', ['--requests', '50000']),
+    ('garnet_synth_traffic', None, ['--sim-cycles', '5000000']),
+    ('memcheck', None, ['--maxtick', '2000000000', '--prefetchers']),
+    ('ruby_mem_test-garnet', 'ruby_mem_test',
+        ['--abs-max-tick', '20000000', '--functional', '10', \
+         '--network=garnet']),
+    ('ruby_mem_test-simple', 'ruby_mem_test',
+        ['--abs-max-tick', '20000000', '--functional', '10', \
+         '--network=simple']),
+    ('ruby_mem_test-simple-extra', 'ruby_mem_test',
+        ['--abs-max-tick', '20000000', '--functional', '10', \
+         '--network=simple', '--simple-physical-channels']),
+    ('ruby_mem_test-simple-extra-multicore', 'ruby_mem_test',
+        ['--abs-max-tick', '20000000', '--functional', '10', \
+         '--network=simple', '--simple-physical-channels',
+         '--num-cpus=4']),
+    ('ruby_random_test', None, ['--maxloads', '5000']),
+    ('ruby_direct_test', None, ['--requests', '50000']),
 ]
 
-for basename_noext, args in null_tests:
+for test_name, basename_noext, args in null_tests:
+    if basename_noext == None:
+        basename_noext = test_name
     gem5_verify_config(
-        name=basename_noext,
+        name=test_name,
         fixtures=(),
         verifiers=(),
         config=joinpath(config.base_dir, 'configs',
diff --git a/tests/gem5/multi_isa/test_multi_isa.py b/tests/gem5/multi_isa/test_multi_isa.py
new file mode 100644
index 0000000..2f1f67c
--- /dev/null
+++ b/tests/gem5/multi_isa/test_multi_isa.py
@@ -0,0 +1,106 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from testlib import *
+
+isa_map = {
+    "sparc": constants.sparc_tag,
+    "mips": constants.mips_tag,
+    "null": constants.null_tag,
+    "arm": constants.arm_tag,
+    "x86": constants.vega_x86_tag,
+    "power": constants.power_tag,
+    "riscv": constants.riscv_tag,
+}
+
+length_map = {
+    "sparc": constants.long_tag,
+    "mips": constants.long_tag,
+    "null": constants.quick_tag,
+    "arm": constants.quick_tag,
+    "x86": constants.quick_tag,
+    "power": constants.long_tag,
+    "riscv": constants.long_tag,
+}
+
+for isa in isa_map.keys():
+    gem5_verify_config(
+        name=f"runtime-isa-check_{isa}-compiled-alone",
+        verifiers=(),
+        fixtures=(),
+        config=joinpath(
+            config.base_dir,
+            "tests",
+            "gem5",
+            "configs",
+            "runtime_isa_check.py",
+        ),
+        config_args=["-e", isa],
+        valid_isas=(isa_map[isa],),
+        valid_hosts=constants.supported_hosts,
+        length=length_map[isa],
+    )
+
+    gem5_verify_config(
+        name=f"supported-isas-check_{isa}-compiled-alone",
+        verifiers=(),
+        fixtures=(),
+        config=joinpath(
+            config.base_dir,
+            "tests",
+            "gem5",
+            "configs",
+            "supported_isa_check.py",
+        ),
+        config_args=["-e", isa],
+        valid_isas=(isa_map[isa],),
+        valid_hosts=constants.supported_hosts,
+        length=length_map[isa],
+    )
+
+    # Remove this when the muli-isa work is incorporated. `build/ALL/gem5.opt`
+    # must be compilable.
+    continue
+
+    if isa != "null":
+        # The null isa is not "supported" in a case where other ISAs are
+        # present.
+        gem5_verify_config(
+            name=f"supported-isas-check_{isa}-all-compiled",
+            verifiers=(),
+            fixtures=(),
+            config=joinpath(
+                config.base_dir,
+                "tests",
+                "gem5",
+                "configs",
+                "supported_isa_check.py",
+            ),
+            config_args=["-e", isa],
+            valid_isas=(constants.all_compiled_tag,),
+            valid_hosts=constants.supported_hosts,
+            length=constants.long_tag,
+        )
diff --git a/tests/gem5/parsec-benchmarks/test_parsec.py b/tests/gem5/parsec-benchmarks/test_parsec.py
index 4e10e6e..1104cf1 100644
--- a/tests/gem5/parsec-benchmarks/test_parsec.py
+++ b/tests/gem5/parsec-benchmarks/test_parsec.py
@@ -86,8 +86,9 @@
             resource_path,
         ],
         valid_isas=(constants.x86_tag,),
-        valid_hosts=constants.supported_hosts,
+        valid_hosts=(constants.host_x86_64_tag,),
         length=length,
+        uses_kvm=True,
     )
 
 
diff --git a/tests/gem5/riscv-boot-tests/test_linux_boot.py b/tests/gem5/riscv-boot-tests/test_linux_boot.py
index 6d4e9f1..e39c409 100644
--- a/tests/gem5/riscv-boot-tests/test_linux_boot.py
+++ b/tests/gem5/riscv-boot-tests/test_linux_boot.py
@@ -112,6 +112,44 @@
 )
 
 test_boot(
+    cpu="minor",
+    num_cpus=1,
+    cache_type="classic",
+    memory_class="SingleChannelDDR3_2133",
+    length=constants.quick_tag,
+    to_tick=10000000000,
+)
+
+test_boot(
+    cpu="minor",
+    num_cpus=4,
+    cache_type="classic",
+    memory_class="SingleChannelDDR3_2133",
+    length=constants.quick_tag,
+    to_tick=10000000000,
+)
+
+test_boot(
+    cpu="minor",
+    num_cpus=1,
+    cache_type="mi_example",
+    memory_class="SingleChannelDDR3_2133",
+    length=constants.quick_tag,
+    to_tick=10000000000,
+)
+
+test_boot(
+    cpu="minor",
+    num_cpus=8,
+    cache_type="mi_example",
+    memory_class="SingleChannelDDR3_2133",
+    length=constants.quick_tag,
+    to_tick=10000000000,
+)
+
+
+
+test_boot(
     cpu="timing",
     num_cpus=1,
     cache_type="mi_example",
diff --git a/tests/gem5/stats/test_hdf5.py b/tests/gem5/stats/test_hdf5.py
index bf35f70..b566235 100644
--- a/tests/gem5/stats/test_hdf5.py
+++ b/tests/gem5/stats/test_hdf5.py
@@ -94,6 +94,7 @@
             "atomic",
             "--resource-directory",
             resource_path,
+            "arm",
         ],
         gem5_args=["--stats-file=h5://stats.h5"],
         valid_isas=(constants.arm_tag,),
diff --git a/tests/gem5/stdlib/test_requires.py b/tests/gem5/stdlib/test_requires.py
new file mode 100644
index 0000000..0a7f123
--- /dev/null
+++ b/tests/gem5/stdlib/test_requires.py
@@ -0,0 +1,64 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from testlib import *
+
+isa_map = {
+    "sparc": constants.sparc_tag,
+    "mips": constants.mips_tag,
+    "null": constants.null_tag,
+    "arm": constants.arm_tag,
+    "x86": constants.vega_x86_tag,
+    "power": constants.power_tag,
+    "riscv": constants.riscv_tag,
+}
+
+length_map = {
+    "sparc": constants.long_tag,
+    "mips": constants.long_tag,
+    "null": constants.quick_tag,
+    "arm": constants.quick_tag,
+    "x86": constants.quick_tag,
+    "power": constants.long_tag,
+    "riscv": constants.long_tag,
+}
+
+for isa in isa_map.keys():
+    gem5_verify_config(
+        name=f"requires-isa-{isa}",
+        verifiers=(),
+        fixtures=(),
+        config=joinpath(
+            config.base_dir,
+            "tests",
+            "gem5",
+            "configs",
+            "requires_check.py",
+        ),
+        config_args=["-i", isa],
+        valid_isas=(isa_map[isa],),
+        length=length_map[isa],
+    )
\ No newline at end of file
diff --git a/tests/gem5/suite.py b/tests/gem5/suite.py
index cef3e7d..354c8ef 100644
--- a/tests/gem5/suite.py
+++ b/tests/gem5/suite.py
@@ -59,7 +59,9 @@
                        valid_variants=constants.supported_variants,
                        length=constants.supported_lengths[0],
                        valid_hosts=constants.supported_hosts,
-                       protocol=None):
+                       protocol=None,
+                       uses_kvm=False,
+                    ):
     '''
     Helper class to generate common gem5 tests using verifiers.
 
@@ -84,6 +86,9 @@
 
     :param valid_variants: An iterable with the variant levels that
         this test can be ran for. (E.g. opt, debug)
+
+    :param uses_kvm: States if this verifier uses KVM. If so, the "kvm" tag
+        will be included.
     '''
     fixtures = list(fixtures)
     testsuites = []
@@ -123,6 +128,9 @@
                 # Add the isa and variant to tags list.
                 tags = [isa, opt, length, host]
 
+                if uses_kvm:
+                    tags.append(constants.kvm_tag)
+
                 # Create the gem5 target for the specific architecture and
                 # variant.
                 _fixtures = copy.copy(fixtures)
diff --git a/tests/gem5/test_build/test_build.py b/tests/gem5/test_build/test_build.py
index 3a6a534..5be5d5d 100644
--- a/tests/gem5/test_build/test_build.py
+++ b/tests/gem5/test_build/test_build.py
@@ -31,10 +31,11 @@
 import os
 from testlib import *
 
-common_isas = [constants.gcn3_x86_tag, constants.arm_tag, constants.riscv_tag]
+common_isas = [constants.vega_x86_tag, constants.arm_tag, constants.riscv_tag]
+skipped_isas = {constants.null_tag, constants.all_compiled_tag}
 
 for isa in constants.supported_isas:
-    if isa is constants.null_tag: continue
+    if isa in skipped_isas: continue
 
     for variant in constants.supported_variants:
         if isa in common_isas:
diff --git a/tests/gem5/x86-boot-tests/test_linux_boot.py b/tests/gem5/x86-boot-tests/test_linux_boot.py
index 77d1c0d..8b517be 100644
--- a/tests/gem5/x86-boot-tests/test_linux_boot.py
+++ b/tests/gem5/x86-boot-tests/test_linux_boot.py
@@ -70,7 +70,7 @@
         isa_to_use=constants.x86_tag
     else:
         protocol_to_use=None
-        isa_to_use=constants.gcn3_x86_tag
+        isa_to_use=constants.vega_x86_tag
 
     gem5_verify_config(
         name=name,
@@ -126,6 +126,15 @@
 )
 
 test_boot(
+    cpu="timing",
+    num_cpus=8,
+    mem_system="classic",
+    memory_class="SingleChannelDDR3_2133",
+    to_tick=10000000000,
+    length=constants.quick_tag,
+)
+
+test_boot(
     cpu="atomic",
     num_cpus=4,
     mem_system="classic",
@@ -173,6 +182,15 @@
 )
 
 test_boot(
+    cpu="timing",
+    num_cpus=4,
+    mem_system="classic",
+    memory_class="DualChannelDDR3_2133",
+    boot_type="init",
+    length=constants.long_tag,
+)
+
+test_boot(
     cpu="atomic",
     num_cpus=4,
     mem_system="classic",
@@ -220,19 +238,19 @@
             1: True,
             2: True,
             4: False,  # We already run this in the long (Nightly) tests.
-            8: True,
+            8: False,  # Jira: https://gem5.atlassian.net/browse/GEM5-1217
         },
         "timing": {
             1: True,
-            2: False,  # Timeout
-            4: False,  # Timeout
-            8: False,  # Timeout
+            2: True,
+            4: True,
+            8: False,  # Jira: https://gem5.atlassian.net/browse/GEM5-1217
         },
         "o3": {
             1: False,  # Timeout
-            2: False,  # Not Supported
-            4: False,  # Not Supported
-            8: False,  # Not Supported
+            2: False,  # Timeout
+            4: False,  # Timeout
+            8: False,  # Timeout
         },
     },
     "mi_example": {
@@ -243,10 +261,10 @@
             8: False,  # Not Supported
         },
         "timing": {
-            1: True,
-            2: True,
-            4: True,
-            8: True,
+            1: False,  # MI_Example does not successfully boot with the Timing
+            2: False,  # Jira: https://gem5.atlassian.net/browse/GEM5-1216
+            4: False,
+            8: False,
         },
         "o3": {
             1: False,  # Timeout
@@ -264,7 +282,8 @@
         },
         "timing": {
             1: True,
-            2: True,
+            2: False,  # Disabled due to
+                       # https://gem5.atlassian.net/browse/GEM5-1219.
             4: True,
             8: True,
         },
@@ -289,3 +308,43 @@
                     boot_type="systemd",
                     length=constants.very_long_tag,
                     )
+
+# To ensure the O3 CPU is working correctly, we include some "init" tests here.
+# There were not included above as booting to "systemd" takes too long with
+# o3 CPUs
+test_boot(
+    cpu="o3",
+    num_cpus=1,
+    mem_system="classic",
+    memory_class="DualChannelDDR4_2400",
+    boot_type="init",
+    length=constants.very_long_tag,
+)
+
+test_boot(
+    cpu="o3",
+    num_cpus=2,
+    mem_system="classic",
+    memory_class="DualChannelDDR4_2400",
+    boot_type="init",
+    length=constants.very_long_tag,
+)
+
+test_boot(
+    cpu="o3",
+    num_cpus=4,
+    mem_system="classic",
+    memory_class="DualChannelDDR4_2400",
+    boot_type="init",
+    length=constants.very_long_tag,
+)
+
+test_boot(
+    cpu="o3",
+    num_cpus=8,
+    mem_system="classic",
+    memory_class="DualChannelDDR4_2400",
+    boot_type="init",
+    length=constants.very_long_tag,
+)
+
diff --git a/tests/jenkins/presubmit.sh b/tests/jenkins/presubmit.sh
index d15df01..2883bf7 100755
--- a/tests/jenkins/presubmit.sh
+++ b/tests/jenkins/presubmit.sh
@@ -37,8 +37,8 @@
 
 set -e
 
-DOCKER_IMAGE_ALL_DEP=gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v21-2
-DOCKER_IMAGE_CLANG_COMPILE=gcr.io/gem5-test/clang-version-9:v21-2
+DOCKER_IMAGE_ALL_DEP=gcr.io/gem5-test/ubuntu-20.04_all-dependencies:latest
+DOCKER_IMAGE_CLANG_COMPILE=gcr.io/gem5-test/clang-version-11:latest
 PRESUBMIT_STAGE2=tests/jenkins/presubmit-stage2.sh
 GEM5ART_TESTS=tests/jenkins/gem5art-tests.sh
 
diff --git a/tests/nightly.sh b/tests/nightly.sh
index 190c33d..4ee8951 100755
--- a/tests/nightly.sh
+++ b/tests/nightly.sh
@@ -32,6 +32,9 @@
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
 gem5_root="${dir}/.."
 
+# The per-container Docker memory limit.
+docker_mem_limit="18g"
+
 # The first argument is the number of threads to be used for compilation. If no
 # argument is given we default to one.
 compile_threads=1
@@ -65,8 +68,8 @@
     # SCons is not perfect, and occasionally does not catch a necessary
     # compilation: https://gem5.atlassian.net/browse/GEM5-753
     docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-        "${gem5_root}" --rm \
-        gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v21-2 \
+        "${gem5_root}" --memory="${docker_mem_limit}" --rm \
+        gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v22-0 \
             bash -c "scons build/${isa}/gem5.opt -j${compile_threads} \
                 || (rm -rf build && scons build/${isa}/gem5.opt -j${compile_threads})"
 }
@@ -75,13 +78,13 @@
     build=$1
 
     docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-        "${gem5_root}" --rm \
-        gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v21-2 \
+        "${gem5_root}" --memory="${docker_mem_limit}" --rm \
+        gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v22-0 \
             scons build/NULL/unittests.${build} -j${compile_threads}
 }
 
 # Ensure we have the latest docker images.
-docker pull gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v21-2
+docker pull gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v22-0
 
 # Try to build the ISA targets.
 build_target NULL
@@ -98,19 +101,31 @@
 
 # Run the gem5 long tests.
 docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}"/tests --rm \
-    gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v21-2 \
+    "${gem5_root}"/tests --memory="${docker_mem_limit}" --rm \
+    gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v22-0 \
         ./main.py run --length long -j${compile_threads} -t${run_threads} -vv
 
+# Unfortunately, due docker being unable run KVM, we do so separately.
+# This script excluses all tags, includes all tests tagged as "kvm", then
+# removes all those part of the 'very-long' (weekly) tests, or for compilation
+# to '.debug' or '.fast'. We also remove ARM targets as our Jenkins is an X86
+# system. Users wishing to run this script elsewhere should be aware of this.
+cd "${gem5_root}/tests"
+./main.py run -j${compile_threads} -vv \
+    --exclude-tags ".*" --include-tags kvm --exclude-tags very\-long \
+    --exclude-tags debug --exclude-tags fast --exclude-tags ARM
+cd "${gem5_root}"
+
 # For the GPU tests we compile and run the GPU ISA inside a gcn-gpu container.
-docker pull gcr.io/gem5-test/gcn-gpu:v21-2
+docker pull gcr.io/gem5-test/gcn-gpu:v22-0
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}" gcr.io/gem5-test/gcn-gpu:v21-2  bash -c \
+    "${gem5_root}" --memory="${docker_mem_limit}" \
+    gcr.io/gem5-test/gcn-gpu:v22-0  bash -c \
     "scons build/${gpu_isa}/gem5.opt -j${compile_threads} \
         || (rm -rf build && scons build/${gpu_isa}/gem5.opt -j${compile_threads})"
 
 # get square
-wget -qN http://dist.gem5.org/dist/v21-2/test-progs/square/square
+wget -qN http://dist.gem5.org/dist/develop/test-progs/square/square
 
 mkdir -p tests/testing-results
 
@@ -118,18 +133,20 @@
 # Thus, we always want to run this in the nightly regressions to make sure
 # basic GPU functionality is working.
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}" gcr.io/gem5-test/gcn-gpu:v21-2  build/${gpu_isa}/gem5.opt \
+    "${gem5_root}" --memory="${docker_mem_limit}" \
+    gcr.io/gem5-test/gcn-gpu:v22-0  build/${gpu_isa}/gem5.opt \
     configs/example/apu_se.py --reg-alloc-policy=dynamic -n3 -c square
 
 # get HeteroSync
-wget -qN http://dist.gem5.org/dist/v21-2/test-progs/heterosync/gcn3/allSyncPrims-1kernel
+wget -qN http://dist.gem5.org/dist/develop/test-progs/heterosync/gcn3/allSyncPrims-1kernel
 
 # run HeteroSync sleepMutex -- 16 WGs (4 per CU in default config), each doing
 # 10 Ld/St per thread and 4 iterations of the critical section is a reasonable
 # moderate contention case for the default 4 CU GPU config and help ensure GPU
 # atomics are tested.
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}" gcr.io/gem5-test/gcn-gpu:v21-2 build/${gpu_isa}/gem5.opt \
+    "${gem5_root}"  --memory="${docker_mem_limit}" \
+    gcr.io/gem5-test/gcn-gpu:v22-0 build/${gpu_isa}/gem5.opt \
     configs/example/apu_se.py --reg-alloc-policy=dynamic -n3 -c \
     allSyncPrims-1kernel --options="sleepMutex 10 16 4"
 
@@ -139,7 +156,8 @@
 # moderate contention case for the default 4 CU GPU config and help ensure GPU
 # atomics are tested.
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}" gcr.io/gem5-test/gcn-gpu:v21-2  build/${gpu_isa}/gem5.opt \
+    "${gem5_root}"  --memory="${docker_mem_limit}" \
+    gcr.io/gem5-test/gcn-gpu:v22-0  build/${gpu_isa}/gem5.opt \
     configs/example/apu_se.py --reg-alloc-policy=dynamic -n3 -c \
     allSyncPrims-1kernel --options="lfTreeBarrUniq 10 16 4"
 
@@ -149,12 +167,37 @@
     variant=$2
 
     docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-        "${gem5_root}" --rm gcr.io/gem5-test/sst-env:v21-2 \
-            bash -c "\
+        "${gem5_root}" --rm  --memory="${docker_mem_limit}" \
+        gcr.io/gem5-test/sst-env:v22-0 bash -c "\
 scons build/${isa}/libgem5_${variant}.so -j${compile_threads} --without-tcmalloc; \
 cd ext/sst; \
 make clean; make -j ${compile_threads}; \
 sst --add-lib-path=./ sst/example.py; \
+cd -;
 "
 }
 build_and_run_SST RISCV opt
+
+build_and_run_systemc () {
+    rm -rf "${gem5_root}/build/ARM"
+    docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
+        "${gem5_root}" --memory="${docker_mem_limit}" --rm \
+        gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v22-0 bash -c "\
+scons -j${compile_threads} build/ARM/gem5.opt; \
+scons --with-cxx-config --without-python --without-tcmalloc USE_SYSTEMC=0 \
+    -j${compile_threads} build/ARM/libgem5_opt.so \
+"
+
+    docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
+        "${gem5_root}" --memory="${docker_mem_limit}" --rm \
+        gcr.io/gem5-test/systemc-env:v22-0 bash -c "\
+cd util/systemc/gem5_within_systemc; \
+make -j${compile_threads}; \
+../../../build/ARM/gem5.opt ../../../configs/example/se.py -c \
+    ../../../tests/test-progs/hello/bin/arm/linux/hello; \
+LD_LIBRARY_PATH=../../../build/ARM/:/opt/systemc/lib-linux64/ \
+    ./gem5.opt.sc m5out/config.ini; \
+cd -; \
+"
+}
+build_and_run_systemc
diff --git a/tests/pyunit/stdlib/__init__.py b/tests/pyunit/stdlib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/pyunit/stdlib/__init__.py
diff --git a/tests/pyunit/stdlib/resources/__init__.py b/tests/pyunit/stdlib/resources/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/pyunit/stdlib/resources/__init__.py
diff --git a/tests/pyunit/stdlib/resources/pyunit_downloader_checks.py b/tests/pyunit/stdlib/resources/pyunit_downloader_checks.py
new file mode 100644
index 0000000..02f9b9b
--- /dev/null
+++ b/tests/pyunit/stdlib/resources/pyunit_downloader_checks.py
@@ -0,0 +1,139 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+import tempfile
+import os
+from typing import Dict
+
+from gem5.resources.downloader import(
+    _get_resources_json_at_path,
+    _get_resources_json,
+    _resources_json_version_required,
+)
+
+class MD5FileTestSuite(unittest.TestCase):
+    """Test cases for gem5.resources.downloader"""
+
+    def create_temp_resources_json(self) -> str:
+        """
+        This creates a simple resource.json temp file for testing purposes.
+        """
+
+        file_contents = \
+            "{"  + f"\"version\" : \"{_resources_json_version_required()}\"," \
+            + """
+    "url_base" : "http://dist.gem5.org/dist/v21-2",
+    "previous-versions" : {},
+    "resources": [
+        {
+            "type": "resource",
+            "name" : "riscv-disk-img",
+            "documentation" : "A simple RISCV disk image based on busybox.",
+            "architecture": "RISCV",
+            "is_zipped" : true,
+            "md5sum" : "d6126db9f6bed7774518ae25aa35f153",
+            "url": "{url_base}/images/riscv/busybox/riscv-disk.img.gz",
+            "source" : "src/riscv-fs",
+            "additional_metadata" : {
+                "root_partition": null
+            }
+        },
+        {
+            "type": "resource",
+            "name" : "riscv-lupio-busybox-img",
+            "documentation" : "A RISCV disk image, based on busybox, to ...",
+            "architecture": "RISCV",
+            "is_zipped" : true,
+            "md5sum" : "e5bee8a31f45f4803f87c0d781553ccc",
+            "url": "{url_base}/images/riscv/busybox/riscv-lupio-busybox.img",
+            "source" : "src/lupv",
+            "additional_metadata" : {
+                "root_partition": "1"
+            }
+        }
+    ]
+}
+        """
+        file = tempfile.NamedTemporaryFile(mode="w", delete=False)
+        file.write(file_contents)
+        file.close()
+        return file.name
+
+    def verify_json(self, json: Dict) -> None:
+        """
+        This verifies the JSON file created in created in
+        "create_temp_resources_json" has been loaded correctly into a Python
+        dictionary.
+        """
+        self.assertTrue("resources" in json)
+        self.assertEquals(2, len(json["resources"]))
+        self.assertTrue("name" in json["resources"][0])
+        self.assertEquals("riscv-disk-img", json["resources"][0]["name"])
+        self.assertTrue("name" in json["resources"][1])
+        self.assertEquals("riscv-lupio-busybox-img",
+                          json["resources"][1]["name"])
+
+    def test_get_resources_json_at_path(self) -> None:
+        # Tests the gem5.resources.downloader._get_resources_json_at_path()
+        # function.
+
+        path = self.create_temp_resources_json()
+        json = _get_resources_json_at_path(path = path)
+
+        self.verify_json(json=json)
+
+        # Cleanup the temp file
+        os.remove(path)
+
+    def test_get_resources_json(self) -> None:
+        # Tests the gem5.resources.downloader._get_resources_json() function.
+
+        path = self.create_temp_resources_json()
+
+        # We set the "GEM5_RESOURCE_JSON" environment variable to allow using
+        # our test temp resources.json.
+        os.environ["GEM5_RESOURCE_JSON"] = path
+        json = _get_resources_json()
+        self.verify_json(json=json)
+
+        # Cleanup the temp file
+        os.remove(path)
+
+    def test_get_resources_json_invalid_url(self) -> None:
+        # Tests the gem5.resources.downloader._get_resources_json() function in
+        # case where an invalid url is passed as the URL/PATH of the
+        # resources.json file.
+
+        path = "NotAURLorFilePath"
+        os.environ["GEM5_RESOURCE_JSON"] = path
+        with self.assertRaises(Exception) as context:
+            _get_resources_json()
+
+        self.assertTrue(
+            f"Resources location '{path}' is not a valid path or URL." in \
+            str(context.exception)
+        )
\ No newline at end of file
diff --git a/tests/pyunit/stdlib/resources/pyunit_md5_utils_check.py b/tests/pyunit/stdlib/resources/pyunit_md5_utils_check.py
new file mode 100644
index 0000000..65bf335
--- /dev/null
+++ b/tests/pyunit/stdlib/resources/pyunit_md5_utils_check.py
@@ -0,0 +1,116 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+import tempfile
+import os
+import shutil
+from pathlib import Path
+
+from gem5.resources.md5_utils import md5_file, md5_dir
+
+
+class MD5FileTestSuite(unittest.TestCase):
+    """Test cases for gem5.resources.md5_utils.md5_file()"""
+
+    def test_md5FileConsistency(self) -> None:
+        # This test ensures the md5 algorithm we use does not change the md5
+        # value over time.
+
+        file = tempfile.NamedTemporaryFile(mode="w", delete=False)
+        file.write("This is a test string, to be put in a temp file")
+        file.close()
+        md5 = md5_file(Path(file.name))
+        os.remove(file.name)
+
+        self.assertEquals("b113b29fce251f2023066c3fda2ec9dd", md5)
+
+    def test_identicalFilesIdenticalMd5(self) -> None:
+        # This test ensures that two files with exactly the same contents have
+        # the same md5 value.
+
+        test_str = "This is a test"
+
+        file = tempfile.NamedTemporaryFile(mode="w", delete=False)
+        file.write(test_str)
+        file.close()
+        first_file_md5 = md5_file(Path(file.name))
+
+        os.remove(file.name)
+
+        file = tempfile.NamedTemporaryFile(mode="w", delete=False)
+        file.write(test_str)
+        file.close()
+        second_file_md5 = md5_file(Path(file.name))
+
+        os.remove(file.name)
+
+        self.assertEquals(first_file_md5, second_file_md5)
+
+
+class MD5DirTestSuite(unittest.TestCase):
+    """Test cases for gem5.resources.md5_utils.md5_dir()"""
+
+    def _create_temp_directory(self) -> Path:
+
+        dir = tempfile.mkdtemp()
+
+        with open(os.path.join(dir, "file1"), "w") as f:
+            f.write("Some test data here")
+
+        with open(os.path.join(dir, "file2"), "w") as f:
+            f.write("Some more test data")
+
+        os.mkdir(os.path.join(dir, "dir2"))
+
+        with open(os.path.join(dir, "dir2", "file1"), "w") as f:
+            f.write("Yet more data")
+
+        return Path(dir)
+
+    def test_md5DirConsistency(self) -> None:
+        # This test ensures the md5 algorithm we use does not change the value
+        # given for directories over time.
+
+        dir = self._create_temp_directory()
+        md5 = md5_dir(dir)
+        shutil.rmtree(dir)
+
+        self.assertEquals("ad5ac785de44c9fc2fe2798cab2d7b1a", md5)
+
+    def test_identicalDirsIdenticalMd5(self) -> None:
+        # This test ensures that two directories with exactly the same contents
+        # have the same md5 value.
+
+        dir1 = self._create_temp_directory()
+        first_md5 = md5_dir(dir1)
+        shutil.rmtree(dir1)
+
+        dir2 = self._create_temp_directory()
+        second_md5 = md5_dir(dir2)
+        shutil.rmtree(dir2)
+
+        self.assertEquals(first_md5, second_md5)
diff --git a/tests/weekly.sh b/tests/weekly.sh
index b6eda61..838ccd4 100755
--- a/tests/weekly.sh
+++ b/tests/weekly.sh
@@ -32,6 +32,9 @@
 dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
 gem5_root="${dir}/.."
 
+# The per-container Docker memory limit.
+docker_mem_limit="24g"
+
 # We assume the first two arguments are the number of threads followed by the
 # GPU ISA to test. These default to 1 and GCN3_X86 is no argument is given.
 threads=1
@@ -55,8 +58,8 @@
 
 # Run the gem5 very-long tests.
 docker run -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}"/tests --rm \
-    gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v21-2 \
+    "${gem5_root}"/tests --memory="${docker_mem_limit}" --rm \
+    gcr.io/gem5-test/ubuntu-20.04_all-dependencies:v22-0 \
         ./main.py run --length very-long -j${threads} -t${threads} -vv
 
 mkdir -p tests/testing-results
@@ -64,7 +67,8 @@
 # GPU weekly tests start here
 # before pulling gem5 resources, make sure it doesn't exist already
 docker run --rm --volume "${gem5_root}":"${gem5_root}" -w \
-       "${gem5_root}" gcr.io/gem5-test/gcn-gpu:v21-2 bash -c \
+       "${gem5_root}" --memory="${docker_mem_limit}" \
+       gcr.io/gem5-test/gcn-gpu:v22-0 bash -c \
        "rm -rf ${gem5_root}/gem5-resources"
 # delete Pannotia datasets and output files in case a failed regression run left
 # them around
@@ -91,8 +95,12 @@
 # To ensure the v21.2 version of these tests continues to run as future
 # versions are released, we run this check. If there's been another release,
 # we checkout the correct version of gem5 resources.
+#
+# Note: We disable this code on the develop branch and just checkout develop.
+
 cd "${gem5_root}/gem5-resources"
-version_tag=$(git tag | grep "v21.2")
+git checkout develop
+version_tag=$(git tag | grep "v22.0")
 
 if [[ ${version_tag} != "" ]]; then
        git checkout "${version_tag}"
@@ -105,11 +113,11 @@
 # avoid needing to set all of these, we instead build a docker for it, which
 # has all these variables pre-set in its Dockerfile
 # To avoid compiling gem5 multiple times, all GPU benchmarks will use this
-docker pull gcr.io/gem5-test/gcn-gpu:v21-2
+docker pull gcr.io/gem5-test/gcn-gpu:v22-0
 docker build -t hacc-test-weekly ${gem5_root}/gem5-resources/src/gpu/halo-finder
 
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}" hacc-test-weekly bash -c \
+    "${gem5_root}" --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
     "scons build/${gpu_isa}/gem5.opt -j${threads} \
         || rm -rf build && scons build/${gpu_isa}/gem5.opt -j${threads}"
 
@@ -117,46 +125,49 @@
 # Note: setting TERM in the environment is necessary as scons fails for m5ops if
 # it is not set.
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}/util/m5" hacc-test-weekly bash -c \
+    "${gem5_root}/util/m5" --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
     "export TERM=xterm-256color ; scons build/x86/out/m5"
 
 # test LULESH
 # build LULESH
 docker run --rm --volume "${gem5_root}":"${gem5_root}" -w \
        "${gem5_root}/gem5-resources/src/gpu/lulesh" \
-       -u $UID:$GID hacc-test-weekly bash -c \
+       -u $UID:$GID --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "make"
 
 # LULESH is heavily used in the HPC community on GPUs, and does a good job of
 # stressing several GPU compute and memory components
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}" hacc-test-weekly build/${gpu_isa}/gem5.opt \
-    configs/example/apu_se.py -n3 --mem-size=8GB --reg-alloc-policy=dynamic \
+    "${gem5_root}" --memory="${docker_mem_limit}" \
+    hacc-test-weekly build/${gpu_isa}/gem5.opt configs/example/apu_se.py -n3 \
+    --mem-size=8GB --reg-alloc-policy=dynamic \
     --benchmark-root="${gem5_root}/gem5-resources/src/gpu/lulesh/bin" -c lulesh
 
 # test DNNMark
 # setup cmake for DNNMark
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
      "${gem5_root}/gem5-resources/src/gpu/DNNMark" \
-     hacc-test-weekly bash -c "./setup.sh HIP"
+     --memory="${docker_mem_limit}" hacc-test-weekly bash -c "./setup.sh HIP"
 
 # make the DNNMark library
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
     "${gem5_root}/gem5-resources/src/gpu/DNNMark/build" \
-    hacc-test-weekly bash -c "make -j${threads}"
+     --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
+     "make -j${threads}"
 
 # generate cachefiles -- since we are testing gfx801 and 4 CUs (default config)
 # in tester, we want cachefiles for this setup
 docker run --rm --volume "${gem5_root}":"${gem5_root}" -w \
     "${gem5_root}/gem5-resources/src/gpu/DNNMark" \
     "-v${gem5_root}/gem5-resources/src/gpu/DNNMark/cachefiles:/root/.cache/miopen/2.9.0" \
-    hacc-test-weekly bash -c \
+    --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
     "python3 generate_cachefiles.py cachefiles.csv --gfx-version=gfx801 \
     --num-cus=4"
 
 # generate mmap data for DNNMark (makes simulation much faster)
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
-    "${gem5_root}/gem5-resources/src/gpu/DNNMark" hacc-test-weekly bash -c \
+    "${gem5_root}/gem5-resources/src/gpu/DNNMark" \
+    --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
     "g++ -std=c++0x generate_rand_data.cpp -o generate_rand_data"
 
 docker run --rm -u $UID:$GID --volume "${gem5_root}":"${gem5_root}" -w \
@@ -171,7 +182,8 @@
 # including both inference and training
 docker run --rm --volume "${gem5_root}":"${gem5_root}" -v \
        "${gem5_root}/gem5-resources/src/gpu/DNNMark/cachefiles:/root/.cache/miopen/2.9.0" \
-       -w "${gem5_root}/gem5-resources/src/gpu/DNNMark" hacc-test-weekly \
+       -w "${gem5_root}/gem5-resources/src/gpu/DNNMark" \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
        "${gem5_root}/build/${gpu_isa}/gem5.opt" "${gem5_root}/configs/example/apu_se.py" -n3 \
        --reg-alloc-policy=dynamic \
        --benchmark-root="${gem5_root}/gem5-resources/src/gpu/DNNMark/build/benchmarks/test_fwd_softmax" \
@@ -181,7 +193,8 @@
 
 docker run --rm --volume "${gem5_root}":"${gem5_root}" -v \
        "${gem5_root}/gem5-resources/src/gpu/DNNMark/cachefiles:/root/.cache/miopen/2.9.0" \
-       -w "${gem5_root}/gem5-resources/src/gpu/DNNMark" hacc-test-weekly \
+       -w "${gem5_root}/gem5-resources/src/gpu/DNNMark" \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
        "${gem5_root}/build/${gpu_isa}/gem5.opt" "${gem5_root}/configs/example/apu_se.py" -n3 \
        --reg-alloc-policy=dynamic \
        --benchmark-root="${gem5_root}/gem5-resources/src/gpu/DNNMark/build/benchmarks/test_fwd_pool" \
@@ -191,7 +204,8 @@
 
 docker run --rm --volume "${gem5_root}":"${gem5_root}" -v \
        "${gem5_root}/gem5-resources/src/gpu/DNNMark/cachefiles:/root/.cache/miopen/2.9.0" \
-       -w "${gem5_root}/gem5-resources/src/gpu/DNNMark" hacc-test-weekly \
+       -w "${gem5_root}/gem5-resources/src/gpu/DNNMark" \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
        "${gem5_root}/build/${gpu_isa}/gem5.opt" "${gem5_root}/configs/example/apu_se.py" -n3 \
        --reg-alloc-policy=dynamic \
        --benchmark-root="${gem5_root}/gem5-resources/src/gpu/DNNMark/build/benchmarks/test_bwd_bn" \
@@ -203,12 +217,13 @@
 # build HACC
 docker run --rm -v ${PWD}:${PWD} -w \
        "${gem5_root}/gem5-resources/src/gpu/halo-finder/src" -u $UID:$GID \
-       hacc-test-weekly make hip/ForceTreeTest
+       --memory="${docker_mem_limit}" hacc-test-weekly make hip/ForceTreeTest
 
 # Like LULESH, HACC is heavily used in the HPC community and is used to stress
 # the GPU memory system
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --reg-alloc-policy=dynamic \
        --benchmark-root=${gem5_root}/gem5-resources/src/gpu/halo-finder/src/hip \
        -c ForceTreeTest --options="0.5 0.1 64 0.1 1 N 12 rcb"
@@ -221,14 +236,15 @@
 # build BC
 docker run --rm -v ${PWD}:${PWD} \
        -w ${gem5_root}/gem5-resources/src/gpu/pannotia/bc -u $UID:$GID \
-       hacc-test-weekly bash -c \
+       --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "export GEM5_PATH=${gem5_root} ; make gem5-fusion"
 
 # # get input dataset for BC test
-wget http://dist.gem5.org/dist/v21-2/datasets/pannotia/bc/1k_128k.gr
+wget http://dist.gem5.org/dist/v22-0/datasets/pannotia/bc/1k_128k.gr
 # run BC
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \
        --reg-alloc-policy=dynamic \
        --benchmark-root=gem5-resources/src/gpu/pannotia/bc/bin -c bc.gem5 \
@@ -237,12 +253,13 @@
 # build Color Max
 docker run --rm -v ${gem5_root}:${gem5_root} -w \
        ${gem5_root}/gem5-resources/src/gpu/pannotia/color -u $UID:$GID \
-       hacc-test-weekly bash -c \
+       --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "export GEM5_PATH=${gem5_root} ; make gem5-fusion"
 
 # run Color (Max) (use same input dataset as BC for faster testing)
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \
        --reg-alloc-policy=dynamic \
        --benchmark-root=${gem5_root}/gem5-resources/src/gpu/pannotia/color/bin \
@@ -251,12 +268,13 @@
 # build Color (MaxMin)
 docker run --rm -v ${gem5_root}:${gem5_root} -w \
        ${gem5_root}/gem5-resources/src/gpu/pannotia/color -u $UID:$GID \
-       hacc-test-weekly bash -c \
+       --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "export GEM5_PATH=${gem5_root} ; export VARIANT=MAXMIN ; make gem5-fusion"
 
 # run Color (MaxMin) (use same input dataset as BC for faster testing)
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \
        --reg-alloc-policy=dynamic \
        --benchmark-root=${gem5_root}/gem5-resources/src/gpu/pannotia/color/bin \
@@ -265,12 +283,13 @@
 # build FW
 docker run --rm -v ${gem5_root}:${gem5_root} -w \
        ${gem5_root}/gem5-resources/src/gpu/pannotia/fw -u $UID:$GID \
-       hacc-test-weekly bash -c \
+       --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "export GEM5_PATH=${gem5_root} ; make gem5-fusion"
 
 # run FW (use same input dataset as BC for faster testing)
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \
        --reg-alloc-policy=dynamic \
        --benchmark-root=${gem5_root}/gem5-resources/src/gpu/pannotia/fw/bin \
@@ -279,12 +298,13 @@
 # build MIS
 docker run --rm -v ${gem5_root}:${gem5_root} -w \
        ${gem5_root}/gem5-resources/src/gpu/pannotia/mis -u $UID:$GID \
-       hacc-test-weekly bash -c \
+       --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "export GEM5_PATH=${gem5_root} ; make gem5-fusion"
 
 # run MIS (use same input dataset as BC for faster testing)
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \
        --reg-alloc-policy=dynamic \
        --benchmark-root=${gem5_root}/gem5-resources/src/gpu/pannotia/mis/bin \
@@ -293,14 +313,15 @@
 # build Pagerank Default variant
 docker run --rm -v ${gem5_root}:${gem5_root} -w \
        ${gem5_root}/gem5-resources/src/gpu/pannotia/pagerank -u $UID:$GID \
-       hacc-test-weekly bash -c \
+       --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "export GEM5_PATH=${gem5_root} ; make gem5-fusion"
 
 # get PageRank input dataset
-wget http://dist.gem5.org/dist/v21-2/datasets/pannotia/pagerank/coAuthorsDBLP.graph
+wget http://dist.gem5.org/dist/v22-0/datasets/pannotia/pagerank/coAuthorsDBLP.graph
 # run PageRank (Default)
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \
        --reg-alloc-policy=dynamic \
        --benchmark-root=${gem5_root}/gem5-resources/src/gpu/pannotia/pagerank/bin \
@@ -309,12 +330,13 @@
 # build PageRank SPMV variant
 docker run --rm -v ${gem5_root}:${gem5_root} -w \
        ${gem5_root}/gem5-resources/src/gpu/pannotia/pagerank -u $UID:$GID \
-       hacc-test-weekly bash -c \
+       --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "export GEM5_PATH=${gem5_root} ; export VARIANT=SPMV ; make gem5-fusion"
 
 # run PageRank (SPMV)
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \
        --reg-alloc-policy=dynamic \
        --benchmark-root=${gem5_root}/gem5-resources/src/gpu/pannotia/pagerank/bin \
@@ -323,12 +345,13 @@
 # build SSSP CSR variant
 docker run --rm -v ${gem5_root}:${gem5_root} -w \
        ${gem5_root}/gem5-resources/src/gpu/pannotia/sssp -u $UID:$GID \
-       hacc-test-weekly bash -c \
+       --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "export GEM5_PATH=${gem5_root} ; make gem5-fusion"
 
 # run SSSP (CSR) (use same input dataset as BC for faster testing)
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \
        --reg-alloc-policy=dynamic \
        --benchmark-root=${gem5_root}/gem5-resources/src/gpu/pannotia/sssp/bin \
@@ -337,12 +360,13 @@
 # build SSSP ELL variant
 docker run --rm -v ${gem5_root}:${gem5_root} -w \
        ${gem5_root}/gem5-resources/src/gpu/pannotia/sssp -u $UID:$GID \
-       hacc-test-weekly bash -c \
+       --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "export GEM5_PATH=${gem5_root} ; export VARIANT=ELL ; make gem5-fusion"
 
 # run SSSP (ELL) (use same input dataset as BC for faster testing)
 docker run --rm -v ${gem5_root}:${gem5_root} -w ${gem5_root} -u $UID:$GID \
-       hacc-test-weekly ${gem5_root}/build/${gpu_isa}/gem5.opt \
+       --memory="${docker_mem_limit}" hacc-test-weekly \
+       ${gem5_root}/build/${gpu_isa}/gem5.opt \
        ${gem5_root}/configs/example/apu_se.py -n3 --mem-size=8GB \
        --reg-alloc-policy=dynamic \
        --benchmark-root=${gem5_root}/gem5-resources/src/gpu/pannotia/sssp/bin \
@@ -351,7 +375,7 @@
 # Delete the gem5 resources repo we created -- need to do in docker because of
 # cachefiles DNNMark creates
 docker run --rm --volume "${gem5_root}":"${gem5_root}" -w \
-       "${gem5_root}" hacc-test-weekly bash -c \
+       "${gem5_root}" --memory="${docker_mem_limit}" hacc-test-weekly bash -c \
        "rm -rf ${gem5_root}/gem5-resources"
 
 # delete Pannotia datasets we downloaded and output files it created
diff --git a/util/cloudbuild/cloudbuild_create_images.yaml b/util/cloudbuild/cloudbuild_create_images.yaml
deleted file mode 100644
index 7549012..0000000
--- a/util/cloudbuild/cloudbuild_create_images.yaml
+++ /dev/null
@@ -1,110 +0,0 @@
-steps:
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/ubuntu-20.04_all-dependencies:latest',
-            'util/dockerfiles/ubuntu-20.04_all-dependencies']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/ubuntu-20.04_min-dependencies:latest',
-            'util/dockerfiles/ubuntu-20.04_min-dependencies']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/ubuntu-18.04_all-dependencies:latest',
-            'util/dockerfiles/ubuntu-18.04_all-dependencies']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/gcc-version-7:latest',
-            '--build-arg', 'version=7',
-            'util/dockerfiles/ubuntu-18.04_gcc-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/gcc-version-8:latest',
-            '--build-arg', 'version=8',
-            'util/dockerfiles/ubuntu-18.04_gcc-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/gcc-version-9:latest',
-            '--build-arg', 'version=9',
-            'util/dockerfiles/ubuntu-20.04_gcc-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/gcc-version-10:latest',
-            '--build-arg', 'version=10',
-            'util/dockerfiles/ubuntu-20.04_gcc-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/clang-version-6.0:latest',
-            '--build-arg', 'version=6.0',
-            'util/dockerfiles/ubuntu-18.04_clang-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/clang-version-7:latest',
-            '--build-arg', 'version=7',
-            'util/dockerfiles/ubuntu-18.04_clang-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/clang-version-8:latest',
-            '--build-arg', 'version=8',
-            'util/dockerfiles/ubuntu-18.04_clang-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/clang-version-9:latest',
-            '--build-arg', 'version=9',
-            'util/dockerfiles/ubuntu-18.04_clang-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/clang-version-10:latest',
-            '--build-arg', 'version=10',
-            'util/dockerfiles/ubuntu-20.04_clang-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/clang-version-11:latest',
-            '--build-arg', 'version=11',
-            'util/dockerfiles/ubuntu-20.04_clang-version']
-
-    - name: 'gcr.io/cloud-builders/docker'
-      args: ['build',
-            '-t',
-            'gcr.io/$PROJECT_ID/gcn-gpu:latest',
-            'util/dockerfiles/gcn-gpu']
-
-images: ['gcr.io/$PROJECT_ID/ubuntu-20.04_all-dependencies:latest',
-         'gcr.io/$PROJECT_ID/ubuntu-20.04_min-dependencies:latest',
-         'gcr.io/$PROJECT_ID/ubuntu-18.04_all-dependencies:latest',
-         'gcr.io/$PROJECT_ID/gcc-version-7:latest',
-         'gcr.io/$PROJECT_ID/gcc-version-8:latest',
-         'gcr.io/$PROJECT_ID/gcc-version-9:latest',
-         'gcr.io/$PROJECT_ID/gcc-version-10:latest',
-         'gcr.io/$PROJECT_ID/clang-version-6.0:latest',
-         'gcr.io/$PROJECT_ID/clang-version-7:latest',
-         'gcr.io/$PROJECT_ID/clang-version-8:latest',
-         'gcr.io/$PROJECT_ID/clang-version-9:latest',
-         'gcr.io/$PROJECT_ID/clang-version-10:latest',
-         'gcr.io/$PROJECT_ID/clang-version-11:latest',
-         'gcr.io/$PROJECT_ID/gcn-gpu:latest']
-timeout: 18000s # 5 Hours
diff --git a/util/cxx_config/Makefile b/util/cxx_config/Makefile
index 1cb2eda..455d031 100644
--- a/util/cxx_config/Makefile
+++ b/util/cxx_config/Makefile
@@ -36,8 +36,8 @@
 ARCH = ARM
 VARIANT = opt
 
-CXXFLAGS = -I../../build/$(ARCH) -L../../build/$(ARCH)
-CXXFLAGS += -std=c++0x
+CXXFLAGS = -I../../build/$(ARCH) -L../../build/$(ARCH) -DTRACING_ON=1
+CXXFLAGS += -std=c++17
 LIBS = -lgem5_$(VARIANT)
 
 ## You may also need protobuf flags
diff --git a/util/cxx_config/main.cc b/util/cxx_config/main.cc
index ede2d1d..dc4435c 100644
--- a/util/cxx_config/main.cc
+++ b/util/cxx_config/main.cc
@@ -108,11 +108,10 @@
     if (argc == 1)
         usage(prog_name);
 
-    cxxConfigInit();
-
     initSignals();
 
     setClockFrequency(1000000000000);
+    fixClockFrequency();
     curEventQueue(getEventQueue(0));
 
     statistics::initSimStats();
@@ -248,7 +247,7 @@
         /* FIXME, this should really be serialising just for
          *  config_manager rather than using serializeAll's ugly
          *  SimObject static object list */
-        Serializable::serializeAll(checkpoint_dir);
+        SimObject::serializeAll(checkpoint_dir);
 
         std::cerr << "Completed checkpoint\n";
 
@@ -258,11 +257,11 @@
     if (checkpoint_restore) {
         std::cerr << "Restoring checkpoint\n";
 
-        CheckpointIn *checkpoint = new CheckpointIn(checkpoint_dir,
-            config_manager->getSimObjectResolver());
+        SimObject::setSimObjectResolver(
+            &config_manager->getSimObjectResolver());
+        CheckpointIn *checkpoint = new CheckpointIn(checkpoint_dir);
 
         DrainManager::instance().preCheckpointRestore();
-        Serializable::unserializeGlobals(*checkpoint);
         config_manager->loadState(*checkpoint);
         config_manager->startup();
 
diff --git a/util/dockerfiles/README.md b/util/dockerfiles/README.md
new file mode 100644
index 0000000..18686fe
--- /dev/null
+++ b/util/dockerfiles/README.md
@@ -0,0 +1,3 @@
+This directory contains Dockerfiles used to create images used in the gem5 project.
+The `docker-compose.yaml` defines the building of each image.
+The images can be built locally using `docker-compose build`.
diff --git a/util/dockerfiles/docker-compose.yaml b/util/dockerfiles/docker-compose.yaml
new file mode 100644
index 0000000..103c221
--- /dev/null
+++ b/util/dockerfiles/docker-compose.yaml
@@ -0,0 +1,127 @@
+version: '2'
+
+services:
+    gcn-gpu:
+        build:
+            context: gcn-gpu
+            dockerfile: Dockerfile
+        image: gcr.io/gem5-test/gcn-gpu
+    gpu-fs:
+        build:
+            context: gpu-fs
+            dockerfile: Dockerfile
+        image: gcr.io/gem5-test/gpu-fs
+    sst:
+        build:
+            context: sst-11.1.0
+            dockerfile: Dockerfile
+        image: gcr.io/gem5-test/sst-env
+    systemc:
+        build:
+            context: systemc-2.3.3
+            dockerfile: Dockerfile
+        image: gcr.io/gem5-test/systemc-env
+    ubuntu-18.04_all-dependencies:
+        build:
+            context: ubuntu-18.04_all-dependencies
+            dockerfile: Dockerfile
+        image: gcr.io/gem5-test/ubuntu-18.04_all-dependencies
+    ubuntu-20.04_all-dependencies:
+        build:
+            context: ubuntu-20.04_all-dependencies
+            dockerfile: Dockerfile
+        image: gcr.io/gem5-test/ubuntu-20.04_all-dependencies
+    ubuntu-20.04_min-dependencies:
+        build:
+            context: ubuntu-20.04_min-dependencies
+            dockerfile: Dockerfile
+        image: gcr.io/gem5-test/ubuntu-20.04_min-dependencies
+    gcc-7:
+        build:
+            context: ubuntu-18.04_gcc-version
+            dockerfile: Dockerfile
+            args:
+                - version=7
+        image: gcr.io/gem5-test/gcc-version-7
+    gcc-8:
+        build:
+            context: ubuntu-18.04_gcc-version
+            dockerfile: Dockerfile
+            args:
+                - version=8
+        image: gcr.io/gem5-test/gcc-version-8
+    gcc-9:
+        build:
+            context: ubuntu-20.04_gcc-version
+            dockerfile: Dockerfile
+            args:
+                - version=9
+        image: gcr.io/gem5-test/gcc-version-9
+    gcc-10:
+        build:
+            context: ubuntu-20.04_gcc-version
+            dockerfile: Dockerfile
+            args:
+                - version=10
+        image: gcr.io/gem5-test/gcc-version-10
+    gcc-11:
+        build:
+            context: ubuntu-20.04_gcc-version-11
+            dockerfile: Dockerfile
+            args:
+                - version=11
+        image: gcr.io/gem5-test/gcc-version-11
+    clang-6:
+        build:
+            context: ubuntu-18.04_clang-version
+            dockerfile: Dockerfile
+            args:
+                - version=6.0
+        image: gcr.io/gem5-test/clang-version-6.0
+    clang-7:
+        build:
+            context: ubuntu-18.04_clang-version
+            dockerfile: Dockerfile
+            args:
+                - version=7
+        image: gcr.io/gem5-test/clang-version-7
+    clang-8:
+        build:
+            context: ubuntu-18.04_clang-version
+            dockerfile: Dockerfile
+            args:
+                - version=8
+        image: gcr.io/gem5-test/clang-version-8
+    clang-9:
+        build:
+            context: ubuntu-18.04_clang-version
+            dockerfile: Dockerfile
+            args:
+                - version=9
+        image: gcr.io/gem5-test/clang-version-9
+    clang-10:
+        build:
+            context: ubuntu-20.04_clang-version
+            dockerfile: Dockerfile
+            args:
+                - version=10
+        image: gcr.io/gem5-test/clang-version-10
+    clang-11:
+        build:
+            context: ubuntu-20.04_clang-version
+            dockerfile: Dockerfile
+            args:
+                - version=11
+        image: gcr.io/gem5-test/clang-version-11
+    clang-12:
+        build:
+            context: ubuntu-20.04_clang-version
+            dockerfile: Dockerfile
+            args:
+                - version=12
+        image: gcr.io/gem5-test/clang-version-12
+    llvm-gnu-cross-compiler-riscv64:
+        build:
+            context: llvm-gnu-cross-compiler-riscv64
+            dockerfile: Dockerfile
+        image: gcr.io/gem5-test/llvm-gnu-cross-compiler-riscv64
diff --git a/util/dockerfiles/gcn-gpu/Dockerfile b/util/dockerfiles/gcn-gpu/Dockerfile
index 4102d68..be58514 100644
--- a/util/dockerfiles/gcn-gpu/Dockerfile
+++ b/util/dockerfiles/gcn-gpu/Dockerfile
@@ -25,11 +25,10 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 FROM ubuntu:20.04
 ENV DEBIAN_FRONTEND=noninteractive
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
+RUN apt -y update && apt -y upgrade && \
+    apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
     libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \
-    python3-dev python3-six python-is-python3 doxygen libboost-all-dev \
+    python3-dev python-is-python3 doxygen libboost-all-dev \
     libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config
 
 # Requirements for ROCm
@@ -70,7 +69,7 @@
 
 WORKDIR /ROCclr
 # The patch allows us to avoid building blit kernels on-the-fly in gem5
-RUN wget -q -O - dist.gem5.org/dist/v21-2/rocm_patches/ROCclr.patch | git apply -v
+RUN wget -q -O - dist.gem5.org/dist/v22-0/rocm_patches/ROCclr.patch | git apply -v
 
 WORKDIR /ROCclr/build
 RUN cmake -DOPENCL_DIR="/ROCm-OpenCL-Runtime" \
@@ -105,11 +104,11 @@
 RUN git clone -b rocm-4.0.0 \
     https://github.com/ROCmSoftwarePlatform/rocBLAS.git && mkdir rocBLAS/build
 
-ENV HCC_AMDGPU_TARGET=gfx801
+ENV HCC_AMDGPU_TARGET=gfx801,gfx803,gfx900,gfx902
 WORKDIR rocBLAS
-# rocBLAS needs to be built from source otherwise gfx801 gets an error in HIP
+# rocBLAS needs to be built from source otherwise certain gfx versions get errors in HIP
 # about there being no GPU binary available
-RUN ./install.sh -d -a all -i
+RUN ./install.sh -d -i
 WORKDIR /
 
 # MIOpen dependencies + MIOpen
diff --git a/util/dockerfiles/gpu-fs/Dockerfile b/util/dockerfiles/gpu-fs/Dockerfile
new file mode 100644
index 0000000..63ae6b0
--- /dev/null
+++ b/util/dockerfiles/gpu-fs/Dockerfile
@@ -0,0 +1,55 @@
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+FROM ubuntu:20.04
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt -y update && apt -y upgrade && \
+    apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
+    libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \
+    python3-dev python-is-python3 doxygen libboost-all-dev \
+    libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config
+
+# Requirements for ROCm
+RUN apt -y install cmake mesa-common-dev libgflags-dev libgoogle-glog-dev
+
+# Needed to get ROCm repo, build packages
+RUN apt -y install wget gnupg2 rpm
+
+# Get the radeon gpg key for apt repository
+RUN wget -q -O - https://repo.radeon.com/rocm/rocm.gpg.key | apt-key add -
+
+# Modify apt sources to pull from ROCm 4.2 repository only
+RUN echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/4.2/ ubuntu main' | tee /etc/apt/sources.list.d/rocm.list
+
+RUN apt-get update
+RUN apt -y install libnuma-dev
+
+# Install the ROCm-dkms source
+RUN apt -y install initramfs-tools
+RUN apt -y install rocm-dkms
diff --git a/util/dockerfiles/llvm-gnu-cross-compiler-riscv64/Dockerfile b/util/dockerfiles/llvm-gnu-cross-compiler-riscv64/Dockerfile
new file mode 100644
index 0000000..df7a58c
--- /dev/null
+++ b/util/dockerfiles/llvm-gnu-cross-compiler-riscv64/Dockerfile
@@ -0,0 +1,73 @@
+# Copyright (c) 2022 The Regents of the University of California
+# All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+# stage 1: download the dependencies
+FROM ubuntu:20.04 AS stage1
+
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt -y update && apt -y upgrade && apt -y install \
+  binutils build-essential libtool texinfo gzip zip unzip patchutils curl git \
+  make cmake ninja-build automake bison flex gperf grep sed gawk bc \
+  zlib1g-dev libexpat1-dev libmpc-dev libglib2.0-dev libfdt-dev libpixman-1-dev
+
+# stage 2: download the compilers and compile them
+FROM stage1 AS stage2
+RUN mkdir -p /riscv/_install
+WORKDIR /riscv
+ENV PATH=`/riscv/_install/bin:$PATH`
+RUN git clone https://github.com/riscv/riscv-gnu-toolchain
+WORKDIR /riscv/riscv-gnu-toolchain
+RUN git checkout 051b9f7ddb7d136777505ea19c70a41926842b96
+RUN git submodule update --init --recursive
+RUN ./configure --prefix=/riscv/_install --enable-multilib
+RUN make linux -j`nproc`
+RUN make install
+
+WORKDIR /riscv
+RUN git clone https://github.com/llvm/llvm-project.git riscv-llvm
+WORKDIR /riscv/riscv-llvm
+RUN git checkout 2ef95efb414e215490a222de05cafdffb8054758
+RUN ln -s ../../clang llvm/tools || true
+RUN mkdir _build
+WORKDIR /riscv/riscv-llvm/_build
+RUN cmake -G Ninja -DCMAKE_BUILD_TYPE="Release" \
+  -DBUILD_SHARED_LIBS=True -DLLVM_USE_SPLIT_DWARF=True \
+  -DCMAKE_INSTALL_PREFIX="/riscv/_install" \
+  -DLLVM_OPTIMIZED_TABLEGEN=True -DLLVM_BUILD_TESTS=False \
+  -DDEFAULT_SYSROOT="/riscv/_install/sysroot" \
+  -DLLVM_DEFAULT_TARGET_TRIPLE="riscv64-unknown-linux-gnu" \
+  -DLLVM_TARGETS_TO_BUILD="RISCV" \
+  ../llvm
+RUN cmake --build . --target install -j`nproc`
+
+# stage 3: create a new container with the compiled cross-compilers only
+FROM stage1
+
+RUN mkdir -p /riscv/
+COPY --from=stage2 /riscv/_install/ /riscv/_install
+ENV PATH=/riscv/_install/bin:$PATH
+
diff --git a/util/dockerfiles/sst-11.1.0/Dockerfile b/util/dockerfiles/sst-11.1.0/Dockerfile
index f5fc0f2..c985374 100644
--- a/util/dockerfiles/sst-11.1.0/Dockerfile
+++ b/util/dockerfiles/sst-11.1.0/Dockerfile
@@ -27,11 +27,10 @@
 FROM ubuntu:20.04
 
 ENV DEBIAN_FRONTEND=noninteractive
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
+RUN apt -y update && apt -y upgrade && \
+    apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
     libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \
-    python3-dev python3-six python-is-python3 doxygen libboost-all-dev \
+    python3-dev python-is-python3 doxygen libboost-all-dev \
     libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config pip \
     python3-venv wget
 
diff --git a/src/cpu/simple/SConsopts b/util/dockerfiles/systemc-2.3.3/Dockerfile
similarity index 61%
copy from src/cpu/simple/SConsopts
copy to util/dockerfiles/systemc-2.3.3/Dockerfile
index f12fee2..0653a86 100644
--- a/src/cpu/simple/SConsopts
+++ b/util/dockerfiles/systemc-2.3.3/Dockerfile
@@ -1,7 +1,5 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright (c) 2021 The Regents of the University of California
+# All Rights Reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -26,6 +24,30 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+FROM ubuntu:20.04
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt -y update && apt -y upgrade && \
+    apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
+    libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \
+    python3-dev python-is-python3 doxygen libboost-all-dev \
+    libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config pip \
+    python3-venv wget
+
+RUN mkdir /systemc
+WORKDIR /systemc
+RUN wget https://www.accellera.org/images/downloads/standards/systemc/systemc-2.3.3.tar.gz; \
+    tar xf systemc-2.3.3.tar.gz
+
+WORKDIR /systemc/systemc-2.3.3
+RUN mkdir objdir
+WORKDIR objdir
+ENV CXX="g++"
+ENV CXXFLAGS="-std=c++17"
+
+RUN ../configure --prefix=/opt/systemc
+RUN make -j8 && make install
+RUN make clean
+
+WORKDIR /
+RUN rm -rf /systemc/systemc-2.3.3/objdir
diff --git a/util/dockerfiles/ubuntu-18.04_all-dependencies/Dockerfile b/util/dockerfiles/ubuntu-18.04_all-dependencies/Dockerfile
index 5a9fd8d..a05e0fe 100644
--- a/util/dockerfiles/ubuntu-18.04_all-dependencies/Dockerfile
+++ b/util/dockerfiles/ubuntu-18.04_all-dependencies/Dockerfile
@@ -26,11 +26,10 @@
 
 FROM ubuntu:18.04
 
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
+RUN apt -y update && apt -y upgrade && \
+    apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
     libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \
-    python3-dev python3 python3-six doxygen libboost-all-dev \
+    python3-dev python3 doxygen libboost-all-dev \
     libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config \
     python3-pip python3-venv
 
diff --git a/util/dockerfiles/ubuntu-18.04_clang-version/Dockerfile b/util/dockerfiles/ubuntu-18.04_clang-version/Dockerfile
index 869a2c1..3d9c3a7 100644
--- a/util/dockerfiles/ubuntu-18.04_clang-version/Dockerfile
+++ b/util/dockerfiles/ubuntu-18.04_clang-version/Dockerfile
@@ -36,11 +36,10 @@
 # 9
 ARG version
 
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install git m4 scons zlib1g zlib1g-dev clang-${version} \
+RUN apt -y update && apt -y upgrade && \
+    apt -y install git m4 scons zlib1g zlib1g-dev clang-${version} \
     libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \
-    python3-dev python3 python3-six doxygen make
+    python3-dev python3 doxygen make
 
 RUN apt-get --purge -y remove gcc
 
diff --git a/util/dockerfiles/ubuntu-18.04_gcc-version/Dockerfile b/util/dockerfiles/ubuntu-18.04_gcc-version/Dockerfile
index 1723fd9..3e94e8d 100644
--- a/util/dockerfiles/ubuntu-18.04_gcc-version/Dockerfile
+++ b/util/dockerfiles/ubuntu-18.04_gcc-version/Dockerfile
@@ -33,11 +33,10 @@
 # 8
 ARG version
 
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install git m4 scons zlib1g zlib1g-dev gcc-multilib \
+RUN apt -y update && apt -y upgrade && \
+    apt -y install git m4 scons zlib1g zlib1g-dev gcc-multilib \
     libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \
-    python3-dev python3 python3-six doxygen wget zip gcc-${version} \
+    python3-dev python3 doxygen wget zip gcc-${version} \
     g++-${version} make
 
 RUN update-alternatives --install \
diff --git a/util/dockerfiles/ubuntu-20.04_all-dependencies/Dockerfile b/util/dockerfiles/ubuntu-20.04_all-dependencies/Dockerfile
index 76d3011..27a6382 100644
--- a/util/dockerfiles/ubuntu-20.04_all-dependencies/Dockerfile
+++ b/util/dockerfiles/ubuntu-20.04_all-dependencies/Dockerfile
@@ -27,12 +27,11 @@
 FROM ubuntu:20.04
 
 ENV DEBIAN_FRONTEND=noninteractive
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
+RUN apt -y update && apt -y upgrade && \
+    apt -y install build-essential git m4 scons zlib1g zlib1g-dev \
     libprotobuf-dev protobuf-compiler libprotoc-dev libgoogle-perftools-dev \
-    python3-dev python3-six python-is-python3 doxygen libboost-all-dev \
+    python3-dev python-is-python3 doxygen libboost-all-dev \
     libhdf5-serial-dev python3-pydot libpng-dev libelf-dev pkg-config pip \
     python3-venv
 
-RUN pip install mypy
\ No newline at end of file
+RUN pip install mypy
diff --git a/util/dockerfiles/ubuntu-20.04_clang-version/Dockerfile b/util/dockerfiles/ubuntu-20.04_clang-version/Dockerfile
index 447a4dd..00f34c4 100644
--- a/util/dockerfiles/ubuntu-20.04_clang-version/Dockerfile
+++ b/util/dockerfiles/ubuntu-20.04_clang-version/Dockerfile
@@ -32,14 +32,14 @@
 # 9
 # 10
 # 11
+# 12
 ARG version
 
 ENV DEBIAN_FRONTEND=noninteractive
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install git m4 scons zlib1g zlib1g-dev libprotobuf-dev \
+RUN apt -y update && apt -y upgrade && \
+    apt -y install git m4 scons zlib1g zlib1g-dev libprotobuf-dev \
     protobuf-compiler libprotoc-dev libgoogle-perftools-dev python3-dev \
-    python3-six python-is-python3 doxygen libboost-all-dev libhdf5-serial-dev \
+    python-is-python3 doxygen libboost-all-dev libhdf5-serial-dev \
     python3-pydot libpng-dev clang-${version} make
 
 RUN apt-get --purge -y remove gcc
diff --git a/util/dockerfiles/ubuntu-20.04_gcc-version-11/Dockerfile b/util/dockerfiles/ubuntu-20.04_gcc-version-11/Dockerfile
index 4effd9c..f01479d5 100644
--- a/util/dockerfiles/ubuntu-20.04_gcc-version-11/Dockerfile
+++ b/util/dockerfiles/ubuntu-20.04_gcc-version-11/Dockerfile
@@ -31,11 +31,10 @@
 # installed via APT more easily.
 
 ENV DEBIAN_FRONTEND=noninteractive
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install git m4 scons zlib1g zlib1g-dev libprotobuf-dev \
+RUN apt -y update && apt -y upgrade && \
+    apt -y install git m4 scons zlib1g zlib1g-dev libprotobuf-dev \
     protobuf-compiler libprotoc-dev libgoogle-perftools-dev python3-dev \
-    python3-six python-is-python3 doxygen libboost-all-dev libhdf5-serial-dev \
+    python-is-python3 doxygen libboost-all-dev libhdf5-serial-dev \
     python3-pydot libpng-dev make software-properties-common
 
 RUN add-apt-repository \
diff --git a/util/dockerfiles/ubuntu-20.04_gcc-version/Dockerfile b/util/dockerfiles/ubuntu-20.04_gcc-version/Dockerfile
index 923fe63..0ec8083 100644
--- a/util/dockerfiles/ubuntu-20.04_gcc-version/Dockerfile
+++ b/util/dockerfiles/ubuntu-20.04_gcc-version/Dockerfile
@@ -33,11 +33,10 @@
 ARG version
 
 ENV DEBIAN_FRONTEND=noninteractive
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install git m4 scons zlib1g zlib1g-dev libprotobuf-dev \
+RUN apt -y update && apt -y upgrade && \
+    apt -y install git m4 scons zlib1g zlib1g-dev libprotobuf-dev \
     protobuf-compiler libprotoc-dev libgoogle-perftools-dev python3-dev \
-    python3-six python-is-python3 doxygen libboost-all-dev libhdf5-serial-dev \
+    python-is-python3 doxygen libboost-all-dev libhdf5-serial-dev \
     python3-pydot libpng-dev gcc-${version} g++-${version} make
 
 RUN update-alternatives --install \
diff --git a/util/dockerfiles/ubuntu-20.04_min-dependencies/Dockerfile b/util/dockerfiles/ubuntu-20.04_min-dependencies/Dockerfile
index 68c61ef..4b65146 100644
--- a/util/dockerfiles/ubuntu-20.04_min-dependencies/Dockerfile
+++ b/util/dockerfiles/ubuntu-20.04_min-dependencies/Dockerfile
@@ -27,6 +27,5 @@
 FROM ubuntu:20.04
 
 ENV DEBIAN_FRONTEND=noninteractive
-RUN apt -y update
-RUN apt -y upgrade
-RUN apt -y install build-essential m4 scons python3-dev python-is-python3
+RUN apt -y update && apt -y upgrade && \
+    apt -y install build-essential m4 scons python3-dev python-is-python3
diff --git a/util/m5/SConstruct b/util/m5/SConstruct
index 941b9fe..62be63c 100644
--- a/util/m5/SConstruct
+++ b/util/m5/SConstruct
@@ -179,7 +179,7 @@
 
 # Bring in the googletest sources.
 native.SConscript(googletest_dir.File('SConscript'),
-        variant_dir=native_dir.Dir('googletest'), exports={ 'main': native })
+        variant_dir=native_dir.Dir('googletest'), exports={ 'env': native })
 
 native.SConscript(src_dir.File('SConscript.native'),
         variant_dir=native_dir, exports={ 'env': native })
@@ -268,6 +268,6 @@
         # Bring in the googletest sources.
         env.SConscript(googletest_dir.File('SConscript'),
                 variant_dir=abi_dir.Dir('googletest'),
-                exports={ 'main': env })
+                exports='env')
         env.SConscript(src_dir.File('SConscript'),
                        variant_dir=abi_dir, exports='env')
diff --git a/src/cpu/simple/SConsopts b/util/md5.py
similarity index 68%
copy from src/cpu/simple/SConsopts
copy to util/md5.py
index f12fee2..333f714 100644
--- a/src/cpu/simple/SConsopts
+++ b/util/md5.py
@@ -1,6 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
+# Copyright (c) 2022 The Regents of the University of California
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -26,6 +24,28 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-Import('*')
+import argparse
+from pathlib import Path
+from gem5.resources.md5_utils import md5_file, md5_dir
 
-main.Append(ALL_CPU_MODELS=['AtomicSimpleCPU', 'TimingSimpleCPU'])
+parser = argparse.ArgumentParser(
+    description="A utility to determine the md5 hash of files and "
+    "directories. These will correspond the hash values recorded "
+    "for gem5-resources entries."
+)
+
+parser.add_argument("path", type=str, help="The path to the file/directory.")
+
+args = parser.parse_args()
+
+path = Path(args.path)
+
+if path.is_file():
+    print(md5_file(path))
+    exit(0)
+elif path.is_dir():
+    print(md5_dir(path))
+    exit(0)
+
+print("Input path is neither a file nor directory.")
+exit(1)
diff --git a/util/stats/barchart.py b/util/stats/barchart.py
deleted file mode 100644
index 77505b1..0000000
--- a/util/stats/barchart.py
+++ /dev/null
@@ -1,339 +0,0 @@
-# Copyright (c) 2005-2006 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import matplotlib, pylab
-from matplotlib.font_manager import FontProperties
-from matplotlib.numerix import array, arange, reshape, shape, transpose, zeros
-from matplotlib.numerix import Float
-from matplotlib.ticker import NullLocator
-from functools import reduce
-
-matplotlib.interactive(False)
-
-from .chart import ChartOptions
-
-class BarChart(ChartOptions):
-    def __init__(self, default=None, **kwargs):
-        super(BarChart, self).__init__(default, **kwargs)
-        self.inputdata = None
-        self.chartdata = None
-        self.inputerr = None
-        self.charterr = None
-
-    def gen_colors(self, count):
-        cmap = matplotlib.cm.get_cmap(self.colormap)
-        if count == 1:
-            return cmap([ 0.5 ])
-
-        if count < 5:
-            return cmap(arange(5) / float(4))[:count]
-
-        return cmap(arange(count) / float(count - 1))
-
-    # The input data format does not match the data format that the
-    # graph function takes because it is intuitive.  The conversion
-    # from input data format to chart data format depends on the
-    # dimensionality of the input data.  Check here for the
-    # dimensionality and correctness of the input data
-    def set_data(self, data):
-        if data is None:
-            self.inputdata = None
-            self.chartdata = None
-            return
-
-        data = array(data)
-        dim = len(shape(data))
-        if dim not in (1, 2, 3):
-            raise AttributeError("Input data must be a 1, 2, or 3d matrix")
-        self.inputdata = data
-
-        # If the input data is a 1d matrix, then it describes a
-        # standard bar chart.
-        if dim == 1:
-            self.chartdata = array([[data]])
-
-        # If the input data is a 2d matrix, then it describes a bar
-        # chart with groups. The matrix being an array of groups of
-        # bars.
-        if dim == 2:
-            self.chartdata = transpose([data], axes=(2,0,1))
-
-        # If the input data is a 3d matrix, then it describes an array
-        # of groups of bars with each bar being an array of stacked
-        # values.
-        if dim == 3:
-            self.chartdata = transpose(data, axes=(1,2,0))
-
-    def get_data(self):
-        return self.inputdata
-
-    data = property(get_data, set_data)
-
-    def set_err(self, err):
-        if err is None:
-            self.inputerr = None
-            self.charterr = None
-            return
-
-        err = array(err)
-        dim = len(shape(err))
-        if dim not in (1, 2, 3):
-            raise AttributeError("Input err must be a 1, 2, or 3d matrix")
-        self.inputerr = err
-
-        if dim == 1:
-            self.charterr = array([[err]])
-
-        if dim == 2:
-            self.charterr = transpose([err], axes=(2,0,1))
-
-        if dim == 3:
-            self.charterr = transpose(err, axes=(1,2,0))
-
-    def get_err(self):
-        return self.inputerr
-
-    err = property(get_err, set_err)
-
-    # Graph the chart data.
-    # Input is a 3d matrix that describes a plot that has multiple
-    # groups, multiple bars in each group, and multiple values stacked
-    # in each bar.  The underlying bar() function expects a sequence of
-    # bars in the same stack location and same group location, so the
-    # organization of the matrix is that the inner most sequence
-    # represents one of these bar groups, then those are grouped
-    # together to make one full stack of bars in each group, and then
-    # the outer most layer describes the groups.  Here is an example
-    # data set and how it gets plotted as a result.
-    #
-    # e.g. data = [[[10,11,12], [13,14,15],  [16,17,18], [19,20,21]],
-    #              [[22,23,24], [25,26,27],  [28,29,30], [31,32,33]]]
-    #
-    # will plot like this:
-    #
-    #    19 31    20 32    21 33
-    #    16 28    17 29    18 30
-    #    13 25    14 26    15 27
-    #    10 22    11 23    12 24
-    #
-    # Because this arrangement is rather conterintuitive, the rearrange
-    # function takes various matricies and arranges them to fit this
-    # profile.
-    #
-    # This code deals with one of the dimensions in the matrix being
-    # one wide.
-    #
-    def graph(self):
-        if self.chartdata is None:
-            raise AttributeError("Data not set for bar chart!")
-
-        dim = len(shape(self.inputdata))
-        cshape = shape(self.chartdata)
-        if self.charterr is not None and shape(self.charterr) != cshape:
-            raise AttributeError('Dimensions of error and data do not match')
-
-        if dim == 1:
-            colors = self.gen_colors(cshape[2])
-            colors = [ [ colors ] * cshape[1] ] * cshape[0]
-
-        if dim == 2:
-            colors = self.gen_colors(cshape[0])
-            colors = [ [ [ c ] * cshape[2] ] * cshape[1] for c in colors ]
-
-        if dim == 3:
-            colors = self.gen_colors(cshape[1])
-            colors = [ [ [ c ] * cshape[2] for c in colors ] ] * cshape[0]
-
-        colors = array(colors)
-
-        self.figure = pylab.figure(figsize=self.chart_size)
-
-        outer_axes = None
-        inner_axes = None
-        if self.xsubticks is not None:
-            color = self.figure.get_facecolor()
-            self.metaaxes = self.figure.add_axes(self.figure_size,
-                                                 axisbg=color, frameon=False)
-            for tick in self.metaaxes.xaxis.majorTicks:
-                tick.tick1On = False
-                tick.tick2On = False
-            self.metaaxes.set_yticklabels([])
-            self.metaaxes.set_yticks([])
-            size = [0] * 4
-            size[0] = self.figure_size[0]
-            size[1] = self.figure_size[1] + .12
-            size[2] = self.figure_size[2]
-            size[3] = self.figure_size[3] - .12
-            self.axes = self.figure.add_axes(size)
-            outer_axes = self.metaaxes
-            inner_axes = self.axes
-        else:
-            self.axes = self.figure.add_axes(self.figure_size)
-            outer_axes = self.axes
-            inner_axes = self.axes
-
-        bars_in_group = len(self.chartdata)
-
-        width = 1.0 / ( bars_in_group + 1)
-        center = width / 2
-
-        bars = []
-        for i,stackdata in enumerate(self.chartdata):
-            bottom = array([0.0] * len(stackdata[0]), Float)
-            stack = []
-            for j,bardata in enumerate(stackdata):
-                bardata = array(bardata)
-                ind = arange(len(bardata)) + i * width + center
-                yerr = None
-                if self.charterr is not None:
-                    yerr = self.charterr[i][j]
-                bar = self.axes.bar(ind, bardata, width, bottom=bottom,
-                                    color=colors[i][j], yerr=yerr)
-                if self.xsubticks is not None:
-                    self.metaaxes.bar(ind, [0] * len(bardata), width)
-                stack.append(bar)
-                bottom += bardata
-            bars.append(stack)
-
-        if self.xlabel is not None:
-            outer_axes.set_xlabel(self.xlabel)
-
-        if self.ylabel is not None:
-            inner_axes.set_ylabel(self.ylabel)
-
-        if self.yticks is not None:
-            ymin, ymax = self.axes.get_ylim()
-            nticks = float(len(self.yticks))
-            ticks = arange(nticks) / (nticks - 1) * (ymax - ymin)  + ymin
-            inner_axes.set_yticks(ticks)
-            inner_axes.set_yticklabels(self.yticks)
-        elif self.ylim is not None:
-            inner_axes.set_ylim(self.ylim)
-
-        if self.xticks is not None:
-            outer_axes.set_xticks(arange(cshape[2]) + .5)
-            outer_axes.set_xticklabels(self.xticks)
-
-        if self.xsubticks is not None:
-            numticks = (cshape[0] + 1) * cshape[2]
-            inner_axes.set_xticks(arange(numticks) * width + 2 * center)
-            xsubticks = list(self.xsubticks) + [ '' ]
-            inner_axes.set_xticklabels(xsubticks * cshape[2], fontsize=7,
-                                       rotation=30)
-
-        if self.legend is not None:
-            if dim == 1:
-                lbars = bars[0][0]
-            if dim == 2:
-                lbars = [ bars[i][0][0] for i in range(len(bars))]
-            if dim == 3:
-                number = len(bars[0])
-                lbars = [ bars[0][number - j - 1][0] for j in range(number)]
-
-            if self.fig_legend:
-                self.figure.legend(lbars, self.legend, self.legend_loc,
-                                   prop=FontProperties(size=self.legend_size))
-            else:
-                self.axes.legend(lbars, self.legend, self.legend_loc,
-                                 prop=FontProperties(size=self.legend_size))
-
-        if self.title is not None:
-            self.axes.set_title(self.title)
-
-    def savefig(self, name):
-        self.figure.savefig(name)
-
-    def savecsv(self, name):
-        f = file(name, 'w')
-        data = array(self.inputdata)
-        dim = len(data.shape)
-
-        if dim == 1:
-            #if self.xlabel:
-            #    f.write(', '.join(list(self.xlabel)) + '\n')
-            f.write(', '.join([ '%f' % val for val in data]) + '\n')
-        if dim == 2:
-            #if self.xlabel:
-            #    f.write(', '.join([''] + list(self.xlabel)) + '\n')
-            for i,row in enumerate(data):
-                ylabel = []
-                #if self.ylabel:
-                #    ylabel = [ self.ylabel[i] ]
-                f.write(', '.join(ylabel + [ '%f' % v for v in row]) + '\n')
-        if dim == 3:
-            f.write("don't do 3D csv files\n")
-            pass
-
-        f.close()
-
-if __name__ == '__main__':
-    from random import randrange
-    import random, sys
-
-    dim = 3
-    number = 5
-
-    args = sys.argv[1:]
-    if len(args) > 3:
-        sys.exit("invalid number of arguments")
-    elif len(args) > 0:
-        myshape = [ int(x) for x in args ]
-    else:
-        myshape = [ 3, 4, 8 ]
-
-    # generate a data matrix of the given shape
-    size = reduce(lambda x,y: x*y, myshape)
-    #data = [ random.randrange(size - i) + 10 for i in xrange(size) ]
-    data = [ float(i)/100.0 for i in range(size) ]
-    data = reshape(data, myshape)
-
-    # setup some test bar charts
-    if True:
-        chart1 = BarChart()
-        chart1.data = data
-
-        chart1.xlabel = 'Benchmark'
-        chart1.ylabel = 'Bandwidth (GBps)'
-        chart1.legend = [ 'x%d' % x for x in range(myshape[-1]) ]
-        chart1.xticks = [ 'xtick%d' % x for x in range(myshape[0]) ]
-        chart1.title = 'this is the title'
-        if len(myshape) > 2:
-            chart1.xsubticks = [ '%d' % x for x in range(myshape[1]) ]
-        chart1.graph()
-        chart1.savefig('/tmp/test1.png')
-        chart1.savefig('/tmp/test1.ps')
-        chart1.savefig('/tmp/test1.eps')
-        chart1.savecsv('/tmp/test1.csv')
-
-    if False:
-        chart2 = BarChart()
-        chart2.data = data
-        chart2.colormap = 'gray'
-        chart2.graph()
-        chart2.savefig('/tmp/test2.png')
-        chart2.savefig('/tmp/test2.ps')
-
-#    pylab.show()
diff --git a/util/stats/categories.py b/util/stats/categories.py
deleted file mode 100644
index 3f570fb..0000000
--- a/util/stats/categories.py
+++ /dev/null
@@ -1,1938 +0,0 @@
-# Copyright (c) 2005-2006 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-func_categories = { \
-    # Buffer management functions
-    '__skb_linearize' : 'buffer',
-    'skb_clone' : 'buffer',
-    'skb_clone_fraglist' : 'buffer',
-    'skb_seq_read' : 'buffer',
-    'sock_alloc_send_skb' : 'buffer',
-    'sinic_rxskb_alloc' : 'buffer',
-
-    # Copy functions
-    'sinic_copyfrom' : 'copy',
-    '__copy_user' : 'copy',
-    'skb_copy_bits' : 'copy',
-    'skb_copy_datagram_iovec' : 'copy',
-    'sinic_vcopy_iov' : 'idle',
-
-    # Driver functions
-    'do_tx_done' : 'driver',
-    'ns83820_get_drvinfo' : 'driver',
-    'ns83820_get_stats' : 'driver',
-    'ns83820_hard_start_xmit' : 'driver',
-    'ns83820_open' : 'driver',
-    'ns83820_rx_kick' : 'driver',
-    'ns83820_update_stats' : 'driver',
-    'ns83820_irq' : 'driver',
-    'phy_intr' : 'driver',
-    'rx_irq' : 'driver',
-    'rx_action' : 'driver',
-    'sinic_intr' : 'driver',
-    'sinic_xmit' : 'driver',
-    'sinic_rxskb_new' : 'driver',
-
-    # Idle functions
-    'cpu_idle' : 'idle',
-
-    # Interrupt functions
-    'do_entInt' : 'interrupt',
-    'entInt' : 'interrupt',
-    'handle_IRQ_event' : 'interrupt',
-    'irq_exit' : 'interrupt',
-
-    # Other functions
-    'ret_from_sys_call' : 'other',
-    'top' : 'other',
-
-    # Stack functions
-    '__ip_conntrack_confirm' : 'stack',
-    '__ip_conntrack_find' : 'stack',
-    '__tcp_ack_snd_check' : 'stack',
-    '__tcp_checksum_complete_user' : 'stack',
-    'dev_queue_xmit' : 'stack',
-    'eth_header_cache' : 'stack',
-    'ether_setup' : 'stack',
-    'icmp_error' : 'stack',
-    'ip_call_ra_chain' : 'stack',
-    'ip_conntrack_alter_reply' : 'stack',
-    'ip_conntrack_tcp_update' : 'stack',
-    'ip_ct_find_helper' : 'stack',
-    'ip_finish_output' : 'stack',
-    'ip_finish_output2' : 'stack',
-    'ip_local_deliver_finish' : 'stack',
-    'ip_nat_setup_info' : 'stack',
-    'ip_rcv' : 'stack',
-    'ip_rcv_finish' : 'stack',
-    'netif_receive_skb' : 'stack',
-    'nf_log_packet' : 'stack',
-    'nf_queue' : 'stack',
-    'tcp_connect' : 'stack',
-    'tcp_data_queue' : 'stack',
-    'tcp_packet' : 'stack',
-    'tcp_read_sock' : 'stack',
-    'tcp_rcv_established' : 'stack',
-    'tcp_recvmsg' : 'stack',
-    'tcp_sendpage' : 'stack',
-    'tcp_transmit_skb' : 'stack',
-    'tcp_v4_do_rcv' : 'stack',
-    'unregister_netdevice' : 'stack',
-
-    # Syscall functions
-    'entSys' : 'syscall',
-
-    # User functions
-    'user' : 'user',
-    }
-
-def func_categorize(symbol):
-    from .categories import func_categories
-    if symbol in func_categories:
-        return func_categories[symbol]
-    return None
-
-
-pc_categories = {
-    'CALL_PALrdunique_' : 'interrupt', #
-    'Call_Pal_Callsys' : 'interrupt', #
-    'Call_Pal_Rdps' : 'interrupt', #
-    'Call_Pal_Rdusp' : 'interrupt', #
-    'Call_Pal_Rti' : 'interrupt', #
-    'Call_Pal_Swpctx' : 'interrupt', #
-    'Call_Pal_Swpipl' : 'interrupt', #
-    'Call_Pal_Wrusp' : 'interrupt', #
-    'SHATransform': 'driver', # drivers/char/random.c,
-    'TRAP_INTERRUPT_10_' : 'interrupt', #
-    'Trap_Dtbmiss_Single' : 'buffer', #
-    'Trap_Dtbmiss_double' : 'buffer', #
-    'Trap_Interrupt' : 'interrupt', #
-    'Trap_Itbmiss' : 'buffer', #
-    'Trap_Unalign' : 'alignment',
-    'UNALIGN_NO_DISMISS' : 'alignment',
-    'UNALIGN_NO_DISMISS_10_' : 'alignment',
-    '__alloc_pages' : 'buffer', # mm/page_alloc.c,
-    '__anon_vma_link': 'buffer', # mm/rmap.c, include/linux/rmap.h,
-    '__bio_add_page' : 'other', # fs/bio.c,
-    '__bitmap_weight' : 'other', # lib/bitmap.c, include/linux/bitmap.h,
-    '__blk_put_request' : 'other', # drivers/block/ll_rw_blk.c,
-    '__block_commit_write' : 'other', # fs/buffer.c,
-    '__block_prepare_write' : 'other', # fs/buffer.c,
-    '__block_write_full_page': 'other', # fs/buffer.c,
-    '__bread' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    '__brelse' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    '__bss_start' : 'user',
-    '__bss_stop' : 'other', # include/asm-generic/sections.h,
-    '__cond_resched' : 'other', # kernel/sched.c, include/linux/sched.h,
-    '__const_udelay': 'other', # include/asm-i386/delay.h,
-    '__constant_c_memset' : 'other', # include/asm-alpha/string.h,
-    '__copy_from_user_ll': 'copy', # include/asm-i386/uaccess.h,
-    '__copy_to_user_ll': 'copy', # include/asm-i386/uaccess.h,
-    '__copy_user' : 'copy', # include/asm-alpha/uaccess.h,
-    '__d_lookup' : 'other', # fs/dcache.c, include/linux/dcache.h,
-    '__d_path': 'other', # fs/dcache.c,
-    '__delay': 'other', # arch/alpha/lib/udelay.c, include/asm-alpha/delay.h, include/asm-i386/delay.h,
-    '__dequeue_signal' : 'other', # kernel/signal.c,
-    '__divl' : 'other', # arch/alpha/kernel/alpha_ksyms.c,
-    '__divlu' : 'other', # arch/alpha/kernel/alpha_ksyms.c,
-    '__divq' : 'other', # arch/alpha/kernel/alpha_ksyms.c,
-    '__divqu' : 'other', # arch/alpha/kernel/alpha_ksyms.c,
-    '__do_softirq' : 'stack', # kernel/softirq.c,
-    '__down': 'interrupt', # include/asm-alpha/semaphore.h, include/asm-i386/semaphore.h,
-    '__down_failed' : 'other', # arch/alpha/kernel/semaphore.c, include/asm-alpha/semaphore.h,
-    '__down_trylock': 'interrupt', # include/asm-alpha/semaphore.h, include/asm-i386/semaphore.h,
-    '__elv_add_request' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    '__end_that_request_first' : 'other', # drivers/block/ll_rw_blk.c,
-    '__exit_sighand': 'other', # kernel/signal.c, include/linux/sched.h,
-    '__exit_signal': 'other', # kernel/signal.c, include/linux/sched.h,
-    '__filemap_copy_from_user_iovec' : 'buffer', # mm/filemap.c,
-    '__filemap_fdatawrite' : 'buffer', # mm/filemap.c,
-    '__find_get_block' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    '__find_get_block_slow' : 'other', # fs/buffer.c,
-    '__fput' : 'other', # fs/file_table.c,
-    '__free_pages' : 'buffer', # mm/page_alloc.c,
-    '__free_pages_ok': 'buffer', # mm/page_alloc.c,
-    '__generic_file_aio_read': 'buffer', # mm/filemap.c, include/linux/fs.h,
-    '__generic_unplug_device' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    '__get_free_pages' : 'other', # mm/page_alloc.c, drivers/md/raid6.h,
-    '__get_page_state': 'buffer', # mm/page_alloc.c,
-    '__get_user_4': 'other', # include/asm-i386/uaccess.h,
-    '__get_zone_counts': 'other', #
-    '__getblk' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    '__getblk_slow' : 'other', # fs/buffer.c,
-    '__group_complete_signal' : 'user', # kernel/signal.c,  is kinda syscall
-    '__group_send_sig_info' : 'user', # kernel/signal.c,  is kinda syscall
-    '__iget' : 'other', # fs/inode.c, include/linux/fs.h,
-    '__insert_inode_hash': 'other', # fs/inode.c, include/linux/fs.h,
-    '__insert_vm_struct': 'buffer', # mm/mmap.c,
-    '__ip_conntrack_confirm' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack_core.h,
-    '__ip_conntrack_find' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c,
-    '__ip_ct_find_proto' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack_core.h,
-    '__ip_route_output_key' : 'stack', # net/ipv4/route.c,
-    '__kfree_skb' : 'buffer', # net/core/skbuff.c, include/linux/skbuff.h,
-    '__kmalloc' : 'buffer', # mm/slab.c, include/linux/slab.h,
-    '__load_new_mm_context': 'buffer',
-    '__lookup': 'other', # lib/radix-tree.c,
-    '__lookup_hash': 'other', # fs/namei.c,
-    '__lookup_tag' : 'buffer', # lib/radix-tree.c,
-    '__make_request' : 'driver', # drivers/block/ll_rw_blk.c, drivers/block/ll_rw_blk.c,
-    '__mark_inode_dirty' : 'other', # fs/fs-writeback.c, include/linux/fs.h,
-    '__memcpy_aligned_up' : 'copy', # arch/alpha/lib/memcpy.c,
-    '__memcpy_unaligned_up' : 'copy', # arch/alpha/lib/memcpy.c,
-    '__memset' : 'copy', # include/asm-alpha/string.h,
-    '__mmdrop': 'other', # kernel/fork.c,
-    '__mod_timer' : 'other', # kernel/timer.c, include/linux/timer.h,
-    '__modify_IO_APIC_irq': 'interrupt', #
-    '__net_random': 'other', #
-    '__page_cache_release' : 'buffer', # mm/swap.c,
-    '__pagevec_free': 'buffer', # mm/page_alloc.c, include/linux/pagevec.h,
-    '__pagevec_lru_add' : 'buffer', # mm/swap.c, include/linux/pagevec.h,
-    '__pagevec_lru_add_active': 'buffer', # mm/swap.c, include/linux/pagevec.h,
-    '__pagevec_release' : 'buffer', # mm/swap.c, include/linux/pagevec.h,
-    '__pollwait' : 'other', # fs/select.c, fs/select.c,
-    '__pskb_trim_head': 'stack', # net/ipv4/tcp_output.c,
-    '__put_task_struct': 'other', # kernel/fork.c, include/linux/sched.h,
-    '__queue_work': 'other', # kernel/workqueue.c,
-    '__rb_erase_color' : 'buffer', # lib/rbtree.c,
-    '__rb_rotate_left' : 'buffer', # lib/rbtree.c,
-    '__rb_rotate_right' : 'buffer', # lib/rbtree.c,
-    '__rcu_process_callbacks': 'other', #
-    '__read_page_state' : 'buffer', # mm/page_alloc.c, include/linux/page-flags.h,
-    '__release_sock' : 'stack', # net/core/sock.c,
-    '__remlu' : 'other', # arch/alpha/kernel/alpha_ksyms.c,
-    '__remove_from_page_cache': 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    '__remove_shared_vm_struct': 'buffer', # mm/mmap.c,
-    '__remqu' : 'other', # arch/alpha/kernel/alpha_ksyms.c,
-    '__rmqueue' : 'buffer', # mm/page_alloc.c,
-    '__scsi_done' : 'other', # drivers/scsi/scsi.c, drivers/scsi/scsi_priv.h,
-    '__scsi_get_command' : 'other', # drivers/scsi/scsi.c,
-    '__set_page_buffers' : 'other', # fs/buffer.c,
-    '__set_page_dirty_nobuffers' : 'buffer', # mm/page-writeback.c, include/linux/mm.h,
-    '__sk_stream_mem_reclaim' : 'buffer', # net/core/stream.c,
-    '__sock_create': 'stack', # net/socket.c,
-    '__strncpy_from_user' : 'copy', # include/asm-alpha/uaccess.h,
-    '__strnlen_user': 'user',
-    '__switch_to': 'interrupt', #
-    '__sync_single_inode' : 'other', # fs/fs-writeback.c,
-    '__tasklet_schedule' : 'other', # kernel/softirq.c,
-    '__tcp_ack_snd_check' : 'stack', # net/ipv4/tcp_input.c,
-    '__tcp_data_snd_check' : 'stack', # net/ipv4/tcp_input.c,
-    '__tcp_grow_window' : 'stack', # net/ipv4/tcp_input.c,
-    '__tcp_put_port' : 'stack', # net/ipv4/tcp_ipv4.c,
-    '__tcp_select_window' : 'stack', # net/ipv4/tcp_output.c,
-    '__tcp_tw_hashdance' : 'stack', # net/ipv4/tcp_minisocks.c,
-    '__tcp_v4_check_established':'stack',
-    '__unhash_process': 'other', # kernel/exit.c,
-    '__unmask_IO_APIC_irq': 'interrupt', #
-    '__up_wakeup' : 'interrupt', # arch/alpha/kernel/semaphore.c, include/asm-alpha/semaphore.h,
-    '__user_walk' : 'other', # fs/namei.c,
-    '__vm_stat_account': 'other', #
-    '__vma_link': 'buffer', # mm/mmap.c,
-    '__vma_link_rb': 'buffer', # mm/mmap.c, include/linux/mm.h,
-    '__wait_on_buffer' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    '__wake_up' : 'other', # kernel/sched.c,
-    '__wake_up_common' : 'other', # kernel/sched.c,
-    '__wake_up_locked': 'other', # kernel/sched.c,
-    '__wake_up_parent': 'other', # kernel/signal.c,
-    '__wake_up_sync': 'other', # kernel/sched.c,
-    '__writeback_single_inode' : 'other', # fs/fs-writeback.c,
-    'acct_process': 'other', # kernel/acct.c, include/linux/acct.h, include/linux/acct.h,
-    'ack_edge_ioapic_irq': 'interrupt', #
-    'ack_edge_ioapic_vector': 'interrupt', #
-    'activate_page' : 'buffer', # mm/swap.c,
-    'activate_task' : 'other', # kernel/sched.c,
-    'add_disk_randomness' : 'other', # drivers/char/random.c, include/linux/genhd.h,
-    'add_interrupt_randomness': 'driver', # drivers/char/random.c, include/linux/random.h,
-    'add_timer_randomness' : 'driver', # drivers/char/random.c,
-    'add_to_page_cache' : 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'add_to_page_cache_lru' : 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'add_wait_queue' : 'other', # kernel/fork.c,
-    'add_wait_queue_exclusive' : 'other', # kernel/fork.c,
-    'aligned' : 'other', #
-    'alloc_buffer_head' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'alloc_dcookie': 'other', # fs/dcookies.c,
-    'alloc_fd_array': 'other', # fs/file.c, include/linux/file.h,
-    'alloc_inode' : 'other', # fs/inode.c,
-    'alloc_pidmap': 'other', # kernel/pid.c, include/linux/pid.h,
-    'alloc_skb' : 'buffer', # net/core/skbuff.c, include/linux/skbuff.h,
-    'alloc_slabmgmt' : 'buffer', # mm/slab.c,
-    'alpha_switch_to' : 'other', # include/asm-alpha/system.h,
-    'anon_vma_link': 'buffer', # mm/rmap.c, include/linux/rmap.h, include/linux/rmap.h,
-    'anon_vma_prepare': 'buffer', # mm/rmap.c, include/linux/rmap.h, include/linux/rmap.h,
-    'anon_vma_unlink': 'buffer', # mm/rmap.c, include/linux/rmap.h,
-    'apache': 'other', #
-    'apic_timer_interrupt': 'interrupt', # include/asm-i386/hw_irq.h,
-    'arch_get_unmapped_area': 'buffer',
-    'arch_get_unmapped_area_1': 'buffer',
-    'arch_get_unmapped_area_topdown': 'other', #
-    'arch_pick_mmap_layout': 'other', #
-    'arch_unmap_area_topdown': 'other', #
-    'arp_hash': 'stack', # net/ipv4/arp.c, net/ipv4/arp.c,
-    'arp_process': 'stack', # net/ipv4/arp.c,
-    'arp_rcv': 'stack', # net/ipv4/arp.c,
-    'artsd': 'other', #
-    'as_add_arq_hash' : 'other', # drivers/block/as-iosched.c,
-    'as_add_arq_rb' : 'other', # drivers/block/as-iosched.c,
-    'as_add_request' : 'other', # drivers/block/as-iosched.c,
-    'as_antic_stop' : 'other', # drivers/block/as-iosched.c,
-    'as_choose_req' : 'other', # drivers/block/as-iosched.c,
-    'as_completed_request' : 'other', # drivers/block/as-iosched.c,
-    'as_dispatch_request' : 'other', # drivers/block/as-iosched.c,
-    'as_fifo_expired' : 'other', # drivers/block/as-iosched.c,
-    'as_find_arq_hash' : 'other', # drivers/block/as-iosched.c,
-    'as_find_arq_rb' : 'other', # drivers/block/as-iosched.c,
-    'as_find_first_arq' : 'other', # drivers/block/as-iosched.c,
-    'as_find_next_arq' : 'other', # drivers/block/as-iosched.c,
-    'as_former_request' : 'other', # drivers/block/as-iosched.c,
-    'as_get_io_context' : 'other', # drivers/block/as-iosched.c,
-    'as_insert_request' : 'other', # drivers/block/as-iosched.c,
-    'as_latter_request' : 'other', # drivers/block/as-iosched.c,
-    'as_merge' : 'other', # drivers/block/as-iosched.c,
-    'as_merged_request' : 'other', # drivers/block/as-iosched.c,
-    'as_merged_requests' : 'other', # drivers/block/as-iosched.c,
-    'as_move_to_dispatch' : 'other', # drivers/block/as-iosched.c,
-    'as_next_request' : 'other', # drivers/block/as-iosched.c,
-    'as_put_request' : 'other', # drivers/block/as-iosched.c,
-    'as_queue_empty' : 'other', # drivers/block/as-iosched.c,
-    'as_remove_dispatched_request' : 'other', # drivers/block/as-iosched.c,
-    'as_remove_merge_hints' : 'other', # drivers/block/as-iosched.c,
-    'as_remove_queued_request' : 'other', # drivers/block/as-iosched.c,
-    'as_remove_request' : 'other', # drivers/block/as-iosched.c,
-    'as_set_request' : 'other', # drivers/block/as-iosched.c,
-    'as_update_arq' : 'other', # drivers/block/as-iosched.c,
-    'as_update_iohist' : 'other', # drivers/block/as-iosched.c,
-    'atomic_dec_and_lock' : 'other', # lib/dec_and_lock.c, include/linux/spinlock.h, include/linux/spinlock.h,
-    'atomic_dec_and_lock_1' : 'other', # arch/alpha/lib/dec_and_lock.c,
-    'attach_pid': 'other', # kernel/pid.c,
-    'attempt_merge' : 'other', # drivers/block/ll_rw_blk.c,
-    'auth_domain_drop' : 'other', # net/sunrpc/svcauth.c,
-    'auth_domain_put' : 'other', # net/sunrpc/svcauth.c, include/linux/sunrpc/svcauth.h,
-    'autoremove_wake_function' : 'other', # kernel/fork.c, include/linux/wait.h,
-    'bad_range' : 'buffer', # mm/page_alloc.c,
-    'balance_dirty_pages' : 'buffer', # mm/page-writeback.c,
-    'balance_dirty_pages_ratelimited' : 'buffer', # mm/page-writeback.c, include/linux/writeback.h,
-    'basename': 'other', #
-    'bash': 'other', #
-    'batch_entropy_store' : 'interrupt', # drivers/char/random.c, include/linux/random.h,
-    'bh_lru_install' : 'other', # fs/buffer.c,
-    'bh_waitq_head' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'bh_wake_function' : 'other', # fs/buffer.c,
-    'bio_add_page' : 'other', # fs/bio.c, include/linux/bio.h,
-    'bio_alloc' : 'other', # fs/bio.c, include/linux/bio.h,
-    'bio_destructor' : 'other', # fs/bio.c,
-    'bio_endio' : 'other', # fs/bio.c, include/linux/bio.h,
-    'bio_get_nr_vecs' : 'other', # fs/bio.c, include/linux/bio.h,
-    'bio_hw_segments' : 'other', # fs/bio.c, include/linux/bio.h,
-    'bio_phys_segments' : 'other', # fs/bio.c, include/linux/bio.h,
-    'bio_put' : 'other', # fs/bio.c, include/linux/bio.h,
-    'blk_backing_dev_unplug' : 'other', # drivers/block/ll_rw_blk.c,
-    'blk_cleanup_queue': 'driver', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'blk_get_queue': 'driver', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'blk_hw_contig_segment' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'blk_phys_contig_segment' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'blk_plug_device' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'blk_queue_bounce' : 'buffer', # mm/highmem.c, include/linux/blkdev.h,
-    'blk_recount_segments' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'blk_remove_plug' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'blk_rq_map_sg' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'blk_run_queue' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'blkdev_ioctl': 'driver', # drivers/block/ioctl.c, include/linux/fs.h,
-    'block_ioctl': 'other', # fs/block_dev.c,
-    'block_prepare_write' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'block_read_full_page': 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'block_write_full_page': 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'bmap': 'other', # fs/jfs/jfs_dmap.h, fs/inode.c, include/linux/fs.h,
-    'buffer_insert_list' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'buffered_rmqueue' : 'buffer', # mm/page_alloc.c,
-    'cache_alloc_refill' : 'buffer', # mm/slab.c,
-    'cache_check' : 'other', # net/sunrpc/cache.c, include/linux/sunrpc/cache.h,
-    'cache_flusharray' : 'buffer', # mm/slab.c,
-    'cache_grow' : 'buffer', # mm/slab.c,
-    'cache_init_objs' : 'buffer', # mm/slab.c,
-    'cache_reap': 'buffer', # mm/slab.c,
-    'cached_lookup': 'other', # fs/namei.c,
-    'call_rcu' : 'other', # kernel/rcupdate.c,
-    'can_share_swap_page': 'buffer', # mm/swapfile.c, include/linux/swap.h, include/linux/swap.h,
-    'can_vma_merge_after': 'buffer', # mm/mmap.c,
-    'can_vma_merge_before': 'buffer', # mm/mmap.c,
-    'capable': 'other',
-    'cascade' : 'interrupt', # kernel/timer.c,
-    'cat': 'other', #
-    'cdev_get': 'other', # fs/char_dev.c, include/linux/cdev.h,
-    'cdrom': 'other', #
-    'check_kill_permission' : 'other', # kernel/signal.c,
-    'chrdev_open': 'other', # fs/char_dev.c, include/linux/fs.h,
-    'cleanup_rbuf' : 'stack', # net/ipv4/tcp.c,
-    'clear_inode' : 'other', # fs/inode.c, include/linux/fs.h,
-    'clear_page' : 'buffer', # include/asm-alpha/page.h,
-    'clear_page_dirty_for_io' : 'buffer', # mm/page-writeback.c, include/linux/mm.h,
-    'clear_page_tables': 'buffer', # mm/memory.c, include/linux/mm.h,
-    'clear_queue_congested' : 'other', # drivers/block/ll_rw_blk.c,
-    'clear_user': 'other', # include/asm-alpha/uaccess.h, include/asm-i386/uaccess.h,
-    'clock_panelapplet.so': 'other', #
-    'close_private_file' : 'other', # fs/file_table.c, include/linux/fs.h,
-    'copy_skb_header' : 'copy',
-    'common_interrupt': 'interrupt', #
-    'complete': 'other', # kernel/sched.c,
-    'compute_creds': 'other', # fs/exec.c, include/linux/binfmts.h,
-    'con_chars_in_buffer': 'driver', # drivers/char/vt.c,
-    'con_write_room': 'driver', # drivers/char/vt.c,
-    'convert_fxsr_from_user': 'interrupt', #
-    'convert_fxsr_to_user': 'interrupt', #
-    'copy_files': 'other', # kernel/fork.c,
-    'copy_from_user': 'copy', # include/asm-alpha/uaccess.h, include/asm-i386/uaccess.h,
-    'copy_mm': 'other', # kernel/fork.c,
-    'copy_namespace': 'other', # fs/namespace.c, include/linux/namespace.h,
-    'copy_page': 'copy',
-    'copy_page_range': 'buffer', # mm/memory.c, include/linux/mm.h,
-    'copy_process': 'other', # kernel/fork.c, include/linux/sched.h,
-    'copy_semundo': 'other', # ipc/sem.c, include/linux/sem.h,
-    'copy_strings': 'other', # fs/exec.c, include/linux/binfmts.h,
-    'copy_strings_kernel': 'other', # fs/exec.c, include/linux/binfmts.h,
-    'copy_thread': 'syscall', # arch/alpha/kernel/process.c, include/linux/sched.h,
-    'copy_to_user': 'copy', # include/asm-alpha/uaccess.h, include/asm-i386/uaccess.h,
-    'copy_vma': 'buffer', # mm/mmap.c, include/linux/mm.h,
-    'count': 'driver', # fs/exec.c, init/initramfs.c, drivers/char/serial_tx3912.c, drivers/char/rocket.c, drivers/isdn/hardware/eicon/diva_didd.c, drivers/isdn/hardware/eicon/divasmain.c, drivers/isdn/hardware/eicon/divasmain.c, drivers/isdn/hardware/eicon/capimain.c, drivers/isdn/hardware/eicon/divasi.c, drivers/isdn/hardware/eicon/divasi.c, drivers/isdn/hardware/eicon/divasi.c, drivers/isdn/hardware/eicon/divasi.c, drivers/isdn/hardware/eicon/divasi.c, drivers/isdn/hardware/eicon/divamnt.c, drivers/isdn/hardware/eicon/divamnt.c, drivers/isdn/hardware/eicon/divamnt.c, drivers/isdn/hardware/eicon/divamnt.c, drivers/isdn/hardware/eicon/divamnt.c, drivers/media/video/w9966.c, drivers/media/video/w9966.c,
-    'count_open_files': 'other', # kernel/fork.c,
-    'cp_new_stat' : 'other', # fs/stat.c,
-    'cp_new_stat64': 'other', # fs/stat.c,
-    'cpu_idle' : 'idle', # arch/alpha/kernel/process.c, init/main.c,
-    'cpu_quiet' : 'other', # kernel/rcupdate.c,
-    'create_buffers' : 'other', # fs/buffer.c,
-    'create_elf_tables': 'other', # fs/binfmt_elf.c,
-    'create_empty_buffers' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'cron': 'other', #
-    'csum_partial' : 'stack', # arch/alpha/lib/checksum.c, include/asm-alpha/checksum.h,
-    'csum_partial_copy_nocheck' : 'copy', # arch/alpha/lib/csum_partial_copy.c, include/asm-alpha/checksum.h,
-    'csum_partial_copy_to_xdr' : 'copy', # net/sunrpc/xprt.c, net/sunrpc/svcsock.c,
-    'csum_tcpudp_magic' : 'stack', # arch/alpha/lib/checksum.c, include/asm-alpha/checksum.h,
-    'csum_tcpudp_nofold' : 'stack', # arch/alpha/lib/checksum.c, include/asm-alpha/checksum.h,
-    'current_kernel_time' : 'other', # kernel/time.c, include/linux/time.h,
-    'cut': 'other', #
-    'd_alloc' : 'other', # fs/dcache.c, include/linux/dcache.h,
-    'd_alloc_anon' : 'other', # fs/dcache.c, include/linux/dcache.h,
-    'd_callback' : 'other', # fs/dcache.c,
-    'd_find_alias' : 'other', # fs/dcache.c, include/linux/dcache.h,
-    'd_free' : 'other', # fs/dcache.c,
-    'd_instantiate' : 'other', # fs/dcache.c, include/linux/dcache.h,
-    'd_lookup': 'other', # fs/dcache.c, include/linux/dcache.h,
-    'd_path': 'other', # fs/dcache.c, include/linux/dcache.h,
-    'd_rehash' : 'other', # fs/dcache.c, include/linux/dcache.h,
-    'deactivate_task' : 'other', # kernel/sched.c,
-    'default_idle' : 'idle', # arch/alpha/kernel/process.c, include/linux/platform.h,
-    'default_llseek': 'other', # fs/read_write.c, include/linux/fs.h,
-    'default_wake_function' : 'other', # kernel/sched.c, include/linux/wait.h,
-    'del_singleshot_timer_sync' : 'other', # kernel/timer.c, include/linux/timer.h, include/linux/timer.h,
-    'del_timer' : 'other', # kernel/timer.c, include/linux/timer.h,
-    'delay_pmtmr': 'interrupt', #
-    'delayed_work_timer_fn': 'other', # kernel/workqueue.c,
-    'dentry_open': 'other', # fs/open.c, include/linux/fs.h,
-    'deny_write_access': 'other', # fs/namei.c, include/linux/fs.h,
-    'dequeue_signal' : 'other', # kernel/signal.c, include/linux/sched.h,
-    'dequeue_task' : 'other', # kernel/sched.c,
-    'destroy_context': 'interrupt', # include/asm-alpha/mmu_context.h, include/asm-i386/mmu_context.h,
-    'destroy_inode' : 'other', # fs/inode.c, include/linux/fs.h,
-    'detach_pid': 'other', # kernel/pid.c,
-    'detach_vmas_to_be_unmapped': 'buffer', # mm/mmap.c,
-    'dev_queue_xmit' : 'stack', # net/core/dev.c, include/linux/netdevice.h,
-    'dev_shutdown' : 'stack', # net/sched/sch_generic.c,
-    'dev_watchdog': 'stack', # net/sched/sch_generic.c,
-    'device_not_available': 'interrupt', #
-    'disable_irq_nosync': 'interrupt', # arch/alpha/kernel/irq.c, include/asm-alpha/irq.h, include/asm-i386/irq.h,
-    'disk_round_stats' : 'other', # drivers/block/ll_rw_blk.c, include/linux/genhd.h,
-    'dnotify_flush' : 'other', # fs/dnotify.c, include/linux/dnotify.h,
-    'dnotify_parent' : 'other', # fs/dnotify.c, include/linux/dnotify.h,
-    'do_IRQ': 'driver', # drivers/s390/cio/cio.c,
-    'do_anonymous_page' : 'buffer', # mm/memory.c,
-    'do_bindings' : 'stack', # net/ipv4/netfilter/ip_nat_core.c, include/linux/netfilter_ipv4/ip_nat_core.h,
-    'do_brk': 'buffer', # mm/mmap.c, mm/nommu.c, include/linux/mm.h,
-    'do_csum_partial_copy_from_user' : 'copy', # arch/alpha/lib/csum_partial_copy.c,
-    'do_entInt' : 'interrupt', # arch/alpha/kernel/irq_alpha.c,
-    'do_entUna': 'alignment',
-    'do_execve': 'other', # fs/exec.c, include/linux/sched.h,
-    'do_exit': 'other', # kernel/exit.c,
-    'do_fcntl' : 'user', # fs/fcntl.c, used to be syscall`
-    'do_fork': 'other', # kernel/fork.c, include/linux/sched.h,
-    'do_futex': 'other', # kernel/futex.c, include/linux/futex.h,
-    'do_generic_mapping_read': 'buffer', # mm/filemap.c, include/linux/fs.h,
-    'do_gettimeofday' : 'user', # arch/alpha/kernel/time.c, include/linux/time.h,  used to by syscall
-    'do_group_exit': 'other', # kernel/exit.c, include/linux/sched.h,
-    'do_invalidatepage': 'buffer', # mm/truncate.c,
-    'do_lookup' : 'user', # fs/namei.c,  used to by syscall
-    'do_mmap_pgoff': 'buffer', # mm/mmap.c, mm/nommu.c, include/linux/mm.h,
-    'do_mpage_readpage': 'other', # fs/mpage.c,
-    'do_mremap': 'buffer', # mm/mremap.c,
-    'do_munmap': 'buffer', # mm/mmap.c, mm/nommu.c, include/linux/mm.h,
-    'do_no_page' : 'user', # mm/memory.c,  used to by syscall
-    'do_nosym': 'other', #
-    'do_notify_parent': 'other', # kernel/signal.c, include/linux/sched.h,
-    'do_notify_resume': 'interrupt', # arch/alpha/kernel/signal.c,
-    'do_osf_sigprocmask' : 'user', # arch/alpha/kernel/signal.c,  used to by syscall
-    'do_page_cache_readahead': 'buffer', # mm/readahead.c, include/linux/mm.h,
-    'do_page_fault' : 'user', # arch/alpha/mm/fault.c,  used to by syscall
-    'do_pipe': 'syscall', # fs/pipe.c, arch/alpha/kernel/osf_sys.c, include/linux/fs.h,
-    'do_poll' : 'user', # fs/select.c, drivers/macintosh/apm_emu.c,  used to by syscall
-    'do_pollfd' : 'user', # fs/select.c,  used to by syscall
-    'do_posix_clock_monotonic_gettime': 'other', # kernel/posix-timers.c, kernel/posix-timers.c, include/linux/time.h,
-    'do_posix_clock_monotonic_gettime_parts': 'other', # kernel/posix-timers.c, kernel/posix-timers.c,
-    'do_posix_gettime': 'other', # kernel/posix-timers.c, kernel/posix-timers.c,
-    'do_readv_writev' : 'user', # fs/read_write.c,  used to by syscall
-    'do_select': 'other', # fs/select.c, include/linux/poll.h,
-    'do_setitimer': 'other', # kernel/itimer.c, include/linux/time.h,
-    'do_sigaction': 'other', # kernel/signal.c, include/linux/sched.h,
-    'do_signal' : 'user', # arch/alpha/kernel/signal.c, arch/alpha/kernel/signal.c,  used to by syscall
-    'do_sigreturn' : 'user', # arch/alpha/kernel/signal.c,  used to by syscall
-    'do_sigsuspend' : 'user', # arch/alpha/kernel/signal.c,  used to by syscall
-    'do_softirq' : 'interrupt', # kernel/softirq.c, include/linux/interrupt.h,
-    'do_switch_stack' : 'other', #
-    'do_sync_read' : 'other', # fs/read_write.c, include/linux/fs.h,
-    'do_sync_write': 'other', # fs/read_write.c, include/linux/fs.h,
-    'do_timer' : 'other', # kernel/timer.c, include/linux/sched.h,
-    'do_truncate': 'other', # fs/open.c, include/linux/fs.h,
-    'do_tx_done' : 'driver', # drivers/net/ns83820.c,
-    'do_wait': 'other', #
-    'do_wp_page': 'buffer', # mm/memory.c,
-    'do_writepages' : 'buffer', # mm/page-writeback.c, include/linux/writeback.h,
-    'done' : 'other', # drivers/usb/gadget/net2280.c, drivers/usb/gadget/goku_udc.c, drivers/usb/gadget/pxa2xx_udc.c, drivers/scsi/aha152x.c, drivers/scsi/aha152x.c, include/linux/wavefront.h,
-    'dp264_disable_irq' : 'interrupt', # arch/alpha/kernel/sys_dp264.c,
-    'dp264_enable_irq' : 'interrupt', # arch/alpha/kernel/sys_dp264.c,
-    'dp264_end_irq' : 'interrupt', # arch/alpha/kernel/sys_dp264.c,
-    'dp264_srm_device_interrupt' : 'interrupt', # arch/alpha/kernel/sys_dp264.c,
-    'dput' : 'other', # fs/dcache.c, include/linux/dcache.h,
-    'drain_array_locked': 'buffer', # mm/slab.c, mm/slab.c,
-    'drive_stat_acct' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'drop_buffers': 'other', # fs/buffer.c,
-    'drop_key_refs': 'other', # kernel/futex.c,
-    'dst_alloc': 'stack', # net/core/dst.c,
-    'dst_output' : 'stack', #
-    'dummy_bprm_alloc_security': 'other', # security/dummy.c,
-    'dummy_bprm_apply_creds': 'other', # security/dummy.c,
-    'dummy_bprm_check_security': 'other', # security/dummy.c,
-    'dummy_bprm_secureexec': 'other', # security/dummy.c,
-    'dummy_bprm_set_security': 'other', # security/dummy.c,
-    'dummy_capable': 'other', # security/dummy.c,
-    'dummy_d_instantiate': 'other', # security/dummy.c,
-    'dummy_file_alloc_security': 'other', # security/dummy.c,
-    'dummy_file_free_security': 'other', # security/dummy.c,
-    'dummy_file_ioctl': 'other', # security/dummy.c,
-    'dummy_file_mmap': 'other', # security/dummy.c,
-    'dummy_file_permission': 'other', # security/dummy.c,
-    'dummy_inode_alloc_security': 'other', # security/dummy.c,
-    'dummy_inode_create': 'other', # security/dummy.c,
-    'dummy_inode_free_security': 'other', # security/dummy.c,
-    'dummy_inode_getattr': 'other', # security/dummy.c,
-    'dummy_inode_mkdir': 'other', # security/dummy.c,
-    'dummy_inode_permission': 'other', # security/dummy.c,
-    'dummy_inode_post_create': 'other', # security/dummy.c,
-    'dummy_inode_post_mkdir': 'other', # security/dummy.c,
-    'dummy_task_create': 'other', # security/dummy.c,
-    'dummy_task_free_security': 'other', # security/dummy.c,
-    'dummy_task_kill': 'other', # security/dummy.c,
-    'dummy_task_wait': 'other', # security/dummy.c,
-    'dummy_vm_enough_memory': 'other', # security/dummy.c,
-    'dup_task_struct': 'other', # kernel/fork.c,
-    'e100': 'driver', #
-    'e1000': 'driver',
-    'effective_prio' : 'other', # kernel/sched.c,
-    'ehci_hcd': 'driver', # drivers/usb/host/ehci.h,
-    'elf_map': 'other', # fs/binfmt_elf.c, fs/binfmt_elf.c,
-    'eligible_child': 'other', # kernel/exit.c,
-    'elv_completed_request' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_former_request' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_latter_request' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_merge' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_merge_requests' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_merged_request' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_next_request' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_put_request' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_queue_empty' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_remove_request' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_rq_merge_ok' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_set_request' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'elv_try_last_merge' : 'other', # drivers/block/elevator.c, include/linux/elevator.h,
-    'enable_irq': 'driver', # arch/alpha/kernel/irq.c, drivers/net/wan/sdla_ppp.c, drivers/net/wan/sdla_x25.c, drivers/net/wan/wanpipe_multppp.c, drivers/net/wan/sdla_chdlc.c, drivers/net/wan/sdlamain.c, drivers/net/wan/sdla_fr.c, include/asm-alpha/irq.h, include/asm-i386/irq.h,
-    'encode_post_op_attr' : 'other', # fs/nfsd/nfs3xdr.c,
-    'encode_wcc_data' : 'other', # fs/nfsd/nfs3xdr.c,
-    'end' : 'other', # arch/alpha/boot/misc.c, drivers/media/video/w9966.c, drivers/media/video/w9966.c,
-    'end_bio_bh_io_sync' : 'other', # fs/buffer.c,
-    'end_buffer_async_write': 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'end_buffer_write_sync' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'end_edge_ioapic_vector': 'other', # include/asm-i386/io_apic.h,
-    'end_level_ioapic_irq': 'interrupt', #
-    'end_level_ioapic_vector': 'interrupt', #
-    'end_page_writeback' : 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'end_that_request_chunk' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'end_that_request_first': 'driver', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'end_that_request_last' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'enqueue_task' : 'other', # kernel/sched.c,
-    'entInt' : 'interrupt', # arch/alpha/kernel/proto.h,
-    'entMM' : 'interrupt', # arch/alpha/kernel/proto.h,
-    'entSys' : 'interrupt', # arch/alpha/kernel/proto.h,
-    'entUna' : 'alignment',
-    'entUnaUser':'alignment',
-    'error_code': 'other', #
-    'eth_header' : 'stack', # net/ethernet/eth.c, include/linux/etherdevice.h,
-    'eth_type_trans' : 'stack', # net/ethernet/eth.c, include/linux/etherdevice.h,
-    'ev5_flush_tlb_current_page': 'buffer',
-    'ev5_switch_mm' : 'other', # include/asm-alpha/mmu_context.h,
-    'eventpoll_init_file' : 'other', # fs/eventpoll.c, include/linux/eventpoll.h,
-    'exec_mmap': 'other', # fs/exec.c,
-    'exim4': 'other', #
-    'exit_aio': 'other', # fs/aio.c,
-    'exit_itimers': 'other', # kernel/posix-timers.c, include/linux/sched.h,
-    'exit_mmap': 'buffer', # mm/mmap.c, mm/nommu.c, include/linux/mm.h,
-    'exit_notify': 'other', # kernel/exit.c,
-    'exit_sem': 'other', # ipc/sem.c, include/linux/sem.h, include/linux/sem.h,
-    'exp_find_key' : 'other', # fs/nfsd/export.c, include/linux/nfsd/export.h,
-    'exp_readlock' : 'other', # fs/nfsd/export.c, include/linux/nfsd/export.h,
-    'exp_readunlock' : 'other', # fs/nfsd/export.c, include/linux/nfsd/export.h,
-    'expand_fd_array': 'other', # fs/file.c, include/linux/file.h,
-    'expand_files': 'other', # fs/fcntl.c,
-    'expand_stack': 'buffer', # mm/mmap.c, include/linux/mm.h,
-    'expkey_put' : 'other', # fs/nfsd/export.c, include/linux/nfsd/export.h,
-    'export_decode_fh' : 'other', # fs/exportfs/expfs.c,
-    'export_iget' : 'other', # fs/exportfs/expfs.c,
-    'expr': 'other', #
-    'ext2_alloc_block' : 'other', # fs/ext2/inode.c,
-    'ext2_alloc_branch' : 'other', # fs/ext2/inode.c,
-    'ext2_block_to_path' : 'other', # fs/ext2/inode.c,
-    'ext2_discard_prealloc' : 'other', # fs/ext2/inode.c, fs/ext2/ext2.h,
-    'ext2_find_near' : 'other', # fs/ext2/inode.c,
-    'ext2_free_blocks' : 'other', # fs/ext2/balloc.c, fs/ext2/ext2.h,
-    'ext2_get_block' : 'other', # fs/ext2/inode.c,
-    'ext2_get_branch' : 'other', # fs/ext2/inode.c,
-    'ext2_get_group_desc' : 'other', # fs/ext2/balloc.c, fs/ext2/ext2.h,
-    'ext2_get_inode' : 'other', # fs/ext2/inode.c,
-    'ext2_new_block' : 'other', # fs/ext2/balloc.c, fs/ext2/ext2.h,
-    'ext2_prepare_write' : 'other', # fs/ext2/inode.c,
-    'ext2_put_inode' : 'other', # fs/ext2/inode.c, fs/ext2/ext2.h,
-    'ext2_release_file' : 'other', # fs/ext2/file.c,
-    'ext2_setattr' : 'other', # fs/ext2/inode.c, fs/ext2/ext2.h,
-    'ext2_sync_file' : 'other', # fs/ext2/fsync.c, fs/ext2/ext2.h,
-    'ext2_sync_inode' : 'other', # fs/ext2/inode.c, fs/ext2/ext2.h,
-    'ext2_update_inode' : 'other', # fs/ext2/inode.c, fs/ext2/inode.c,
-    'ext2_write_inode' : 'other', # fs/ext2/inode.c, fs/ext2/ext2.h,
-    'ext2_writepages' : 'other', # fs/ext2/inode.c,
-    'ext3': 'other', #
-    'fasync_helper': 'other', # fs/fcntl.c, include/linux/fs.h,
-    'fd_install' : 'other', # fs/open.c,
-    'fget' : 'other', # fs/file_table.c,
-    'fget_light' : 'other', # fs/file_table.c,
-    'fh_put' : 'other', # fs/nfsd/nfsfh.c, include/linux/nfsd/nfsfh.h,
-    'fh_verify' : 'other', # fs/nfsd/nfsfh.c, include/linux/nfsd/nfsfh.h,
-    'fib_lookup': 'stack', # net/ipv4/fib_rules.c,
-    'fib_rule_put': 'stack', # net/ipv4/fib_rules.c,
-    'fib_semantic_match': 'stack', # net/ipv4/fib_semantics.c,
-    'file_ioctl': 'other', # fs/ioctl.c,
-    'file_kill' : 'other', # fs/file_table.c, include/linux/fs.h,
-    'file_move': 'other', # fs/file_table.c, include/linux/fs.h,
-    'file_ra_state_init': 'buffer', # mm/readahead.c, include/linux/fs.h,
-    'file_read_actor': 'buffer', # mm/filemap.c, include/linux/fs.h,
-    'filemap_fdatawait' : 'buffer', # mm/filemap.c, include/linux/fs.h,
-    'filemap_fdatawrite' : 'buffer', # mm/filemap.c, include/linux/fs.h,
-    'filemap_nopage': 'buffer', # mm/filemap.c, include/linux/mm.h,
-    'filesystems_read_proc': 'other', # fs/proc/proc_misc.c,
-    'filp_close' : 'other', # fs/open.c, include/linux/fs.h,
-    'filp_open' : 'other', # fs/open.c, include/linux/fs.h,
-    'find_best_ips_proto_fast' : 'stack', # net/ipv4/netfilter/ip_nat_core.c,
-    'find_busiest_group' : 'other', # kernel/sched.c,
-    'find_dcookie': 'other', # fs/dcookies.c,
-    'find_exported_dentry' : 'other', # fs/exportfs/expfs.c, fs/nfsd/export.c,
-    'find_extend_vma': 'buffer', # mm/mmap.c, mm/nommu.c, include/linux/mm.h,
-    'find_get_page' : 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'find_get_pages': 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'find_get_pages_tag' : 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'find_inode_fast' : 'other', # fs/inode.c,
-    'find_inode_number' : 'other', # fs/dcache.c, include/linux/fs.h,
-    'find_lock_page' : 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'find_mergeable_anon_vma': 'buffer', # mm/mmap.c, include/linux/mm.h,
-    'find_nat_proto' : 'stack', # net/ipv4/netfilter/ip_nat_core.c, include/linux/netfilter_ipv4/ip_nat_protocol.h,
-    'find_next_zero_bit': 'other', # include/asm-alpha/bitops.h, include/asm-i386/bitops.h,
-    'find_or_create_page' : 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'find_pid' : 'user', # kernel/pid.c, used to be syscall
-    'find_snap_client': 'stack', # net/802/psnap.c,
-    'find_task_by_pid' : 'user', # kernel/pid.c, include/linux/sched.h, used to be syscall
-    'find_task_by_pid_type': 'other', #
-    'find_vma' : 'buffer', # mm/mmap.c, mm/nommu.c, include/linux/mm.h, used to be syscall
-    'find_vma_prepare': 'buffer', # mm/mmap.c,
-    'find_vma_prev': 'buffer', # mm/mmap.c, include/linux/mm.h,
-    'finish_task_switch' : 'other', # kernel/sched.c, used to be syscall
-    'finish_wait' : 'other', # kernel/fork.c, used to be syscall
-    'flush_old_exec': 'other', # fs/exec.c, include/linux/binfmts.h,
-    'flush_signal_handlers': 'other', # kernel/signal.c, include/linux/sched.h,
-    'flush_sigqueue': 'other', # kernel/signal.c,
-    'flush_thread': 'syscall', # arch/alpha/kernel/process.c, include/linux/sched.h,
-    'fn_hash_lookup': 'stack', # net/ipv4/fib_hash.c,
-    'follow_mount' : 'user', # fs/namei.c, used to be syscall
-    'found' : 'other', # sound/oss/forte.c, scripts/kconfig/gconf.c, drivers/net/fec.c, drivers/scsi/ibmmca.c, drivers/scsi/fd_mcs.c,
-    'fput' : 'user', # fs/file_table.c, used to be syscall
-    'free_block' : 'buffer', # mm/slab.c, drivers/char/drm/radeon_mem.c, mm/slab.c,
-    'free_buffer_head': 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'free_fd_array': 'other', # fs/file.c, include/linux/file.h,
-    'free_hot_cold_page' : 'buffer', # mm/page_alloc.c,
-    'free_hot_page' : 'buffer', # mm/page_alloc.c,
-    'free_page_and_swap_cache': 'buffer', # mm/swap_state.c, include/linux/swap.h, include/linux/swap.h,
-    'free_pages' : 'buffer', # mm/page_alloc.c, drivers/char/drm/drm_memory_debug.h, drivers/md/raid6.h, drivers/char/drm/drmP.h,
-    'free_pages_bulk': 'buffer', # mm/page_alloc.c,
-    'free_pgtables': 'buffer', # mm/mmap.c,
-    'free_pidmap': 'other', # kernel/pid.c,
-    'free_task': 'other', # kernel/fork.c,
-    'free_uid' : 'other', # kernel/user.c, include/linux/sched.h,
-    'freed_request' : 'other', # drivers/block/ll_rw_blk.c,
-    'fs_may_remount_ro' : 'other', # fs/file_table.c, include/linux/fs.h,
-    'fsync_buffers_list' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'futex_wait': 'other', # kernel/futex.c,
-    'futex_wake': 'other', # kernel/futex.c,
-    'gconfd-2': 'other', #
-    'generic_commit_write' : 'user', # fs/buffer.c, include/linux/buffer_head.h, used to be syscall
-    'generic_delete_inode': 'other', # fs/inode.c, include/linux/fs.h,
-    'generic_drop_inode' : 'user', # fs/inode.c, used to be syscall
-    'generic_file_aio_read': 'buffer', # mm/filemap.c, include/linux/fs.h,
-    'generic_file_aio_write': 'buffer', # mm/filemap.c, include/linux/fs.h,
-    'generic_file_aio_write_nolock' : 'user', # mm/filemap.c, include/linux/fs.h, used to be syscall
-    'generic_file_buffered_write': 'other', #
-    'generic_file_llseek': 'other', # fs/read_write.c, include/linux/fs.h,
-    'generic_file_mmap': 'buffer', # mm/filemap.c, include/linux/fs.h,
-    'generic_file_open' : 'user', # fs/open.c, include/linux/fs.h, used to be syscall
-    'generic_file_write' : 'user', # mm/filemap.c, include/linux/fs.h, used to be syscall
-    'generic_file_write_nolock' : 'user', # mm/filemap.c, include/linux/fs.h, used to be syscall
-    'generic_file_writev' : 'user', # mm/filemap.c, include/linux/fs.h, used to be syscall
-    'generic_fillattr' : 'user', # fs/stat.c, include/linux/fs.h, used to be syscall
-    'generic_forget_inode' : 'user', # fs/inode.c, used to be syscall
-    'generic_make_request' : 'user', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h, used to be syscall
-    'generic_unplug_device' : 'driver', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'get_conntrack_index' : 'stack', # net/ipv4/netfilter/ip_conntrack_proto_tcp.c,
-    'get_device' : 'driver', # drivers/base/core.c, include/linux/device.h,
-    'get_dirty_limits' : 'buffer', # mm/page-writeback.c,
-    'get_empty_filp' : 'other', # fs/file_table.c, include/linux/fs.h,
-    'get_free_idx': 'interrupt', #
-    'get_futex_key': 'other', # kernel/futex.c,
-    'get_io_context' : 'other', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'get_jiffies_64': 'other', # kernel/time.c, include/linux/jiffies.h, include/linux/jiffies.h,
-    'get_new_inode_fast': 'other', # fs/inode.c,
-    'get_object' : 'other', # fs/exportfs/expfs.c,
-    'get_offset_pmtmr': 'interrupt', #
-    'get_one_pte_map_nested': 'buffer', # mm/mremap.c,
-    'get_page_state': 'buffer', # mm/page_alloc.c, include/linux/page-flags.h,
-    'get_pipe_inode': 'other', # fs/pipe.c,
-    'get_request' : 'other', # drivers/block/ll_rw_blk.c,
-    'get_sample_stats' : 'stack', # net/core/dev.c,
-    'get_signal_to_deliver' : 'other', # kernel/signal.c, include/linux/signal.h,
-    'get_task_mm': 'other', # include/linux/sched.h,
-    'get_tuple' : 'driver', # net/ipv4/netfilter/ip_conntrack_core.c, drivers/isdn/hisax/elsa_cs.c, drivers/isdn/hisax/teles_cs.c, drivers/isdn/hisax/avma1_cs.c, drivers/isdn/hardware/avm/avm_cs.c, drivers/bluetooth/bt3c_cs.c, drivers/bluetooth/btuart_cs.c, drivers/bluetooth/dtl1_cs.c, include/linux/netfilter_ipv4/ip_conntrack_core.h,
-    'get_unique_tuple' : 'stack', # net/ipv4/netfilter/ip_nat_core.c,
-    'get_unmapped_area': 'buffer', # mm/mmap.c, mm/nommu.c, include/linux/mm.h,
-    'get_unused_fd' : 'other', # fs/open.c, include/linux/file.h,  used to be syscall
-    'get_vmalloc_info': 'other', # fs/proc/proc_misc.c,
-    'get_write_access' : 'other', # fs/namei.c, include/linux/fs.h,  used to be syscall
-    'get_writeback_state' : 'other', # mm/page-writeback.c,  used to be syscall
-    'get_zone_counts': 'buffer', # mm/page_alloc.c, include/linux/mmzone.h,
-    'getname' : 'other', # fs/namei.c, include/linux/fs.h,  used to be syscall
-    'getnstimeofday': 'other', #
-    'getrusage': 'other', # kernel/sys.c, kernel/exit.c,
-    'grab_block' : 'other', # fs/ext2/balloc.c,
-    'grep': 'other', #
-    'group_release_blocks' : 'other', # fs/ext2/balloc.c,
-    'group_reserve_blocks' : 'other', # fs/ext2/balloc.c,
-    'group_send_sig_info' : 'other', # kernel/signal.c, include/linux/signal.h,
-    'groups_alloc' : 'other', # kernel/sys.c, include/linux/sched.h,  used to be syscall
-    'groups_free' : 'other', # kernel/sys.c, include/linux/sched.h,  used to be syscall
-    'groups_search' : 'other', # kernel/sys.c,  used to be syscall
-    'groups_sort' : 'other', # kernel/sys.c,  used to be syscall
-    'groups_to_user': 'other', # kernel/sys.c,
-    'grow_dev_page' : 'other', # fs/buffer.c,
-    'halfMD4Transform' : 'other', # fs/ext3/hash.c, drivers/char/random.c,
-    'handle_IRQ_event' : 'interrupt', # arch/alpha/kernel/irq.c, include/asm-alpha/irq.h,
-    'handle_irq' : 'interrupt', # arch/alpha/kernel/irq.c, arch/alpha/kernel/irq_impl.h,
-    'handle_mm_fault' : 'interrupt', # mm/memory.c, include/linux/mm.h,
-    'handle_signal': 'interrupt', # arch/alpha/kernel/signal.c,
-    'handle_stop_signal' : 'interrupt', # kernel/signal.c,
-    'hash_conntrack' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c,
-    'hash_futex': 'other', # kernel/futex.c,
-    'hash_refile' : 'other', # fs/nfsd/nfscache.c,
-    'i8042_interrupt': 'interrupt', # drivers/input/serio/i8042.c, drivers/input/serio/i8042.c,
-    'i8042_timer_func': 'driver', # drivers/input/serio/i8042.c,
-    'i_waitq_head' : 'other', # fs/inode.c,
-    'ide_cd': 'other', #
-    'ide_core': 'other', #
-    'ide_disk': 'other', # include/linux/ide.h,
-    'idle_cpu' : 'idle', # kernel/sched.c, include/linux/sched.h,
-    'iget_locked' : 'other', # fs/inode.c, include/linux/fs.h,
-    'in_group_p' : 'other', # kernel/sys.c, include/linux/sched.h,
-    'inet_accept' : 'stack', # net/ipv4/af_inet.c,
-    'inet_create': 'stack', # net/ipv4/af_inet.c,
-    'inet_getname' : 'stack', # net/ipv4/af_inet.c,
-    'inet_release' : 'stack', # net/ipv4/af_inet.c,
-    'inet_sendmsg' : 'stack', # net/ipv4/af_inet.c,
-    'inet_sendpage' : 'stack', # net/ipv4/af_inet.c,
-    'inet_shutdown' : 'stack', # net/ipv4/af_inet.c,
-    'inet_sock_destruct' : 'stack', # net/ipv4/af_inet.c,
-    'init': 'driver', # net/core/pktgen.c, net/ipv4/netfilter/ip_conntrack_ftp.c, net/ipv4/netfilter/ip_conntrack_irc.c, net/ipv4/netfilter/ip_tables.c, net/ipv4/netfilter/ipt_ECN.c, net/ipv4/netfilter/ipt_LOG.c, net/ipv4/netfilter/ipt_helper.c, net/ipv4/netfilter/ipt_TOS.c, net/ipv4/netfilter/ipt_addrtype.c, net/ipv4/netfilter/ipt_limit.c, net/ipv4/netfilter/ipt_tcpmss.c, net/ipv4/netfilter/ipt_ecn.c, net/ipv4/netfilter/ipt_esp.c, net/ipv4/netfilter/ipt_mac.c, net/ipv4/netfilter/ipt_tos.c, net/ipv4/netfilter/ipt_ttl.c, net/ipv4/netfilter/ip_nat_ftp.c, net/ipv4/netfilter/ip_nat_irc.c, net/ipv4/netfilter/ipt_multiport.c, net/ipv4/netfilter/ipt_dscp.c, net/ipv4/netfilter/arp_tables.c, net/ipv4/netfilter/ip_conntrack_tftp.c, net/ipv4/netfilter/ipt_physdev.c, net/ipv4/netfilter/ipt_state.c, net/ipv4/netfilter/ipt_ah.c, net/ipv4/netfilter/ipt_mark.c, net/ipv4/netfilter/ip_queue.c, net/ipv4/netfilter/ipt_conntrack.c, net/ipv4/netfilter/ip_fw_compat.c, net/ipv4/netfilter/ipt_NETMAP.c, net/ipv4/netfilter/ipt_pkttype.c, net/ipv4/netfilter/ipt_MASQUERADE.c, net/ipv4/netfilter/ip_conntrack_standalone.c, net/ipv4/netfilter/ip_nat_snmp_basic.c, net/ipv4/netfilter/ipt_length.c, net/ipv4/netfilter/arpt_mangle.c, net/ipv4/netfilter/ipt_CLASSIFY.c, net/ipv4/netfilter/ip_nat_standalone.c, net/ipv4/netfilter/ipt_NOTRACK.c, net/ipv4/netfilter/ip_nat_amanda.c, net/ipv4/netfilter/ipt_REDIRECT.c, net/ipv4/netfilter/ipt_TCPMSS.c, net/ipv4/netfilter/ipt_REJECT.c, net/ipv4/netfilter/ip_conntrack_amanda.c, net/ipv4/netfilter/ipt_owner.c, net/ipv4/netfilter/ipt_DSCP.c, net/ipv4/netfilter/ip_nat_tftp.c, net/ipv4/netfilter/arptable_filter.c, net/ipv4/netfilter/ipt_iprange.c, net/ipv4/netfilter/ipt_MARK.c, net/ipv4/netfilter/iptable_filter.c, net/ipv4/netfilter/iptable_mangle.c, net/ipv4/netfilter/ipt_SAME.c, net/ipv4/netfilter/ipt_realm.c, net/ipv4/netfilter/ipt_ULOG.c, net/ipv4/netfilter/iptable_raw.c, net/ipv6/netfilter/ip6t_length.c, net/ipv6/netfilter/ip6t_eui64.c, net/ipv6/netfilter/ip6t_frag.c, net/ipv6/netfilter/ip6t_multiport.c, net/ipv6/netfilter/ip6t_ah.c, net/ipv6/netfilter/ip6t_hl.c, net/ipv6/netfilter/ip6t_rt.c, net/ipv6/netfilter/ip6t_mark.c, net/ipv6/netfilter/ip6_queue.c, net/ipv6/netfilter/ip6table_filter.c, net/ipv6/netfilter/ip6table_mangle.c, net/ipv6/netfilter/ip6t_owner.c, net/ipv6/netfilter/ip6t_LOG.c, net/ipv6/netfilter/ip6t_dst.c, net/ipv6/netfilter/ip6t_esp.c, net/ipv6/netfilter/ip6t_hbh.c, net/ipv6/netfilter/ip6t_mac.c, net/ipv6/netfilter/ip6_tables.c, net/ipv6/netfilter/ip6t_MARK.c, net/ipv6/netfilter/ip6table_raw.c, net/ipv6/netfilter/ip6t_limit.c, net/bridge/netfilter/ebt_among.c, net/bridge/netfilter/ebt_dnat.c, net/bridge/netfilter/ebt_802_3.c, net/bridge/netfilter/ebt_mark.c, net/bridge/netfilter/ebt_redirect.c, net/bridge/netfilter/ebt_pkttype.c, net/bridge/netfilter/ebt_snat.c, net/bridge/netfilter/ebt_vlan.c, net/bridge/netfilter/ebt_arp.c, net/bridge/netfilter/ebt_log.c, net/bridge/netfilter/ebt_stp.c, net/bridge/netfilter/ebtables.c, net/bridge/netfilter/ebt_limit.c, net/bridge/netfilter/ebtable_broute.c, net/bridge/netfilter/ebt_arpreply.c, net/bridge/netfilter/ebt_ip.c, net/bridge/netfilter/ebtable_filter.c, net/bridge/netfilter/ebt_mark_m.c, net/bridge/netfilter/ebtable_nat.c, net/decnet/netfilter/dn_rtmsg.c, init/main.c, scripts/kconfig/qconf.cc, , as member of class ConfigItemdrivers/usb/host/ehci-hcd.c, drivers/usb/gadget/ether.c, drivers/usb/gadget/net2280.c, drivers/usb/gadget/goku_udc.c, drivers/usb/gadget/zero.c, drivers/usb/gadget/dummy_hcd.c, drivers/usb/gadget/inode.c, drivers/media/dvb/frontends/grundig_29504-401.c, crypto/tcrypt.c, crypto/khazad.c, crypto/digest.c, crypto/des.c, crypto/md4.c, crypto/md5.c, crypto/tea.c, crypto/serpent.c, crypto/blowfish.c, crypto/sha1.c, crypto/crypto_null.c, crypto/crc32c.c, crypto/deflate.c, crypto/cast5.c, crypto/cast6.c, crypto/sha256.c, crypto/sha512.c, crypto/twofish.c, kernel/futex.c, init/main.c, net/ipv4/netfilter/ipt_recent.c, drivers/i2c/chips/w83781d.c, drivers/i2c/chips/w83627hf.c, drivers/media/video/saa7114.c,
-    'init_bictcp' : 'stack', # net/ipv4/tcp_input.c,
-    'init_buffer_head' : 'other', # fs/buffer.c,
-    'init_conntrack' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c,
-    'init_fpu': 'interrupt', # include/asm-i386/i387.h,
-    'init_new_context': 'interrupt', # include/asm-alpha/mmu_context.h, include/asm-i386/mmu_context.h,
-    'init_page_buffers' : 'other', # fs/buffer.c,
-    'init_westwood' : 'stack', # net/ipv4/tcp_input.c,
-    'inode_add_bytes' : 'other', # fs/stat.c, include/linux/fs.h,
-    'inode_change_ok': 'other', # fs/attr.c, include/linux/fs.h,
-    'inode_has_buffers' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'inode_setattr': 'other', # fs/attr.c, include/linux/fs.h,
-    'inode_sub_bytes' : 'other', # fs/stat.c, include/linux/fs.h,
-    'inode_times_differ' : 'other', # fs/inode.c,
-    'inode_update_time' : 'other', # fs/inode.c, include/linux/fs.h,
-    'insert_vm_struct': 'buffer', # mm/mmap.c, include/linux/mm.h,
-    'install_arg_page': 'other', # fs/exec.c, include/linux/mm.h,
-    'internal_add_timer' : 'other', # kernel/timer.c,
-    'invalid_dpte_no_dismiss_10_' : 'interrupt', #
-    'invalidate_inode_buffers' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'invert_tuple' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c,
-    'invert_tuplepr' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack.h,
-    'io_schedule' : 'other', # kernel/sched.c, include/linux/sched.h,
-    'ip_append_data' : 'stack', # net/ipv4/ip_output.c,
-    'ip_append_page' : 'stack', # net/ipv4/ip_output.c,
-    'ip_build_and_send_pkt' : 'stack', # net/ipv4/ip_output.c,
-    'ip_cmsg_recv': 'stack', # net/ipv4/ip_sockglue.c,
-    'ip_cmsg_send' : 'stack', # net/ipv4/ip_sockglue.c,
-    'ip_confirm' : 'stack', # net/ipv4/netfilter/ip_conntrack_standalone.c,
-    'ip_conntrack': 'other', # include/linux/netfilter_ipv4/ip_conntrack.h,
-    'ip_conntrack_alter_reply' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack.h,
-    'ip_conntrack_defrag' : 'stack', # net/ipv4/netfilter/ip_conntrack_standalone.c,
-    'ip_conntrack_find_get' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack_core.h,
-    'ip_conntrack_get' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack.h,
-    'ip_conntrack_in' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack_core.h,
-    'ip_conntrack_local' : 'stack', # net/ipv4/netfilter/ip_conntrack_standalone.c,
-    'ip_conntrack_tuple_taken' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack.h,
-    'ip_conntrack_unexpect_related' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack_helper.h,
-    'ip_ct_find_helper' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack_helper.h,
-    'ip_ct_find_proto' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack_core.h,
-    'ip_ct_gather_frags' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack.h,
-    'ip_ct_refresh' : 'stack', # net/ipv4/netfilter/ip_conntrack_core.c, include/linux/netfilter_ipv4/ip_conntrack.h,
-    'ip_defrag' : 'stack', # net/ipv4/ip_fragment.c,
-    'ip_evictor' : 'stack', # net/ipv4/ip_fragment.c,
-    'ip_fast_csum' : 'stack', # arch/alpha/lib/checksum.c, include/asm-alpha/checksum.h,
-    'ip_finish_output' : 'stack', # net/ipv4/ip_output.c,
-    'ip_finish_output2' : 'stack', # net/ipv4/ip_output.c,
-    'ip_frag_create' : 'stack', # net/ipv4/ip_fragment.c,
-    'ip_frag_destroy' : 'stack', # net/ipv4/ip_fragment.c,
-    'ip_frag_intern' : 'stack', # net/ipv4/ip_fragment.c,
-    'ip_frag_queue' : 'stack', # net/ipv4/ip_fragment.c,
-    'ip_frag_reasm' : 'stack', # net/ipv4/ip_fragment.c,
-    'ip_local_deliver' : 'stack', # net/ipv4/ip_input.c,
-    'ip_local_deliver_finish' : 'stack', # net/ipv4/ip_input.c,
-    'ip_map_lookup' : 'stack', # net/sunrpc/svcauth_unix.c,
-    'ip_map_put' : 'stack', # net/sunrpc/svcauth_unix.c,
-    'ip_mc_drop_socket' : 'stack', # net/ipv4/igmp.c, net/ipv4/af_inet.c, include/linux/igmp.h,
-    'ip_nat_fn' : 'stack', # net/ipv4/netfilter/ip_nat_standalone.c,
-    'ip_nat_out' : 'stack', # net/ipv4/netfilter/ip_nat_standalone.c,
-    'ip_nat_rule_find' : 'stack', # net/ipv4/netfilter/ip_nat_rule.c, include/linux/netfilter_ipv4/ip_nat_rule.h,
-    'ip_nat_setup_info' : 'stack', # net/ipv4/netfilter/ip_nat_core.c, include/linux/netfilter_ipv4/ip_nat.h,
-    'ip_nat_used_tuple' : 'stack', # net/ipv4/netfilter/ip_nat_core.c, include/linux/netfilter_ipv4/ip_nat.h,
-    'ip_output' : 'stack', # net/ipv4/ip_output.c,
-    'ip_push_pending_frames' : 'stack', # net/ipv4/ip_output.c,
-    'ip_queue_xmit' : 'stack', # net/ipv4/ip_output.c,
-    'ip_rcv' : 'stack', # net/ipv4/ip_input.c,
-    'ip_rcv_finish' : 'stack', # net/ipv4/ip_input.c,
-    'ip_refrag' : 'stack', # net/ipv4/netfilter/ip_conntrack_standalone.c,
-    'ip_route_input' : 'stack', # net/ipv4/route.c,
-    'ip_route_input_slow': 'stack', # net/ipv4/route.c,
-    'ip_route_output_flow' : 'stack', # net/ipv4/route.c,
-    'ip_send_check' : 'stack', # net/ipv4/ip_output.c,
-    'ip_tables': 'other', #
-    'ipq_kill' : 'stack', # net/ipv4/ip_fragment.c,
-    'ipqhashfn' : 'stack', # net/ipv4/ip_fragment.c,
-    'ipt_do_table' : 'stack', # net/ipv4/netfilter/ip_tables.c, include/linux/netfilter_ipv4/ip_tables.h,
-    'ipt_find_target_lock' : 'stack', # net/ipv4/netfilter/ip_tables.c, include/linux/netfilter_ipv4/ip_tables.h, include/linux/netfilter.h,
-    'ipt_hook' : 'stack', # net/ipv4/netfilter/iptable_filter.c, net/ipv4/netfilter/iptable_raw.c,
-    'ipt_local_hook' : 'stack', # net/ipv4/netfilter/iptable_mangle.c,
-    'ipt_local_out_hook' : 'stack', # net/ipv4/netfilter/iptable_filter.c,
-    'ipt_route_hook' : 'stack', # net/ipv4/netfilter/iptable_mangle.c,
-    'iptable_filter': 'other', #
-    'iptable_mangle': 'other', #
-    'iptable_nat': 'other', #
-    'iput' : 'other', # fs/inode.c, include/linux/fs.h,
-    'ipv4_sabotage_in' : 'stack', # net/bridge/br_netfilter.c,
-    'ipv4_sabotage_out' : 'stack', # net/bridge/br_netfilter.c,
-    'irq_entries_start': 'interrupt', #
-    'is_bad_inode' : 'other', # fs/bad_inode.c, include/linux/fs.h,
-    'it_real_fn': 'other', # kernel/itimer.c, include/linux/timer.h,
-    'jbd': 'other', #
-    'juk': 'other', #
-    'kded_kmilod.so': 'other', #
-    'kdeinit': 'other', #
-    'kernel_read': 'other', # fs/exec.c, include/linux/fs.h,
-    'kfree' : 'buffer', # mm/slab.c, include/linux/slab.h,
-    'kfree_skbmem' : 'buffer', # net/core/skbuff.c, include/linux/skbuff.h,
-    'kill_fasync': 'other', # fs/fcntl.c, include/linux/fs.h,
-    'kill_proc_info' : 'other', # kernel/signal.c, include/linux/sched.h,
-    'kill_something_info' : 'other', # kernel/signal.c,
-    'kmap': 'buffer', # include/asm-i386/highmem.h,
-    'kmap_atomic': 'buffer', # include/linux/highmem.h, include/asm-i386/highmem.h,
-    'kmap_high': 'buffer', # mm/highmem.c,
-    'kmem_cache_alloc' : 'buffer', # mm/slab.c, include/linux/slab.h,
-    'kmem_cache_free' : 'buffer', # mm/slab.c, include/linux/slab.h,
-    'kmem_flagcheck' : 'buffer', # mm/slab.c,
-    'kmem_freepages' : 'buffer', # mm/slab.c,
-    'kmem_getpages' : 'buffer', # mm/slab.c,
-    'kobject_get' : 'other', # lib/kobject.c, include/linux/kobject.h,
-    'kobject_put' : 'other', # lib/kobject.c, include/linux/kobject.h,
-    'kref_get': 'other', # lib/kref.c, include/linux/kref.h,
-    'kscd': 'other', #
-    'ksoftirqd' : 'interrupt', # kernel/softirq.c,
-    'ksysguardd': 'other', #
-    'kthread_should_stop' : 'other', # kernel/kthread.c, include/linux/kthread.h,
-    'kunmap': 'buffer', # include/linux/highmem.h, include/asm-i386/highmem.h,
-    'kunmap_atomic': 'buffer', # include/linux/highmem.h, include/asm-i386/highmem.h,
-    'kunmap_high': 'buffer', # mm/highmem.c,
-    'kwrapper': 'other', #
-    'ld-2.3.2.so': 'other', #
-    'lease_get_mtime' : 'other', # fs/locks.c, include/linux/fs.h,
-    'libORBit-2.so.0.0.0': 'other', #
-    'libX11.so.6.2': 'other', #
-    'libXext.so.6.4': 'other', #
-    'libXft.so.2.1.1': 'other', #
-    'libXrender.so.1.2.2': 'other', #
-    'libacl.so.1.1.0': 'other', #
-    'libarts.so': 'other', #
-    'libartsdsp.so.0.0.0': 'other', #
-    'libartsflow.so.1.0.0': 'other', #
-    'libartsmidi.so.0.0.0': 'other', #
-    'libattr.so.1.1.0': 'other', #
-    'libc-2.3.2.so' : 'user',
-    'libcdaudio.so': 'other', #
-    'libcrypt-2.3.2.so': 'other', #
-    'libcrypto.so.0.9.7': 'other', #
-    'libdb3.so.3.0.2': 'other', #
-    'libdl-2.3.2.so': 'other', #
-    'libgcc_s.so.1': 'other', #
-    'libgconf-2.so.4.1.0': 'other', #
-    'libgcrypt.so.11.1.1': 'other', #
-    'libgdk-1.2.so.0.9.1': 'other', #
-    'libgdk-x11-2.0.so.0.400.13': 'other', #
-    'libgfx_gtk.so': 'other', #
-    'libgkgfx.so': 'other', #
-    'libgklayout.so': 'other', #
-    'libglib-1.2.so.0.0.10': 'other', #
-    'libglib-2.0.so.0.400.8': 'other', #
-    'libgnutls.so.11.1.16': 'other', #
-    'libgobject-2.0.so.0.400.8': 'other', #
-    'libgthread-2.0.so.0.400.8': 'other', #
-    'libgtk-x11-2.0.so.0.400.13': 'other', #
-    'libhtmlpars.so': 'other', #
-    'libimglib2.so': 'other', #
-    'libkdecore.so.4.2.0': 'other', #
-    'libkdefx.so.4.2.0': 'other', #
-    'libkdeinit_kded.so': 'other', #
-    'libkdeinit_kdesktop.so': 'other', #
-    'libkdeinit_kicker.so': 'other', #
-    'libkdeinit_klauncher.so': 'other', #
-    'libkdeinit_klipper.so': 'other', #
-    'libkdeui.so.4.2.0': 'other', #
-    'libksgrd.so.1.2.0': 'other', #
-    'libm-2.3.2.so': 'other', #
-    'libmcop.so.1.0.0': 'other', #
-    'libmcop_mt.so.1.0.0': 'other', #
-    'libmikmod.so': 'other', #
-    'libmpg123.so': 'other', #
-    'libncurses.so.5.4': 'other', #
-    'libnecko.so': 'other', #
-    'libnsl-2.3.2.so': 'other', #
-    'libnspr4.so': 'other', #
-    'libnss_compat-2.3.2.so': 'other', #
-    'libnss_files-2.3.2.so': 'other', #
-    'libnss_nis-2.3.2.so': 'other', #
-    'libpcre.so.3.10.0': 'other', #
-    'libplc4.so': 'other', #
-    'libplds4.so': 'other', #
-    'libpref.so': 'other', #
-    'libpthread-0.10.so': 'user',
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libpthread-0.60.so': 'other', #
-    'libqt-mt.so.3.3.3': 'other', #
-    'libqtmcop.so.1.0.0': 'other', #
-    'librdf.so': 'other', #
-    'libresolv-2.3.2.so': 'other', #
-    'librt-2.3.2.so': 'other', #
-    'libstdc++.so.5.0.7': 'other', #
-    'libtasn1.so.2.0.10': 'other', #
-    'libuconv.so': 'other', #
-    'libwidget_gtk2.so': 'other', #
-    'libwrap.so.0.7.6': 'other', #
-    'libxmms.so.1.3.1': 'other', #
-    'libxpcom.so': 'other', #
-    'link_path_walk' : 'other', # fs/namei.c,
-    'll_back_merge_fn' : 'other', # drivers/block/ll_rw_blk.c,
-    'll_front_merge_fn' : 'other', # drivers/block/ll_rw_blk.c,
-    'll_merge_requests_fn' : 'other', # drivers/block/ll_rw_blk.c,
-    'll_rw_block' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'llc_rcv': 'stack', # net/llc/llc_input.c,
-    'llc_sap_find': 'stack', # net/llc/llc_core.c,
-    'load_balance' : 'other', # kernel/sched.c,
-    'load_balance_newidle' : 'other', # kernel/sched.c,
-    'load_elf_binary': 'other', # fs/binfmt_elf.c, fs/binfmt_elf.c,
-    'load_elf_interp': 'other', # fs/binfmt_elf.c,
-    'load_script': 'other', # fs/binfmt_script.c,
-    'local_bh_enable' : 'interrupt', # kernel/softirq.c, include/linux/interrupt.h,
-    'lock_sock' : 'stack', # net/core/sock.c,
-    'lockfile-create': 'other', #
-    'lockfile-remove': 'other', #
-    'locks_remove_flock' : 'other', # fs/locks.c, include/linux/fs.h,
-    'locks_remove_posix' : 'other', # fs/locks.c, include/linux/fs.h,
-    'lookup_create': 'other', # fs/namei.c, include/linux/dcache.h,
-    'lookup_hash': 'other', # fs/namei.c, include/linux/namei.h,
-    'lookup_mnt' : 'other', # fs/namespace.c, include/linux/dcache.h,
-    'loop' : 'interrupt', #
-    'loopback_xmit': 'driver',
-    'lru_add_drain' : 'buffer', # mm/swap.c, include/linux/swap.h,
-    'lru_cache_add' : 'buffer', # mm/swap.c,
-    'lru_cache_add_active': 'buffer', # mm/swap.c,
-    'lru_put_front' : 'other', # fs/nfsd/nfscache.c,
-    'ls': 'driver', # drivers/fc4/fc.c,
-    'mail': 'other', #
-    'mapping_tagged' : 'buffer', # mm/page-writeback.c, include/linux/fs.h,
-    'mark_buffer_dirty' : 'other', # fs/buffer.c,
-    'mark_buffer_dirty_inode' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'mark_offset_pmtmr': 'interrupt', #
-    'mark_page_accessed' : 'buffer', # mm/swap.c,
-    'mask_and_ack_level_ioapic_vector': 'interrupt', # include/asm-i386/io_apic.h,
-    'math_state_restore': 'interrupt', #
-    'mawk': 'other', #
-    'max_sane_readahead': 'buffer', # mm/readahead.c, include/linux/mm.h,
-    'max_select_fd': 'other', # fs/select.c,
-    'may_open': 'other', # fs/namei.c, include/linux/fs.h,
-    'memcmp' : 'copy', # lib/string.c,
-    'memcpy' : 'copy', # lib/string.c, arch/alpha/lib/memcpy.c, arch/alpha/kernel/alpha_ksyms.c, include/asm-alpha/string.h, include/asm-alpha/string.h,
-    'memcpy_fromiovec': 'copy', # net/core/iovec.c, include/linux/socket.h,
-    'memcpy_fromiovecend': 'copy', # net/core/iovec.c, include/linux/socket.h,
-    'memcpy_toiovec' : 'copy', # net/core/iovec.c, include/linux/socket.h,
-    'meminfo_read_proc': 'other', # fs/proc/proc_misc.c,
-    'memmove' : 'copy', # lib/string.c, include/asm-alpha/string.h,
-    'mempool_alloc' : 'buffer', # mm/mempool.c, include/linux/mempool.h,
-    'mempool_alloc_slab' : 'buffer', # mm/mempool.c, include/linux/mempool.h,
-    'mempool_free' : 'buffer', # mm/mempool.c, include/linux/mempool.h,
-    'mempool_free_slab' : 'buffer', # mm/mempool.c, include/linux/mempool.h,
-    'memscan' : 'copy', # lib/string.c,
-    'mkdir': 'other', #
-    'mm_alloc': 'buffer', # kernel/fork.c, include/linux/sched.h,
-    'mm_init': 'driver', # drivers/block/umem.c, kernel/fork.c,
-    'mm_release': 'other', # kernel/fork.c, include/linux/sched.h,
-    'mmput': 'other', # kernel/fork.c, include/linux/sched.h,
-    'mod_timer' : 'other', # kernel/timer.c, include/linux/timer.h,
-    'move_addr_to_user' : 'copy', # net/socket.c, include/linux/socket.h,
-    'move_one_page': 'buffer', # mm/mremap.c,
-    'move_vma': 'buffer', # mm/mremap.c,
-    'mpage_alloc' : 'other', # fs/mpage.c,
-    'mpage_bio_submit' : 'other', # fs/mpage.c,
-    'mpage_end_io_write' : 'other', # fs/mpage.c,
-    'mpage_readpage': 'other', # fs/mpage.c, include/linux/mpage.h,
-    'mpage_readpages': 'other', # fs/mpage.c, include/linux/mpage.h,
-    'mpage_writepage' : 'other', # fs/mpage.c,
-    'mpage_writepages' : 'other', # fs/mpage.c, include/linux/mpage.h,
-    'mv': 'other', #
-    'n_tty_chars_in_buffer': 'driver', # drivers/char/n_tty.c,
-    'n_tty_receive_buf': 'driver', # drivers/char/n_tty.c,
-    'n_tty_receive_room': 'driver', # drivers/char/n_tty.c,
-    'need_resched': 'driver', # include/linux/sched.h, drivers/char/tipar.c,
-    'neigh_lookup': 'stack', # net/core/neighbour.c,
-    'neigh_periodic_timer': 'stack', # net/core/neighbour.c,
-    'neigh_resolve_output' : 'stack', # net/core/neighbour.c,
-    'neigh_timer_handler': 'stack', # net/core/neighbour.c, net/core/neighbour.c,
-    'neigh_update': 'stack', # net/core/neighbour.c,
-    'net_rx_action' : 'driver', # net/core/dev.c,
-    'net_tx_action' : 'driver', # net/core/dev.c,
-    'netif_receive_skb' : 'driver', # net/core/dev.c, include/linux/netdevice.h,
-    'netif_rx' : 'driver', # net/core/dev.c, include/linux/netdevice.h,
-    'netperf' : 'user',
-    'netserver': 'user',
-    'new_inode' : 'other', # fs/inode.c, include/linux/fs.h,
-    'next_signal' : 'other', # kernel/signal.c,
-    'next_thread': 'other', # kernel/exit.c,
-    'nf_hook_slow' : 'stack', # net/core/netfilter.c, include/linux/netfilter.h,
-    'nf_iterate' : 'stack', # net/core/netfilter.c,
-    'nfs3svc_decode_commitargs' : 'other', # fs/nfsd/nfs3xdr.c, include/linux/nfsd/xdr3.h,
-    'nfs3svc_decode_writeargs' : 'other', # fs/nfsd/nfs3xdr.c, include/linux/nfsd/xdr3.h,
-    'nfs3svc_encode_commitres' : 'other', # fs/nfsd/nfs3xdr.c, include/linux/nfsd/xdr3.h,
-    'nfs3svc_encode_writeres' : 'other', # fs/nfsd/nfs3xdr.c, include/linux/nfsd/xdr3.h,
-    'nfs3svc_release_fhandle' : 'other', # fs/nfsd/nfs3xdr.c, include/linux/nfsd/xdr3.h,
-    'nfsd' : 'other', # fs/nfsd/nfssvc.c, fs/nfsd/nfssvc.c,
-    'nfsd3_proc_commit' : 'other', # fs/nfsd/nfs3proc.c,
-    'nfsd3_proc_write' : 'other', # fs/nfsd/nfs3proc.c,
-    'nfsd_acceptable' : 'other', # fs/nfsd/nfsfh.c,
-    'nfsd_cache_append' : 'other', # fs/nfsd/nfscache.c, fs/nfsd/nfscache.c,
-    'nfsd_cache_lookup' : 'other', # fs/nfsd/nfscache.c, include/linux/nfsd/cache.h,
-    'nfsd_cache_update' : 'other', # fs/nfsd/nfscache.c, include/linux/nfsd/cache.h,
-    'nfsd_close' : 'other', # fs/nfsd/vfs.c, include/linux/nfsd/nfsd.h,
-    'nfsd_commit' : 'other', # fs/nfsd/vfs.c, include/linux/nfsd/nfsd.h,
-    'nfsd_dispatch' : 'other', # fs/nfsd/nfssvc.c, include/linux/nfsd/nfsd.h,
-    'nfsd_open' : 'other', # fs/nfsd/vfs.c, include/linux/nfsd/nfsd.h,
-    'nfsd_permission' : 'other', # fs/nfsd/vfs.c, include/linux/nfsd/nfsd.h,
-    'nfsd_setuser' : 'other', # fs/nfsd/auth.c, include/linux/nfsd/auth.h,
-    'nfsd_sync' : 'other', # fs/nfsd/vfs.c,
-    'nfsd_write' : 'other', # fs/nfsd/vfs.c, include/linux/nfsd/nfsd.h,
-    'no_pm_change_10_' : 'interrupt', #
-    'no_quad' : 'interrupt', #
-    'nonseekable_open' : 'other', # fs/open.c, include/linux/fs.h,
-    'normal_int' : 'other', #
-    'normal_poll': 'driver', # drivers/char/n_tty.c,
-    'note_interrupt': 'interrupt', #
-    'notifier_call_chain': 'other', # kernel/sys.c, include/linux/notifier.h,
-    'notify_change': 'other', # fs/attr.c, include/linux/fs.h,
-    'nr_blockdev_pages': 'other', # fs/block_dev.c, include/linux/blkdev.h,
-    'nr_free_pages': 'buffer', # mm/page_alloc.c, include/linux/swap.h,
-    'nr_running': 'other', # kernel/sched.c, include/linux/sched.h,
-    'ns83820': 'driver',
-    'ns83820_do_isr' : 'driver',
-    'ns83820_hard_start_xmit' : 'driver',
-    'ns83820_irq' : 'driver',
-    'ns83820_rx_kick' : 'driver',
-    'ns83820_tx_watch' : 'driver',
-    'ns83821_do_isr' : 'driver', #
-    'ns83821_hard_start_xmit' : 'driver', #
-    'ns83821_irq' : 'driver', #
-    'ns83821_rx_kick' : 'driver', #
-    'number' : 'interrupt', # lib/vsprintf.c, arch/alpha/kernel/srm_env.c,
-    'nvidia': 'other', #
-    'old_mmap': 'interrupt', #
-    'open_exec': 'other', # fs/exec.c, include/linux/fs.h,
-    'open_namei' : 'user', # fs/namei.c, include/linux/fs.h,  used to by syscall
-    'open_private_file' : 'user', # fs/file_table.c, include/linux/fs.h,  used to by syscall
-    'oprofile': 'other', #
-    'oprofiled': 'other', #
-    'osf_brk': 'user',
-    'osf_mmap': 'user',
-    'osf_sigprocmask' : 'other', #
-    'osync_buffers_list' : 'other', # fs/buffer.c,
-    'padzero': 'other', # fs/binfmt_elf.c,
-    'page_add_anon_rmap' : 'buffer', # mm/rmap.c, include/linux/rmap.h,
-    'page_add_file_rmap': 'buffer', # mm/rmap.c, include/linux/rmap.h,
-    'page_address': 'buffer', # mm/highmem.c, include/linux/mm.h, include/linux/mm.h, include/linux/mm.h,
-    'page_cache_readahead': 'buffer', # mm/readahead.c, include/linux/mm.h,
-    'page_fault': 'interrupt', #
-    'page_remove_rmap': 'buffer', # mm/rmap.c, include/linux/rmap.h,
-    'page_slot': 'buffer', # mm/highmem.c,
-    'page_symlink' : 'other', # fs/namei.c, include/linux/fs.h,
-    'page_waitqueue' : 'buffer', # mm/filemap.c,
-    'pagevec_lookup': 'buffer', # mm/swap.c, include/linux/pagevec.h,
-    'pagevec_lookup_tag' : 'buffer', # mm/swap.c, include/linux/pagevec.h,
-    'pal_dtb_ldq' : 'interrupt', #
-    'pal_itb_ldq' : 'interrupt', #
-    'pal_post_interrupt' : 'interrupt', #
-    'path_lookup' : 'user', # fs/namei.c,  used to by syscall
-    'path_release' : 'user', # fs/namei.c, include/linux/namei.h,  used to by syscall
-    'pci_bus_read_config_word': 'driver', # include/linux/pci.h,
-    'pci_conf1_read': 'driver', #
-    'pci_dac_dma_supported' : 'driver', # arch/alpha/kernel/pci_iommu.c, include/asm-alpha/pci.h,
-    'pci_map_page' : 'driver', # arch/alpha/kernel/pci_iommu.c, include/asm-generic/pci-dma-compat.h, include/asm-alpha/pci.h,
-    'pci_map_single' : 'driver', # arch/alpha/kernel/pci_iommu.c, arch/alpha/kernel/pci-noop.c, include/asm-generic/pci-dma-compat.h, drivers/net/wan/wanxl.c, drivers/net/wan/wanxl.c, drivers/scsi/aic7xxx/aic79xx_osm.h, drivers/scsi/aic7xxx/aic7xxx_osm.h, include/asm-alpha/pci.h,
-    'pci_map_single_1' : 'driver', # arch/alpha/kernel/pci_iommu.c,
-    'pci_read': 'driver', #
-    'pci_unmap_page' : 'driver', # arch/alpha/kernel/pci_iommu.c, include/asm-generic/pci-dma-compat.h, include/asm-alpha/pci.h,
-    'pci_unmap_single' : 'driver', # arch/alpha/kernel/pci_iommu.c, arch/alpha/kernel/pci-noop.c, include/asm-generic/pci-dma-compat.h, drivers/scsi/aic7xxx/aic79xx_osm.h, drivers/scsi/aic7xxx/aic7xxx_osm.h, include/asm-alpha/pci.h,
-    'percpu_counter_mod' : 'buffer', # mm/swap.c, include/linux/percpu_counter.h,
-    'perl': 'other', #
-    'permission' : 'user', # fs/namei.c, include/linux/fs.h, used to be syscall
-    'pfifo_fast_dequeue' : 'stack', # net/sched/sch_generic.c,
-    'pfifo_fast_enqueue' : 'stack', # net/sched/sch_generic.c,
-    'pgd_alloc': 'buffer', # arch/alpha/mm/init.c, include/asm-alpha/pgalloc.h, include/asm-i386/pgalloc.h,
-    'pgd_ctor': 'buffer', # include/asm-i386/pgtable.h,
-    'pgd_free': 'buffer', # include/asm-alpha/pgalloc.h, include/asm-i386/pgalloc.h,
-    'pipe_ioctl': 'other', # fs/pipe.c,
-    'pipe_new': 'other', # fs/pipe.c, include/linux/pipe_fs_i.h,
-    'pipe_poll': 'other', # fs/pipe.c,
-    'pipe_read': 'other', # fs/pipe.c,
-    'pipe_read_release': 'other', # fs/pipe.c,
-    'pipe_readv': 'other', # fs/pipe.c,
-    'pipe_release': 'other', # fs/pipe.c,
-    'pipe_wait': 'other', # fs/pipe.c, include/linux/pipe_fs_i.h,
-    'pipe_write': 'other', # fs/pipe.c,
-    'pipe_write_fasync': 'other', # fs/pipe.c,
-    'pipe_write_release': 'other', # fs/pipe.c,
-    'pipe_writev': 'other', # fs/pipe.c,
-    'pipefs_delete_dentry': 'other', # fs/pipe.c,
-    'place_in_hashes' : 'stack', # net/ipv4/netfilter/ip_nat_core.c, include/linux/netfilter_ipv4/ip_nat_core.h,
-    'poll_freewait' : 'other', # fs/select.c, include/linux/poll.h,
-    'poll_idle': 'idle', #
-    'poll_initwait' : 'other', # fs/select.c, include/linux/poll.h,
-    'portmap': 'other', #
-    'preempt_schedule': 'other', # kernel/sched.c, include/linux/preempt.h,
-    'prep_new_page' : 'buffer', # mm/page_alloc.c,
-    'prepare_binprm': 'other', # fs/exec.c, include/linux/binfmts.h,
-    'prepare_to_copy': 'interrupt', # include/asm-alpha/processor.h, include/asm-i386/processor.h,
-    'prepare_to_wait' : 'other', # kernel/fork.c,
-    'prio_tree_expand': 'buffer', # mm/prio_tree.c,
-    'prio_tree_insert': 'buffer', # mm/prio_tree.c,
-    'prio_tree_remove': 'buffer', # mm/prio_tree.c,
-    'prio_tree_replace': 'buffer', # mm/prio_tree.c,
-    'proc_alloc_inode': 'other', # fs/proc/inode.c,
-    'proc_calc_metrics': 'other', # fs/proc/proc_misc.c,
-    'proc_delete_inode': 'other', # fs/proc/inode.c,
-    'proc_destroy_inode': 'other', # fs/proc/inode.c,
-    'proc_file_read': 'other', # fs/proc/generic.c, fs/proc/generic.c,
-    'proc_get_inode': 'other', # fs/proc/inode.c, include/linux/proc_fs.h,
-    'proc_lookup': 'other', # fs/proc/generic.c, include/linux/proc_fs.h,
-    'proc_pid_unhash': 'other', # fs/proc/base.c, include/linux/proc_fs.h,
-    'proc_pident_lookup': 'other', # fs/proc/base.c,
-    'proc_root_lookup': 'other', # fs/proc/root.c,
-    'process_backlog' : 'stack', # net/core/dev.c,
-    'process_timeout': 'other', # kernel/timer.c,
-    'profile_hit': 'other', #
-    'profile_hook': 'other', # kernel/profile.c, include/linux/profile.h, include/linux/profile.h,
-    'profile_munmap': 'other', #
-    'profile_task_exit': 'other', #
-    'profile_tick': 'other', #
-    'pskb_expand_head': 'stack', # net/core/skbuff.c, include/linux/skbuff.h,
-    'pte_alloc_map': 'buffer', # mm/memory.c,
-    'pte_alloc_one': 'buffer', # include/asm-alpha/pgalloc.h, include/asm-i386/pgalloc.h,
-    'ptrace_cancel_bpt' : 'user', # arch/alpha/kernel/ptrace.c, arch/alpha/kernel/proto.h, used to be syscall
-    'pty_chars_in_buffer': 'driver', # drivers/char/pty.c,
-    'pty_open': 'driver', # drivers/char/pty.c,
-    'pty_write_room': 'driver', # drivers/char/pty.c,
-    'put_device' : 'driver', # drivers/base/core.c, include/linux/device.h,
-    'put_files_struct': 'other', # kernel/exit.c,
-    'put_filp': 'other', # fs/file_table.c, include/linux/file.h,
-    'put_io_context' : 'driver', # drivers/block/ll_rw_blk.c, include/linux/blkdev.h,
-    'put_unused_fd' : 'other', # fs/open.c,
-    'qdisc_restart' : 'stack', # net/sched/sch_generic.c,
-    'queue_delayed_work': 'other', # kernel/workqueue.c,
-    'queue_me': 'other', # kernel/futex.c,
-    'quiesce' : 'idle', #
-    'radix_tree_delete': 'other', # lib/radix-tree.c, include/linux/radix-tree.h,
-    'radix_tree_extend': 'other', # lib/radix-tree.c,
-    'radix_tree_gang_lookup': 'other', # lib/radix-tree.c, include/linux/radix-tree.h,
-    'radix_tree_gang_lookup_tag' : 'other', # lib/radix-tree.c, include/linux/radix-tree.h,
-    'radix_tree_insert' : 'other', # lib/radix-tree.c, include/linux/radix-tree.h,
-    'radix_tree_lookup' : 'other', # lib/radix-tree.c, include/linux/radix-tree.h,
-    'radix_tree_node_alloc' : 'other', # lib/radix-tree.c,
-    'radix_tree_preload' : 'other', # lib/radix-tree.c, lib/radix-tree.c, include/linux/radix-tree.h,
-    'radix_tree_tag_clear' : 'other', # lib/radix-tree.c, include/linux/radix-tree.h,
-    'radix_tree_tag_set' : 'other', # lib/radix-tree.c, include/linux/radix-tree.h,
-    'radix_tree_tagged' : 'other', # lib/radix-tree.c, include/linux/radix-tree.h,
-    'raise_softirq' : 'interrupt', # kernel/softirq.c,
-    'raise_softirq_irqoff' : 'interrupt', # kernel/softirq.c,
-    'rb_erase' : 'buffer', # lib/rbtree.c, include/linux/rbtree.h,
-    'rb_insert_color' : 'buffer', # lib/rbtree.c, include/linux/rbtree.h,
-    'rb_next' : 'buffer', # lib/rbtree.c, fs/jffs2/nodelist.h, include/linux/rbtree.h,
-    'rb_prev' : 'buffer', # lib/rbtree.c, fs/jffs2/nodelist.h, include/linux/rbtree.h,
-    'rcu_check_callbacks' : 'other', # kernel/rcupdate.c, include/linux/rcupdate.h,
-    'rcu_check_quiescent_state' : 'other', # kernel/rcupdate.c,
-    'rcu_do_batch' : 'other', # kernel/rcupdate.c,
-    'rcu_process_callbacks' : 'other', # kernel/rcupdate.c,
-    'rcu_start_batch' : 'other', # kernel/rcupdate.c,
-    'read_block_bitmap' : 'other', # fs/udf/balloc.c, fs/ext2/balloc.c, fs/ext3/balloc.c,
-    'real_lookup': 'other', # fs/namei.c,
-    'rebalance_tick' : 'other', # kernel/sched.c,
-    'recalc_bh_state' : 'other', # fs/buffer.c,
-    'recalc_sigpending' : 'interrupt', # kernel/signal.c, include/linux/sched.h,
-    'recalc_sigpending_tsk' : 'interrupt', # kernel/signal.c,
-    'recalc_task_prio' : 'other', # kernel/sched.c,
-    'release_blocks' : 'other', # fs/ext2/balloc.c,
-    'release_pages' : 'buffer', # mm/swap.c, include/linux/pagemap.h,
-    'release_sock' : 'stack', # net/core/sock.c,
-    'release_task': 'other', # kernel/exit.c, include/linux/sched.h,
-    'release_thread': 'interrupt', # arch/alpha/kernel/process.c, include/asm-um/processor-generic.h, include/asm-alpha/processor.h, include/asm-i386/processor.h,
-    'release_x86_irqs': 'interrupt', # include/asm-i386/irq.h,
-    'remove_arg_zero': 'other', # fs/exec.c, include/linux/binfmts.h,
-    'remove_from_page_cache': 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'remove_suid' : 'buffer', # mm/filemap.c, include/linux/fs.h,
-    'remove_vm_struct': 'buffer', # mm/mmap.c,
-    'remove_wait_queue' : 'other', # kernel/fork.c,
-    'resched_task' : 'other', # kernel/sched.c,
-    'reserve_blocks' : 'other', # fs/ext2/balloc.c,
-    'restore_all' : 'other', #
-    'restore_fpu': 'interrupt', # include/asm-i386/i387.h,
-    'restore_i387': 'interrupt', # include/asm-i386/i387.h,
-    'restore_i387_fxsave': 'interrupt', #
-    'restore_sigcontext' : 'interrupt', # arch/alpha/kernel/signal.c,
-    'resume_kernel': 'interrupt', #
-    'resume_userspace': 'other', #
-    'ret_from_exception': 'other', #
-    'ret_from_intr': 'interrupt', #
-    'ret_from_reschedule' : 'other', #
-    'ret_from_sys_call' : 'user', # arch/alpha/kernel/signal.c, used to be syscall
-    'rm': 'other', #
-    'rm_from_queue': 'other', # kernel/signal.c,
-    'rmqueue_bulk' : 'buffer', # mm/page_alloc.c,
-    'rt_check_expire': 'stack', # net/ipv4/route.c,
-    'rt_hash_code' : 'stack', # net/ipv4/route.c,
-    'rt_intern_hash': 'stack', # net/ipv4/route.c, net/ipv4/route.c,
-    'rt_may_expire': 'stack', # net/ipv4/route.c,
-    'rtc_enable_disable' : 'interrupt', # arch/alpha/kernel/irq_alpha.c,
-    'rti_to_kern' : 'interrupt', #
-    'rti_to_user' : 'user', # used to be syscall
-    'run-parts': 'other', #
-    'run_local_timers' : 'other', # kernel/timer.c, include/linux/timer.h,
-    'run_timer_softirq' : 'other', # kernel/timer.c,
-    'rx_action' : 'driver', # drivers/net/ns83820.c,
-    'rx_irq' : 'driver', # drivers/net/ns83820.c,
-    'rx_refill_atomic' : 'driver', # drivers/net/ns83820.c,
-    'save_i387': 'interrupt', # include/asm-i386/i387.h,
-    'save_i387_fxsave': 'interrupt', #
-    'sched_clock' : 'user', # arch/alpha/kernel/time.c, include/linux/sched.h, used to be syscall
-    'sched_exit': 'other', # kernel/sched.c,
-    'sched_fork': 'other', # kernel/sched.c,
-    'schedule' : 'other', # kernel/sched.c, include/linux/sched.h,
-    'schedule_delayed_work': 'other', # kernel/workqueue.c,
-    'schedule_tail': 'other', # kernel/sched.c,
-    'schedule_timeout' : 'other', # kernel/timer.c, sound/oss/cs4281/cs4281m.c,
-    'scheduler_tick' : 'other', # kernel/sched.c, include/linux/sched.h,
-    'scsi_add_timer' : 'other', # drivers/scsi/scsi_error.c,
-    'scsi_alloc_sgtable' : 'other', # drivers/scsi/scsi_lib.c,
-    'scsi_cmd_ioctl': 'driver', # drivers/block/scsi_ioctl.c, include/linux/blkdev.h,
-    'scsi_decide_disposition' : 'other', # drivers/scsi/scsi_error.c, drivers/scsi/scsi_priv.h,
-    'scsi_delete_timer' : 'other', # drivers/scsi/scsi_error.c,
-    'scsi_device_unbusy' : 'other', # drivers/scsi/scsi_lib.c, drivers/scsi/scsi_priv.h,
-    'scsi_dispatch_cmd' : 'other', # drivers/scsi/scsi.c, drivers/scsi/scsi_priv.h,
-    'scsi_done' : 'other', # drivers/scsi/scsi.c, drivers/scsi/scsi_priv.h,
-    'scsi_end_request' : 'other', # drivers/scsi/scsi_lib.c,
-    'scsi_finish_command' : 'other', # drivers/scsi/scsi.c,
-    'scsi_free_sgtable' : 'other', # drivers/scsi/scsi_lib.c,
-    'scsi_get_command' : 'other', # drivers/scsi/scsi.c,
-    'scsi_init_cmd_errh' : 'other', # drivers/scsi/scsi_lib.c,
-    'scsi_init_io' : 'other', # drivers/scsi/scsi_lib.c,
-    'scsi_io_completion' : 'other', # drivers/scsi/scsi_lib.c,
-    'scsi_mod': 'driver',
-    'scsi_next_command' : 'other', # drivers/scsi/scsi_lib.c, drivers/scsi/scsi_priv.h,
-    'scsi_prep_fn' : 'other', # drivers/scsi/scsi_lib.c,
-    'scsi_put_command' : 'other', # drivers/scsi/scsi.c,
-    'scsi_request_fn' : 'other', # drivers/scsi/scsi_lib.c,
-    'scsi_run_queue' : 'other', # drivers/scsi/scsi_lib.c,
-    'scsi_softirq' : 'other', # drivers/scsi/scsi.c,
-    'sd_init_command' : 'other', # drivers/scsi/sd.c, drivers/scsi/sd.c,
-    'sd_rw_intr' : 'other', # drivers/scsi/sd.c, drivers/scsi/sd.c,
-    'search_binary_handler': 'other', # fs/exec.c, include/linux/binfmts.h,
-    'second_overflow': 'interrupt', # kernel/timer.c,
-    'secure_tcp_sequence_number' : 'stack', # drivers/char/random.c, include/linux/random.h,
-    'sed': 'other', #
-    'select_bits_alloc': 'other', # fs/compat.c, fs/select.c,
-    'select_bits_free': 'other', # fs/compat.c, fs/select.c,
-    'send_group_sig_info': 'other', # kernel/signal.c, include/linux/sched.h,
-    'send_signal' : 'user', # kernel/signal.c, used to be syscall
-    'seq_read': 'other', # fs/seq_file.c, include/linux/seq_file.h,
-    'set_bh_page' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'set_binfmt': 'other', # fs/exec.c, include/linux/binfmts.h,
-    'set_brk': 'user', # fs/binfmt_aout.c, fs/binfmt_elf.c,
-    'set_current_groups' : 'other', # kernel/sys.c, include/linux/sched.h,
-    'set_page_address': 'buffer', # mm/highmem.c, include/linux/mm.h, include/linux/mm.h, include/linux/mm.h,
-    'set_page_dirty': 'buffer', # mm/page-writeback.c,
-    'set_slab_attr' : 'buffer', # mm/slab.c,
-    'set_task_comm': 'other', #
-    'setfl' : 'user', # fs/fcntl.c, used to be syscall
-    'setup_arg_pages': 'other', # fs/exec.c, include/linux/binfmts.h,
-    'setup_frame' : 'interrupt', # arch/alpha/kernel/signal.c,
-    'setup_sigcontext' : 'interrupt', # arch/alpha/kernel/signal.c,
-    'show_stat': 'other', # fs/proc/proc_misc.c,
-    'si_swapinfo': 'buffer', # mm/swapfile.c, include/linux/swap.h, include/linux/swap.h,
-    'sig_ignored' : 'other', # kernel/signal.c,
-    'signal_wake_up' : 'other', # kernel/signal.c, include/linux/sched.h,
-    'sigprocmask' : 'other', # kernel/signal.c, include/linux/signal.h,
-    'single_open': 'other', # fs/seq_file.c, include/linux/seq_file.h,
-    'sk_alloc' : 'buffer', # net/core/sock.c,
-    'sk_free' : 'buffer', # net/core/sock.c,
-    'sk_reset_timer' : 'buffer', # net/core/sock.c,
-    'sk_stop_timer' : 'buffer', # net/core/sock.c,
-    'sk_stream_kill_queues' : 'buffer', # net/core/stream.c,
-    'sk_stream_mem_schedule' : 'buffer', # net/core/stream.c,
-    'sk_stream_rfree' : 'buffer', # net/core/stream.c,
-    'sk_stream_wait_close' : 'buffer', # net/core/stream.c,
-    'sk_stream_wait_memory' : 'buffer', # net/core/stream.c,
-    'sk_stream_write_space' : 'buffer', # net/core/stream.c,
-    'sk_wait_data' : 'buffer', # net/core/sock.c,
-    'skb_checksum': 'stack', # net/core/skbuff.c, include/linux/skbuff.h,
-    'skb_checksum_help': 'stack', # net/core/dev.c, include/linux/netdevice.h,
-    'skb_clone' : 'buffer', # net/core/skbuff.c, include/linux/skbuff.h,
-    'skb_copy_and_csum_bits' : 'copy', # net/core/skbuff.c, include/linux/skbuff.h,
-    'skb_copy_and_csum_datagram':'copy',
-    'skb_copy_bits' : 'copy', # net/core/skbuff.c, include/linux/skbuff.h,
-    'skb_copy_datagram_iovec' : 'copy', # net/core/datagram.c, include/linux/skbuff.h,
-    'skb_dequeue' : 'buffer', # net/core/skbuff.c, include/linux/skbuff.h,
-    'skb_drop_fraglist' : 'buffer', # net/core/skbuff.c,
-    'skb_free_datagram' : 'buffer', # net/core/datagram.c, include/linux/skbuff.h,
-    'skb_queue_head': 'stack', # net/core/skbuff.c, include/linux/skbuff.h,
-    'skb_queue_tail' : 'buffer', # net/core/skbuff.c, include/linux/skbuff.h,
-    'skb_read_and_csum_bits' : 'buffer', # net/sunrpc/xprt.c,
-    'skb_recv_datagram' : 'buffer', # net/core/datagram.c, include/linux/skbuff.h,
-    'skb_release_data' : 'buffer', # net/core/skbuff.c, net/core/dev.c,
-    'skip_atoi': 'other', # lib/vsprintf.c,
-    'slab_destroy' : 'buffer', # mm/slab.c,
-    'smp_apic_timer_interrupt': 'interrupt', #
-    'smp_percpu_timer_interrupt' : 'interrupt', # arch/alpha/kernel/smp.c, arch/alpha/kernel/proto.h,
-    'snap_rcv': 'stack', # net/802/psnap.c,
-    'sock_aio_read' : 'stack', # net/socket.c, net/socket.c,
-    'sock_aio_write': 'stack', # net/socket.c, net/socket.c,
-    'sock_alloc' : 'user', # net/socket.c, include/linux/net.h, used to be syscall
-    'sock_alloc_inode' : 'user', # net/socket.c, used to be syscall
-    'sock_alloc_send_pskb' : 'user', # net/core/sock.c, used to be syscall
-    'sock_alloc_send_skb' : 'user', # net/core/sock.c, used to be syscall
-    'sock_close' : 'user', # net/socket.c, net/socket.c, used to be syscall
-    'sock_common_recvmsg' : 'user', # net/core/sock.c, used to be syscall
-    'sock_def_readable' : 'user', # net/core/sock.c, used to be syscall
-    'sock_def_wakeup' : 'user', # net/core/sock.c, used to be syscall
-    'sock_destroy_inode' : 'user', # net/socket.c, used to be syscall
-    'sock_disable_timestamp' : 'user', # net/core/sock.c, used to be syscall
-    'sock_fasync' : 'user', # net/socket.c, net/socket.c, used to be syscall
-    'sock_init_data': 'stack', # net/core/sock.c,
-    'sock_ioctl': 'stack', # net/socket.c, net/socket.c,
-    'sock_map_fd' : 'user', # net/socket.c, include/linux/net.h, used to be syscall
-    'sock_poll' : 'user', # net/socket.c, net/socket.c, used to be syscall
-    'sock_readv': 'stack', # net/socket.c, net/socket.c,
-    'sock_readv_writev' : 'user', # net/socket.c, include/linux/net.h, used to be syscall
-    'sock_recvmsg' : 'user', # net/socket.c, include/linux/net.h, used to be syscall
-    'sock_release' : 'user', # net/socket.c, include/linux/net.h, used to be syscall
-    'sock_rfree' : 'user', # net/core/sock.c, used to be syscall
-    'sock_sendmsg' : 'user', # net/socket.c, include/linux/net.h, used to be syscall
-    'sock_wfree' : 'user', # net/core/sock.c, used to be syscall
-    'sock_wmalloc' : 'user', # net/core/sock.c, used to be syscall
-    'sock_writev' : 'user', # net/socket.c, net/socket.c, used to be syscall
-    'sockfd_lookup' : 'user', # net/socket.c, net/sched/sch_atm.c, include/linux/net.h, used to be syscall
-    'sockfs_delete_dentry' : 'user', # net/socket.c, used to be syscall
-    'sort': 'driver', # drivers/scsi/eata.c, drivers/scsi/u14-34f.c,
-    'split_vma': 'buffer', # mm/mmap.c, include/linux/mm.h,
-    'sprintf' : 'other', # lib/vsprintf.c, drivers/isdn/hardware/eicon/platform.h,
-    'sshd': 'other', #
-    'steal_locks': 'other', # fs/locks.c, include/linux/fs.h,
-    'strcmp' : 'copy', # lib/string.c,
-    'strlcpy': 'other', # lib/string.c,
-    'strlen' : 'copy', # lib/string.c, include/asm-alpha/string.h,
-    'strncpy' : 'copy', # lib/string.c, include/asm-alpha/string.h,
-    'strncpy_from_user': 'copy', # include/asm-alpha/uaccess.h, include/asm-i386/uaccess.h,
-    'strnlen_user': 'other', # include/asm-alpha/uaccess.h, include/asm-i386/uaccess.h,
-    'submit_bh' : 'buffer', # fs/buffer.c, include/linux/buffer_head.h,
-    'submit_bio' : 'other', # drivers/block/ll_rw_blk.c, include/linux/fs.h,
-    'sunrpc': 'other', #
-    'svc_authenticate' : 'other', # net/sunrpc/svcauth.c, include/linux/sunrpc/svcauth.h,
-    'svc_authorise' : 'other', # net/sunrpc/svcauth.c, include/linux/sunrpc/svcauth.h,
-    'svc_deferred_dequeue' : 'other', # net/sunrpc/svcsock.c, net/sunrpc/svcsock.c,
-    'svc_drop' : 'other', # net/sunrpc/svcsock.c, include/linux/sunrpc/svcsock.h,
-    'svc_expkey_lookup' : 'other', # fs/nfsd/export.c,
-    'svc_export_put' : 'other', # fs/nfsd/export.c, include/linux/nfsd/export.h,
-    'svc_process' : 'other', # net/sunrpc/svc.c, include/linux/sunrpc/svc.h,
-    'svc_recv' : 'other', # net/sunrpc/svcsock.c, include/linux/sunrpc/svcsock.h,
-    'svc_reserve' : 'other', # net/sunrpc/svcsock.c, include/linux/sunrpc/svc.h,
-    'svc_send' : 'other', # net/sunrpc/svcsock.c, include/linux/sunrpc/svcsock.h,
-    'svc_sendto' : 'other', # net/sunrpc/svcsock.c,
-    'svc_sock_enqueue' : 'other', # net/sunrpc/svcsock.c,
-    'svc_sock_release' : 'other', # net/sunrpc/svcsock.c,
-    'svc_udp_data_ready' : 'other', # net/sunrpc/svcsock.c, net/sunrpc/svcsock.c,
-    'svc_udp_recvfrom' : 'other', # net/sunrpc/svcsock.c, net/sunrpc/svcsock.c,
-    'svc_udp_sendto' : 'other', # net/sunrpc/svcsock.c, net/sunrpc/svcsock.c,
-    'svc_write_space' : 'other', # net/sunrpc/svcsock.c,
-    'svcauth_unix_accept' : 'other', # net/sunrpc/svcauth_unix.c,
-    'svcauth_unix_release' : 'other', # net/sunrpc/svcauth_unix.c,
-    'switch_names': 'other', # fs/dcache.c,
-    'swpctx_cont' : 'other', #
-    'sync_buffer' : 'other', # fs/buffer.c, drivers/oprofile/buffer_sync.c,
-    'sync_dirty_buffer' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'sync_inode' : 'other', # fs/fs-writeback.c, include/linux/fs.h,
-    'sync_mapping_buffers' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'sync_sb_inodes': 'other', # fs/fs-writeback.c,
-    'sync_supers': 'other', # fs/super.c, include/linux/fs.h,
-    'sys_accept' : 'user', # net/socket.c, include/linux/syscalls.h, used to be syscall
-    'sys_access': 'other', # fs/open.c, include/linux/syscalls.h,
-    'sys_brk': 'user', # mm/mmap.c, mm/nommu.c, include/linux/syscalls.h,
-    'sys_clock_gettime': 'user', # kernel/posix-timers.c, include/linux/syscalls.h,
-    'sys_clone': 'user', # include/asm-i386/unistd.h,
-    'sys_close' : 'user', # fs/open.c, include/linux/syscalls.h, used to be syscall
-    'sys_dup2': 'user', # fs/fcntl.c, include/linux/syscalls.h,
-    'sys_execve': 'user', # include/asm-alpha/unistd.h, include/asm-i386/unistd.h,
-    'sys_exit_group': 'user', # kernel/exit.c, include/linux/syscalls.h,
-    'sys_fcntl' : 'user', # fs/fcntl.c, include/linux/syscalls.h, used to be syscall
-    'sys_fcntl64': 'user', # fs/fcntl.c, include/linux/syscalls.h,
-    'sys_fstat64': 'user', # fs/stat.c, include/linux/syscalls.h,
-    'sys_ftruncate': 'user', # fs/open.c, include/linux/syscalls.h,
-    'sys_futex': 'user', # kernel/futex.c, include/linux/syscalls.h,
-    'sys_getdents64': 'user',
-    'sys_geteuid': 'other', # kernel/timer.c, include/linux/syscalls.h,
-    'sys_getgroups': 'user', # kernel/sys.c, include/linux/syscalls.h,
-    'sys_getpid': 'user', # kernel/timer.c, include/linux/syscalls.h,
-    'sys_getppid': 'other', # kernel/timer.c, include/linux/syscalls.h,
-    'sys_getrlimit': 'other', # kernel/sys.c, include/linux/syscalls.h,
-    'sys_getsockname' : 'user', # net/socket.c, include/linux/syscalls.h, used to be syscall
-    'sys_gettimeofday' : 'user', # kernel/time.c, include/linux/syscalls.h, used to be syscall
-    'sys_getuid': 'user', # kernel/timer.c, include/linux/syscalls.h,
-    'sys_getxpid' : 'user', # used to be syscall
-    'sys_int_21' : 'interrupt', #
-    'sys_int_22' : 'interrupt', #
-    'sys_interrupt' : 'interrupt', #
-    'sys_ioctl': 'user', # fs/ioctl.c, drivers/block/cciss.c, include/linux/syscalls.h,
-    'sys_kill' : 'user', # kernel/signal.c, include/linux/syscalls.h, used to be syscall
-    'sys_llseek': 'other', # fs/read_write.c, include/linux/syscalls.h,
-    'sys_lseek': 'user', # fs/read_write.c, include/linux/syscalls.h,
-    'sys_mkdir': 'user', # fs/namei.c, include/linux/syscalls.h,
-    'sys_mmap2': 'user', # include/asm-i386/unistd.h,
-    'sys_mremap': 'user', # mm/mremap.c, include/linux/syscalls.h,
-    'sys_munmap': 'user', # mm/mmap.c, mm/nommu.c, include/linux/syscalls.h,
-    'sys_nanosleep': 'user', # kernel/timer.c, include/linux/syscalls.h,
-    'sys_newlstat' : 'user', # fs/stat.c, include/linux/syscalls.h, used to be syscall
-    'sys_newstat' : 'user', # fs/stat.c, include/linux/syscalls.h, used to be syscall
-    'sys_newuname': 'user', # kernel/sys.c, include/linux/syscalls.h,
-    'sys_open' : 'user', # fs/open.c, include/linux/syscalls.h, used to be syscall
-    'sys_pipe': 'interrupt', # include/asm-i386/unistd.h,
-    'sys_poll' : 'user', # fs/select.c, include/linux/syscalls.h, used to be syscall
-    'sys_read' : 'user', # fs/read_write.c, include/linux/syscalls.h, used to be syscall
-    'sys_recv' : 'user', # net/socket.c, include/linux/syscalls.h, used to be syscall
-    'sys_recvfrom' : 'user', # net/socket.c, include/linux/syscalls.h, used to be syscall
-    'sys_rename': 'other', # fs/namei.c, include/linux/syscalls.h,
-    'sys_rmdir': 'user',
-    'sys_rt_sigaction': 'user', # arch/alpha/kernel/signal.c, kernel/signal.c, include/asm-alpha/unistd.h, include/asm-i386/unistd.h,
-    'sys_rt_sigprocmask': 'user', # kernel/signal.c, include/linux/syscalls.h,
-    'sys_select': 'user', # fs/select.c, include/linux/syscalls.h,
-    'sys_send' : 'user', # net/socket.c, include/linux/syscalls.h, used to be syscall
-    'sys_sendto' : 'user', # net/socket.c, include/linux/syscalls.h, used to be syscall
-    'sys_set_thread_area': 'user', #
-    'sys_setitimer': 'user', # kernel/itimer.c, include/linux/syscalls.h,
-    'sys_shutdown' : 'user', # net/socket.c, include/linux/syscalls.h, used to be syscall
-    'sys_sigreturn' : 'user', # used to be syscall
-    'sys_sigsuspend' : 'user', # used to be syscall
-    'sys_socketcall': 'user', # net/socket.c, include/linux/syscalls.h,
-    'sys_stat64': 'user', # fs/stat.c, include/linux/syscalls.h,
-    'sys_time': 'other', # kernel/time.c, include/linux/syscalls.h,
-    'sys_times': 'other', # kernel/sys.c, include/linux/syscalls.h,
-    'sys_umask': 'other', # kernel/sys.c, include/linux/syscalls.h,
-    'sys_unlink': 'other', # fs/namei.c, include/linux/syscalls.h,
-    'sys_wait4': 'user', # kernel/exit.c, include/linux/syscalls.h,
-    'sys_waitpid': 'user', # kernel/exit.c, include/linux/syscalls.h,
-    'sys_write' : 'user', # fs/read_write.c, include/linux/syscalls.h, used to be syscall
-    'sys_writev' : 'user', # fs/read_write.c, include/linux/syscalls.h, used to be syscall
-    'syscall_call': 'other', #
-    'syscall_exit': 'other', #
-    'sysguard_panelapplet.so': 'other', #
-    'syslogd': 'other', #
-    'system_call': 'interrupt', #
-    'tail': 'other', #
-    'task_curr' : 'other', # kernel/sched.c, include/linux/sched.h,
-    'task_rq_lock' : 'other', # kernel/sched.c,
-    'task_timeslice' : 'other', # kernel/sched.c,
-    'tasklet_action' : 'other', # kernel/softirq.c,
-    'tcp_accept' : 'stack', # net/ipv4/tcp.c,
-    'tcp_ack' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_ack_no_tstamp' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_ack_update_window' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_bucket_destroy' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_check_req' : 'stack', # net/ipv4/tcp_minisocks.c,
-    'tcp_child_process' : 'stack', # net/ipv4/tcp_minisocks.c,
-    'tcp_clean_rtx_queue' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_clear_xmit_timers' : 'stack', # net/ipv4/tcp_timer.c,
-    'tcp_close' : 'stack', # net/ipv4/tcp.c,
-    'tcp_close_state' : 'stack', # net/ipv4/tcp.c,
-    'tcp_copy_to_iovec' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_create_openreq_child' : 'stack', # net/ipv4/tcp_minisocks.c,
-    'tcp_current_mss': 'other', #
-    'tcp_cwnd_application_limited': 'stack', # net/ipv4/tcp_input.c,
-    'tcp_data_queue' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_delete_keepalive_timer' : 'stack', # net/ipv4/tcp_timer.c,
-    'tcp_destroy_sock' : 'stack', # net/ipv4/tcp.c,
-    'tcp_enter_quickack_mode' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_event_data_recv' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_fin' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_fixup_rcvbuf' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_fixup_sndbuf' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_fragment': 'stack', # net/ipv4/tcp_output.c,
-    'tcp_incr_quickack' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_init_buffer_space' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_init_metrics' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_init_xmit_timers' : 'stack', # net/ipv4/tcp_timer.c,
-    'tcp_invert_tuple' : 'stack', # net/ipv4/netfilter/ip_conntrack_proto_tcp.c,
-    'tcp_make_synack' : 'stack', # net/ipv4/tcp_output.c,
-    'tcp_new' : 'stack', # net/ipv4/netfilter/ip_conntrack_proto_tcp.c,
-    'tcp_new_space' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_ofo_queue' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_packet' : 'stack', # net/ipv4/netfilter/ip_conntrack_proto_tcp.c,
-    'tcp_parse_options' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_pkt_to_tuple' : 'stack', # net/ipv4/netfilter/ip_conntrack_proto_tcp.c,
-    'tcp_poll' : 'stack', # net/ipv4/tcp.c,
-    'tcp_prequeue_process' : 'stack', # net/ipv4/tcp.c,
-    'tcp_push_one' : 'stack', # net/ipv4/tcp_output.c,
-    'tcp_put_port' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_queue_skb' : 'stack', # net/ipv4/tcp_output.c,
-    'tcp_rcv_established' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_rcv_rtt_update' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_rcv_space_adjust' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_rcv_state_process' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_recvmsg' : 'stack', # net/ipv4/tcp.c,
-    'tcp_reset_keepalive_timer' : 'stack', # net/ipv4/tcp_timer.c,
-    'tcp_rtt_estimator' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_send_ack' : 'stack', # net/ipv4/tcp_output.c,
-    'tcp_send_delayed_ack' : 'stack', # net/ipv4/tcp_output.c,
-    'tcp_send_fin' : 'stack', # net/ipv4/tcp_output.c,
-    'tcp_sendmsg' : 'stack', # net/ipv4/tcp.c,
-    'tcp_set_skb_tso_segs': 'other', #
-    'tcp_shutdown' : 'stack', # net/ipv4/tcp.c,
-    'tcp_sync_mss' : 'stack', # net/ipv4/tcp_output.c,
-    'tcp_time_wait' : 'stack', # net/ipv4/tcp_minisocks.c,
-    'tcp_transmit_skb' : 'stack', # net/ipv4/tcp_output.c,
-    'tcp_trim_head': 'stack', # net/ipv4/tcp_output.c,
-    'tcp_tso_acked': 'stack', #
-    'tcp_tw_schedule' : 'stack', # net/ipv4/tcp_minisocks.c,
-    'tcp_unhash' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_update_metrics' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_urg' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_v4_checksum_init':'stack',
-    'tcp_v4_conn_request' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_connect': 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_destroy_sock' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_do_rcv' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_hnd_req' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_init_sock': 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_rcv' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_rebuild_header' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_route_req' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_search_req' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_send_check' : 'stack', # net/ipv4/tcp_ipv4.c, net/ipv4/tcp_ipv4.c,
-    'tcp_v4_send_synack' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_syn_recv_sock' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_synq_add' : 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_vegas_init' : 'stack', # net/ipv4/tcp_input.c,
-    'tcp_write_xmit' : 'stack', # net/ipv4/tcp_output.c,
-    'test_clear_page_dirty': 'buffer', # mm/page-writeback.c, include/linux/page-flags.h,
-    'test_clear_page_writeback' : 'buffer', # mm/page-writeback.c, include/linux/page-flags.h,
-    'test_set_page_writeback' : 'buffer', # mm/page-writeback.c, include/linux/page-flags.h,
-    'timer_interrupt' : 'interrupt', # arch/alpha/kernel/time.c, arch/alpha/kernel/proto.h,
-    'tr': 'other', #
-    'truncate_complete_page': 'buffer', # mm/truncate.c,
-    'truncate_inode_pages': 'buffer', # mm/truncate.c, include/linux/mm.h,
-    'try_to_wake_up' : 'other', # kernel/sched.c,
-    'tsunami_readb': 'driver',
-    'tsunami_readl' : 'interrupt', # include/asm-alpha/core_tsunami.h,
-    'tsunami_update_irq_hw' : 'interrupt', # arch/alpha/kernel/sys_dp264.c,
-    'tsunami_writeb': 'driver',
-    'tsunami_writel' : 'interrupt', # include/asm-alpha/core_tsunami.h,
-    'tty_hung_up_p': 'driver', # drivers/char/tty_io.c, include/linux/tty.h,
-    'tty_ldisc_deref': 'other', #
-    'tty_ldisc_ref_wait': 'other', #
-    'tty_ldisc_try': 'other', #
-    'tty_open': 'driver', # drivers/char/tty_io.c, drivers/char/tty_io.c, drivers/net/wan/sdla_chdlc.c,
-    'tty_poll': 'driver', # drivers/char/tty_io.c, drivers/char/tty_io.c,
-    'tty_write': 'driver', # drivers/char/tty_io.c, drivers/char/tty_io.c,
-    'udp_checksum_init' : 'stack', # net/ipv4/udp.c,
-    'udp_ioctl': 'stack', # net/ipv4/udp.c,
-    'udp_packet' : 'stack', # net/ipv4/netfilter/ip_conntrack_proto_udp.c,
-    'udp_pkt_to_tuple' : 'stack', # net/ipv4/netfilter/ip_conntrack_proto_udp.c,
-    'udp_push_pending_frames' : 'stack', # net/ipv4/udp.c,
-    'udp_queue_rcv_skb' : 'stack', # net/ipv4/udp.c,
-    'udp_rcv' : 'stack', # net/ipv4/udp.c,
-    'udp_recvmsg': 'stack', # net/ipv4/udp.c,
-    'udp_sendmsg' : 'stack', # net/ipv4/udp.c,
-    'udp_sendpage' : 'stack', # net/ipv4/udp.c,
-    'udp_v4_get_port': 'stack', # net/ipv4/udp.c,
-    'udp_v4_lookup_longway' : 'stack', # net/ipv4/udp.c,
-    'unalign_trap_cont' : 'alignment',
-    'unalign_trap_count' : 'alignment',
-    'undo_switch_stack' : 'other', #
-    'unix': 'other', #
-    'unlock_buffer' : 'other', # fs/buffer.c,
-    'unlock_new_inode': 'other', # fs/inode.c, include/linux/fs.h,
-    'unlock_page' : 'buffer', # mm/filemap.c,
-    'unmap_mapping_range': 'buffer', # mm/memory.c, include/linux/mm.h,
-    'unmap_page_range': 'buffer', # mm/memory.c,
-    'unmap_region': 'buffer', # mm/mmap.c,
-    'unmap_underlying_metadata' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'unmap_vma': 'buffer', # mm/mmap.c,
-    'unmap_vma_list': 'buffer', # mm/mmap.c,
-    'unmap_vmas': 'buffer', # mm/memory.c, include/linux/mm.h,
-    'unmask_IO_APIC_irq': 'interrupt', #
-    'unmask_IO_APIC_vector': 'interrupt', #
-    'unqueue_me': 'other', # kernel/futex.c,
-    'unshare_files': 'other', # kernel/fork.c, include/linux/fs.h,
-    'up' : 'driver', # arch/alpha/kernel/semaphore.c, include/asm-alpha/semaphore.h, net/ipv4/netfilter/ip_tables.c, net/ipv6/netfilter/ip6_tables.c, drivers/video/atafb.c, include/asm-alpha/semaphore.h,
-    'update_atime': 'other', # fs/inode.c, include/linux/fs.h,
-    'update_one_process' : 'other', # kernel/timer.c,
-    'update_process_times' : 'other', # kernel/timer.c, include/linux/sched.h,
-    'update_wall_time' : 'other', # kernel/timer.c,
-    'update_wall_time_one_tick' : 'other', # kernel/timer.c,
-    'usbcore': 'other', #
-    'vfs_create': 'other', # fs/namei.c, include/linux/fs.h,
-    'vfs_fstat': 'other', # fs/stat.c, include/linux/fs.h,
-    'vfs_getattr' : 'user', # fs/stat.c, include/linux/fs.h, used to be syscall
-    'vfs_llseek': 'other', # fs/read_write.c, include/linux/fs.h,
-    'vfs_lstat' : 'user', # fs/stat.c, include/linux/fs.h, used to be syscall
-    'vfs_mkdir': 'other', # fs/namei.c, include/linux/fs.h,
-    'vfs_permission' : 'user', # fs/namei.c, include/linux/fs.h, used to be syscall
-    'vfs_read' : 'user', # fs/read_write.c, include/linux/fs.h, used to be syscall
-    'vfs_rename': 'other', # fs/namei.c, include/linux/fs.h,
-    'vfs_rename_other': 'other', # fs/namei.c,
-    'vfs_stat' : 'user', # fs/stat.c, include/linux/fs.h, used to be syscall
-    'vfs_unlink': 'other', # fs/namei.c, include/linux/fs.h,
-    'vfs_write' : 'user', # fs/read_write.c, include/linux/fs.h, used to be syscall
-    'vfs_writev' : 'user', # fs/read_write.c, include/linux/fs.h, used to be syscall
-    'vma_adjust': 'buffer', # mm/mmap.c, include/linux/mm.h,
-    'vma_link': 'buffer', # mm/mmap.c,
-    'vma_merge': 'buffer', # mm/mmap.c, include/linux/mm.h,
-    'vma_prio_tree_add': 'buffer', # mm/prio_tree.c, include/linux/mm.h,
-    'vma_prio_tree_insert': 'buffer', # mm/prio_tree.c, include/linux/mm.h,
-    'vma_prio_tree_remove': 'buffer', # mm/prio_tree.c, include/linux/mm.h,
-    'vmstat_open': 'other', # fs/proc/proc_misc.c,
-    'vmstat_show': 'buffer', # mm/page_alloc.c,
-    'vmtruncate': 'buffer', # mm/nommu.c, mm/memory.c, include/linux/mm.h,
-    'vsnprintf' : 'other', # lib/vsprintf.c, include/linux/kernel.h,
-    'vsprintf' : 'driver', # lib/vsprintf.c, arch/alpha/boot/main.c, drivers/scsi/aic7xxx_old/aic7xxx_proc.c, include/linux/kernel.h,
-    'wait_for_completion': 'driver', # drivers/acorn/block/mfmhd.c, kernel/sched.c,
-    'wait_on_page_writeback_range' : 'buffer', # mm/filemap.c,
-    'wait_task_zombie': 'other', # kernel/exit.c,
-    'wake_futex': 'other', # kernel/futex.c,
-    'wake_up_buffer' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'wake_up_inode' : 'other', # fs/inode.c, include/linux/writeback.h,
-    'wake_up_new_task': 'other', #
-    'wake_up_page' : 'buffer', # mm/filemap.c,
-    'wake_up_process' : 'other', # kernel/sched.c,
-    'wake_up_state' : 'other', # kernel/sched.c,
-    'wb_timer_fn': 'buffer', # mm/page-writeback.c, mm/page-writeback.c,
-    'wc': 'other', #
-    'work_notifysig': 'other', #
-    'work_pending' : 'other', #
-    'work_resched': 'other', #
-    'worker_thread': 'other', # kernel/workqueue.c,
-    'write_boundary_block' : 'other', # fs/buffer.c, include/linux/buffer_head.h,
-    'write_chan': 'driver', # drivers/char/n_tty.c,
-    'write_inode' : 'other', # fs/fs-writeback.c,
-    'write_null': 'driver', # drivers/char/mem.c,
-    'writeback_acquire': 'other', # fs/fs-writeback.c, include/linux/backing-dev.h,
-    'writeback_in_progress' : 'other', # fs/fs-writeback.c, include/linux/backing-dev.h,
-    'writeback_inodes': 'other', # fs/fs-writeback.c, include/linux/writeback.h,
-    'xdr_partial_copy_from_skb' : 'copy', # net/sunrpc/xdr.c, include/linux/sunrpc/xdr.h,
-    'xfrm_lookup' : 'stack', # net/xfrm/xfrm_policy.c,
-    'xmms': 'other', #
-    'zap_pmd_range': 'buffer', # mm/memory.c,
-    'zap_pte_range': 'buffer', # mm/memory.c,
-    'zone_statistics' : 'buffer', # mm/page_alloc.c,
-    'libaprutil-0.so.0' : 'user',
-    'libapr-0.so.0' : 'user',
-    'httpd' : 'user',
-    'do_tcp_sendpages': 'copy',
-    'tcp_setsockopt' : 'stack',
-    'sys_setsockopt' : 'stack',
-    'do_sendfile' : 'copy',
-    'ip_route_output_slow': 'stack',
-    'tcp_sendpage': 'copy',
-    'file_send_actor': 'copy',
-    'flush_tlb_page': 'buffer',
-    'sock_common_setsockopt': 'stack',
-    'sock_sendpage': 'copy',
-
-#
-#   New functions
-#
-
-    '__alloc_percpu': 'buffer', # mm/slab.c, include/linux/percpu.h,
-    '__pskb_pull_tail': 'stack', # net/core/skbuff.c, include/linux/skbuff.h,
-    '__reml': 'other', # arch/alpha/kernel/alpha_ksyms.c,
-    '__tasklet_hi_schedule': 'interrupt', # kernel/softirq.c,
-    '__tcp_checksum_complete_user': 'stack', # net/ipv4/tcp_input.c,
-    '__tcp_v4_lookup_listener': 'stack', # net/ipv4/tcp_ipv4.c,
-    '__tcp_v4_rehash': 'stack', # net/ipv4/tcp_ipv4.c,
-    '__tcp_westwood_fast_bw': 'stack', # net/ipv4/tcp_input.c,
-    '__tcp_westwood_slow_bw': 'stack', # net/ipv4/tcp_input.c,
-    '__xfrm_policy_check': 'stack', # net/xfrm/xfrm_policy.c,
-    'alcor_disable_irq': 'interrupt', # arch/alpha/kernel/sys_alcor.c,
-    'alpha_read_fp_reg': 'other', # arch/alpha/lib/fpreg.c, arch/alpha/kernel/proto.h, arch/alpha/math-emu/math.c, include/asm-alpha/fpu.h,
-    'atkbd_probe': 'other', # drivers/input/keyboard/atkbd.c,
-    'background_writeout': 'buffer', # mm/page-writeback.c, mm/page-writeback.c,
-    'bad_page': 'buffer', # mm/page_alloc.c,
-    'batch_entropy_process': 'other', # drivers/char/random.c, drivers/char/random.c,
-    'block_hotplug_filter': 'driver', # drivers/block/genhd.c,
-    'brioctl_set': 'stack', # net/socket.c, include/linux/if_bridge.h,
-    'cdev_put': 'fs', # fs/char_dev.c, include/linux/cdev.h,
-    'change_protection': 'buffer', # mm/mprotect.c,
-    'check_timer_failed': 'interrupt', # kernel/timer.c,
-    'clipper_disable_irq': 'interrupt', # arch/alpha/kernel/sys_dp264.c,
-    'clipper_enable_irq': 'interrupt', # arch/alpha/kernel/sys_dp264.c,
-    'count_active_tasks': 'interrupt', # kernel/timer.c,
-    'csum_ipv6_magic': 'stack', # include/asm-i386/checksum.h, include/asm-alpha/checksum.h,
-    'del_timer_sync': 'interrupt', # kernel/timer.c, include/linux/timer.h, include/linux/timer.h,
-    'dev_ifname': 'stack', # net/core/dev.c,
-    'dev_queue_xmit_nit': 'stack', # net/core/dev.c, include/linux/netdevice.h,
-    'dev_valid_name': 'stack', # net/core/dev.c,
-    'do_entDbg': 'interrupt', # arch/alpha/kernel/traps.c,
-    'do_proc_dointvec_jiffies_conv': 'interrupt', # kernel/sysctl.c,
-    'down_interruptible': 'interrupt', # arch/alpha/kernel/semaphore.c, include/asm-alpha/semaphore.h, include/asm-i386/semaphore.h, include/asm-alpha/semaphore.h, net/ipv4/netfilter/ip_tables.c, net/ipv6/netfilter/ip6_tables.c,
-    'drain_array': 'buffer', #
-    'drain_cpu_caches': 'buffer', # mm/slab.c,
-    'dummy_file_fcntl': 'other', # security/dummy.c,
-    'dummy_sem_semop': 'other', # security/dummy.c,
-    'emit_log_char': 'other', # kernel/printk.c,
-    'entDbg': 'interrupt', # arch/alpha/kernel/proto.h,
-    'entIF': 'interrupt', # arch/alpha/kernel/proto.h,
-    'eth_header_cache_update': 'stack', # net/ethernet/eth.c, include/linux/etherdevice.h,
-    'eth_header_parse': 'stack', # net/ethernet/eth.c, include/linux/etherdevice.h,
-    'ethtool_get_settings': 'stack', # net/core/ethtool.c,
-    'fifo_open': 'fs', # fs/fifo.c,
-    'find_trylock_page': 'buffer', # mm/filemap.c, include/linux/pagemap.h,
-    'find_undo': 'buffer', # ipc/sem.c,
-    'find_user': 'buffer', # kernel/user.c, include/linux/sched.h,
-    'flow_cache_cpu_prepare': 'stack', # net/core/flow.c,
-    'flow_cache_flush_per_cpu': 'stack', # net/core/flow.c,
-    'flow_cache_flush_tasklet': 'stack', # net/core/flow.c,
-    'flow_key_compare': 'stack', # net/core/flow.c,
-    'flush_icache_user_range': 'interrupt', # arch/alpha/kernel/smp.c, include/asm-alpha/cacheflush.h, include/asm-alpha/cacheflush.h, include/asm-i386/cacheflush.h,
-    'flush_tlb_mm': 'interrupt', # arch/alpha/kernel/smp.c, include/asm-alpha/tlbflush.h, include/asm-i386/tlbflush.h, include/asm-alpha/tlbflush.h, include/asm-i386/tlbflush.h,
-    'force_page_cache_readahead': 'buffer', # mm/readahead.c, include/linux/mm.h,
-    'free_percpu': 'buffer', # mm/slab.c, include/linux/percpu.h, include/linux/percpu.h,
-    'generic_file_sendfile': 'buffer', # mm/filemap.c, include/linux/fs.h,
-    'get_one_pte_map': 'buffer', # mm/mremap.c,
-    'gunzip': 'other', # lib/inflate.c,
-    'handle_ipi': 'interrupt', # arch/alpha/kernel/smp.c, arch/alpha/kernel/proto.h,
-    'input_devices_read': 'driver', # drivers/input/input.c,
-    'input_link_handle': 'driver', # drivers/input/input.c,
-    'input_register_device': 'driver', # drivers/input/input.c, include/linux/input.h,
-    'insb': 'driver', # arch/alpha/kernel/io.c, include/asm-alpha/io.h,
-    'insl': 'driver', # arch/alpha/kernel/io.c, include/asm-alpha/io.h, drivers/net/smc9194.c, drivers/net/smc9194.c,
-    'invalidate_bh_lru': 'fs', # fs/buffer.c,
-    'iommu_arena_alloc': 'interrupt', # arch/alpha/kernel/pci_iommu.c,
-    'iommu_arena_find_pages': 'interrupt', # arch/alpha/kernel/pci_iommu.c,
-    'iommu_arena_free': 'interrupt', # arch/alpha/kernel/pci_iommu.c,
-    'ip_compute_csum': 'stack', # arch/alpha/lib/checksum.c, include/asm-i386/checksum.h, include/asm-alpha/checksum.h,
-    'ip_getsockopt': 'stack', # net/ipv4/ip_sockglue.c,
-    'ip_mc_output': 'stack', # net/ipv4/ip_output.c,
-    'ip_options_compile': 'stack', # net/ipv4/ip_options.c,
-    'ip_rt_dump': 'stack', # net/ipv4/route.c,
-    'ipc_checkid': 'stack', # ipc/util.c, ipc/util.h,
-    'ipc_lock': 'stack', # ipc/util.c, ipc/util.h,
-    'ipc_unlock': 'stack', # ipc/util.c, ipc/util.h,
-    'ipcperms': 'stack', # ipc/util.c, ipc/util.h,
-    'ipi_flush_tlb_page': 'interrupt', # arch/alpha/kernel/smp.c,
-    'isp1020_intr_handler': 'other', # drivers/scsi/qlogicisp.c, drivers/scsi/qlogicisp.c,
-    'isp1020_queuecommand': 'other', # drivers/scsi/qlogicisp.c, drivers/scsi/qlogicisp.h,
-    'kernel_thread': 'interrupt', # include/asm-um/processor-generic.h, include/asm-alpha/processor.h, include/asm-i386/processor.h,
-    'kmem_find_general_cachep': 'buffer', # mm/slab.c,
-    'kmem_ptr_validate': 'buffer', # mm/slab.c,
-    'llc_mac_hdr_init': 'stack', # net/llc/llc_output.c, net/llc/llc_output.h,
-    'lock_rename': 'fs', # fs/namei.c, include/linux/namei.h,
-    'lookup_undo': 'stack', # ipc/sem.c,
-    'memcpy_tokerneliovec': 'stack', # net/core/iovec.c, include/linux/socket.h,
-    'migrate_task': 'other', # kernel/sched.c,
-    'net_ratelimit': 'stack', # net/core/utils.c, include/linux/net.h,
-    'netlink_release': 'stack', # net/netlink/netlink_dev.c, net/netlink/af_netlink.c,
-    'nf_log_packet': 'stack', # net/core/netfilter.c, include/linux/netfilter_logging.h, include/linux/netfilter.h,
-    'nf_queue': 'stack', # net/core/netfilter.c,
-    'nr_free_zone_pages': 'buffer', # mm/page_alloc.c,
-    'osf_writev': 'driver', # arch/alpha/kernel/osf_sys.c,
-    'pci_map_sg': 'driver', # arch/alpha/kernel/pci-noop.c, arch/alpha/kernel/pci_iommu.c, include/asm-generic/pci-dma-compat.h, include/asm-alpha/pci.h,
-    'pci_unmap_sg': 'driver', # arch/alpha/kernel/pci-noop.c, arch/alpha/kernel/pci_iommu.c, include/asm-generic/pci-dma-compat.h, include/asm-alpha/pci.h,
-    'pcibios_align_resource': 'driver', # arch/alpha/kernel/pci.c, include/linux/pci.h,
-    'pfifo_fast_requeue': 'stack', # net/sched/sch_generic.c,
-    'pointer_lock': 'interrupt', # arch/alpha/kernel/smp.c,
-    'posix_unblock_lock': 'fs', # fs/locks.c, include/linux/fs.h,
-    'prepare_timeout': 'interrupt', # ipc/mqueue.c,
-    'printk': 'other', # kernel/printk.c, drivers/md/raid6.h,
-    'process_mcheck_info': 'interrupt', # arch/alpha/kernel/irq_alpha.c, arch/alpha/kernel/proto.h,
-    'read_cache_pages': 'buffer', # mm/readahead.c, include/linux/pagemap.h,
-    'register_gifconf': 'stack', # net/core/dev.c, include/linux/netdevice.h,
-    'rwsem_down_read_failed': 'interrupt', # lib/rwsem.c, include/asm-alpha/rwsem.h,
-    'search_exception_tables': 'interrupt', # kernel/extable.c, include/linux/module.h,
-    'security_fixup_ops': 'other', # security/dummy.c, security/security.c,
-    'send_ipi_message': 'interrupt', # arch/alpha/kernel/smp.c,
-    'send_sig_info': 'interrupt', # kernel/signal.c, include/linux/sched.h,
-    'set_fs_altroot': 'fs', # fs/namei.c, include/linux/fs_struct.h,
-    'sg_classify': 'interrupt', # arch/alpha/kernel/pci_iommu.c,
-    'sg_fill': 'interrupt', # arch/alpha/kernel/pci_iommu.c,
-    'sk_common_release': 'stack', # net/core/sock.c,
-    'sk_stream_wait_connect': 'stack', # net/core/stream.c,
-    'skb_over_panic': 'stack', # net/core/skbuff.c, include/linux/skbuff.h,
-    'skb_under_panic': 'stack', # net/core/skbuff.c, include/linux/skbuff.h,
-    'smp_call_function_on_cpu': 'interrupt', # arch/alpha/kernel/smp.c, include/asm-alpha/smp.h, include/asm-alpha/smp.h,
-    'sock_def_write_space': 'stack', # net/core/sock.c,
-    'sock_getsockopt': 'stack', # net/core/sock.c,
-    'sock_wait_for_wmem': 'stack', # net/core/sock.c,
-    'srm_dispatch': 'other', #
-    'srm_fixup': 'other', # include/asm-alpha/console.h,
-    'stxcpy_aligned': 'buffer', #
-    'sys_capset': 'other', # kernel/capability.c, include/linux/syscalls.h,
-    'sys_fadvise64': 'buffer', # mm/fadvise.c, include/linux/syscalls.h,
-    'sys_fadvise64_64': 'buffer', # mm/fadvise.c, include/linux/syscalls.h,
-    'sys_newfstat': 'fs', # fs/stat.c, include/linux/syscalls.h,
-    'sys_semop': 'stack', # ipc/sem.c, include/linux/syscalls.h,
-    'sys_semtimedop': 'stack', # ipc/sem.c, include/linux/syscalls.h,
-    'sys_sendfile64': 'fs', # fs/read_write.c, include/linux/syscalls.h,
-    'sys_socketpair': 'stack', # net/socket.c, include/linux/syscalls.h,
-    'sys_vhangup': 'fs', # fs/open.c, include/linux/syscalls.h,
-    'tasklet_hi_action': 'interrupt', # kernel/softirq.c,
-    'tcp_ack_probe': 'stack', # net/ipv4/tcp_input.c,
-    'tcp_advertise_mss': 'stack', # net/ipv4/tcp_output.c,
-    'tcp_enter_loss': 'stack', # net/ipv4/tcp_input.c,
-    'tcp_fastretrans_alert': 'stack', # net/ipv4/tcp_input.c,
-    'tcp_ioctl': 'stack', # net/ipv4/tcp.c,
-    'tcp_process_frto': 'stack', # net/ipv4/tcp_input.c,
-    'tcp_rcv_synsent_state_process': 'stack', # net/ipv4/tcp_input.c,
-    'tcp_recv_urg': 'stack', # net/ipv4/tcp.c,
-    'tcp_reset': 'stack', # net/ipv4/tcp_input.c,
-    'tcp_retransmit_skb': 'stack', # net/ipv4/tcp_output.c,
-    'tcp_sacktag_write_queue': 'stack', # net/ipv4/tcp_input.c,
-    'tcp_time_to_recover': 'stack', # net/ipv4/tcp_input.c,
-    'tcp_v4_err': 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_get_port': 'stack', # net/ipv4/tcp_ipv4.c,
-    'tcp_v4_lookup': 'stack', # net/ipv4/tcp_ipv4.c, net/ipv4/tcp_diag.c,
-    'tcp_v4_reselect_saddr': 'stack', # net/ipv4/tcp_ipv4.c,
-    'this_rq_lock': 'interrupt', #
-    'tr_source_route': 'stack', # net/802/tr.c, include/linux/trdevice.h,
-    'try_atomic_semop': 'stack', # ipc/sem.c,
-    'tsunami_outw': 'driver', #
-    'twothirdsMD4Transform': 'other', # drivers/char/random.c,
-    'unregister_netdevice': 'stack', # net/core/dev.c, include/linux/netdevice.h,
-    'update_queue': 'stack', # ipc/sem.c,
-    'vegas_cong_avoid': 'stack', # net/ipv4/tcp_input.c,
-    'vm_acct_memory': 'buffer', # mm/swap.c, include/linux/mman.h,
-    'vsscanf': 'other', # lib/vsprintf.c, include/linux/kernel.h,
-    'wait_for_packet': 'stack', # net/core/datagram.c,
-    'westwood_update_window': 'stack', # net/ipv4/tcp_input.c,
-    'within_one_quad': 'other', #
-}
-
-pc_categories_re = [
-#    ( re.compile('.*'), 'other' )
-]
-
-def pc_categorize(symbol):
-    from .categories import pc_categories, pc_categories_re
-    if symbol in pc_categories:
-        return pc_categories[symbol]
-    for regexp, category in pc_categories_re:
-        if regexp.match(symbol):
-            return category
-
-    return None
-
diff --git a/util/stats/chart.py b/util/stats/chart.py
deleted file mode 100644
index cc5eccf..0000000
--- a/util/stats/chart.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright (c) 2005-2006 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-class ChartOptions(object):
-    defaults = { 'chart_size' : (8, 4),
-                 'figure_size' : [0.1, 0.1, 0.6, 0.85],
-                 'title' : None,
-                 'fig_legend' : True,
-                 'legend' : None,
-                 'legend_loc' : 'upper right',
-                 'legend_size' : 6,
-                 'colormap' : 'jet',
-                 'xlabel' : None,
-                 'ylabel' : None,
-                 'xticks' : None,
-                 'xsubticks' : None,
-                 'yticks' : None,
-                 'ylim' : None,
-                 }
-
-    def __init__(self, options=None, **kwargs):
-        self.init(options, **kwargs)
-
-    def clear(self):
-        self.options = {}
-
-    def init(self, options=None, **kwargs):
-        self.clear()
-        self.update(options, **kwargs)
-
-    def update(self, options=None, **kwargs):
-        if options is not None:
-            if not isinstance(options, ChartOptions):
-                raise AttributeError(
-                    'attribute options of type %s should be %s' %
-                    (type(options), ChartOptions))
-            self.options.update(options.options)
-
-        for key,value in kwargs.items():
-            if key not in ChartOptions.defaults:
-                raise AttributeError(
-                    "%s instance has no attribute '%s'" % (type(self), key))
-            self.options[key] = value
-
-    def __getattr__(self, attr):
-        if attr in self.options:
-            return self.options[attr]
-
-        if attr in ChartOptions.defaults:
-            return ChartOptions.defaults[attr]
-
-        raise AttributeError("%s instance has no attribute '%s'" % (type(self), attr))
-
-    def __setattr__(self, attr, value):
-        if attr in ChartOptions.defaults:
-            self.options[attr] = value
-        else:
-            super(ChartOptions, self).__setattr__(attr, value)
-
diff --git a/util/stats/db.py b/util/stats/db.py
deleted file mode 100644
index b6acf8d..0000000
--- a/util/stats/db.py
+++ /dev/null
@@ -1,434 +0,0 @@
-# Copyright (c) 2003-2004 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import MySQLdb, re, string
-
-def statcmp(a, b):
-    v1 = a.split('.')
-    v2 = b.split('.')
-
-    last = min(len(v1), len(v2)) - 1
-    for i,j in zip(v1[0:last], v2[0:last]):
-        if i != j:
-            return cmp(i, j)
-
-    # Special compare for last element.
-    if len(v1) == len(v2):
-        return cmp(v1[last], v2[last])
-    else:
-        return cmp(len(v1), len(v2))
-
-class RunData:
-    def __init__(self, row):
-        self.run = int(row[0])
-        self.name = row[1]
-        self.user = row[2]
-        self.project = row[3]
-
-class SubData:
-    def __init__(self, row):
-        self.stat = int(row[0])
-        self.x = int(row[1])
-        self.y = int(row[2])
-        self.name = row[3]
-        self.descr = row[4]
-
-class Data:
-    def __init__(self, row):
-        if len(row) != 5:
-            raise 'stat db error'
-        self.stat = int(row[0])
-        self.run = int(row[1])
-        self.x = int(row[2])
-        self.y = int(row[3])
-        self.data = float(row[4])
-
-    def __repr__(self):
-        return '''Data(['%d', '%d', '%d', '%d', '%f'])''' % ( self.stat,
-            self.run, self.x, self.y, self.data)
-
-class StatData(object):
-    def __init__(self, row):
-        self.stat = int(row[0])
-        self.name = row[1]
-        self.desc = row[2]
-        self.type = row[3]
-        self.prereq = int(row[5])
-        self.precision = int(row[6])
-
-        from . import flags
-        self.flags = 0
-        if int(row[4]): self.flags |= flags.printable
-        if int(row[7]): self.flags |= flags.nozero
-        if int(row[8]): self.flags |= flags.nonan
-        if int(row[9]): self.flags |= flags.total
-        if int(row[10]): self.flags |= flags.pdf
-        if int(row[11]): self.flags |= flags.cdf
-
-        if self.type == 'DIST' or self.type == 'VECTORDIST':
-            self.min = float(row[12])
-            self.max = float(row[13])
-            self.bktsize = float(row[14])
-            self.size = int(row[15])
-
-        if self.type == 'FORMULA':
-            self.formula = self.db.allFormulas[self.stat]
-
-class Node(object):
-    def __init__(self, name):
-        self.name = name
-    def __str__(self):
-        return self.name
-
-class Result(object):
-    def __init__(self, x, y):
-        self.data = {}
-        self.x = x
-        self.y = y
-
-    def __contains__(self, run):
-        return run in self.data
-
-    def __getitem__(self, run):
-        if run not in self.data:
-            self.data[run] = [ [ 0.0 ] * self.y for i in range(self.x) ]
-        return self.data[run]
-
-class Database(object):
-    def __init__(self):
-        self.host = 'zizzer.pool'
-        self.user = ''
-        self.passwd = ''
-        self.db = 'm5stats'
-        self.cursor = None
-
-        self.allStats = []
-        self.allStatIds = {}
-        self.allStatNames = {}
-
-        self.allSubData = {}
-
-        self.allRuns = []
-        self.allRunIds = {}
-        self.allRunNames = {}
-
-        self.allFormulas = {}
-
-        self.stattop = {}
-        self.statdict = {}
-        self.statlist = []
-
-        self.mode = 'sum';
-        self.runs = None
-        self.ticks = None
-        self.method = 'sum'
-        self._method = type(self).sum
-
-    def get(self, job, stat, system=None):
-        run = self.allRunNames.get(str(job), None)
-        if run is None:
-            return None
-
-        from .info import ProxyError, scalar, vector, value, values, total, len
-        if system is None and hasattr(job, 'system'):
-            system = job.system
-
-        if system is not None:
-            stat.system = self[system]
-        try:
-            if scalar(stat):
-                return value(stat, run.run)
-            if vector(stat):
-                return values(stat, run.run)
-        except ProxyError:
-            return None
-
-        return None
-
-    def query(self, sql):
-        self.cursor.execute(sql)
-
-    def update_dict(self, dict):
-        dict.update(self.stattop)
-
-    def append(self, stat):
-        statname = re.sub(':', '__', stat.name)
-        path = string.split(statname, '.')
-        pathtop = path[0]
-        fullname = ''
-
-        x = self
-        while len(path) > 1:
-            name = path.pop(0)
-            if name not in x.__dict__:
-                x.__dict__[name] = Node(fullname + name)
-            x = x.__dict__[name]
-            fullname = '%s%s.' % (fullname, name)
-
-        name = path.pop(0)
-        x.__dict__[name] = stat
-
-        self.stattop[pathtop] = self.__dict__[pathtop]
-        self.statdict[statname] = stat
-        self.statlist.append(statname)
-
-    def connect(self):
-        # connect
-        self.thedb = MySQLdb.connect(db=self.db,
-                                     host=self.host,
-                                     user=self.user,
-                                     passwd=self.passwd)
-
-        # create a cursor
-        self.cursor = self.thedb.cursor()
-
-        self.query('''select rn_id,rn_name,rn_sample,rn_user,rn_project
-                   from runs''')
-        for result in self.cursor.fetchall():
-            run = RunData(result);
-            self.allRuns.append(run)
-            self.allRunIds[run.run] = run
-            self.allRunNames[run.name] = run
-
-        self.query('select sd_stat,sd_x,sd_y,sd_name,sd_descr from subdata')
-        for result in self.cursor.fetchall():
-            subdata = SubData(result)
-            if subdata.stat in self.allSubData:
-                self.allSubData[subdata.stat].append(subdata)
-            else:
-                self.allSubData[subdata.stat] = [ subdata ]
-
-        self.query('select * from formulas')
-        for id,formula in self.cursor.fetchall():
-            self.allFormulas[int(id)] = formula.tostring()
-
-        StatData.db = self
-        self.query('select * from stats')
-        from . import info
-        for result in self.cursor.fetchall():
-            stat = info.NewStat(self, StatData(result))
-            self.append(stat)
-            self.allStats.append(stat)
-            self.allStatIds[stat.stat] = stat
-            self.allStatNames[stat.name] = stat
-
-    # Name: listruns
-    # Desc: Prints all runs matching a given user, if no argument
-    #       is given all runs are returned
-    def listRuns(self, user=None):
-        print('%-40s %-10s %-5s' % ('run name', 'user', 'id'))
-        print('-' * 62)
-        for run in self.allRuns:
-            if user == None or user == run.user:
-                print('%-40s %-10s %-10d' % (run.name, run.user, run.run))
-
-    # Name: listTicks
-    # Desc: Prints all samples for a given run
-    def listTicks(self, runs=None):
-        print("tick")
-        print("----------------------------------------")
-        sql = 'select distinct dt_tick from data where dt_stat=1180 and ('
-        if runs != None:
-            first = True
-            for run in runs:
-               if first:
-            #       sql += ' where'
-                   first = False
-               else:
-                   sql += ' or'
-               sql += ' dt_run=%s' % run.run
-            sql += ')'
-        self.query(sql)
-        for r in self.cursor.fetchall():
-            print(r[0])
-
-    # Name: retTicks
-    # Desc: Prints all samples for a given run
-    def retTicks(self, runs=None):
-        sql = 'select distinct dt_tick from data where dt_stat=1180 and ('
-        if runs != None:
-            first = True
-            for run in runs:
-               if first:
-                   first = False
-               else:
-                   sql += ' or'
-               sql += ' dt_run=%s' % run.run
-            sql += ')'
-        self.query(sql)
-        ret = []
-        for r in self.cursor.fetchall():
-            ret.append(r[0])
-        return ret
-
-    # Name: liststats
-    # Desc: Prints all statistics that appear in the database,
-    #         the optional argument is a regular expression that can
-    #         be used to prune the result set
-    def listStats(self, regex=None):
-        print('%-60s %-8s %-10s' % ('stat name', 'id', 'type'))
-        print('-' * 80)
-
-        rx = None
-        if regex != None:
-            rx = re.compile(regex)
-
-        stats = [ stat.name for stat in self.allStats ]
-        stats.sort(statcmp)
-        for stat in stats:
-            stat = self.allStatNames[stat]
-            if rx == None or rx.match(stat.name):
-                print('%-60s %-8s %-10s' % (stat.name, stat.stat, stat.type))
-
-    # Name: liststats
-    # Desc: Prints all statistics that appear in the database,
-    #         the optional argument is a regular expression that can
-    #         be used to prune the result set
-    def listFormulas(self, regex=None):
-        print('%-60s %s' % ('formula name', 'formula'))
-        print('-' * 80)
-
-        rx = None
-        if regex != None:
-            rx = re.compile(regex)
-
-        stats = [ stat.name for stat in self.allStats ]
-        stats.sort(statcmp)
-        for stat in stats:
-            stat = self.allStatNames[stat]
-            if stat.type == 'FORMULA' and (rx == None or rx.match(stat.name)):
-                print('%-60s %s' % (stat.name, self.allFormulas[stat.stat]))
-
-    def getStat(self, stats):
-        if type(stats) is not list:
-            stats = [ stats ]
-
-        ret = []
-        for stat in stats:
-            if type(stat) is int:
-                ret.append(self.allStatIds[stat])
-
-            if type(stat) is str:
-                rx = re.compile(stat)
-                for stat in self.allStats:
-                    if rx.match(stat.name):
-                        ret.append(stat)
-        return ret
-
-    #########################################
-    # get the data
-    #
-    def query(self, op, stat, ticks, group=False):
-        sql = 'select '
-        sql += 'dt_stat as stat, '
-        sql += 'dt_run as run, '
-        sql += 'dt_x as x, '
-        sql += 'dt_y as y, '
-        if group:
-            sql += 'dt_tick as tick, '
-        sql += '%s(dt_data) as data ' % op
-        sql += 'from data '
-        sql += 'where '
-
-        if isinstance(stat, list):
-            val = ' or '.join([ 'dt_stat=%d' % s.stat for s in stat ])
-            sql += ' (%s)' % val
-        else:
-            sql += ' dt_stat=%d' % stat.stat
-
-        if self.runs != None and len(self.runs):
-            val = ' or '.join([ 'dt_run=%d' % r for r in self.runs ])
-            sql += ' and (%s)' % val
-
-        if ticks != None and len(ticks):
-            val = ' or '.join([ 'dt_tick=%d' % s for s in ticks ])
-            sql += ' and (%s)' % val
-
-        sql += ' group by dt_stat,dt_run,dt_x,dt_y'
-        if group:
-            sql += ',dt_tick'
-        return sql
-
-    # Name: sum
-    # Desc: given a run, a stat and an array of samples, total the samples
-    def sum(self, *args, **kwargs):
-        return self.query('sum', *args, **kwargs)
-
-    # Name: avg
-    # Desc: given a run, a stat and an array of samples, average the samples
-    def avg(self, stat, ticks):
-        return self.query('avg', *args, **kwargs)
-
-    # Name: stdev
-    # Desc: given a run, a stat and an array of samples, get the standard
-    #       deviation
-    def stdev(self, stat, ticks):
-        return self.query('stddev', *args, **kwargs)
-
-    def __setattr__(self, attr, value):
-        super(Database, self).__setattr__(attr, value)
-        if attr != 'method':
-            return
-
-        if value == 'sum':
-            self._method = self.sum
-        elif value == 'avg':
-            self._method = self.avg
-        elif value == 'stdev':
-            self._method = self.stdev
-        else:
-            raise AttributeError("can only set get to: sum | avg | stdev")
-
-    def data(self, stat, ticks=None):
-        if ticks is None:
-            ticks = self.ticks
-        sql = self._method(self, stat, ticks)
-        self.query(sql)
-
-        runs = {}
-        xmax = 0
-        ymax = 0
-        for x in self.cursor.fetchall():
-            data = Data(x)
-            if data.run not in runs:
-                runs[data.run] = {}
-            if data.x not in runs[data.run]:
-                runs[data.run][data.x] = {}
-
-            xmax = max(xmax, data.x)
-            ymax = max(ymax, data.y)
-            runs[data.run][data.x][data.y] = data.data
-
-        results = Result(xmax + 1, ymax + 1)
-        for run,data in runs.items():
-            result = results[run]
-            for x,ydata in data.items():
-                for y,data in ydata.items():
-                    result[x][y] = data
-        return results
-
-    def __getitem__(self, key):
-        return self.stattop[key]
diff --git a/util/stats/dbinit.py b/util/stats/dbinit.py
deleted file mode 100644
index 2035824..0000000
--- a/util/stats/dbinit.py
+++ /dev/null
@@ -1,383 +0,0 @@
-# Copyright (c) 2003-2004 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import MySQLdb
-
-class MyDB(object):
-    def __init__(self, options):
-        self.name = options.db
-        self.host = options.host
-        self.user = options.user
-        self.passwd = options.passwd
-        self.mydb = None
-        self.cursor = None
-
-    def admin(self):
-        self.close()
-        self.mydb = MySQLdb.connect(db='mysql', host=self.host, user=self.user,
-                                    passwd=self.passwd)
-        self.cursor = self.mydb.cursor()
-
-    def connect(self):
-        self.close()
-        self.mydb = MySQLdb.connect(db=self.name, host=self.host,
-                                    user=self.user, passwd=self.passwd)
-        self.cursor = self.mydb.cursor()
-
-    def close(self):
-        if self.mydb is not None:
-            self.mydb.close()
-        self.cursor = None
-
-    def query(self, sql):
-        self.cursor.execute(sql)
-
-    def drop(self):
-        self.query('DROP DATABASE IF EXISTS %s' % self.name)
-
-    def create(self):
-        self.query('CREATE DATABASE %s' % self.name)
-
-    def populate(self):
-        #
-        # Each run (or simulation) gets its own entry in the runs table to
-        # group stats by where they were generated
-        #
-        # COLUMNS:
-        #   'id' is a unique identifier for each run to be used in other
-        #       tables.
-        #   'name' is the user designated name for the data generated.  It is
-        #       configured in the simulator.
-        #   'user' identifies the user that generated the data for the given
-        #       run.
-        #   'project' another name to identify runs for a specific goal
-        #   'date' is a timestamp for when the data was generated.  It can be
-        #       used to easily expire data that was generated in the past.
-        #   'expire' is a timestamp for when the data should be removed from
-        #       the database so we don't have years worth of junk.
-        #
-        # INDEXES:
-        #   'run' is indexed so you can find out details of a run if the run
-        #       was retreived from the data table.
-        #   'name' is indexed so that two all run names are forced to be unique
-        #
-        self.query('''
-        CREATE TABLE runs(
-            rn_id	SMALLINT UNSIGNED	NOT NULL AUTO_INCREMENT,
-            rn_name	VARCHAR(200)		NOT NULL,
-            rn_sample	VARCHAR(32)		NOT NULL,
-            rn_user	VARCHAR(32)		NOT NULL,
-            rn_project	VARCHAR(100)            NOT NULL,
-            rn_date	TIMESTAMP		NOT NULL,
-            rn_expire	TIMESTAMP               NOT NULL,
-            PRIMARY KEY (rn_id),
-            UNIQUE (rn_name,rn_sample)
-        ) TYPE=InnoDB''')
-
-        #
-        # The stat table gives us all of the data for a particular stat.
-        #
-        # COLUMNS:
-        #   'stat' is a unique identifier for each stat to be used in other
-        #       tables for references.
-        #   'name' is simply the simulator derived name for a given
-        #       statistic.
-        #   'descr' is the description of the statistic and what it tells
-        #       you.
-        #   'type' defines what the stat tells you.  Types are:
-        #       SCALAR: A simple scalar statistic that holds one value
-        #       VECTOR: An array of statistic values.  Such a something that
-        #           is generated per-thread.  Vectors exist to give averages,
-        #	     pdfs, cdfs, means, standard deviations, etc across the
-        #           stat values.
-        #       DIST: Is a distribution of data.  When the statistic value is
-        #	     sampled, its value is counted in a particular bucket.
-        #           Useful for keeping track of utilization of a resource.
-        #           (e.g. fraction of time it is 25% used vs. 50% vs. 100%)
-        #       VECTORDIST: Can be used when the distribution needs to be
-        #	     factored out into a per-thread distribution of data for
-        #	     example.  It can still be summed across threads to find
-        #           the total distribution.
-        #       VECTOR2D: Can be used when you have a stat that is not only
-        #           per-thread, but it is per-something else.  Like
-        #           per-message type.
-        #       FORMULA: This statistic is a formula, and its data must be
-        #	     looked up in the formula table, for indicating how to
-        #           present its values.
-        #   'subdata' is potentially used by any of the vector types to
-        #       give a specific name to all of the data elements within a
-        #       stat.
-        #   'print' indicates whether this stat should be printed ever.
-        #       (Unnamed stats don't usually get printed)
-        #   'prereq' only print the stat if the prereq is not zero.
-        #   'prec' number of decimal places to print
-        #   'nozero' don't print zero values
-        #   'nonan' don't print NaN values
-        #   'total' for vector type stats, print the total.
-        #   'pdf' for vector type stats, print the pdf.
-        #   'cdf' for vector type stats, print the cdf.
-        #
-        #   The Following are for dist type stats:
-        #   'min' is the minimum bucket value. Anything less is an underflow.
-        #   'max' is the maximum bucket value. Anything more is an overflow.
-        #   'bktsize' is the approximate number of entries in each bucket.
-        #   'size' is the number of buckets. equal to (min/max)/bktsize.
-        #
-        # INDEXES:
-        #   'stat' is indexed so that you can find out details about a stat
-        #       if the stat id was retrieved from the data table.
-        #   'name' is indexed so that you can simply look up data about a
-        #       named stat.
-        #
-        self.query('''
-        CREATE TABLE stats(
-            st_id	SMALLINT UNSIGNED	NOT NULL AUTO_INCREMENT,
-            st_name	VARCHAR(255)		NOT NULL,
-            st_descr	TEXT			NOT NULL,
-            st_type	ENUM("SCALAR", "VECTOR", "DIST", "VECTORDIST",
-                "VECTOR2D", "FORMULA")	NOT NULL,
-            st_print	BOOL			NOT NULL,
-            st_prereq	SMALLINT UNSIGNED	NOT NULL,
-            st_prec	TINYINT			NOT NULL,
-            st_nozero	BOOL			NOT NULL,
-            st_nonan	BOOL			NOT NULL,
-            st_total	BOOL			NOT NULL,
-            st_pdf	BOOL			NOT NULL,
-            st_cdf	BOOL			NOT NULL,
-            st_min	DOUBLE			NOT NULL,
-            st_max	DOUBLE			NOT NULL,
-            st_bktsize	DOUBLE			NOT NULL,
-            st_size	SMALLINT UNSIGNED	NOT NULL,
-            PRIMARY KEY (st_id),
-            UNIQUE (st_name)
-        ) TYPE=InnoDB''')
-
-        #
-        # This is the main table of data for stats.
-        #
-        # COLUMNS:
-        #   'stat' refers to the stat field given in the stat table.
-        #
-        #   'x' referrs to the first dimension of a multi-dimensional stat. For
-        #       a vector, x will start at 0 and increase for each vector
-        #       element.
-        #       For a distribution:
-        #       -1: sum (for calculating standard deviation)
-        #       -2: sum of squares (for calculating standard deviation)
-        #       -3: total number of samples taken (for calculating
-        #           standard deviation)
-        #       -4: minimum value
-        #       -5: maximum value
-        #       -6: underflow
-        #       -7: overflow
-        #   'y' is used by a VECTORDIST and the VECTOR2D to describe the second
-        #       dimension.
-        #   'run' is the run that the data was generated from.  Details up in
-        #       the run table
-        #   'tick' is a timestamp generated by the simulator.
-        #   'data' is the actual stat value.
-        #
-        # INDEXES:
-        #   'stat' is indexed so that a user can find all of the data for a
-        #       particular stat. It is not unique, because that specific stat
-        #       can be found in many runs and samples, in addition to
-        #       having entries for the mulidimensional cases.
-        #   'run' is indexed to allow a user to remove all of the data for a
-        #       particular execution run.  It can also be used to allow the
-        #       user to print out all of the data for a given run.
-        #
-        self.query('''
-        CREATE TABLE data(
-            dt_stat	SMALLINT UNSIGNED	NOT NULL,
-            dt_x	SMALLINT		NOT NULL,
-            dt_y	SMALLINT		NOT NULL,
-            dt_run	SMALLINT UNSIGNED	NOT NULL,
-            dt_tick	BIGINT UNSIGNED		NOT NULL,
-            dt_data	DOUBLE			NOT NULL,
-            INDEX (dt_stat),
-            INDEX (dt_run),
-            UNIQUE (dt_stat,dt_x,dt_y,dt_run,dt_tick)
-        ) TYPE=InnoDB;''')
-
-        #
-        # Names and descriptions for multi-dimensional stats (vectors, etc.)
-        # are stored here instead of having their own entry in the statistics
-        # table. This allows all parts of a single stat to easily share a
-        # single id.
-        #
-        # COLUMNS:
-        #   'stat' is the unique stat identifier from the stat table.
-        #   'x' is the first dimension for multi-dimensional stats
-        #       corresponding to the data table above.
-        #   'y' is the second dimension for multi-dimensional stats
-        #       corresponding to the data table above.
-        #   'name' is the specific subname for the unique stat,x,y combination.
-        #   'descr' is the specific description for the uniqe stat,x,y
-        #        combination.
-        #
-        # INDEXES:
-        #   'stat' is indexed so you can get the subdata for a specific stat.
-        #
-        self.query('''
-        CREATE TABLE subdata(
-            sd_stat	SMALLINT UNSIGNED	NOT NULL,
-            sd_x	SMALLINT		NOT NULL,
-            sd_y	SMALLINT		NOT NULL,
-            sd_name	VARCHAR(255)		NOT NULL,
-            sd_descr	TEXT,
-            UNIQUE (sd_stat,sd_x,sd_y)
-        ) TYPE=InnoDB''')
-
-
-        #
-        # The formula table is maintained separately from the data table
-        # because formula data, unlike other stat data cannot be represented
-        # there.
-        #
-        # COLUMNS:
-        #   'stat' refers to the stat field generated in the stat table.
-        #   'formula' is the actual string representation of the formula
-        #       itself.
-        #
-        # INDEXES:
-        #   'stat' is indexed so that you can just look up a formula.
-        #
-        self.query('''
-        CREATE TABLE formulas(
-            fm_stat	SMALLINT UNSIGNED	NOT NULL,
-            fm_formula	BLOB			NOT NULL,
-            PRIMARY KEY(fm_stat)
-        ) TYPE=InnoDB''')
-
-        #
-        # Each stat used in each formula is kept in this table.  This way, if
-        # you want to print out a particular formula, you can simply find out
-        # which stats you need by looking in this table.  Additionally, when
-        # you remove a stat from the stats table and data table, you remove
-        # any references to the formula in this table.  When a formula is no
-        # longer referred to, you remove its entry.
-        #
-        # COLUMNS:
-        #   'stat' is the stat id from the stat table above.
-        #   'child' is the stat id of a stat that is used for this formula.
-        #       There may be many children for any given 'stat' (formula)
-        #
-        # INDEXES:
-        #   'stat' is indexed so you can look up all of the children for a
-        #       particular stat.
-        #   'child' is indexed so that you can remove an entry when a stat is
-        #       removed.
-        #
-        self.query('''
-        CREATE TABLE formula_ref(
-            fr_stat	SMALLINT UNSIGNED	NOT NULL,
-            fr_run	SMALLINT UNSIGNED	NOT NULL,
-            UNIQUE (fr_stat,fr_run),
-            INDEX (fr_stat),
-            INDEX (fr_run)
-        ) TYPE=InnoDB''')
-
-        # COLUMNS:
-        #   'event' is the unique event id from the event_desc table
-        #   'run' is simulation run id that this event took place in
-        #   'tick' is the tick when the event happened
-        #
-        # INDEXES:
-        #   'event' is indexed so you can look up all occurences of a
-        #       specific event
-        #   'run' is indexed so you can find all events in a run
-        #   'tick' is indexed because we want the unique thing anyway
-        #   'event,run,tick' is unique combination
-        self.query('''
-        CREATE TABLE events(
-            ev_event	SMALLINT UNSIGNED	NOT NULL,
-            ev_run	SMALLINT UNSIGNED	NOT NULL,
-            ev_tick	BIGINT   UNSIGNED       NOT NULL,
-            INDEX(ev_event),
-            INDEX(ev_run),
-            INDEX(ev_tick),
-            UNIQUE(ev_event,ev_run,ev_tick)
-        ) TYPE=InnoDB''')
-
-        # COLUMNS:
-        #   'id' is the unique description id
-        #   'name' is the name of the event that occurred
-        #
-        # INDEXES:
-        #   'id' is indexed because it is the primary key and is what you use
-        #       to look up the descriptions
-        #   'name' is indexed so one can find the event based on name
-        #
-        self.query('''
-        CREATE TABLE event_names(
-            en_id	SMALLINT UNSIGNED	NOT NULL AUTO_INCREMENT,
-            en_name	VARCHAR(255)		NOT NULL,
-            PRIMARY KEY (en_id),
-            UNIQUE (en_name)
-        ) TYPE=InnoDB''')
-
-    def clean(self):
-        self.query('''
-        DELETE data
-        FROM data
-        LEFT JOIN runs ON dt_run=rn_id
-        WHERE rn_id IS NULL''')
-
-        self.query('''
-        DELETE formula_ref
-        FROM formula_ref
-        LEFT JOIN runs ON fr_run=rn_id
-        WHERE rn_id IS NULL''')
-
-        self.query('''
-        DELETE formulas
-        FROM formulas
-        LEFT JOIN formula_ref ON fm_stat=fr_stat
-        WHERE fr_stat IS NULL''')
-
-        self.query('''
-        DELETE stats
-        FROM stats
-        LEFT JOIN data ON st_id=dt_stat
-        WHERE dt_stat IS NULL''')
-
-        self.query('''
-        DELETE subdata
-        FROM subdata
-        LEFT JOIN data ON sd_stat=dt_stat
-        WHERE dt_stat IS NULL''')
-
-        self.query('''
-        DELETE events
-        FROM events
-        LEFT JOIN runs ON ev_run=rn_id
-        WHERE rn_id IS NULL''')
-
-        self.query('''
-        DELETE event_names
-        FROM event_names
-        LEFT JOIN events ON en_id=ev_event
-        WHERE ev_event IS NULL''')
diff --git a/util/stats/display.py b/util/stats/display.py
deleted file mode 100644
index 4f04eb3..0000000
--- a/util/stats/display.py
+++ /dev/null
@@ -1,151 +0,0 @@
-# Copyright (c) 2003-2004 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from functools import reduce
-
-class Value:
-    def __init__(self, value, precision, percent = False):
-        self.value = float(value)
-        self.precision = precision
-        self.percent = percent
-    def __str__(self):
-        if isinstance(self.value, str):
-            if self.value.lower() == 'nan':
-                value = 'NaN'
-            if self.value.lower() == 'inf':
-                value = 'Inf'
-        else:
-            if self.precision >= 0:
-                format = "%%.%df" % self.precision
-            elif self.value == 0.0:
-                format = "%.0f"
-            elif self.value % 1.0 == 0.0:
-                format = "%.0f"
-            else:
-                format = "%f"
-            value = self.value
-            if self.percent:
-                value = value * 100.0
-            value = format % value
-
-        if self.percent:
-            value = value + "%"
-
-        return value
-
-class Print:
-    def __init__(self, **vals):
-        self.__dict__.update(vals)
-
-    def __str__(self):
-        value = Value(self.value, self.precision)
-        pdf = ''
-        cdf = ''
-        if 'pdf' in self.__dict__:
-            pdf = Value(self.pdf, 2, True)
-        if 'cdf' in self.__dict__:
-            cdf = Value(self.cdf, 2, True)
-
-        output = "%-40s %12s %8s %8s" % (self.name, value, pdf, cdf)
-
-        if descriptions and 'desc' in self.__dict__ and self.desc:
-            output = "%s # %s" % (output, self.desc)
-
-        return output
-
-    def doprint(self):
-        if display_all:
-            return True
-        if self.value == 0.0 and (self.flags & flags_nozero):
-            return False
-        if isinstance(self.value, str):
-            if self.value == 'NaN' and (self.flags & flags_nonan):
-                return False
-        return True
-
-    def display(self):
-        if self.doprint():
-            print(self)
-
-class VectorDisplay:
-    def display(self):
-        if not self.value:
-            return
-
-        p = Print()
-        p.flags = self.flags
-        p.precision = self.precision
-
-        if not isinstance(self.value, (list, tuple)):
-            p.name = self.name
-            p.desc = self.desc
-            p.value = self.value
-            p.display()
-            return
-
-        mytotal = reduce(lambda x,y: float(x) + float(y), self.value)
-        mycdf = 0.0
-
-        value = self.value
-
-        if display_all:
-            subnames = [ '[%d]' % i for i in range(len(value)) ]
-        else:
-            subnames = [''] * len(value)
-
-        if 'subnames' in self.__dict__:
-            for i,each in enumerate(self.subnames):
-                if len(each) > 0:
-                    subnames[i] = '.%s' % each
-
-        subdescs = [self.desc]*len(value)
-        if 'subdescs' in self.__dict__:
-            for i in range(min(len(value), len(self.subdescs))):
-                subdescs[i] = self.subdescs[i]
-
-        for val,sname,sdesc in map(None, value, subnames, subdescs):
-            if mytotal > 0.0:
-                mypdf = float(val) / float(mytotal)
-                mycdf += mypdf
-                if (self.flags & flags_pdf):
-                    p.pdf = mypdf
-                    p.cdf = mycdf
-
-            if len(sname) == 0:
-                continue
-
-            p.name = self.name + sname
-            p.desc = sdesc
-            p.value = val
-            p.display()
-
-        if (self.flags & flags_total):
-            if ('pdf' in p.__dict__): del p.__dict__['pdf']
-            if ('cdf' in p.__dict__): del p.__dict__['cdf']
-            p.name = self.name + '.total'
-            p.desc = self.desc
-            p.value = mytotal
-            p.display()
diff --git a/util/stats/flags.py b/util/stats/flags.py
deleted file mode 100644
index ab72ca7..0000000
--- a/util/stats/flags.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (c) 2004 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-init      = 0x00000001
-printable = 0x00000002
-total     = 0x00000010
-pdf       = 0x00000020
-cdf       = 0x00000040
-dist      = 0x00000080
-nozero    = 0x00000100
-nonan     = 0x00000200
diff --git a/util/stats/info.py b/util/stats/info.py
deleted file mode 100644
index b7a1e35..0000000
--- a/util/stats/info.py
+++ /dev/null
@@ -1,767 +0,0 @@
-# Copyright (c) 2003-2004 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-import operator, re, types
-from functools import reduce
-
-class ProxyError(Exception):
-    pass
-
-def unproxy(proxy):
-    if hasattr(proxy, '__unproxy__'):
-        return proxy.__unproxy__()
-
-    return proxy
-
-def scalar(stat):
-    stat = unproxy(stat)
-    assert(stat.__scalar__() != stat.__vector__())
-    return stat.__scalar__()
-
-def vector(stat):
-    stat = unproxy(stat)
-    assert(stat.__scalar__() != stat.__vector__())
-    return stat.__vector__()
-
-def value(stat, *args):
-    stat = unproxy(stat)
-    return stat.__value__(*args)
-
-def values(stat, run):
-    stat = unproxy(stat)
-    result = []
-    for i in range(len(stat)):
-        val = value(stat, run, i)
-        if val is None:
-            return None
-        result.append(val)
-    return result
-
-def total(stat, run):
-    return sum(values(stat, run))
-
-def len(stat):
-    stat = unproxy(stat)
-    return stat.__len__()
-
-class Value(object):
-    def __scalar__(self):
-        raise AttributeError("must define __scalar__ for %s" % (type (self)))
-    def __vector__(self):
-        raise AttributeError("must define __vector__ for %s" % (type (self)))
-
-    def __add__(self, other):
-        return BinaryProxy(operator.__add__, self, other)
-    def __sub__(self, other):
-        return BinaryProxy(operator.__sub__, self, other)
-    def __mul__(self, other):
-        return BinaryProxy(operator.__mul__, self, other)
-    def __div__(self, other):
-        return BinaryProxy(operator.__div__, self, other)
-    def __truediv__(self, other):
-        return BinaryProxy(operator.__truediv__, self, other)
-    def __floordiv__(self, other):
-        return BinaryProxy(operator.__floordiv__, self, other)
-
-    def __radd__(self, other):
-        return BinaryProxy(operator.__add__, other, self)
-    def __rsub__(self, other):
-        return BinaryProxy(operator.__sub__, other, self)
-    def __rmul__(self, other):
-        return BinaryProxy(operator.__mul__, other, self)
-    def __rdiv__(self, other):
-        return BinaryProxy(operator.__div__, other, self)
-    def __rtruediv__(self, other):
-        return BinaryProxy(operator.__truediv__, other, self)
-    def __rfloordiv__(self, other):
-        return BinaryProxy(operator.__floordiv__, other, self)
-
-    def __neg__(self):
-        return UnaryProxy(operator.__neg__, self)
-    def __pos__(self):
-        return UnaryProxy(operator.__pos__, self)
-    def __abs__(self):
-        return UnaryProxy(operator.__abs__, self)
-
-class Scalar(Value):
-    def __scalar__(self):
-        return True
-
-    def __vector__(self):
-        return False
-
-    def __value__(self, run):
-        raise AttributeError('__value__ must be defined')
-
-class VectorItemProxy(Value):
-    def __init__(self, proxy, index):
-        self.proxy = proxy
-        self.index = index
-
-    def __scalar__(self):
-        return True
-
-    def __vector__(self):
-        return False
-
-    def __value__(self, run):
-        return value(self.proxy, run, self.index)
-
-class Vector(Value):
-    def __scalar__(self):
-        return False
-
-    def __vector__(self):
-        return True
-
-    def __value__(self, run, index):
-        raise AttributeError('__value__ must be defined')
-
-    def __getitem__(self, index):
-        return VectorItemProxy(self, index)
-
-class ScalarConstant(Scalar):
-    def __init__(self, constant):
-        self.constant = constant
-    def __value__(self, run):
-        return self.constant
-    def __str__(self):
-        return str(self.constant)
-
-class VectorConstant(Vector):
-    def __init__(self, constant):
-        self.constant = constant
-    def __value__(self, run, index):
-        return self.constant[index]
-    def __len__(self):
-        return len(self.constant)
-    def __str__(self):
-        return str(self.constant)
-
-def WrapValue(value):
-    if isinstance(value, (int, float)):
-        return ScalarConstant(value)
-    if isinstance(value, (list, tuple)):
-        return VectorConstant(value)
-    if isinstance(value, Value):
-        return value
-
-    raise AttributeError('Only values can be wrapped')
-
-class Statistic(object):
-    def __getattr__(self, attr):
-        if attr in ('data', 'x', 'y'):
-            result = self.source.data(self, self.ticks)
-            self.data = result.data
-            self.x = result.x
-            self.y = result.y
-        return super(Statistic, self).__getattribute__(attr)
-
-    def __setattr__(self, attr, value):
-        if attr == 'stat':
-            raise AttributeError('%s is read only' % stat)
-        if attr in ('source', 'ticks'):
-            if getattr(self, attr) != value:
-                if hasattr(self, 'data'):
-                    delattr(self, 'data')
-
-        super(Statistic, self).__setattr__(attr, value)
-
-    def __str__(self):
-        return self.name
-
-class ValueProxy(Value):
-    def __getattr__(self, attr):
-        if attr == '__value__':
-            if scalar(self):
-                return self.__scalarvalue__
-            if vector(self):
-                return self.__vectorvalue__
-        if attr == '__len__':
-            if vector(self):
-                return self.__vectorlen__
-        return super(ValueProxy, self).__getattribute__(attr)
-
-class UnaryProxy(ValueProxy):
-    def __init__(self, op, arg):
-        self.op = op
-        self.arg = WrapValue(arg)
-
-    def __scalar__(self):
-        return scalar(self.arg)
-
-    def __vector__(self):
-        return vector(self.arg)
-
-    def __scalarvalue__(self, run):
-        val = value(self.arg, run)
-        if val is None:
-            return None
-        return self.op(val)
-
-    def __vectorvalue__(self, run, index):
-        val = value(self.arg, run, index)
-        if val is None:
-            return None
-        return self.op(val)
-
-    def __vectorlen__(self):
-        return len(unproxy(self.arg))
-
-    def __str__(self):
-        if self.op == operator.__neg__:
-            return '-%s' % str(self.arg)
-        if self.op == operator.__pos__:
-            return '+%s' % str(self.arg)
-        if self.op == operator.__abs__:
-            return 'abs(%s)' % self.arg
-
-class BinaryProxy(ValueProxy):
-    def __init__(self, op, arg0, arg1):
-        super(BinaryProxy, self).__init__()
-        self.op = op
-        self.arg0 = WrapValue(arg0)
-        self.arg1 = WrapValue(arg1)
-
-    def __scalar__(self):
-        return scalar(self.arg0) and scalar(self.arg1)
-
-    def __vector__(self):
-        return vector(self.arg0) or vector(self.arg1)
-
-    def __scalarvalue__(self, run):
-        val0 = value(self.arg0, run)
-        val1 = value(self.arg1, run)
-        if val0 is None or val1 is None:
-            return None
-        try:
-            return self.op(val0, val1)
-        except ZeroDivisionError:
-            return None
-
-    def __vectorvalue__(self, run, index):
-        if scalar(self.arg0):
-            val0 = value(self.arg0, run)
-        if vector(self.arg0):
-            val0 = value(self.arg0, run, index)
-        if scalar(self.arg1):
-            val1 = value(self.arg1, run)
-        if vector(self.arg1):
-            val1 = value(self.arg1, run, index)
-
-        if val0 is None or val1 is None:
-            return None
-
-        try:
-            return self.op(val0, val1)
-        except ZeroDivisionError:
-            return None
-
-    def __vectorlen__(self):
-        if vector(self.arg0) and scalar(self.arg1):
-            return len(self.arg0)
-        if scalar(self.arg0) and vector(self.arg1):
-            return len(self.arg1)
-
-        len0 = len(self.arg0)
-        len1 = len(self.arg1)
-
-        if len0 != len1:
-            raise AttributeError(
-                "vectors of different lengths %d != %d" % (len0, len1))
-
-        return len0
-
-    def __str__(self):
-        ops = { operator.__add__ : '+',
-                operator.__sub__ : '-',
-                operator.__mul__ : '*',
-                operator.__div__ : '/',
-                operator.__truediv__ : '/',
-                operator.__floordiv__ : '//' }
-
-        return '(%s %s %s)' % (str(self.arg0), ops[self.op], str(self.arg1))
-
-class Proxy(Value):
-    def __init__(self, name, dict):
-        self.name = name
-        self.dict = dict
-
-    def __unproxy__(self):
-        return unproxy(self.dict[self.name])
-
-    def __getitem__(self, index):
-        return ItemProxy(self, index)
-
-    def __getattr__(self, attr):
-        return AttrProxy(self, attr)
-
-    def __str__(self):
-        return str(self.dict[self.name])
-
-class ItemProxy(Proxy):
-    def __init__(self, proxy, index):
-        self.proxy = proxy
-        self.index = index
-
-    def __unproxy__(self):
-        return unproxy(unproxy(self.proxy)[self.index])
-
-    def __str__(self):
-        return '%s[%s]' % (self.proxy, self.index)
-
-class AttrProxy(Proxy):
-    def __init__(self, proxy, attr):
-        self.proxy = proxy
-        self.attr = attr
-
-    def __unproxy__(self):
-        proxy = unproxy(self.proxy)
-        try:
-            attr = getattr(proxy, self.attr)
-        except AttributeError as e:
-            raise ProxyError(e)
-        return unproxy(attr)
-
-    def __str__(self):
-        return '%s.%s' % (self.proxy, self.attr)
-
-class ProxyGroup(object):
-    def __init__(self, dict=None, **kwargs):
-        self.__dict__['dict'] = {}
-
-        if dict is not None:
-            self.dict.update(dict)
-
-        if kwargs:
-            self.dict.update(kwargs)
-
-    def __getattr__(self, name):
-        return Proxy(name, self.dict)
-
-    def __setattr__(self, attr, value):
-        self.dict[attr] = value
-
-class ScalarStat(Statistic,Scalar):
-    def __value__(self, run):
-        if run not in self.data:
-            return None
-        return self.data[run][0][0]
-
-    def display(self, run=None):
-        from . import display
-        p = display.Print()
-        p.name = self.name
-        p.desc = self.desc
-        p.value = value(self, run)
-        p.flags = self.flags
-        p.precision = self.precision
-        if display.all or (self.flags & flags.printable):
-            p.display()
-
-class VectorStat(Statistic,Vector):
-    def __value__(self, run, item):
-        if run not in self.data:
-            return None
-        return self.data[run][item][0]
-
-    def __len__(self):
-        return self.x
-
-    def display(self, run=None):
-        from . import display
-        d = display.VectorDisplay()
-        d.name = self.name
-        d.desc = self.desc
-        d.value = [ value(self, run, i) for i in range(len(self)) ]
-        d.flags = self.flags
-        d.precision = self.precision
-        d.display()
-
-class Formula(Value):
-    def __getattribute__(self, attr):
-        if attr not in ( '__scalar__', '__vector__', '__value__', '__len__' ):
-            return super(Formula, self).__getattribute__(attr)
-
-        formula = re.sub(':', '__', self.formula)
-        value = eval(formula, self.source.stattop)
-        return getattr(value, attr)
-
-    def __str__(self):
-        return self.name
-
-class SimpleDist(Statistic):
-    def __init__(self, sums, squares, samples):
-        self.sums = sums
-        self.squares = squares
-        self.samples = samples
-
-    def display(self, name, desc, flags, precision):
-        from . import display
-        p = display.Print()
-        p.flags = flags
-        p.precision = precision
-
-        if self.samples > 0:
-            p.name = name + ".mean"
-            p.value = self.sums / self.samples
-            p.display()
-
-            p.name = name + ".stdev"
-            if self.samples > 1:
-                var = (self.samples * self.squares - self.sums ** 2) \
-                      / (self.samples * (self.samples - 1))
-                if var >= 0:
-                    p.value = math.sqrt(var)
-                else:
-                    p.value = 'NaN'
-            else:
-                p.value = 0.0
-            p.display()
-
-        p.name = name + ".samples"
-        p.value = self.samples
-        p.display()
-
-    def comparable(self, other):
-        return True
-
-    def __eq__(self, other):
-        return self.sums == other.sums and self.squares == other.squares and \
-               self.samples == other.samples
-
-    def __isub__(self, other):
-        self.sums -= other.sums
-        self.squares -= other.squares
-        self.samples -= other.samples
-        return self
-
-    def __iadd__(self, other):
-        self.sums += other.sums
-        self.squares += other.squares
-        self.samples += other.samples
-        return self
-
-    def __itruediv__(self, other):
-        if not other:
-            return self
-        self.sums /= other
-        self.squares /= other
-        self.samples /= other
-        return self
-
-class FullDist(SimpleDist):
-    def __init__(self, sums, squares, samples, minval, maxval,
-                 under, vec, over, min, max, bsize, size):
-        self.sums = sums
-        self.squares = squares
-        self.samples = samples
-        self.minval = minval
-        self.maxval = maxval
-        self.under = under
-        self.vec = vec
-        self.over = over
-        self.min = min
-        self.max = max
-        self.bsize = bsize
-        self.size = size
-
-    def display(self, name, desc, flags, precision):
-        from . import display
-        p = display.Print()
-        p.flags = flags
-        p.precision = precision
-
-        p.name = name + '.min_val'
-        p.value = self.minval
-        p.display()
-
-        p.name = name + '.max_val'
-        p.value = self.maxval
-        p.display()
-
-        p.name = name + '.underflow'
-        p.value = self.under
-        p.display()
-
-        i = self.min
-        for val in self.vec[:-1]:
-            p.name = name + '[%d:%d]' % (i, i + self.bsize - 1)
-            p.value = val
-            p.display()
-            i += self.bsize
-
-        p.name = name + '[%d:%d]' % (i, self.max)
-        p.value = self.vec[-1]
-        p.display()
-
-
-        p.name = name + '.overflow'
-        p.value = self.over
-        p.display()
-
-        SimpleDist.display(self, name, desc, flags, precision)
-
-    def comparable(self, other):
-        return self.min == other.min and self.max == other.max and \
-               self.bsize == other.bsize and self.size == other.size
-
-    def __eq__(self, other):
-        return self.sums == other.sums and self.squares == other.squares and \
-               self.samples == other.samples
-
-    def __isub__(self, other):
-        self.sums -= other.sums
-        self.squares -= other.squares
-        self.samples -= other.samples
-
-        if other.samples:
-            self.minval = min(self.minval, other.minval)
-            self.maxval = max(self.maxval, other.maxval)
-            self.under -= under
-            self.vec = list(map(lambda x,y: x - y, self.vec, other.vec))
-            self.over -= over
-        return self
-
-    def __iadd__(self, other):
-        if not self.samples and other.samples:
-            self = other
-            return self
-
-        self.sums += other.sums
-        self.squares += other.squares
-        self.samples += other.samples
-
-        if other.samples:
-            self.minval = min(self.minval, other.minval)
-            self.maxval = max(self.maxval, other.maxval)
-            self.under += other.under
-            self.vec = list(map(lambda x,y: x + y, self.vec, other.vec))
-            self.over += other.over
-        return self
-
-    def __itruediv__(self, other):
-        if not other:
-            return self
-        self.sums /= other
-        self.squares /= other
-        self.samples /= other
-
-        if self.samples:
-            self.under /= other
-            for i in range(len(self.vec)):
-                self.vec[i] /= other
-            self.over /= other
-        return self
-
-class Dist(Statistic):
-    def display(self):
-        from . import display
-        if not display.all and not (self.flags & flags.printable):
-            return
-
-        self.dist.display(self.name, self.desc, self.flags, self.precision)
-
-    def comparable(self, other):
-        return self.name == other.name and \
-               self.dist.compareable(other.dist)
-
-    def __eq__(self, other):
-        return self.dist == other.dist
-
-    def __isub__(self, other):
-        self.dist -= other.dist
-        return self
-
-    def __iadd__(self, other):
-        self.dist += other.dist
-        return self
-
-    def __itruediv__(self, other):
-        if not other:
-            return self
-        self.dist /= other
-        return self
-
-class VectorDist(Statistic):
-    def display(self):
-        from . import display
-        if not display.all and not (self.flags & flags.printable):
-            return
-
-        if isinstance(self.dist, SimpleDist):
-            return
-
-        for dist,sn,sd,i in map(None, self.dist, self.subnames, self.subdescs,
-                                range(len(self.dist))):
-            if len(sn) > 0:
-                name = '%s.%s' % (self.name, sn)
-            else:
-                name = '%s[%d]' % (self.name, i)
-
-            if len(sd) > 0:
-                desc = sd
-            else:
-                desc = self.desc
-
-            dist.display(name, desc, self.flags, self.precision)
-
-        if (self.flags & flags.total) or 1:
-            if isinstance(self.dist[0], SimpleDist):
-                disttotal = SimpleDist( \
-                    reduce(sums, [d.sums for d in self.dist]),
-                    reduce(sums, [d.squares for d in self.dist]),
-                    reduce(sums, [d.samples for d in self.dist]))
-            else:
-                disttotal = FullDist( \
-                    reduce(sums, [d.sums for d in self.dist]),
-                    reduce(sums, [d.squares for d in self.dist]),
-                    reduce(sums, [d.samples for d in self.dist]),
-                    min([d.minval for d in self.dist]),
-                    max([d.maxval for d in self.dist]),
-                    reduce(sums, [d.under for d in self.dist]),
-                    reduce(sums, [d.vec for d in self.dist]),
-                    reduce(sums, [d.over for d in self.dist]),
-                    dist[0].min,
-                    dist[0].max,
-                    dist[0].bsize,
-                    dist[0].size)
-
-            name = '%s.total' % (self.name)
-            desc = self.desc
-            disttotal.display(name, desc, self.flags, self.precision)
-
-    def comparable(self, other):
-        return self.name == other.name and \
-               alltrue(map(lambda x, y : x.comparable(y),
-                       self.dist,
-                       other.dist))
-
-    def __eq__(self, other):
-        return alltrue(map(lambda x, y : x == y, self.dist, other.dist))
-
-    def __isub__(self, other):
-        if isinstance(self.dist, (list, tuple)) and \
-               isinstance(other.dist, (list, tuple)):
-            for sd,od in zip(self.dist, other.dist):
-                sd -= od
-        else:
-            self.dist -= other.dist
-        return self
-
-    def __iadd__(self, other):
-        if isinstance(self.dist, (list, tuple)) and \
-               isinstance(other.dist, (list, tuple)):
-            for sd,od in zip(self.dist, other.dist):
-                sd += od
-        else:
-            self.dist += other.dist
-        return self
-
-    def __itruediv__(self, other):
-        if not other:
-            return self
-        if isinstance(self.dist, (list, tuple)):
-            for dist in self.dist:
-                dist /= other
-        else:
-            self.dist /= other
-        return self
-
-class Vector2d(Statistic):
-    def display(self):
-        from . import display
-        if not display.all and not (self.flags & flags.printable):
-            return
-
-        d = display.VectorDisplay()
-        d.__dict__.update(self.__dict__)
-
-        if 'ysubnames' in self.__dict__:
-            ysubnames = list(self.ysubnames)
-            slack = self.x - len(ysubnames)
-            if slack > 0:
-                ysubnames.extend(['']*slack)
-        else:
-            ysubnames = list(range(self.x))
-
-        for x,sname in enumerate(ysubnames):
-            o = x * self.y
-            d.value = self.value[o:o+self.y]
-            d.name = '%s[%s]' % (self.name, sname)
-            d.display()
-
-        if self.flags & flags.total:
-            d.value = []
-            for y in range(self.y):
-                xtot = 0.0
-                for x in range(self.x):
-                    xtot += self.value[y + x * self.x]
-                d.value.append(xtot)
-
-            d.name = self.name + '.total'
-            d.display()
-
-    def comparable(self, other):
-        return self.name == other.name and self.x == other.x and \
-               self.y == other.y
-
-    def __eq__(self, other):
-        return True
-
-    def __isub__(self, other):
-        return self
-
-    def __iadd__(self, other):
-        return self
-
-    def __itruediv__(self, other):
-        if not other:
-            return self
-        return self
-
-def NewStat(source, data):
-    stat = None
-    if data.type == 'SCALAR':
-        stat = ScalarStat()
-    elif data.type == 'VECTOR':
-        stat = VectorStat()
-    elif data.type == 'DIST':
-        stat = Dist()
-    elif data.type == 'VECTORDIST':
-        stat = VectorDist()
-    elif data.type == 'VECTOR2D':
-        stat = Vector2d()
-    elif data.type == 'FORMULA':
-        stat = Formula()
-
-    stat.__dict__['source'] = source
-    stat.__dict__['ticks'] = None
-    stat.__dict__.update(data.__dict__)
-
-    return stat
-
diff --git a/util/stats/output.py b/util/stats/output.py
deleted file mode 100644
index b453d0d..0000000
--- a/util/stats/output.py
+++ /dev/null
@@ -1,212 +0,0 @@
-# Copyright (c) 2005-2006 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from .chart import ChartOptions
-
-class StatOutput(ChartOptions):
-    def __init__(self, jobfile, info, stat=None):
-        super(StatOutput, self).__init__()
-        self.jobfile = jobfile
-        self.stat = stat
-        self.invert = False
-        self.info = info
-
-    def display(self, name, printmode = 'G'):
-        from . import info
-
-        if printmode == 'G':
-            valformat = '%g'
-        elif printmode != 'F' and value > 1e6:
-            valformat = '%0.5e'
-        else:
-            valformat = '%f'
-
-        for job in self.jobfile.jobs():
-            value = self.info.get(job, self.stat)
-            if value is None:
-                return
-
-            if not isinstance(value, list):
-                value = [ value ]
-
-            if self.invert:
-                for i,val in enumerate(value):
-                    if val != 0.0:
-                        value[i] = 1 / val
-
-            valstring = ', '.join([ valformat % val for val in value ])
-            print('%-50s    %s' % (job.name + ':', valstring))
-
-    def graph(self, name, graphdir, proxy=None):
-        from os.path import expanduser, isdir, join as joinpath
-        from .barchart import BarChart
-        from matplotlib.numerix import Float, array, zeros
-        import os, re, urllib.request, urllib.parse, urllib.error
-        from jobfile import crossproduct
-
-        confgroups = self.jobfile.groups()
-        ngroups = len(confgroups)
-        skiplist = [ False ] * ngroups
-        groupopts = []
-        baropts = []
-        groups = []
-        for i,group in enumerate(confgroups):
-            if group.flags.graph_group:
-                groupopts.append(group.subopts())
-                skiplist[i] = True
-            elif group.flags.graph_bars:
-                baropts.append(group.subopts())
-                skiplist[i] = True
-            else:
-                groups.append(group)
-
-        has_group = bool(groupopts)
-        if has_group:
-            groupopts = [ group for group in crossproduct(groupopts) ]
-        else:
-            groupopts = [ None ]
-
-        if baropts:
-            baropts = [ bar for bar in crossproduct(baropts) ]
-        else:
-            raise AttributeError('No group selected for graph bars')
-
-        directory = expanduser(graphdir)
-        if not isdir(directory):
-            os.mkdir(directory)
-        html = file(joinpath(directory, '%s.html' % name), 'w')
-        print('<html>', file=html)
-        print('<title>Graphs for %s</title>' % name, file=html)
-        print('<body>', file=html)
-        html.flush()
-
-        for options in self.jobfile.options(groups):
-            chart = BarChart(self)
-
-            data = [ [ None ] * len(baropts) for i in range(len(groupopts)) ]
-            enabled = False
-            stacked = 0
-            for g,gopt in enumerate(groupopts):
-                for b,bopt in enumerate(baropts):
-                    if gopt is None:
-                        gopt = []
-                    job = self.jobfile.job(options + gopt + bopt)
-                    if not job:
-                        continue
-
-                    if proxy:
-                        from . import db
-                        proxy.dict['system'] = self.info[job.system]
-                    val = self.info.get(job, self.stat)
-                    if val is None:
-                        print('stat "%s" for job "%s" not found' % \
-                              (self.stat, job))
-
-                    if isinstance(val, (list, tuple)):
-                        if len(val) == 1:
-                            val = val[0]
-                        else:
-                            stacked = len(val)
-
-                    data[g][b] = val
-
-            if stacked == 0:
-                for i in range(len(groupopts)):
-                    for j in range(len(baropts)):
-                        if data[i][j] is None:
-                            data[i][j] = 0.0
-            else:
-                for i in range(len(groupopts)):
-                    for j in range(len(baropts)):
-                        val = data[i][j]
-                        if val is None:
-                            data[i][j] = [ 0.0 ] * stacked
-                        elif len(val) != stacked:
-                            raise ValueError("some stats stacked, some not")
-
-            data = array(data)
-            if data.sum() == 0:
-                continue
-
-            dim = len(data.shape)
-            x = data.shape[0]
-            xkeep = [ i for i in range(x) if data[i].sum() != 0 ]
-            y = data.shape[1]
-            ykeep = [ i for i in range(y) if data[:,i].sum() != 0 ]
-            data = data.take(xkeep, axis=0)
-            data = data.take(ykeep, axis=1)
-            if not has_group:
-                data = data.take([ 0 ], axis=0)
-            chart.data = data
-
-
-            bopts = [ baropts[i] for i in ykeep ]
-            bdescs = [ ' '.join([o.desc for o in opt]) for opt in bopts]
-
-            if has_group:
-                gopts = [ groupopts[i] for i in xkeep ]
-                gdescs = [ ' '.join([o.desc for o in opt]) for opt in gopts]
-
-            if chart.legend is None:
-                if stacked:
-                    try:
-                        chart.legend = self.info.rcategories
-                    except:
-                        chart.legend = [ str(i) for i in range(stacked) ]
-                else:
-                    chart.legend = bdescs
-
-            if chart.xticks is None:
-                if has_group:
-                    chart.xticks = gdescs
-                else:
-                    chart.xticks = []
-            chart.graph()
-
-            names = [ opt.name for opt in options ]
-            descs = [ opt.desc for opt in options ]
-
-            if names[0] == 'run':
-                names = names[1:]
-                descs = descs[1:]
-
-            basename = '%s-%s' % (name, ':'.join(names))
-            desc = ' '.join(descs)
-
-            pngname = '%s.png' % basename
-            psname = '%s.eps' % re.sub(':', '-', basename)
-            epsname = '%s.ps' % re.sub(':', '-', basename)
-            chart.savefig(joinpath(directory, pngname))
-            chart.savefig(joinpath(directory, epsname))
-            chart.savefig(joinpath(directory, psname))
-            html_name = urllib.parse.quote(pngname)
-            print('''%s<br><img src="%s"><br>''' % (desc, html_name),
-                file=html)
-            html.flush()
-
-        print('</body>', file=html)
-        print('</html>', file=html)
-        html.close()
diff --git a/util/stats/print.py b/util/stats/print.py
deleted file mode 100644
index 38c51d9..0000000
--- a/util/stats/print.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Copyright (c) 2003-2004 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from functools import reduce
-
-all = False
-descriptions = False
-
-class Value:
-    def __init__(self, value, precision, percent = False):
-        self.value = value
-        self.precision = precision
-        self.percent = percent
-    def __str__(self):
-        if isinstance(self.value, str):
-            if self.value.lower() == 'nan':
-                value = 'NaN'
-            if self.value.lower() == 'inf':
-                value = 'Inf'
-        else:
-            if self.precision >= 0:
-                format = "%%.%df" % self.precision
-            elif self.value == 0.0:
-                format = "%.0f"
-            elif self.value % 1.0 == 0.0:
-                format = "%.0f"
-            else:
-                format = "%f"
-            value = self.value
-            if self.percent:
-                value = value * 100.0
-            value = format % value
-
-        if self.percent:
-            value = value + "%"
-
-        return value
-
-class Print:
-    def __init__(self, **vals):
-        self.__dict__.update(vals)
-
-    def __str__(self):
-        value = Value(self.value, self.precision)
-        pdf = ''
-        cdf = ''
-        if 'pdf' in self.__dict__:
-            pdf = Value(self.pdf, 2, True)
-        if 'cdf' in self.__dict__:
-            cdf = Value(self.cdf, 2, True)
-
-        output = "%-40s %12s %8s %8s" % (self.name, value, pdf, cdf)
-
-        if descriptions and 'desc' in self.__dict__ and self.desc:
-            output = "%s # %s" % (output, self.desc)
-
-        return output
-
-    def doprint(self):
-        if display_all:
-            return True
-        if self.value == 0.0 and (self.flags & flags_nozero):
-            return False
-        if isinstance(self.value, str):
-            if self.value == 'NaN' and (self.flags & flags_nonan):
-                return False
-        return True
-
-    def display(self):
-        if self.doprint():
-            print(self)
-
-class VectorDisplay:
-    def display(self):
-        p = Print()
-        p.flags = self.flags
-        p.precision = self.precision
-
-        if isinstance(self.value, (list, tuple)):
-            if not len(self.value):
-                return
-
-            mytotal = reduce(lambda x,y: float(x) + float(y), self.value)
-            mycdf = 0.0
-
-            value = self.value
-
-            if display_all:
-                subnames = [ '[%d]' % i for i in range(len(value)) ]
-            else:
-                subnames = [''] * len(value)
-
-            if 'subnames' in self.__dict__:
-                for i,each in enumerate(self.subnames):
-                    if len(each) > 0:
-                        subnames[i] = '.%s' % each
-
-            subdescs = [self.desc]*len(value)
-            if 'subdescs' in self.__dict__:
-                for i in range(min(len(value), len(self.subdescs))):
-                    subdescs[i] = self.subdescs[i]
-
-            for val,sname,sdesc in map(None, value, subnames, subdescs):
-                if mytotal > 0.0:
-                    mypdf = float(val) / float(mytotal)
-                    mycdf += mypdf
-                    if (self.flags & flags_pdf):
-                        p.pdf = mypdf
-                        p.cdf = mycdf
-
-                if len(sname) == 0:
-                    continue
-
-                p.name = self.name + sname
-                p.desc = sdesc
-                p.value = val
-                p.display()
-
-            if (self.flags & flags_total):
-                if ('pdf' in p.__dict__): del p.__dict__['pdf']
-                if ('cdf' in p.__dict__): del p.__dict__['cdf']
-                p.name = self.name + '.total'
-                p.desc = self.desc
-                p.value = mytotal
-                p.display()
-
-        else:
-            p.name = self.name
-            p.desc = self.desc
-            p.value = self.value
-            p.display()
-
diff --git a/util/stats/profile.py b/util/stats/profile.py
deleted file mode 100644
index bed8088..0000000
--- a/util/stats/profile.py
+++ /dev/null
@@ -1,502 +0,0 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from . import output
-
-class FileData(dict):
-    def __init__(self, filename):
-        self.filename = filename
-        fd = file(filename)
-        current = []
-        for line in fd:
-            line = line.strip()
-            if line.startswith('>>>'):
-                current = []
-                self[line[3:]] = current
-            else:
-                current.append(line)
-        fd.close()
-
-class RunData(dict):
-    def __init__(self, filename):
-        self.filename = filename
-
-    def __getattribute__(self, attr):
-        if attr == 'total':
-            total = 0.0
-            for value in self.values():
-                total += value
-            return total
-
-        if attr == 'filedata':
-            return FileData(self.filename)
-
-        if attr == 'maxsymlen':
-            return max([ len(sym) for sym in self.keys() ])
-
-        return super(RunData, self).__getattribute__(attr)
-
-    def display(self, output=None, limit=None, maxsymlen=None):
-        if not output:
-            import sys
-            output = sys.stdout
-        elif isinstance(output, str):
-            output = file(output, 'w')
-
-        total = float(self.total)
-
-        # swap (string,count) order so we can sort on count
-        symbols = [ (count,name) for name,count in self.items() ]
-        symbols.sort(reverse=True)
-        if limit is not None:
-            symbols = symbols[:limit]
-
-        if not maxsymlen:
-            maxsymlen = self.maxsymlen
-
-        symbolf = "%-" + str(maxsymlen + 1) + "s %.2f%%"
-        for number,name in symbols:
-            print(symbolf % (name, 100.0 * (float(number) / total)),
-                file=output)
-
-class PCData(RunData):
-    def __init__(self, filename=None, categorize=None, showidle=True):
-        super(PCData, self).__init__(self, filename)
-
-        filedata = self.filedata['PC data']
-        for line in filedata:
-            (symbol, count) = line.split()
-            if symbol == "0x0":
-                continue
-            count = int(count)
-
-            if categorize is not None:
-                category = categorize(symbol)
-                if category is None:
-                    category = 'other'
-                elif category == 'idle' and not showidle:
-                    continue
-
-                self[category] = count
-
-class FuncNode(object):
-    def __new__(cls, filedata=None):
-        if filedata is None:
-            return super(FuncNode, cls).__new__(cls)
-
-        nodes = {}
-        for line in filedata['function data']:
-            data = line.split(' ')
-            node_id = int(data[0], 16)
-            node = FuncNode()
-            node.symbol = data[1]
-            if node.symbol == '':
-                node.symbol = 'unknown'
-            node.count = int(data[2])
-            node.children = [ int(child, 16) for child in data[3:] ]
-            nodes[node_id] = node
-
-        for node in nodes.values():
-            children = []
-            for cid in node.children:
-                child = nodes[cid]
-                children.append(child)
-                child.parent = node
-            node.children = tuple(children)
-        if not nodes:
-            print(filedata.filename)
-            print(nodes)
-        return nodes[0]
-
-    def total(self):
-        total = self.count
-        for child in self.children:
-            total += child.total()
-
-        return total
-
-    def aggregate(self, dict, categorize, incategory):
-        category = None
-        if categorize:
-            category = categorize(self.symbol)
-
-        total = self.count
-        for child in self.children:
-            total += child.aggregate(dict, categorize, category or incategory)
-
-        if category:
-            dict[category] = dict.get(category, 0) + total
-            return 0
-        elif not incategory:
-            dict[self.symbol] = dict.get(self.symbol, 0) + total
-
-        return total
-
-    def dump(self):
-        kids = [ child.symbol for child in self.children]
-        print('%s %d <%s>' % (self.symbol, self.count, ', '.join(kids)))
-        for child in self.children:
-            child.dump()
-
-    def _dot(self, dot, threshold, categorize, total):
-        from pydot import Dot, Edge, Node
-        self.dot_node = None
-
-        value = self.total() * 100.0 / total
-        if value < threshold:
-            return
-        if categorize:
-            category = categorize(self.symbol)
-            if category and category != 'other':
-                return
-        label = '%s %.2f%%' % (self.symbol, value)
-        self.dot_node = Node(self, label=label)
-        dot.add_node(self.dot_node)
-
-        for child in self.children:
-            child._dot(dot, threshold, categorize, total)
-            if child.dot_node is not None:
-                dot.add_edge(Edge(self, child))
-
-    def _cleandot(self):
-        for child in self.children:
-            child._cleandot()
-            self.dot_node = None
-            del self.__dict__['dot_node']
-
-    def dot(self, dot, threshold=0.1, categorize=None):
-        self._dot(dot, threshold, categorize, self.total())
-        self._cleandot()
-
-class FuncData(RunData):
-    def __init__(self, filename, categorize=None):
-        super(FuncData, self).__init__(filename)
-        tree = self.tree
-        tree.aggregate(self, categorize, incategory=False)
-        self.total = tree.total()
-
-    def __getattribute__(self, attr):
-        if attr == 'tree':
-            return FuncNode(self.filedata)
-        return super(FuncData, self).__getattribute__(attr)
-
-    def displayx(self, output=None, maxcount=None):
-        if output is None:
-            import sys
-            output = sys.stdout
-
-        items = [ (val,key) for key,val in self.items() ]
-        items.sort(reverse=True)
-        for val,key in items:
-            if maxcount is not None:
-                if maxcount == 0:
-                    return
-                maxcount -= 1
-
-            percent = val * 100.0 / self.total
-            print('%-30s %8s' % (key, '%3.2f%%' % percent), file=output)
-
-class Profile(object):
-    # This list controls the order of values in stacked bar data output
-    default_categories = [ 'interrupt',
-                           'driver',
-                           'stack',
-                           'buffer',
-                           'copy',
-                           'syscall',
-                           'user',
-                           'other',
-                           'idle']
-
-    def __init__(self, datatype, categorize=None):
-        categories = Profile.default_categories
-
-        self.datatype = datatype
-        self.categorize = categorize
-        self.data = {}
-        self.categories = categories[:]
-        self.rcategories = categories[:]
-        self.rcategories.reverse()
-        self.cpu = 0
-
-    # Read in files
-    def inputdir(self, directory):
-        import os, os.path, re
-        from os.path import expanduser, join as joinpath
-
-        directory = expanduser(directory)
-        label_ex = re.compile(r'profile\.(.*).dat')
-        for root,dirs,files in os.walk(directory):
-            for name in files:
-                match = label_ex.match(name)
-                if not match:
-                    continue
-
-                filename = joinpath(root, name)
-                prefix = os.path.commonprefix([root, directory])
-                dirname = root[len(prefix)+1:]
-                data = self.datatype(filename, self.categorize)
-                self.setdata(dirname, match.group(1), data)
-
-    def setdata(self, run, cpu, data):
-        if run not in self.data:
-            self.data[run] = {}
-
-        if cpu in self.data[run]:
-            raise AttributeError(
-                'data already stored for run %s and cpu %s' % (run, cpu))
-
-        self.data[run][cpu] = data
-
-    def getdata(self, run, cpu):
-        try:
-            return self.data[run][cpu]
-        except KeyError:
-            print(run, cpu)
-            return None
-
-    def alldata(self):
-        for run,cpus in self.data.items():
-            for cpu,data in cpus.items():
-                yield run,cpu,data
-
-    def get(self, job, stat, system=None):
-        if system is None and hasattr('system', job):
-            system = job.system
-
-        if system is None:
-            raise AttributeError('The job must have a system set')
-
-        cpu = '%s.run%d' % (system, self.cpu)
-
-        data = self.getdata(str(job), cpu)
-        if not data:
-            return None
-
-        values = []
-        for category in self.categories:
-            val = float(data.get(category, 0.0))
-            if val < 0.0:
-                raise ValueError('value is %f' % val)
-            values.append(val)
-        total = sum(values)
-        return [ v / total * 100.0 for v in values ]
-
-    def dump(self):
-        for run,cpu,data in self.alldata():
-            print('run %s, cpu %s' % (run, cpu))
-            data.dump()
-            print()
-
-    def write_dot(self, threshold, jobfile=None, jobs=None):
-        import pydot
-
-        if jobs is None:
-            jobs = [ job for job in jobfile.jobs() ]
-
-        for job in jobs:
-            cpu =  '%s.run%d' % (job.system, self.cpu)
-            symbols = self.getdata(job.name, cpu)
-            if not symbols:
-                continue
-
-            dot = pydot.Dot()
-            symbols.tree.dot(dot, threshold=threshold)
-            dot.write(symbols.filename[:-3] + 'dot')
-
-    def write_txt(self, jobfile=None, jobs=None, limit=None):
-        if jobs is None:
-            jobs = [ job for job in jobfile.jobs() ]
-
-        for job in jobs:
-            cpu =  '%s.run%d' % (job.system, self.cpu)
-            symbols = self.getdata(job.name, cpu)
-            if not symbols:
-                continue
-
-            output = file(symbols.filename[:-3] + 'txt', 'w')
-            symbols.display(output, limit)
-
-    def display(self, jobfile=None, jobs=None, limit=None):
-        if jobs is None:
-            jobs = [ job for job in jobfile.jobs() ]
-
-        maxsymlen = 0
-
-        thejobs = []
-        for job in jobs:
-            cpu =  '%s.run%d' % (job.system, self.cpu)
-            symbols = self.getdata(job.name, cpu)
-            if symbols:
-                thejobs.append(job)
-                maxsymlen = max(maxsymlen, symbols.maxsymlen)
-
-        for job in thejobs:
-            cpu =  '%s.run%d' % (job.system, self.cpu)
-            symbols = self.getdata(job.name, cpu)
-            print(job.name)
-            symbols.display(limit=limit, maxsymlen=maxsymlen)
-            print()
-
-
-from .categories import func_categorize, pc_categorize
-class PCProfile(Profile):
-    def __init__(self, categorize=pc_categorize):
-        super(PCProfile, self).__init__(PCData, categorize)
-
-
-class FuncProfile(Profile):
-    def __init__(self, categorize=func_categorize):
-        super(FuncProfile, self).__init__(FuncData, categorize)
-
-def usage(exitcode = None):
-    print('''\
-Usage: %s [-bc] [-g <dir>] [-j <jobfile>] [-n <num>]
-
-    -c           groups symbols into categories
-    -b           dumps data for bar charts
-    -d           generate dot output
-    -g <d>       draw graphs and send output to <d>
-    -j <jobfile> specify a different jobfile (default is Test.py)
-    -n <n>       selects number of top symbols to print (default 5)
-''' % sys.argv[0])
-
-    if exitcode is not None:
-        sys.exit(exitcode)
-
-if __name__ == '__main__':
-    import getopt, re, sys
-    from os.path import expanduser
-    from .output import StatOutput
-
-    # default option values
-    numsyms = 10
-    graph = None
-    cpus = [ 0 ]
-    categorize = False
-    showidle = True
-    funcdata = True
-    jobfilename = 'Test.py'
-    dodot = False
-    dotfile = None
-    textout = False
-    threshold = 0.01
-    inputfile = None
-
-    try:
-        opts, args = getopt.getopt(sys.argv[1:], 'C:cdD:f:g:ij:n:pT:t')
-    except getopt.GetoptError:
-        usage(2)
-
-    for o,a in opts:
-        if o == '-C':
-            cpus = [ int(x) for x in a.split(',') ]
-        elif o == '-c':
-            categorize = True
-        elif o == '-D':
-            dotfile = a
-        elif o == '-d':
-            dodot = True
-        elif o == '-f':
-            inputfile = expanduser(a)
-        elif o == '-g':
-            graph = a
-        elif o == '-i':
-            showidle = False
-        elif o == '-j':
-            jobfilename = a
-        elif o == '-n':
-            numsyms = int(a)
-        elif o == '-p':
-            funcdata = False
-        elif o == '-T':
-            threshold = float(a)
-        elif o == '-t':
-            textout = True
-
-    if args:
-        print("'%s'" % args, len(args))
-        usage(1)
-
-    if inputfile:
-        catfunc = None
-        if categorize:
-            catfunc = func_categorize
-        data = FuncData(inputfile, categorize=catfunc)
-
-        if dodot:
-            import pydot
-            dot = pydot.Dot()
-            data.tree.dot(dot, threshold=threshold)
-            #dot.orientation = 'landscape'
-            #dot.ranksep='equally'
-            #dot.rank='samerank'
-            dot.write(dotfile, format='png')
-        else:
-            data.display(limit=numsyms)
-
-    else:
-        from jobfile import JobFile
-        jobfile = JobFile(jobfilename)
-
-        if funcdata:
-            profile = FuncProfile()
-        else:
-            profile = PCProfile()
-
-        if not categorize:
-            profile.categorize = None
-        profile.inputdir(jobfile.rootdir)
-
-        if graph:
-            for cpu in cpus:
-                profile.cpu = cpu
-                if funcdata:
-                    name = 'funcstacks%d' % cpu
-                else:
-                    name = 'stacks%d' % cpu
-                output = StatOutput(jobfile, info=profile)
-                output.xlabel = 'System Configuration'
-                output.ylabel = '% CPU utilization'
-                output.stat = name
-                output.graph(name, graph)
-
-        if dodot:
-            for cpu in cpus:
-                profile.cpu = cpu
-                profile.write_dot(jobfile=jobfile, threshold=threshold)
-
-        if textout:
-            for cpu in cpus:
-                profile.cpu = cpu
-                profile.write_txt(jobfile=jobfile)
-
-        if not graph and not textout and not dodot:
-            for cpu in cpus:
-                if not categorize:
-                    profile.categorize = None
-                profile.cpu = cpu
-                profile.display(jobfile=jobfile, limit=numsyms)
diff --git a/util/stats/stats.py b/util/stats/stats.py
deleted file mode 100755
index 00e5d1e..0000000
--- a/util/stats/stats.py
+++ /dev/null
@@ -1,484 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (c) 2003-2004 The Regents of The University of Michigan
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import re, sys, math
-
-def usage():
-    print('''\
-Usage: %s [-E] [-F] [ -G <get> ] [-d <db> ] [-g <graphdir> ] [-h <host>] [-p]
-       [-s <system>] [-r <runs> ] [-T <samples>] [-u <username>]
-       <command> [command args]
-
-       commands    extra parameters   description
-       ----------- ------------------ ---------------------------------------
-       formula     <formula>          Evaluated formula specified
-       formulas    [regex]            List formulas (only matching regex)
-       runs        none               List all runs in database
-       samples     none               List samples present in database
-       stability   <pairnum> <stats>  Calculated statistical info about stats
-       stat        <regex>            Show stat data (only matching regex)
-       stats       [regex]            List all stats (only matching regex)
-
-       database    <command>          Where command is drop, init, or clean
-
-''' % sys.argv[0])
-    sys.exit(1)
-
-def getopts(list, flags):
-    import getopt
-    try:
-        opts, args = getopt.getopt(list, flags)
-    except getopt.GetoptError:
-        usage()
-
-    return opts, args
-
-class CommandException(Exception):
-    pass
-
-def commands(options, command, args):
-    if command == 'database':
-        if len(args) == 0: raise CommandException
-
-        from . import dbinit
-        mydb = dbinit.MyDB(options)
-
-        if args[0] == 'drop':
-            if len(args) > 2: raise CommandException
-            mydb.admin()
-            mydb.drop()
-            if len(args) == 2 and args[1] == 'init':
-                mydb.create()
-                mydb.connect()
-                mydb.populate()
-            mydb.close()
-            return
-
-        if args[0] == 'init':
-            if len(args) > 1: raise CommandException
-            mydb.admin()
-            mydb.create()
-            mydb.connect()
-            mydb.populate()
-            mydb.close()
-            return
-
-        if args[0] == 'clean':
-            if len(args) > 1: raise CommandException
-            mydb.connect()
-            mydb.clean()
-            return
-
-        raise CommandException
-
-    from . import db
-    source = db.Database()
-    source.host = options.host
-    source.db = options.db
-    source.passwd = options.passwd
-    source.user = options.user
-    source.connect()
-    #source.update_dict(globals())
-
-    if type(options.method) is str:
-        source.method = options.method
-
-    if options.runs is None:
-        runs = source.allRuns
-    else:
-        rx = re.compile(options.runs)
-        runs = []
-        for run in source.allRuns:
-            if rx.match(run.name):
-                runs.append(run)
-
-    if command == 'runs':
-        user = None
-        opts, args = getopts(args, '-u')
-        if len(args):
-            raise CommandException
-        for o,a in opts:
-            if o == '-u':
-                user = a
-        source.listRuns(user)
-        return
-
-    if command == 'stats':
-        if len(args) == 0:
-            source.listStats()
-        elif len(args) == 1:
-            source.listStats(args[0])
-        else:
-            raise CommandException
-
-        return
-
-    if command == 'formulas':
-        if len(args) == 0:
-            source.listFormulas()
-        elif len(args) == 1:
-            source.listFormulas(args[0])
-        else:
-            raise CommandException
-
-        return
-
-    if command == 'samples':
-        if len(args):
-            raise CommandException
-
-        source.listTicks(runs)
-        return
-
-    if command == 'stability':
-        if len(args) < 2:
-            raise CommandException
-
-        try:
-            merge = int(args[0])
-        except ValueError:
-            usage()
-        stats = source.getStat(args[1])
-        source.method = 'sum'
-
-        def disp(*args):
-            print("%-35s %12s %12s %4s %5s %5s %5s %10s" % args)
-
-        # temporary variable containing a bunch of dashes
-        d = '-' * 100
-
-        #loop through all the stats selected
-        for stat in stats:
-            print("%s:" % stat.name)
-            disp("run name", "average", "stdev", ">10%", ">1SDV", ">2SDV",
-                 "SAMP", "CV")
-            disp(d[:35], d[:12], d[:12], d[:4], d[:5], d[:5], d[:5], d[:10])
-
-            #loop through all the selected runs
-            for run in runs:
-                runTicks = source.retTicks([ run ])
-                #throw away the first one, it's 0
-                runTicks.pop(0)
-                source.ticks = runTicks
-                avg = 0
-                stdev = 0
-                numoutsideavg  = 0
-                numoutside1std = 0
-                numoutside2std = 0
-                pairRunTicks = []
-                if value(stat, run.run) == 1e300*1e300:
-                    continue
-                for t in range(0, len(runTicks)-(merge-1), merge):
-                    tempPair = []
-                    for p in range(0,merge):
-                        tempPair.append(runTicks[t+p])
-                    pairRunTicks.append(tempPair)
-                #loop through all the various ticks for each run
-                for tick in pairRunTicks:
-                    source.ticks = tick
-                    avg += value(stat, run.run)
-                avg /= len(pairRunTicks)
-                for tick in pairRunTicks:
-                    source.ticks = tick
-                    val = value(stat, run.run)
-                    stdev += pow((val-avg),2)
-                stdev = math.sqrt(stdev / len(pairRunTicks))
-                for tick in pairRunTicks:
-                    source.ticks = tick
-                    val = value(stat, run.run)
-                    if (val < (avg * .9)) or (val > (avg * 1.1)):
-                        numoutsideavg += 1
-                    if (val < (avg - stdev)) or (val > (avg + stdev)):
-                        numoutside1std += 1
-                    if (val < (avg - (2*stdev))) or (val > (avg + (2*stdev))):
-                        numoutside2std += 1
-                if avg > 1000:
-                    disp(run.name, "%.1f" % avg, "%.1f" % stdev,
-                         "%d" % numoutsideavg, "%d" % numoutside1std,
-                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
-                         "%.3f" % (stdev/avg*100))
-                elif avg > 100:
-                    disp(run.name, "%.1f" % avg, "%.1f" % stdev,
-                         "%d" % numoutsideavg, "%d" % numoutside1std,
-                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
-                         "%.5f" % (stdev/avg*100))
-                else:
-                    disp(run.name, "%.5f" % avg, "%.5f" % stdev,
-                         "%d" % numoutsideavg, "%d" % numoutside1std,
-                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
-                         "%.7f" % (stdev/avg*100))
-        return
-
-    if command == 'all':
-        if len(args):
-            raise CommandException
-
-        all = [ 'bps', 'misses', 'mpkb', 'ipkb', 'pps', 'bpt' ]
-        for command in all:
-            commands(options, command, args)
-
-    if options.ticks:
-        if not options.graph:
-            print('only displaying sample %s' % options.ticks)
-        source.ticks = [ int(x) for x in options.ticks.split() ]
-
-    from .output import StatOutput
-    output = StatOutput(options.jobfile, source)
-    output.xlabel = 'System Configuration'
-    output.colormap = 'RdYlGn'
-
-    if command == 'stat' or command == 'formula':
-        if len(args) != 1:
-            raise CommandException
-
-        if command == 'stat':
-            stats = source.getStat(args[0])
-        if command == 'formula':
-            stats = eval(args[0])
-
-        for stat in stats:
-            output.stat = stat
-            output.ylabel = stat.name
-            if options.graph:
-                output.graph(stat.name, options.graphdir)
-            else:
-                output.display(stat.name, options.printmode)
-
-        return
-
-    if len(args):
-        raise CommandException
-
-    from .info import ProxyGroup
-    proxy = ProxyGroup(system = source[options.system])
-    system = proxy.system
-
-    etherdev = system.tsunami.etherdev0
-    bytes = etherdev.rxBytes + etherdev.txBytes
-    kbytes = bytes / 1024
-    packets = etherdev.rxPackets + etherdev.txPackets
-
-    def display():
-        if options.graph:
-            output.graph(command, options.graphdir, proxy)
-        else:
-            output.display(command, options.printmode)
-
-    if command == 'ticks':
-        output.stat = system.run0.numCycles
-
-        display()
-        return
-
-    if command == 'bytes':
-        output.stat = bytes
-        display()
-        return
-
-    if command == 'packets':
-        output.stat = packets
-        display()
-        return
-
-    if command == 'ppt' or command == 'tpp':
-        output.stat = packets / system.run0.numCycles
-        output.invert = command == 'tpp'
-        display()
-        return
-
-    if command == 'pps':
-        output.stat = packets / source['sim_seconds']
-        output.ylabel = 'Packets/s'
-        display()
-        return
-
-    if command == 'bpt' or command == 'tpb':
-        output.stat = bytes / system.run0.numCycles * 8
-        output.ylabel = 'bps / Hz'
-        output.invert = command == 'tpb'
-        display()
-        return
-
-    if command in ('rxbps', 'txbps', 'bps'):
-        if command == 'rxbps':
-            output.stat = etherdev.rxBandwidth / 1e9
-        if command == 'txbps':
-            output.stat = etherdev.txBandwidth / 1e9
-        if command == 'bps':
-            output.stat = (etherdev.rxBandwidth + etherdev.txBandwidth) / 1e9
-
-        output.ylabel = 'Bandwidth (Gbps)'
-        output.ylim = [ 0.0, 10.0 ]
-        display()
-        return
-
-    if command == 'bpp':
-        output.stat = bytes / packets
-        output.ylabel = 'Bytes / Packet'
-        display()
-        return
-
-    if command == 'rxbpp':
-        output.stat = etherdev.rxBytes / etherdev.rxPackets
-        output.ylabel = 'Receive Bytes / Packet'
-        display()
-        return
-
-    if command == 'txbpp':
-        output.stat = etherdev.txBytes / etherdev.txPackets
-        output.ylabel = 'Transmit Bytes / Packet'
-        display()
-        return
-
-    if command == 'rtp':
-        output.stat = etherdev.rxPackets / etherdev.txPackets
-        output.ylabel = 'rxPackets / txPackets'
-        display()
-        return
-
-    if command == 'rtb':
-        output.stat = etherdev.rxBytes / etherdev.txBytes
-        output.ylabel = 'rxBytes / txBytes'
-        display()
-        return
-
-    misses = system.l2.overall_mshr_misses
-
-    if command == 'misses':
-        output.stat = misses
-        output.ylabel = 'Overall MSHR Misses'
-        display()
-        return
-
-    if command == 'mpkb':
-        output.stat = misses / (bytes / 1024)
-        output.ylabel = 'Misses / KB'
-        display()
-        return
-
-    if command == 'ipkb':
-        interrupts = system.run0.kern.faults[4]
-        output.stat = interrupts / kbytes
-        output.ylabel = 'Interrupts / KB'
-        display()
-        return
-
-    if command == 'execute':
-        output.stat = system.run0.ISSUE__count
-        display()
-        return
-
-    if command == 'commit':
-        output.stat = system.run0.COM__count
-        display()
-        return
-
-    if command == 'fetch':
-        output.stat = system.run0.FETCH__count
-        display()
-        return
-
-    raise CommandException
-
-
-class Options: pass
-
-if __name__ == '__main__':
-    import getpass
-
-    options = Options()
-    options.host = None
-    options.db = None
-    options.passwd = ''
-    options.user = getpass.getuser()
-    options.runs = None
-    options.system = 'client'
-    options.method = None
-    options.graph = False
-    options.ticks = False
-    options.printmode = 'G'
-    jobfilename = None
-    options.jobfile = None
-    options.all = False
-
-    opts, args = getopts(sys.argv[1:], '-EFJad:g:h:j:m:pr:s:u:T:')
-    for o,a in opts:
-        if o == '-E':
-            options.printmode = 'E'
-        if o == '-F':
-            options.printmode = 'F'
-        if o == '-a':
-            options.all = True
-        if o == '-d':
-            options.db = a
-        if o == '-g':
-            options.graph = True;
-            options.graphdir = a
-        if o == '-h':
-            options.host = a
-        if o == '-J':
-            jobfilename = None
-        if o == '-j':
-            jobfilename = a
-        if o == '-m':
-            options.method = a
-        if o == '-p':
-            options.passwd = getpass.getpass()
-        if o == '-r':
-            options.runs = a
-        if o == '-u':
-            options.user = a
-        if o == '-s':
-            options.system = a
-        if o == '-T':
-            options.ticks = a
-
-    if jobfilename:
-        from jobfile import JobFile
-        options.jobfile = JobFile(jobfilename)
-        if not options.host:
-            options.host = options.jobfile.dbhost
-        if not options.db:
-            options.db = options.jobfile.statdb
-
-    if not options.host:
-        sys.exit('Database server must be provided from a jobfile or -h')
-
-    if not options.db:
-        sys.exit('Database name must be provided from a jobfile or -d')
-
-    if len(args) == 0:
-        usage()
-
-    command = args[0]
-    args = args[1:]
-
-    try:
-        commands(options, command, args)
-    except CommandException:
-        usage()
diff --git a/util/systemc/gem5_within_systemc/Makefile b/util/systemc/gem5_within_systemc/Makefile
index 8c103c6..cc6a389 100644
--- a/util/systemc/gem5_within_systemc/Makefile
+++ b/util/systemc/gem5_within_systemc/Makefile
@@ -36,14 +36,14 @@
 ARCH = ARM
 VARIANT = opt
 
-SYSTEMC_INC = ./systemc/include
-SYSTEMC_LIB = ./systemc/lib-linux64
+SYSTEMC_INC = /opt/systemc/include
+SYSTEMC_LIB = /opt/systemc/lib-linux64
 
 CXXFLAGS = -I../../../build/$(ARCH) -L../../../build/$(ARCH)
 CXXFLAGS += -I$(SYSTEMC_INC) -L$(SYSTEMC_LIB)
-CXXFLAGS += -std=c++0x
-CXXFLAGS += -g
-LIBS = -lgem5_$(VARIANT) -lsystemc
+CXXFLAGS += -std=c++17
+CXXFLAGS += -g -DTRACING_ON
+LIBS = -lgem5_$(VARIANT) -lsystemc -lpng
 
 ALL = gem5.$(VARIANT).sc
 
diff --git a/util/systemc/gem5_within_systemc/README b/util/systemc/gem5_within_systemc/README
index 6bfb055..b50ed71 100644
--- a/util/systemc/gem5_within_systemc/README
+++ b/util/systemc/gem5_within_systemc/README
@@ -32,12 +32,12 @@
 
 > cd ../../..
 > scons build/ARM/gem5.opt
-> scons --with-cxx-config --without-python USE_SYSTEMC=0 \
+> scons --with-cxx-config --without-python --without-tcmalloc USE_SYSTEMC=0 \
 >       build/ARM/libgem5_opt.so
 > cd util/systemc
 
 Note: For MAC / OSX this command should be used:
-> scons --with-cxx-config --without-python USE_SYSTEMC=0 \
+> scons --with-cxx-config --without-python --without-tcmalloc USE_SYSTEMC=0 \
 >       build/ARM/libgem5_opt.dylib
 
 Set a proper LD_LIBRARY_PATH e.g. for bash:
diff --git a/util/systemc/gem5_within_systemc/main.cc b/util/systemc/gem5_within_systemc/main.cc
index e9e2dd5..c7f9dd6 100644
--- a/util/systemc/gem5_within_systemc/main.cc
+++ b/util/systemc/gem5_within_systemc/main.cc
@@ -64,8 +64,8 @@
 #include "sc_module.hh"
 #include "sim/cxx_config_ini.hh"
 #include "sim/cxx_manager.hh"
+#include "sim/globals.hh"
 #include "sim/init_signals.hh"
-#include "sim/serialize.hh"
 #include "sim/simulate.hh"
 #include "sim/stat_control.hh"
 #include "sim/system.hh"
@@ -156,8 +156,6 @@
     if (argc == 1)
         usage(prog_name);
 
-    cxxConfigInit();
-
     /* Pass DPRINTF messages to SystemC */
     Trace::setDebugLogger(&logger);
 
@@ -296,8 +294,9 @@
         if (checkpoint_restore) {
             std::cerr << "Restoring checkpoint\n";
 
-            CheckpointIn *checkpoint = new CheckpointIn(checkpoint_dir,
-                config_manager->getSimObjectResolver());
+            SimObject::setSimObjectResolver(
+                &config_manager->getSimObjectResolver());
+            CheckpointIn *checkpoint = new CheckpointIn(checkpoint_dir);
 
             /* Catch SystemC up with gem5 after checkpoint restore.
              *  Note that gem5 leading SystemC is always a violation of the
@@ -305,7 +304,6 @@
              *  catchup */
 
             DrainManager::instance().preCheckpointRestore();
-            Serializable::unserializeGlobals(*checkpoint);
 
             Tick systemc_time = sc_core::sc_time_stamp().value();
             if (curTick() > systemc_time) {
@@ -353,7 +351,7 @@
         /* FIXME, this should really be serialising just for
          *  config_manager rather than using serializeAll's ugly
          *  SimObject static object list */
-        Serializable::serializeAll(checkpoint_dir);
+        SimObject::serializeAll(checkpoint_dir);
 
         std::cerr << "Completed checkpoint\n";
 
diff --git a/util/systemc/gem5_within_systemc/sc_gem5_control.cc b/util/systemc/gem5_within_systemc/sc_gem5_control.cc
index 18aec6a..568c0eb 100644
--- a/util/systemc/gem5_within_systemc/sc_gem5_control.cc
+++ b/util/systemc/gem5_within_systemc/sc_gem5_control.cc
@@ -214,8 +214,6 @@
 {
     SC_THREAD(run);
 
-    gem5::cxxConfigInit();
-
     /* Pass DPRINTF messages to SystemC */
     gem5::Trace::setDebugLogger(&logger);
 
@@ -237,12 +235,12 @@
 
     /* Enable stats */
     gem5::statistics::initSimStats();
-    gem5::statistics::registerHandlers(gem5::CxxConfig::statsReset,
-        gem5::CxxConfig::statsDump);
+    gem5::statistics::registerHandlers(CxxConfig::statsReset,
+        CxxConfig::statsDump);
 
     gem5::Trace::enable();
 
-    config_file = new CxxIniFile();
+    config_file = new gem5::CxxIniFile();
 
     if (!config_file->load(config_filename)) {
         fatal("Gem5TopLevelModule: Can't open config file: %s",
@@ -251,7 +249,7 @@
 
     root_manager = new gem5::CxxConfigManager(*config_file);
 
-    gem5::CxxConfig::statsEnable();
+    CxxConfig::statsEnable();
 
     /* Make the root object */
     try {
@@ -285,7 +283,7 @@
     std::cerr << "Exit at tick " << gem5::curTick()
         << ", cause: " << exit_event->getCause() << '\n';
 
-    getEventQueue(0)->dump();
+    gem5::getEventQueue(0)->dump();
 }
 
 void
diff --git a/util/systemc/gem5_within_systemc/sc_logger.cc b/util/systemc/gem5_within_systemc/sc_logger.cc
index e184b16..c833cc5 100644
--- a/util/systemc/gem5_within_systemc/sc_logger.cc
+++ b/util/systemc/gem5_within_systemc/sc_logger.cc
@@ -77,7 +77,7 @@
 
 void CuttingStreambuf::outputLine()
 {
-    logger->logMessage((Tick)-1, "gem5", "", line.str());
+    logger->logMessage((gem5::Tick)-1, "gem5", "", line.str());
     line.clear();
     line.str("");
 }
diff --git a/util/systemc/gem5_within_systemc/sc_module.cc b/util/systemc/gem5_within_systemc/sc_module.cc
index ae27b51..9d70952 100644
--- a/util/systemc/gem5_within_systemc/sc_module.cc
+++ b/util/systemc/gem5_within_systemc/sc_module.cc
@@ -63,6 +63,7 @@
 #include "sim/core.hh"
 #include "sim/cur_tick.hh"
 #include "sim/eventq.hh"
+#include "sim/sim_events.hh"
 #include "sim/sim_exit.hh"
 #include "sim/stat_control.hh"
 
@@ -101,19 +102,19 @@
 void
 Module::setupEventQueues(Module &module)
 {
-    fatal_if(mainEventQueue.size() != 0,
+    fatal_if(gem5::mainEventQueue.size() != 0,
         "Gem5SystemC::Module::setupEventQueues must be called"
         " before any gem5 event queues are set up");
 
     gem5::numMainEventQueues = 1;
     gem5::mainEventQueue.push_back(new SCEventQueue("events", module));
-    gem5::curEventQueue(getEventQueue(0));
+    gem5::curEventQueue(gem5::getEventQueue(0));
 }
 
 void
 Module::catchup()
 {
-    gem5::EventQueue *eventq = getEventQueue(0);
+    gem5::EventQueue *eventq = gem5::getEventQueue(0);
     gem5::Tick systemc_time = sc_core::sc_time_stamp().value();
     gem5::Tick gem5_time = gem5::curTick();
 
@@ -144,7 +145,7 @@
     gem5::EventQueue *eventq = gem5::getEventQueue(0);
     std::lock_guard<gem5::EventQueue> lock(*eventq);
 
-    assert(async_event);
+    assert(gem5::async_event);
 
     /* Catch up gem5 time with SystemC time so that any event here won't
      * be in the past relative to the current time */
@@ -153,36 +154,37 @@
     /* Move time on to match SystemC */
     catchup();
 
-    async_event = false;
-    if (async_statdump || async_statreset) {
-        statistics::schedStatEvent(async_statdump, async_statreset);
-        async_statdump = false;
-        async_statreset = false;
+    gem5::async_event = false;
+    if (gem5::async_statdump || gem5::async_statreset) {
+        gem5::statistics::schedStatEvent(gem5::async_statdump,
+                                         gem5::async_statreset);
+        gem5::async_statdump = false;
+        gem5::async_statreset = false;
     }
 
-    if (async_exit) {
-        async_exit = false;
+    if (gem5::async_exit) {
+        gem5::async_exit = false;
         gem5::exitSimLoop("user interrupt received");
     }
 
-    if (async_io) {
-        async_io = false;
-        pollQueue.service();
+    if (gem5::async_io) {
+        gem5::async_io = false;
+        gem5::pollQueue.service();
     }
 
-    if (async_exception)
+    if (gem5::async_exception)
         fatal("received async_exception, shouldn't be possible");
 }
 
 void
 Module::serviceExternalEvent()
 {
-    gem5::EventQueue *eventq = getEventQueue(0);
+    gem5::EventQueue *eventq = gem5::getEventQueue(0);
 
-    if (!in_simulate && !async_event)
+    if (!in_simulate && !gem5::async_event)
         warn("Gem5SystemC external event received while not in simulate");
 
-    if (async_event)
+    if (gem5::async_event)
         serviceAsyncEvent();
 
     if (in_simulate && !eventq->empty())
@@ -192,12 +194,12 @@
 void
 Module::eventLoop()
 {
-    gem5::EventQueue *eventq = getEventQueue(0);
+    gem5::EventQueue *eventq = gem5::getEventQueue(0);
 
     fatal_if(!in_simulate, "Gem5SystemC event loop entered while"
         " outside Gem5SystemC::Module::simulate");
 
-    if (async_event)
+    if (gem5::async_event)
         serviceAsyncEvent();
 
     while (!eventq->empty()) {
@@ -248,8 +250,8 @@
     fatal("Ran out of events without seeing exit event");
 }
 
-GlobalSimLoopExitEvent *
-Module::simulate(Tick num_cycles)
+gem5::GlobalSimLoopExitEvent *
+Module::simulate(gem5::Tick num_cycles)
 {
     inform("Entering event queue @ %d.  Starting simulation...",
         gem5::curTick());
@@ -259,8 +261,9 @@
     else /* counter would roll over or be set to MaxTick anyhow */
         num_cycles = gem5::MaxTick;
 
-    gem5::GlobalEvent *limit_event = new GlobalSimLoopExitEvent(num_cycles,
-        "simulate() limit reached", 0, 0);
+    gem5::GlobalEvent *limit_event =
+        new gem5::GlobalSimLoopExitEvent(num_cycles,
+            "simulate() limit reached", 0, 0);
 
     exitEvent = NULL;
 
@@ -286,7 +289,7 @@
     assert(global_event != NULL);
 
     gem5::GlobalSimLoopExitEvent *global_exit_event =
-        dynamic_cast<GlobalSimLoopExitEvent *>(global_event);
+        dynamic_cast<gem5::GlobalSimLoopExitEvent *>(global_event);
     assert(global_exit_event != NULL);
 
     if (global_exit_event != limit_event) {
diff --git a/util/systemc/systemc_within_gem5/README b/util/systemc/systemc_within_gem5/README
index 6c743b9..af10fc4 100644
--- a/util/systemc/systemc_within_gem5/README
+++ b/util/systemc/systemc_within_gem5/README
@@ -26,6 +26,7 @@
 
 systemc_sc_main - Run code based on an sc_main function.
 systemc_simple_object - Build systemc objects into a gem5 object hierarchy.
+systemc_tlm - Simple LT-Based TLM system
 
 
 Note that these directories all have a systemc_ prefix so that when EXTRAS
diff --git a/src/cpu/o3/SConsopts b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/SConscript
similarity index 90%
rename from src/cpu/o3/SConsopts
rename to util/systemc/systemc_within_gem5/systemc_gem5_tlm/SConscript
index 3479484..8c7b257 100644
--- a/src/cpu/o3/SConsopts
+++ b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/SConscript
@@ -1,7 +1,4 @@
-# -*- mode:python -*-
-
-# Copyright (c) 2006 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2018 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -28,4 +25,5 @@
 
 Import('*')
 
-main.Append(ALL_CPU_MODELS=['O3CPU'])
+SimObject('SystemC_Example.py', sim_objects=["TLM_Target"])
+Source('sc_tlm_target.cc')
diff --git a/src/cpu/o3/O3Checker.py b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/SystemC_Example.py
similarity index 71%
copy from src/cpu/o3/O3Checker.py
copy to util/systemc/systemc_within_gem5/systemc_gem5_tlm/SystemC_Example.py
index c343cd6..0f1c9ed 100644
--- a/src/cpu/o3/O3Checker.py
+++ b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/SystemC_Example.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2007 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2018 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -25,9 +24,18 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from m5.params import *
-from m5.objects.CheckerCPU import CheckerCPU
+from m5.SimObject import SimObject
+from m5.params import *
+from m5.proxy import *
 
-class O3Checker(CheckerCPU):
-    type = 'O3Checker'
-    cxx_class = 'gem5::o3::Checker'
-    cxx_header = 'cpu/o3/checker.hh'
+from m5.objects.SystemC import SystemC_ScModule
+from m5.objects.Tlm import TlmTargetSocket
+
+# This class is a subclass of sc_module, and all the special magic which makes
+# that work is handled in the base classes.
+class TLM_Target(SystemC_ScModule):
+    type = 'TLM_Target'
+    cxx_class = 'Target'
+    cxx_header = 'systemc_gem5_tlm/sc_tlm_target.hh'
+    tlm = TlmTargetSocket(32, 'TLM target socket')
+    system = Param.System(Parent.any, "system")
diff --git a/util/systemc/systemc_within_gem5/systemc_gem5_tlm/config.py b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/config.py
new file mode 100755
index 0000000..4d6e260
--- /dev/null
+++ b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/config.py
@@ -0,0 +1,77 @@
+# Copyright (c) 2022, Fraunhofer IESE
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+#    this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+import os
+import m5
+
+from m5.objects import *
+
+# Create a config to be used by the traffic generator
+cfg_file_name = "memcheck.cfg"
+cfg_file_path = os.path.dirname(__file__) + "/" +cfg_file_name
+cfg_file = open(cfg_file_path, 'w')
+
+# Three states, with random, linear and idle behaviours. The random
+# and linear states access memory in the range [0 : 16 Mbyte] with 8
+# byte and 64 byte accesses respectively.
+cfg_file.write("STATE 0 10000000 RANDOM 65 0 16777216 8 50000 150000 0\n")
+cfg_file.write("STATE 1 10000000 LINEAR 65 0 16777216 64 50000 150000 0\n")
+cfg_file.write("STATE 2 10000000 IDLE\n")
+cfg_file.write("INIT 0\n")
+cfg_file.write("TRANSITION 0 1 0.5\n")
+cfg_file.write("TRANSITION 0 2 0.5\n")
+cfg_file.write("TRANSITION 1 0 0.5\n")
+cfg_file.write("TRANSITION 1 2 0.5\n")
+cfg_file.write("TRANSITION 2 0 0.5\n")
+cfg_file.write("TRANSITION 2 1 0.5\n")
+cfg_file.close()
+
+system = System()
+vd = VoltageDomain(voltage = '1V')
+
+system.mem_mode = 'timing'
+
+system.cpu = TrafficGen(config_file = cfg_file_path)
+system.target = TLM_Target()
+system.physmem = SimpleMemory() # This must be instanciated, even if not needed
+#system.mem.addr_ranges = [AddrRange('512MB')]
+system.transactor = Gem5ToTlmBridge32()
+system.clk_domain = SrcClockDomain(clock = '1.5GHz', voltage_domain = vd)
+
+# Connect everything:
+system.transactor.gem5 = system.cpu.port
+system.transactor.tlm = system.target.tlm
+
+kernel = SystemC_Kernel(system=system)
+root = Root(full_system=False, systemc_kernel=kernel)
+
+m5.instantiate(None)
+
+cause = m5.simulate(m5.MaxTick).getCause()
+print(cause)
diff --git a/util/systemc/systemc_within_gem5/systemc_gem5_tlm/sc_tlm_target.cc b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/sc_tlm_target.cc
new file mode 100644
index 0000000..482eeb0
--- /dev/null
+++ b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/sc_tlm_target.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2022 Fraunhofer IESE
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <tlm_utils/simple_target_socket.h>
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <queue>
+#include <vector>
+
+#include "base/trace.hh"
+#include "params/TLM_Target.hh"
+#include "sc_tlm_target.hh"
+
+#include "systemc/ext/systemc"
+#include "systemc/ext/tlm"
+
+using namespace std;
+using namespace sc_core;
+using namespace gem5;
+
+void Target::b_transport(tlm::tlm_generic_payload& trans,
+                            sc_time& delay)
+{
+    executeTransaction(trans);
+}
+
+void Target::executeTransaction(tlm::tlm_generic_payload& trans)
+{
+    tlm::tlm_command cmd = trans.get_command();
+    sc_dt::uint64    adr = trans.get_address();
+    unsigned char*   ptr = trans.get_data_ptr();
+    unsigned int     len = trans.get_data_length();
+    unsigned char*   byt = trans.get_byte_enable_ptr();
+
+    if (trans.get_address() >= 16*1024*1024) {
+        cout << "\033[1;31m("
+             << "Address Error"
+             << "\033[0m" << endl;
+        trans.set_response_status( tlm::TLM_ADDRESS_ERROR_RESPONSE );
+        return;
+    }
+    if (byt != 0) {
+        cout << "\033[1;31m("
+             << "Byte Enable Error"
+             << "\033[0m" << endl;
+        trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE );
+        return;
+    }
+    if (len < 1 || len > 64) {
+        cout << "\033[1;31m("
+             << "Burst Error"
+             << "\033[0m" << endl;
+        trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
+        return;
+    }
+
+    if (cmd == tlm::TLM_READ_COMMAND)
+    {
+        memcpy(mem+trans.get_address(), // destination
+                trans.get_data_ptr(),      // source
+                trans.get_data_length());  // size
+    }
+    else if (cmd == tlm::TLM_WRITE_COMMAND)
+    {
+        memcpy(trans.get_data_ptr(),      // destination
+                mem + trans.get_address(), // source
+                trans.get_data_length());  // size
+    }
+
+    cout << "\033[1;32m("
+            << name()
+            << ")@"  << setfill(' ') << setw(12) << sc_time_stamp()
+            << ": " << setw(12) << (cmd ? "Exec. Write " : "Exec. Read ")
+            << "Addr = " << setfill('0') << setw(8) << hex << adr
+            << " Data = " << "0x" << setfill('0') << setw(8) << hex
+            << *reinterpret_cast<int*>(ptr)
+            << "\033[0m" << endl;
+
+    trans.set_response_status( tlm::TLM_OK_RESPONSE );
+}
+
+// This "create" method bridges the python configuration and the systemc
+// objects. It instantiates the Printer object and sets it up using the
+// parameter values from the config, just like it would for a SimObject. The
+// systemc object could accept those parameters however it likes, for instance
+// through its constructor or by assigning them to a member variable.
+Target *
+gem5::TLM_TargetParams::create() const
+{
+    Target *target = new Target(name.c_str());
+    return target;
+}
+
+gem5::Port
+&Target::gem5_getPort(const std::string &if_name, int idx)
+{
+    return wrapper;
+}
diff --git a/util/systemc/systemc_within_gem5/systemc_gem5_tlm/sc_tlm_target.hh b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/sc_tlm_target.hh
new file mode 100644
index 0000000..f9a5d28
--- /dev/null
+++ b/util/systemc/systemc_within_gem5/systemc_gem5_tlm/sc_tlm_target.hh
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2022 Fraunhofer IESE
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __SYSTEC_TLM_GEM5_EXAMPLE__
+#define __SYSTEC_TLM_GEM5_EXAMPLE__
+
+#include <tlm_utils/simple_target_socket.h>
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <queue>
+#include <vector>
+
+#include "base/trace.hh"
+#include "systemc/ext/core/sc_module_name.hh"
+#include "systemc/tlm_port_wrapper.hh"
+
+#include "systemc/ext/systemc"
+#include "systemc/ext/tlm"
+
+using namespace std;
+using namespace sc_core;
+using namespace gem5;
+
+SC_MODULE(Target)
+{
+  public:
+    tlm_utils::simple_target_socket<Target> tSocket;
+    sc_gem5::TlmTargetWrapper<32> wrapper;
+
+  private:
+    //unsigned char mem[512];
+    unsigned char *mem;
+
+  public:
+    SC_HAS_PROCESS(Target);
+    Target(sc_module_name name) :
+         sc_module(name),
+         tSocket("tSocket"),
+         wrapper(tSocket, std::string(name) + ".tlm", InvalidPortID)
+    {
+        tSocket.register_b_transport(this, &Target::b_transport);
+        mem = (unsigned char *)malloc(16*1024*1024);
+        std::cout << "TLM Target Online" << std::endl;
+    }
+
+    gem5::Port &gem5_getPort(const std::string &if_name, int idx=-1) override;
+
+    virtual void b_transport(tlm::tlm_generic_payload& trans,
+                             sc_time& delay);
+
+    void executeTransaction(tlm::tlm_generic_payload& trans);
+};
+
+#endif // __SYSTEC_TLM_GEM5_EXAMPLE__
diff --git a/util/systemc/systemc_within_gem5/systemc_simple_object/SConscript b/util/systemc/systemc_within_gem5/systemc_simple_object/SConscript
index bb4b9f2..511ed62 100644
--- a/util/systemc/systemc_within_gem5/systemc_simple_object/SConscript
+++ b/util/systemc/systemc_within_gem5/systemc_simple_object/SConscript
@@ -25,6 +25,6 @@
 
 Import('*')
 
-SimObject('SystemC_Example.py')
+SimObject('SystemC_Example.py', sim_objects=["SystemC_Printer", "Gem5_Feeder"])
 Source('printer.cc')
 Source('feeder.cc')
diff --git a/util/stats/__init__.py b/util/systemc/systemc_within_gem5/systemc_tlm/SConscript
similarity index 93%
copy from util/stats/__init__.py
copy to util/systemc/systemc_within_gem5/systemc_tlm/SConscript
index b3f54da..62b9b1d 100644
--- a/util/stats/__init__.py
+++ b/util/systemc/systemc_within_gem5/systemc_tlm/SConscript
@@ -1,5 +1,4 @@
-# Copyright (c) 2005 The Regents of The University of Michigan
-# All rights reserved.
+# Copyright 2018 Google, Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -24,3 +23,6 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+Import('*')
+
+Source('sc_tlm.cc')
diff --git a/util/systemc/systemc_within_gem5/systemc_tlm/config.py b/util/systemc/systemc_within_gem5/systemc_tlm/config.py
new file mode 100755
index 0000000..0d20e9e
--- /dev/null
+++ b/util/systemc/systemc_within_gem5/systemc_tlm/config.py
@@ -0,0 +1,67 @@
+# Copyright 2018 Google, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+import m5
+import sys
+
+from m5.objects import SystemC_Kernel, Root
+
+# pylint:disable=unused-variable
+
+# The python version of the systemc kernel acts as an interface to sc_main. The
+# c++ version of the kernel object has a lot of important jobs supporting
+# systemc objects and needs to exist in simulations using systemc.
+kernel = SystemC_Kernel()
+root = Root(full_system=True, systemc_kernel=kernel)
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--word', action="append", default=[],
+        help='Add a word to the list of words to print. Can be repeated.')
+
+args = parser.parse_args()
+
+# Tell gem5 to run the c++ sc_main function. If one isn't defined, gem5 will
+# detect that and report an error. If gem5 isn't finding your sc_main, make
+# sure its signature matches exactly so your compiler doesn't think it's a
+# different function.
+#
+# The arguements passed to this function will be treated as the argv values
+# passed to the c++ sc_main, with the argc value set appropriately.
+m5.systemc.sc_main(*args.word);
+
+# Construct the SimObject hierarchy. Anything sc_main built has already been
+# constructed.
+m5.instantiate(None)
+
+# Run the simulation until something kicks us back to the config file. sc_main
+# will be at the point it first called sc_start and may keep executing as the
+# simulation runs, or it may be completed if it never called sc_start.
+cause = m5.simulate(m5.MaxTick).getCause()
+
+# If sc_main finished, extract what it returned and do something with it.
+result = m5.systemc.sc_main_result()
+if result.code != 0:
+    sys.exit(int(result.code))
diff --git a/util/systemc/systemc_within_gem5/systemc_tlm/sc_tlm.cc b/util/systemc/systemc_within_gem5/systemc_tlm/sc_tlm.cc
new file mode 100644
index 0000000..472e870
--- /dev/null
+++ b/util/systemc/systemc_within_gem5/systemc_tlm/sc_tlm.cc
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2022 Fraunhofer IESE
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <tlm_utils/simple_initiator_socket.h>
+#include <tlm_utils/simple_target_socket.h>
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <queue>
+#include <vector>
+
+#include "base/trace.hh"
+
+#include "systemc/ext/systemc"
+#include "systemc/ext/tlm"
+#define N 1024
+
+using namespace std;
+using namespace sc_core;
+using namespace gem5;
+
+SC_MODULE(Initiator)
+{
+    public:
+    tlm_utils::simple_initiator_socket<Initiator> iSocket;
+
+    protected:
+    int data[16];
+
+    public:
+    SC_CTOR(Initiator): iSocket("iSocket")
+    {
+        SC_THREAD(process);
+
+        for (int i=0; i<16; i++) {
+            data[i] = 0;
+        }
+    }
+
+    protected:
+    void process()
+    {
+        sc_time delay;
+
+        for (int i = 0; i < N; i++)
+        {
+            tlm::tlm_generic_payload trans;
+            data[i % 16] = i;
+            trans.set_address(rand()%N);
+            trans.set_data_length(4);
+            trans.set_streaming_width(4);
+            trans.set_command(tlm::TLM_WRITE_COMMAND);
+            trans.set_data_ptr(reinterpret_cast<unsigned char*>(&data[i%16]));
+            trans.set_response_status( tlm::TLM_INCOMPLETE_RESPONSE );
+
+            sc_time delay = sc_time(10, SC_NS);
+
+            iSocket->b_transport(trans, delay);
+
+            if (trans.is_response_error())
+            {
+                SC_REPORT_FATAL(name(), "Response error");
+            }
+
+            wait(delay);
+
+            cout << "\033[1;31m("
+                 << name()
+                 << ")@"  << setfill(' ') << setw(12) << sc_time_stamp()
+                 << ": " << setw(12) << "Write to "
+                 << "Addr = " << setfill('0') << setw(8)
+                 << dec << trans.get_address()
+                 << " Data = " << "0x" << setfill('0') << setw(8)
+                 << hex << data[i%16] << "(b_transport) \033[0m" << endl;
+        }
+    }
+};
+
+SC_MODULE(Target)
+{
+    public:
+    tlm_utils::simple_target_socket<Target> tSocket;
+
+    private:
+    unsigned char mem[512];
+
+    public:
+    SC_HAS_PROCESS(Target);
+    Target(sc_module_name name, unsigned int bufferSize = 8) :
+         sc_module(name),
+         tSocket("tSocket")
+    {
+        tSocket.register_b_transport(this, &Target::b_transport);
+    }
+
+    virtual void b_transport(tlm::tlm_generic_payload& trans,
+                             sc_time& delay)
+    {
+        executeTransaction(trans);
+    }
+
+
+    // Common to b_transport and nb_transport
+    void executeTransaction(tlm::tlm_generic_payload& trans)
+    {
+        tlm::tlm_command cmd = trans.get_command();
+        sc_dt::uint64    adr = trans.get_address();
+        unsigned char*   ptr = trans.get_data_ptr();
+        unsigned int     len = trans.get_data_length();
+        unsigned char*   byt = trans.get_byte_enable_ptr();
+        unsigned int     wid = trans.get_streaming_width();
+
+
+        if (trans.get_address() >= 512) {
+            trans.set_response_status( tlm::TLM_ADDRESS_ERROR_RESPONSE );
+            return;
+        }
+        if (byt != 0) {
+            trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE );
+            return;
+        }
+        if (len > 4 || wid < len) {
+            trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
+            return;
+        }
+
+        if (cmd == tlm::TLM_READ_COMMAND)
+        {
+            memcpy(&mem[trans.get_address()], // destination
+                   trans.get_data_ptr(),      // source
+                   trans.get_data_length());  // size
+        }
+        else if (cmd == tlm::TLM_WRITE_COMMAND)
+        {
+            memcpy(trans.get_data_ptr(),      // destination
+                   &mem[trans.get_address()], // source
+                   trans.get_data_length());  // size
+        }
+
+        cout << "\033[1;32m("
+             << name()
+             << ")@"  << setfill(' ') << setw(12) << sc_time_stamp()
+             << ": " << setw(12) << (cmd ? "Exec. Write " : "Exec. Read ")
+             << "Addr = " << setfill('0') << setw(8) << dec << adr
+             << " Data = " << "0x" << setfill('0') << setw(8) << hex
+             << *reinterpret_cast<int*>(ptr)
+             << "\033[0m" << endl;
+
+        trans.set_response_status( tlm::TLM_OK_RESPONSE );
+    }
+
+};
+
+template<unsigned int I, unsigned int T>
+SC_MODULE(Interconnect)
+{
+    public:
+    tlm_utils::simple_target_socket_tagged<Interconnect> tSocket[T];
+    tlm_utils::simple_initiator_socket_tagged<Interconnect> iSocket[I];
+
+    SC_CTOR(Interconnect)
+    {
+        for (unsigned int i = 0; i < T; i++) {
+            tSocket[i].register_b_transport(this,
+                                            &Interconnect::b_transport,
+                                            i);
+        }
+    }
+
+    private:
+
+    int routeFW(int inPort, tlm::tlm_generic_payload &trans)
+    {
+        int outPort = 0;
+
+        // Memory map implementation:
+        if (trans.get_address() < 512) {
+            outPort = 0;
+        } else if (trans.get_address() >= 512 && trans.get_address() < 1024) {
+            // Correct Address:
+            trans.set_address(trans.get_address() - 512);
+            outPort = 1;
+        } else {
+            trans.set_response_status( tlm::TLM_ADDRESS_ERROR_RESPONSE );
+        }
+
+        return outPort;
+    }
+
+    virtual void b_transport( int id,
+                              tlm::tlm_generic_payload& trans,
+                              sc_time& delay )
+    {
+        sc_assert(id < T);
+        int outPort = routeFW(id, trans);
+        iSocket[outPort]->b_transport(trans, delay);
+    }
+
+};
+
+
+int
+sc_main (int __attribute__((unused)) sc_argc,
+             char __attribute__((unused)) *sc_argv[])
+{
+
+    Initiator * cpu1   = new Initiator("C1");
+    Initiator * cpu2   = new Initiator("C2");
+
+    Target * memory1   = new Target("M1");
+    Target * memory2   = new Target("M2");
+
+    Interconnect<2,2> * bus = new Interconnect<2,2>("B1");
+
+    cpu1->iSocket.bind(bus->tSocket[0]);
+    cpu2->iSocket.bind(bus->tSocket[1]);
+    bus->iSocket[0].bind(memory1->tSocket);
+    bus->iSocket[1].bind(memory2->tSocket);
+
+    sc_core::sc_start();
+
+    return 0;
+}